databricks-sql-connector 3.0.0b1__tar.gz → 3.0.1__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 (57) hide show
  1. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/CHANGELOG.md +29 -5
  2. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/PKG-INFO +7 -9
  3. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/README.md +2 -3
  4. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/pyproject.toml +6 -9
  5. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/__init__.py +2 -4
  6. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/retry.py +11 -3
  7. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/client.py +135 -47
  8. databricks_sql_connector-3.0.1/src/databricks/sql/parameters/__init__.py +15 -0
  9. databricks_sql_connector-3.0.1/src/databricks/sql/parameters/native.py +606 -0
  10. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/thrift_api/TCLIService/ttypes.py +6 -6
  11. databricks_sql_connector-3.0.1/src/databricks/sql/thrift_api/__init__.py +0 -0
  12. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/thrift_backend.py +12 -5
  13. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/types.py +3 -1
  14. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/utils.py +94 -180
  15. databricks_sql_connector-3.0.1/src/databricks/sqlalchemy/README.sqlalchemy.md +204 -0
  16. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/_parse.py +4 -1
  17. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/base.py +1 -1
  18. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test/_future.py +20 -0
  19. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test/_regression.py +5 -2
  20. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test/_unsupported.py +16 -0
  21. databricks_sql_connector-3.0.1/src/databricks/sqlalchemy/test/overrides/_componentreflectiontest.py +189 -0
  22. databricks_sql_connector-3.0.1/src/databricks/sqlalchemy/test/overrides/_ctetest.py +33 -0
  23. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test/test_suite.py +0 -13
  24. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test_local/e2e/test_basic.py +43 -15
  25. databricks_sql_connector-3.0.0b1/src/databricks/sqlalchemy/_pytest.ini +0 -3
  26. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/LICENSE +0 -0
  27. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/__init__.py +0 -0
  28. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/__init__.py +0 -0
  29. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/auth.py +0 -0
  30. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/authenticators.py +0 -0
  31. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/endpoint.py +0 -0
  32. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/oauth.py +0 -0
  33. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
  34. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/auth/thrift_http_client.py +0 -0
  35. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/cloudfetch/download_manager.py +0 -0
  36. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/cloudfetch/downloader.py +0 -0
  37. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/exc.py +0 -0
  38. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/experimental/__init__.py +0 -0
  39. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
  40. /databricks_sql_connector-3.0.0b1/src/databricks/sql/thrift_api/__init__.py → /databricks_sql_connector-3.0.1/src/databricks/sql/parameters/py.typed +0 -0
  41. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
  42. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
  43. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
  44. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
  45. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/README.tests.md +0 -0
  46. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/__init__.py +0 -0
  47. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/_ddl.py +0 -0
  48. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/_types.py +0 -0
  49. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/pytest.ini +0 -0
  50. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/requirements.py +0 -0
  51. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/setup.cfg +0 -0
  52. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test/_extra.py +0 -0
  53. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test/conftest.py +0 -0
  54. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test_local/__init__.py +0 -0
  55. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test_local/e2e/MOCK_DATA.xlsx +0 -0
  56. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test_local/test_parsing.py +0 -0
  57. {databricks_sql_connector-3.0.0b1 → databricks_sql_connector-3.0.1}/src/databricks/sqlalchemy/test_local/test_types.py +0 -0
@@ -1,11 +1,35 @@
1
1
  # Release History
2
2
 
3
- ## 2.9.4 (Unreleased)
4
-
5
- - Other: Introduce SQLAlchemy dialect compliance test suite and enumerate all excluded tests
6
- - Add integration tests for Databricks UC Volumes ingestion queries
7
- - Add `_retry_max_redirects` config
3
+ ## 3.0.1 (2023-12-01)
4
+
5
+ - Other: updated docstring comment about default parameterization approach (#287)
6
+ - Other: added tests for reading complex types and revised docstrings and type hints (#293)
7
+ - Fix: SQLAlchemy dialect raised DeprecationWarning due to `dbapi` classmethod (#294)
8
+ - Fix: SQLAlchemy dialect could not reflect TIMESTAMP_NTZ columns (#296)
9
+
10
+ ## 3.0.0 (2023-11-17)
11
+
12
+ - Remove support for Python 3.7
13
+ - Add support for native parameterized SQL queries. Requires DBR 14.2 and above. See docs/parameters.md for more info.
14
+ - Completely rewritten SQLAlchemy dialect
15
+ - Adds support for SQLAlchemy >= 2.0 and drops support for SQLAlchemy 1.x
16
+ - Full e2e test coverage of all supported features
17
+ - Detailed usage notes in `README.sqlalchemy.md`
18
+ - Adds support for:
19
+ - New types: `TIME`, `TIMESTAMP`, `TIMESTAMP_NTZ`, `TINYINT`
20
+ - `Numeric` type scale and precision, like `Numeric(10,2)`
21
+ - Reading and writing `PrimaryKeyConstraint` and `ForeignKeyConstraint`
22
+ - Reading and writing composite keys
23
+ - Reading and writing from views
24
+ - Writing `Identity` to tables (i.e. autoincrementing primary keys)
25
+ - `LIMIT` and `OFFSET` for paging through results
26
+ - Caching metadata calls
8
27
  - Enable cloud fetch by default. To disable, set `use_cloud_fetch=False` when building `databricks.sql.client`.
28
+ - Add integration tests for Databricks UC Volumes ingestion queries
29
+ - Retries:
30
+ - Add `_retry_max_redirects` config
31
+ - Set `_enable_v3_retries=True` and warn if users override it
32
+ - Security: bump minimum pyarrow version to 14.0.1 (CVE-2023-47248)
9
33
 
10
34
  ## 2.9.3 (2023-08-24)
11
35
 
@@ -1,29 +1,28 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: databricks-sql-connector
3
- Version: 3.0.0b1
3
+ Version: 3.0.1
4
4
  Summary: Databricks SQL Connector for Python
5
5
  License: Apache-2.0
6
6
  Author: Databricks
7
7
  Author-email: databricks-sql-connector-maintainers@databricks.com
8
- Requires-Python: >=3.7.1,<4.0.0
8
+ Requires-Python: >=3.8.0,<4.0.0
9
9
  Classifier: License :: OSI Approved :: Apache Software License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.8
12
12
  Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
15
16
  Provides-Extra: alembic
16
17
  Provides-Extra: sqlalchemy
17
18
  Requires-Dist: alembic (>=1.0.11,<2.0.0) ; extra == "alembic"
18
19
  Requires-Dist: lz4 (>=4.0.2,<5.0.0)
19
- Requires-Dist: numpy (>=1.16.6) ; python_version >= "3.7" and python_version < "3.11"
20
+ Requires-Dist: numpy (>=1.16.6) ; python_version >= "3.8" and python_version < "3.11"
20
21
  Requires-Dist: numpy (>=1.23.4) ; python_version >= "3.11"
21
22
  Requires-Dist: oauthlib (>=3.1.0,<4.0.0)
22
23
  Requires-Dist: openpyxl (>=3.0.10,<4.0.0)
23
- Requires-Dist: pandas (>=1.2.5,<1.4.0) ; python_version >= "3.7" and python_version < "3.8"
24
24
  Requires-Dist: pandas (>=1.2.5,<3.0.0) ; python_version >= "3.8"
25
- Requires-Dist: pyarrow (>=10.0.1) ; python_version >= "3.11"
26
- Requires-Dist: pyarrow (>=6.0.0) ; python_version >= "3.7" and python_version < "3.11"
25
+ Requires-Dist: pyarrow (>=14.0.1,<15.0.0)
27
26
  Requires-Dist: requests (>=2.18.1,<3.0.0)
28
27
  Requires-Dist: sqlalchemy (>=2.0.21) ; extra == "sqlalchemy" or extra == "alembic"
29
28
  Requires-Dist: thrift (>=0.16.0,<0.17.0)
@@ -45,7 +44,7 @@ You are welcome to file an issue here for general use cases. You can also contac
45
44
 
46
45
  ## Requirements
47
46
 
48
- Python 3.7 or above is required.
47
+ Python 3.8 or above is required.
49
48
 
50
49
  ## Documentation
51
50
 
@@ -81,8 +80,7 @@ connection = sql.connect(
81
80
  access_token=access_token)
82
81
 
83
82
  cursor = connection.cursor()
84
-
85
- cursor.execute('SELECT * FROM RANGE(10)')
83
+ cursor.execute('SELECT :param `p`, * FROM RANGE(10)', {"param": "foo"})
86
84
  result = cursor.fetchall()
87
85
  for row in result:
88
86
  print(row)
@@ -11,7 +11,7 @@ You are welcome to file an issue here for general use cases. You can also contac
11
11
 
12
12
  ## Requirements
13
13
 
14
- Python 3.7 or above is required.
14
+ Python 3.8 or above is required.
15
15
 
16
16
  ## Documentation
17
17
 
@@ -47,8 +47,7 @@ connection = sql.connect(
47
47
  access_token=access_token)
48
48
 
49
49
  cursor = connection.cursor()
50
-
51
- cursor.execute('SELECT * FROM RANGE(10)')
50
+ cursor.execute('SELECT :param `p`, * FROM RANGE(10)', {"param": "foo"})
52
51
  result = cursor.fetchall()
53
52
  for row in result:
54
53
  print(row)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "databricks-sql-connector"
3
- version = "3.0.0b1"
3
+ version = "3.0.1"
4
4
  description = "Databricks SQL Connector for Python"
5
5
  authors = ["Databricks <databricks-sql-connector-maintainers@databricks.com>"]
6
6
  license = "Apache-2.0"
@@ -9,21 +9,18 @@ packages = [{ include = "databricks", from = "src" }]
9
9
  include = ["CHANGELOG.md"]
10
10
 
11
11
  [tool.poetry.dependencies]
12
- python = "^3.7.1"
12
+ python = "^3.8.0"
13
13
  thrift = "^0.16.0"
14
14
  pandas = [
15
- { version = ">=1.2.5,<1.4.0", python = ">=3.7,<3.8" },
16
- { version = ">=1.2.5,<3.0.0", python = ">=3.8" },
17
- ]
18
- pyarrow = [
19
- { version = ">=6.0.0", python = ">=3.7,<3.11" },
20
- { version = ">=10.0.1", python = ">=3.11" },
15
+ { version = ">=1.2.5,<3.0.0", python = ">=3.8" }
21
16
  ]
17
+ pyarrow = "^14.0.1"
18
+
22
19
  lz4 = "^4.0.2"
23
20
  requests = "^2.18.1"
24
21
  oauthlib = "^3.1.0"
25
22
  numpy = [
26
- { version = ">=1.16.6", python = ">=3.7,<3.11" },
23
+ { version = ">=1.16.6", python = ">=3.8,<3.11" },
27
24
  { version = ">=1.23.4", python = ">=3.11" },
28
25
  ]
29
26
  sqlalchemy = { version = ">=2.0.21", optional = true }
@@ -6,9 +6,7 @@ from databricks.sql.exc import *
6
6
  apilevel = "2.0"
7
7
  threadsafety = 1 # Threads may share the module, but not connections.
8
8
 
9
- # Python extended format codes, e.g. ...WHERE name=%(name)s
10
- # Note that when we switch to ParameterApproach.NATIVE, paramstyle will be `named`
11
- paramstyle = "pyformat"
9
+ paramstyle = "named"
12
10
 
13
11
 
14
12
  class DBAPITypeObject(object):
@@ -31,7 +29,7 @@ DATETIME = DBAPITypeObject("timestamp")
31
29
  DATE = DBAPITypeObject("date")
32
30
  ROWID = DBAPITypeObject()
33
31
 
34
- __version__ = "3.0.0b1"
32
+ __version__ = "3.0.1"
35
33
  USER_AGENT_NAME = "PyDatabricksSqlConnector"
36
34
 
37
35
  # These two functions are pyhive legacy
@@ -30,6 +30,7 @@ class CommandType(Enum):
30
30
  EXECUTE_STATEMENT = "ExecuteStatement"
31
31
  CLOSE_SESSION = "CloseSession"
32
32
  CLOSE_OPERATION = "CloseOperation"
33
+ GET_OPERATION_STATUS = "GetOperationStatus"
33
34
  OTHER = "Other"
34
35
 
35
36
  @classmethod
@@ -314,9 +315,9 @@ class DatabricksRetryPolicy(Retry):
314
315
  2. The request received a 501 (Not Implemented) status code
315
316
  Because this request can never succeed.
316
317
  3. The request received a 404 (Not Found) code and the request CommandType
317
- was CloseSession or CloseOperation. This code indicates that the session
318
- or cursor was already closed. Further retries will always return the same
319
- code.
318
+ was GetOperationStatus, CloseSession or CloseOperation. This code indicates
319
+ that the command, session or cursor was already closed. Further retries will
320
+ always return the same code.
320
321
  4. The request CommandType was ExecuteStatement and the HTTP code does not
321
322
  appear in the default status_forcelist or force_dangerous_codes list. By
322
323
  default, this means ExecuteStatement is only retried for codes 429 and 503.
@@ -343,6 +344,13 @@ class DatabricksRetryPolicy(Retry):
343
344
  if not self._is_method_retryable(method): # type: ignore
344
345
  return False, "Only POST requests are retried"
345
346
 
347
+ # Request failed with 404 and was a GetOperationStatus. This is not recoverable. Don't retry.
348
+ if status_code == 404 and self.command_type == CommandType.GET_OPERATION_STATUS:
349
+ return (
350
+ False,
351
+ "GetOperationStatus received 404 code from Databricks. Operation was canceled.",
352
+ )
353
+
346
354
  # Request failed with 404 because CloseSession returns 404 if you repeat the request.
347
355
  if (
348
356
  status_code == 404
@@ -1,10 +1,11 @@
1
- from typing import Dict, Tuple, List, Optional, Any, Union
1
+ from typing import Dict, Tuple, List, Optional, Any, Union, Sequence
2
2
 
3
3
  import pandas
4
4
  import pyarrow
5
5
  import requests
6
6
  import json
7
7
  import os
8
+ import decimal
8
9
 
9
10
  from databricks.sql import __version__
10
11
  from databricks.sql import *
@@ -18,10 +19,21 @@ from databricks.sql.thrift_backend import ThriftBackend
18
19
  from databricks.sql.utils import (
19
20
  ExecuteResponse,
20
21
  ParamEscaper,
21
- named_parameters_to_tsparkparams,
22
22
  inject_parameters,
23
+ transform_paramstyle,
24
+ )
25
+ from databricks.sql.parameters.native import (
26
+ DbsqlParameterBase,
27
+ TDbsqlParameter,
28
+ TParameterDict,
29
+ TParameterSequence,
30
+ TParameterCollection,
31
+ ParameterStructure,
32
+ dbsql_parameter_from_primitive,
23
33
  ParameterApproach,
24
34
  )
35
+
36
+
25
37
  from databricks.sql.types import Row
26
38
  from databricks.sql.auth.auth import get_python_sql_connector_auth_provider
27
39
  from databricks.sql.experimental.oauth_persistence import OAuthPersistence
@@ -49,6 +61,7 @@ class Connection:
49
61
  session_configuration: Dict[str, Any] = None,
50
62
  catalog: Optional[str] = None,
51
63
  schema: Optional[str] = None,
64
+ _use_arrow_native_complex_types: Optional[bool] = True,
52
65
  **kwargs,
53
66
  ) -> None:
54
67
  """
@@ -62,11 +75,13 @@ class Connection:
62
75
  Http Bearer access token, e.g. Databricks Personal Access Token.
63
76
  Unless if you use auth_type=`databricks-oauth` you need to pass `access_token.
64
77
  Examples:
78
+ ```
65
79
  connection = sql.connect(
66
80
  server_hostname='dbc-12345.staging.cloud.databricks.com',
67
81
  http_path='sql/protocolv1/o/6789/12abc567',
68
82
  access_token='dabpi12345678'
69
83
  )
84
+ ```
70
85
  :param http_headers: An optional list of (k, v) pairs that will be set as Http headers on every request
71
86
  :param session_configuration: An optional dictionary of Spark session parameters. Defaults to None.
72
87
  Execute the SQL command `SET -v` to get a full list of available commands.
@@ -74,12 +89,12 @@ class Connection:
74
89
  :param schema: An optional initial schema to use. Requires DBR version 9.0+
75
90
 
76
91
  Other Parameters:
77
- use_inline_params: `boolean`, optional (default is True)
92
+ use_inline_params: `boolean` | str, optional (default is False)
78
93
  When True, parameterized calls to cursor.execute() will try to render parameter values inline with the
79
94
  query text instead of using native bound parameters supported in DBR 14.1 and above. This connector will attempt to
80
- sanitise parameterized inputs to prevent SQL injection. Before you can switch this to False, you must
81
- update your queries to use the PEP-249 `named` paramstyle instead of the `pyformat` paramstyle used
82
- in INLINE mode.
95
+ sanitise parameterized inputs to prevent SQL injection. The inline parameter approach is maintained for
96
+ legacy purposes and will be deprecated in a future release. When this parameter is `True` you will see
97
+ a warning log message. To suppress this log message, set `use_inline_params="silent"`.
83
98
  auth_type: `str`, optional
84
99
  `databricks-oauth` : to use oauth with fine-grained permission scopes, set to `databricks-oauth`.
85
100
  This is currently in private preview for Databricks accounts on AWS.
@@ -127,6 +142,7 @@ class Connection:
127
142
  own implementation of OAuthPersistence.
128
143
 
129
144
  Examples:
145
+ ```
130
146
  # for development only
131
147
  from databricks.sql.experimental.oauth_persistence import DevOnlyFilePersistence
132
148
 
@@ -136,8 +152,14 @@ class Connection:
136
152
  auth_type="databricks-oauth",
137
153
  experimental_oauth_persistence=DevOnlyFilePersistence("~/dev-oauth.json")
138
154
  )
139
-
140
-
155
+ ```
156
+ :param _use_arrow_native_complex_types: `bool`, optional
157
+ Controls whether a complex type field value is returned as a string or as a native Arrow type. Defaults to True.
158
+ When True:
159
+ MAP is returned as List[Tuple[str, Any]]
160
+ STRUCT is returned as Dict[str, Any]
161
+ ARRAY is returned as numpy.ndarray
162
+ When False, complex types are returned as a strings. These are generally deserializable as JSON.
141
163
  """
142
164
 
143
165
  # Internal arguments in **kwargs:
@@ -168,9 +190,6 @@ class Connection:
168
190
  # _disable_pandas
169
191
  # In case the deserialisation through pandas causes any issues, it can be disabled with
170
192
  # this flag.
171
- # _use_arrow_native_complex_types
172
- # DBR will return native Arrow types for structs, arrays and maps instead of Arrow strings
173
- # (True by default)
174
193
  # _use_arrow_native_decimals
175
194
  # Databricks runtime will return native Arrow types for decimals instead of Arrow strings
176
195
  # (True by default)
@@ -209,6 +228,7 @@ class Connection:
209
228
  http_path,
210
229
  (http_headers or []) + base_headers,
211
230
  auth_provider,
231
+ _use_arrow_native_complex_types=_use_arrow_native_complex_types,
212
232
  **kwargs,
213
233
  )
214
234
 
@@ -222,8 +242,36 @@ class Connection:
222
242
  logger.info("Successfully opened session " + str(self.get_session_id_hex()))
223
243
  self._cursors = [] # type: List[Cursor]
224
244
 
225
- self._suppress_inline_warning = "use_inline_params" in kwargs
226
- self.use_inline_params = kwargs.get("use_inline_params", True)
245
+ self.use_inline_params = self._set_use_inline_params_with_warning(
246
+ kwargs.get("use_inline_params", False)
247
+ )
248
+
249
+ def _set_use_inline_params_with_warning(self, value: Union[bool, str]):
250
+ """Valid values are True, False, and "silent"
251
+
252
+ False: Use native parameters
253
+ True: Use inline parameters and log a warning
254
+ "silent": Use inline parameters and don't log a warning
255
+ """
256
+
257
+ if value is False:
258
+ return False
259
+
260
+ if value not in [True, "silent"]:
261
+ raise ValueError(
262
+ f"Invalid value for use_inline_params: {value}. "
263
+ + 'Valid values are True, False, and "silent"'
264
+ )
265
+
266
+ if value is True:
267
+ logger.warning(
268
+ "Parameterised queries executed with this client will use the inline parameter approach."
269
+ "This approach will be deprecated in a future release. Consider using native parameters."
270
+ "Learn more: https://github.com/databricks/databricks-sql-python/tree/main/docs/parameters.md"
271
+ 'To suppress this warning, set use_inline_params="silent"'
272
+ )
273
+
274
+ return value
227
275
 
228
276
  def __enter__(self):
229
277
  return self
@@ -377,7 +425,7 @@ class Cursor:
377
425
  raise Error("There is no active result set")
378
426
 
379
427
  def _determine_parameter_approach(
380
- self, params: Optional[Union[List, Dict[str, Any]]] = None
428
+ self, params: Optional[TParameterCollection]
381
429
  ) -> ParameterApproach:
382
430
  """Encapsulates the logic for choosing whether to send parameters in native vs inline mode
383
431
 
@@ -394,34 +442,60 @@ class Cursor:
394
442
  if params is None:
395
443
  return ParameterApproach.NONE
396
444
 
397
- server_supports_native_approach = (
398
- self.connection.server_parameterized_queries_enabled(
399
- self.connection.protocol_version
400
- )
401
- )
402
-
403
445
  if self.connection.use_inline_params:
404
- if (
405
- server_supports_native_approach
406
- and not self.connection._suppress_inline_warning
407
- ):
408
- logger.warning(
409
- "This query will be executed with inline parameters."
410
- "Consider using native parameters."
411
- "Learn more: https://github.com/databricks/databricks-sql-python/tree/main/docs/parameters.md"
412
- "To suppress this warning, pass use_inline_params=True when creating the connection."
413
- )
414
446
  return ParameterApproach.INLINE
415
447
 
416
- elif server_supports_native_approach:
448
+ else:
417
449
  return ParameterApproach.NATIVE
450
+
451
+ def _all_dbsql_parameters_are_named(self, params: List[TDbsqlParameter]) -> bool:
452
+ """Return True if all members of the list have a non-null .name attribute"""
453
+ return all([i.name is not None for i in params])
454
+
455
+ def _normalize_tparametersequence(
456
+ self, params: TParameterSequence
457
+ ) -> List[TDbsqlParameter]:
458
+ """Retains the same order as the input list."""
459
+
460
+ output: List[TDbsqlParameter] = []
461
+ for p in params:
462
+ if isinstance(p, DbsqlParameterBase):
463
+ output.append(p) # type: ignore
464
+ else:
465
+ output.append(dbsql_parameter_from_primitive(value=p)) # type: ignore
466
+
467
+ return output
468
+
469
+ def _normalize_tparameterdict(
470
+ self, params: TParameterDict
471
+ ) -> List[TDbsqlParameter]:
472
+ return [
473
+ dbsql_parameter_from_primitive(value=value, name=name)
474
+ for name, value in params.items()
475
+ ]
476
+
477
+ def _normalize_tparametercollection(
478
+ self, params: Optional[TParameterCollection]
479
+ ) -> List[TDbsqlParameter]:
480
+ if params is None:
481
+ return []
482
+ if isinstance(params, dict):
483
+ return self._normalize_tparameterdict(params)
484
+ if isinstance(params, Sequence):
485
+ return self._normalize_tparametersequence(list(params))
486
+
487
+ def _determine_parameter_structure(
488
+ self,
489
+ parameters: List[TDbsqlParameter],
490
+ ) -> ParameterStructure:
491
+ all_named = self._all_dbsql_parameters_are_named(parameters)
492
+ if all_named:
493
+ return ParameterStructure.NAMED
418
494
  else:
419
- raise NotSupportedError(
420
- "Parameterized operations are not supported by this server. DBR 14.1 is required."
421
- )
495
+ return ParameterStructure.POSITIONAL
422
496
 
423
497
  def _prepare_inline_parameters(
424
- self, stmt: str, params: Optional[Union[List, Dict[str, Any]]]
498
+ self, stmt: str, params: Optional[Union[Sequence, Dict[str, Any]]]
425
499
  ) -> Tuple[str, List]:
426
500
  """Return a statement and list of native parameters to be passed to thrift_backend for execution
427
501
 
@@ -446,7 +520,10 @@ class Cursor:
446
520
  return rendered_statement, NO_NATIVE_PARAMS
447
521
 
448
522
  def _prepare_native_parameters(
449
- self, stmt: str, params: Optional[Union[List[Any], Dict[str, Any]]]
523
+ self,
524
+ stmt: str,
525
+ params: List[TDbsqlParameter],
526
+ param_structure: ParameterStructure,
450
527
  ) -> Tuple[str, List[TSparkParameter]]:
451
528
  """Return a statement and a list of native parameters to be passed to thrift_backend for execution
452
529
 
@@ -466,9 +543,12 @@ class Cursor:
466
543
  """
467
544
 
468
545
  stmt = stmt
469
- params = named_parameters_to_tsparkparams(params) # type: ignore
546
+ output = [
547
+ p.as_tspark_param(named=param_structure == ParameterStructure.NAMED)
548
+ for p in params
549
+ ]
470
550
 
471
- return stmt, params
551
+ return stmt, output
472
552
 
473
553
  def _close_and_clear_active_result_set(self):
474
554
  try:
@@ -627,24 +707,27 @@ class Cursor:
627
707
  def execute(
628
708
  self,
629
709
  operation: str,
630
- parameters: Optional[Union[List[Any], Dict[str, Any]]] = None,
710
+ parameters: Optional[TParameterCollection] = None,
631
711
  ) -> "Cursor":
632
712
  """
633
713
  Execute a query and wait for execution to complete.
634
714
 
635
715
  The parameterisation behaviour of this method depends on which parameter approach is used:
636
- - With INLINE mode (default), parameters are rendered inline with the query text
637
- - With NATIVE mode, parameters are sent to the server separately for binding
716
+ - With INLINE mode, parameters are rendered inline with the query text
717
+ - With NATIVE mode (default), parameters are sent to the server separately for binding
638
718
 
639
719
  This behaviour is controlled by the `use_inline_params` argument passed when building a connection.
640
720
 
641
- The syntax for these approaches is different:
721
+ The paramstyle for these approaches is different:
642
722
 
643
- If the connection was instantiated with use_inline_params=False, then parameters
644
- should be given in PEP-249 `named` paramstyle like :param_name
723
+ If the connection was instantiated with use_inline_params=False (default), then parameters
724
+ should be given in PEP-249 `named` paramstyle like :param_name. Parameters passed by positionally
725
+ are indicated using a `?` in the query text.
645
726
 
646
- If the connection was instantiated with use_inline_params=True (default), then parameters
647
- should be given in PEP-249 `pyformat` paramstyle like %(param_name)s
727
+ If the connection was instantiated with use_inline_params=True, then parameters
728
+ should be given in PEP-249 `pyformat` paramstyle like %(param_name)s. Parameters passed by positionally
729
+ are indicated using a `%s` marker in the query. Note: this approach is not recommended as it can break
730
+ your SQL query syntax and will be removed in a future release.
648
731
 
649
732
  ```python
650
733
  inline_operation = "SELECT * FROM table WHERE field = %(some_value)s"
@@ -668,8 +751,13 @@ class Cursor:
668
751
  operation, parameters
669
752
  )
670
753
  elif param_approach == ParameterApproach.NATIVE:
754
+ normalized_parameters = self._normalize_tparametercollection(parameters)
755
+ param_structure = self._determine_parameter_structure(normalized_parameters)
756
+ transformed_operation = transform_paramstyle(
757
+ operation, normalized_parameters, param_structure # type: ignore
758
+ )
671
759
  prepared_operation, prepared_params = self._prepare_native_parameters(
672
- operation, parameters
760
+ transformed_operation, normalized_parameters, param_structure
673
761
  )
674
762
 
675
763
  self._check_not_closed()
@@ -0,0 +1,15 @@
1
+ from databricks.sql.parameters.native import (
2
+ IntegerParameter,
3
+ StringParameter,
4
+ BigIntegerParameter,
5
+ BooleanParameter,
6
+ DateParameter,
7
+ DoubleParameter,
8
+ FloatParameter,
9
+ VoidParameter,
10
+ SmallIntParameter,
11
+ TimestampParameter,
12
+ TimestampNTZParameter,
13
+ TinyIntParameter,
14
+ DecimalParameter,
15
+ )