databricks-sql-connector 3.1.1__tar.gz → 3.2.0__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 (58) hide show
  1. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/CHANGELOG.md +12 -1
  2. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/PKG-INFO +3 -3
  3. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/pyproject.toml +3 -3
  4. {databricks_sql_connector-3.1.1/src/databricks/sql/parameters → databricks_sql_connector-3.2.0/src/databricks}/py.typed +0 -0
  5. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/__init__.py +8 -2
  6. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/oauth.py +1 -1
  7. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/thrift_http_client.py +6 -11
  8. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/client.py +4 -2
  9. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/cloudfetch/download_manager.py +49 -0
  10. databricks_sql_connector-3.2.0/src/databricks/sql/thrift_api/__init__.py +0 -0
  11. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/utils.py +35 -2
  12. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/_ddl.py +5 -3
  13. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test_local/test_ddl.py +2 -1
  14. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/LICENSE +0 -0
  15. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/README.md +0 -0
  16. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/__init__.py +0 -0
  17. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/__init__.py +0 -0
  18. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/auth.py +0 -0
  19. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/authenticators.py +0 -0
  20. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/endpoint.py +0 -0
  21. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
  22. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/auth/retry.py +0 -0
  23. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/cloudfetch/downloader.py +0 -0
  24. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/exc.py +0 -0
  25. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/experimental/__init__.py +0 -0
  26. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
  27. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/parameters/__init__.py +0 -0
  28. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/parameters/native.py +0 -0
  29. /databricks_sql_connector-3.1.1/src/databricks/sql/thrift_api/__init__.py → /databricks_sql_connector-3.2.0/src/databricks/sql/parameters/py.typed +0 -0
  30. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
  31. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
  32. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
  33. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
  34. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/thrift_api/TCLIService/ttypes.py +0 -0
  35. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/thrift_backend.py +0 -0
  36. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sql/types.py +0 -0
  37. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/README.sqlalchemy.md +0 -0
  38. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/README.tests.md +0 -0
  39. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/__init__.py +0 -0
  40. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/_parse.py +0 -0
  41. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/_types.py +0 -0
  42. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/base.py +0 -0
  43. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/requirements.py +0 -0
  44. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/setup.cfg +0 -0
  45. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/_extra.py +0 -0
  46. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/_future.py +0 -0
  47. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/_regression.py +0 -0
  48. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/_unsupported.py +0 -0
  49. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/conftest.py +0 -0
  50. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/overrides/_componentreflectiontest.py +0 -0
  51. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/overrides/_ctetest.py +0 -0
  52. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test/test_suite.py +0 -0
  53. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test_local/__init__.py +0 -0
  54. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test_local/conftest.py +0 -0
  55. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test_local/e2e/MOCK_DATA.xlsx +0 -0
  56. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test_local/e2e/test_basic.py +0 -0
  57. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test_local/test_parsing.py +0 -0
  58. {databricks_sql_connector-3.1.1 → databricks_sql_connector-3.2.0}/src/databricks/sqlalchemy/test_local/test_types.py +0 -0
@@ -1,6 +1,17 @@
1
1
  # Release History
2
2
 
3
- # x.x.x (TBD)
3
+ # 3.2.0 (2024-06-06)
4
+
5
+ - Update proxy authentication (databricks/databricks-sql-python#354 by @amir-haroun)
6
+ - Relax `pyarrow` pin (databricks/databricks-sql-python#389 by @dhirschfeld)
7
+ - Fix error logging in OAuth manager (databricks/databricks-sql-python#269 by @susodapop)
8
+ - SQLAlchemy: enable delta.feature.allowColumnDefaults for all tables (databricks/databricks-sql-python#343 by @dhirschfeld)
9
+ - Update `thrift` dependency (databricks/databricks-sql-python#397 by @m1n0)
10
+
11
+ # 3.1.2 (2024-04-18)
12
+
13
+ - Remove broken cookie code (#379)
14
+ - Small typing fixes (#382, #384 thanks @wyattscarpenter)
4
15
 
5
16
  # 3.1.1 (2024-03-19)
6
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: databricks-sql-connector
3
- Version: 3.1.1
3
+ Version: 3.2.0
4
4
  Summary: Databricks SQL Connector for Python
5
5
  License: Apache-2.0
6
6
  Author: Databricks
@@ -22,10 +22,10 @@ Requires-Dist: numpy (>=1.23.4) ; python_version >= "3.11"
22
22
  Requires-Dist: oauthlib (>=3.1.0,<4.0.0)
23
23
  Requires-Dist: openpyxl (>=3.0.10,<4.0.0)
24
24
  Requires-Dist: pandas (>=1.2.5,<2.2.0) ; python_version >= "3.8"
25
- Requires-Dist: pyarrow (>=14.0.1,<15.0.0)
25
+ Requires-Dist: pyarrow (>=14.0.1,<17)
26
26
  Requires-Dist: requests (>=2.18.1,<3.0.0)
27
27
  Requires-Dist: sqlalchemy (>=2.0.21) ; extra == "sqlalchemy" or extra == "alembic"
28
- Requires-Dist: thrift (>=0.16.0,<0.17.0)
28
+ Requires-Dist: thrift (>=0.16.0,<0.21.0)
29
29
  Requires-Dist: urllib3 (>=1.26)
30
30
  Project-URL: Bug Tracker, https://github.com/databricks/databricks-sql-python/issues
31
31
  Project-URL: Homepage, https://github.com/databricks/databricks-sql-python
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "databricks-sql-connector"
3
- version = "3.1.1"
3
+ version = "3.2.0"
4
4
  description = "Databricks SQL Connector for Python"
5
5
  authors = ["Databricks <databricks-sql-connector-maintainers@databricks.com>"]
6
6
  license = "Apache-2.0"
@@ -10,11 +10,11 @@ include = ["CHANGELOG.md"]
10
10
 
11
11
  [tool.poetry.dependencies]
12
12
  python = "^3.8.0"
13
- thrift = "^0.16.0"
13
+ thrift = ">=0.16.0,<0.21.0"
14
14
  pandas = [
15
15
  { version = ">=1.2.5,<2.2.0", python = ">=3.8" }
16
16
  ]
17
- pyarrow = "^14.0.1"
17
+ pyarrow = ">=14.0.1,<17"
18
18
 
19
19
  lz4 = "^4.0.2"
20
20
  requests = "^2.18.1"
@@ -10,6 +10,12 @@ paramstyle = "named"
10
10
 
11
11
  import re
12
12
 
13
+ from typing import TYPE_CHECKING
14
+
15
+ if TYPE_CHECKING:
16
+ # Use this import purely for type annotations, a la https://mypy.readthedocs.io/en/latest/runtime_troubles.html#import-cycles
17
+ from .client import Connection
18
+
13
19
 
14
20
  class RedactUrlQueryParamsFilter(logging.Filter):
15
21
  pattern = re.compile(r"(\?|&)([\w-]+)=([^&]+)")
@@ -62,7 +68,7 @@ DATETIME = DBAPITypeObject("timestamp")
62
68
  DATE = DBAPITypeObject("date")
63
69
  ROWID = DBAPITypeObject()
64
70
 
65
- __version__ = "3.1.1"
71
+ __version__ = "3.2.0"
66
72
  USER_AGENT_NAME = "PyDatabricksSqlConnector"
67
73
 
68
74
  # These two functions are pyhive legacy
@@ -78,7 +84,7 @@ def TimestampFromTicks(ticks):
78
84
  return Timestamp(*time.localtime(ticks)[:6])
79
85
 
80
86
 
81
- def connect(server_hostname, http_path, access_token=None, **kwargs):
87
+ def connect(server_hostname, http_path, access_token=None, **kwargs) -> "Connection":
82
88
  from .client import Connection
83
89
 
84
90
  return Connection(server_hostname, http_path, access_token, **kwargs)
@@ -125,7 +125,7 @@ class OAuthManager:
125
125
  logger.info(f"Port {port} is in use")
126
126
  last_error = e
127
127
  except Exception as e:
128
- logger.error("unexpected error", e)
128
+ logger.error("unexpected error: %s", e)
129
129
  if self.redirect_port is None:
130
130
  logger.error(
131
131
  f"Tried all the ports {self.port_range} for oauth redirect, but can't find free port"
@@ -14,7 +14,7 @@ from http.client import HTTPResponse
14
14
  from io import BytesIO
15
15
 
16
16
  from urllib3 import HTTPConnectionPool, HTTPSConnectionPool, ProxyManager
17
-
17
+ from urllib3.util import make_headers
18
18
  from databricks.sql.auth.retry import CommandType, DatabricksRetryPolicy
19
19
 
20
20
 
@@ -78,7 +78,7 @@ class THttpClient(thrift.transport.THttpClient.THttpClient):
78
78
  self.proxy_uri: str = proxy
79
79
  self.host = parsed.hostname
80
80
  self.port = parsed.port
81
- self.proxy_auth = self.basic_proxy_auth_header(parsed)
81
+ self.proxy_auth = self.basic_proxy_auth_headers(parsed)
82
82
  else:
83
83
  self.realhost = self.realport = self.proxy_auth = None
84
84
 
@@ -120,7 +120,7 @@ class THttpClient(thrift.transport.THttpClient.THttpClient):
120
120
  proxy_manager = ProxyManager(
121
121
  self.proxy_uri,
122
122
  num_pools=1,
123
- headers={"Proxy-Authorization": self.proxy_auth},
123
+ proxy_headers=self.proxy_auth,
124
124
  )
125
125
  self.__pool = proxy_manager.connection_from_host(
126
126
  host=self.realhost,
@@ -167,7 +167,7 @@ class THttpClient(thrift.transport.THttpClient.THttpClient):
167
167
  }
168
168
 
169
169
  if self.using_proxy() and self.scheme == "http" and self.proxy_auth is not None:
170
- headers["Proxy-Authorization" : self.proxy_auth]
170
+ headers.update(self.proxy_auth)
171
171
 
172
172
  if self.__custom_headers:
173
173
  custom_headers = {key: val for key, val in self.__custom_headers.items()}
@@ -189,20 +189,15 @@ class THttpClient(thrift.transport.THttpClient.THttpClient):
189
189
  self.message = self.__resp.reason
190
190
  self.headers = self.__resp.headers
191
191
 
192
- # Saves the cookie sent by the server response
193
- if "Set-Cookie" in self.headers:
194
- self.setCustomHeaders(dict("Cookie", self.headers["Set-Cookie"]))
195
-
196
192
  @staticmethod
197
- def basic_proxy_auth_header(proxy):
193
+ def basic_proxy_auth_headers(proxy):
198
194
  if proxy is None or not proxy.username:
199
195
  return None
200
196
  ap = "%s:%s" % (
201
197
  urllib.parse.unquote(proxy.username),
202
198
  urllib.parse.unquote(proxy.password),
203
199
  )
204
- cr = base64.b64encode(ap.encode()).strip()
205
- return "Basic " + six.ensure_str(cr)
200
+ return make_headers(proxy_basic_auth=ap)
206
201
 
207
202
  def set_retry_command_type(self, value: CommandType):
208
203
  """Pass the provided CommandType to the retry policy"""
@@ -271,7 +271,8 @@ class Connection:
271
271
 
272
272
  return value
273
273
 
274
- def __enter__(self):
274
+ # The ideal return type for this method is perhaps Self, but that was not added until 3.11, and we support pre-3.11 pythons, currently.
275
+ def __enter__(self) -> "Connection":
275
276
  return self
276
277
 
277
278
  def __exit__(self, exc_type, exc_value, traceback):
@@ -409,7 +410,8 @@ class Cursor:
409
410
  self.escaper = ParamEscaper()
410
411
  self.lastrowid = None
411
412
 
412
- def __enter__(self):
413
+ # The ideal return type for this method is perhaps Self, but that was not added until 3.11, and we support pre-3.11 pythons, currently.
414
+ def __enter__(self) -> "Cursor":
413
415
  return self
414
416
 
415
417
  def __exit__(self, exc_type, exc_value, traceback):
@@ -49,6 +49,11 @@ class ResultFileDownloadManager:
49
49
  for link in t_spark_arrow_result_links:
50
50
  if link.rowCount <= 0:
51
51
  continue
52
+ logger.debug(
53
+ "ResultFileDownloadManager.add_file_links: start offset {}, row count: {}".format(
54
+ link.startRowOffset, link.rowCount
55
+ )
56
+ )
52
57
  self.download_handlers.append(
53
58
  ResultSetDownloadHandler(self.downloadable_result_settings, link)
54
59
  )
@@ -88,6 +93,12 @@ class ResultFileDownloadManager:
88
93
 
89
94
  # Check (and wait) for download status
90
95
  if self._check_if_download_successful(handler):
96
+ link = handler.result_link
97
+ logger.debug(
98
+ "ResultFileDownloadManager: file found for row index {}: start {}, row count: {}".format(
99
+ next_row_offset, link.startRowOffset, link.rowCount
100
+ )
101
+ )
91
102
  # Buffer should be empty so set buffer to new ArrowQueue with result_file
92
103
  result = DownloadedFile(
93
104
  handler.result_file,
@@ -97,15 +108,32 @@ class ResultFileDownloadManager:
97
108
  self.download_handlers.pop(idx)
98
109
  # Return True upon successful download to continue loop and not force a retry
99
110
  return result
111
+ else:
112
+ logger.debug(
113
+ "ResultFileDownloadManager: cannot find file for row index {}".format(
114
+ next_row_offset
115
+ )
116
+ )
117
+
100
118
  # Download was not successful for next download item, force a retry
101
119
  self._shutdown_manager()
102
120
  return None
103
121
 
104
122
  def _remove_past_handlers(self, next_row_offset: int):
123
+ logger.debug(
124
+ "ResultFileDownloadManager: removing past handlers, current offset: {}".format(
125
+ next_row_offset
126
+ )
127
+ )
105
128
  # Any link in which its start to end range doesn't include the next row to be fetched does not need downloading
106
129
  i = 0
107
130
  while i < len(self.download_handlers):
108
131
  result_link = self.download_handlers[i].result_link
132
+ logger.debug(
133
+ "- checking result link: start {}, row count: {}, current offset: {}".format(
134
+ result_link.startRowOffset, result_link.rowCount, next_row_offset
135
+ )
136
+ )
109
137
  if result_link.startRowOffset + result_link.rowCount > next_row_offset:
110
138
  i += 1
111
139
  continue
@@ -113,10 +141,16 @@ class ResultFileDownloadManager:
113
141
 
114
142
  def _schedule_downloads(self):
115
143
  # Schedule downloads for all download handlers if not already scheduled.
144
+ logger.debug("ResultFileDownloadManager: schedule downloads")
116
145
  for handler in self.download_handlers:
117
146
  if handler.is_download_scheduled:
118
147
  continue
119
148
  try:
149
+ logger.debug(
150
+ "- start: {}, row count: {}".format(
151
+ handler.result_link.startRowOffset, handler.result_link.rowCount
152
+ )
153
+ )
120
154
  self.thread_pool.submit(handler.run)
121
155
  except Exception as e:
122
156
  logger.error(e)
@@ -124,13 +158,28 @@ class ResultFileDownloadManager:
124
158
  handler.is_download_scheduled = True
125
159
 
126
160
  def _find_next_file_index(self, next_row_offset: int):
161
+ logger.debug(
162
+ "ResultFileDownloadManager: trying to find file for row {}".format(
163
+ next_row_offset
164
+ )
165
+ )
127
166
  # Get the handler index of the next file in order
128
167
  next_indices = [
129
168
  i
130
169
  for i, handler in enumerate(self.download_handlers)
131
170
  if handler.is_download_scheduled
171
+ # TODO: shouldn't `next_row_offset` be tested against the range, not just start row offset?
132
172
  and handler.result_link.startRowOffset == next_row_offset
133
173
  ]
174
+
175
+ for i in next_indices:
176
+ link = self.download_handlers[i].result_link
177
+ logger.debug(
178
+ "- found file: start {}, row count {}".format(
179
+ link.startRowOffset, link.rowCount
180
+ )
181
+ )
182
+
134
183
  return next_indices[0] if len(next_indices) > 0 else None
135
184
 
136
185
  def _check_if_download_successful(self, handler: ResultSetDownloadHandler):
@@ -156,6 +156,19 @@ class CloudFetchQueue(ResultSetQueue):
156
156
  self.lz4_compressed = lz4_compressed
157
157
  self.description = description
158
158
 
159
+ logger.debug(
160
+ "Initialize CloudFetch loader, row set start offset: {}, file list:".format(
161
+ start_row_offset
162
+ )
163
+ )
164
+ if result_links is not None:
165
+ for result_link in result_links:
166
+ logger.debug(
167
+ "- start row offset: {}, row count: {}".format(
168
+ result_link.startRowOffset, result_link.rowCount
169
+ )
170
+ )
171
+
159
172
  self.download_manager = ResultFileDownloadManager(
160
173
  self.max_download_threads, self.lz4_compressed
161
174
  )
@@ -175,8 +188,10 @@ class CloudFetchQueue(ResultSetQueue):
175
188
  pyarrow.Table
176
189
  """
177
190
  if not self.table:
191
+ logger.debug("CloudFetchQueue: no more rows available")
178
192
  # Return empty pyarrow table to cause retry of fetch
179
193
  return self._create_empty_table()
194
+ logger.debug("CloudFetchQueue: trying to get {} next rows".format(num_rows))
180
195
  results = self.table.slice(0, 0)
181
196
  while num_rows > 0 and self.table:
182
197
  # Get remaining of num_rows or the rest of the current table, whichever is smaller
@@ -190,6 +205,8 @@ class CloudFetchQueue(ResultSetQueue):
190
205
  self.table = self._create_next_table()
191
206
  self.table_row_index = 0
192
207
  num_rows -= table_slice.num_rows
208
+
209
+ logger.debug("CloudFetchQueue: collected {} next rows".format(results.num_rows))
193
210
  return results
194
211
 
195
212
  def remaining_rows(self) -> pyarrow.Table:
@@ -214,11 +231,21 @@ class CloudFetchQueue(ResultSetQueue):
214
231
  return results
215
232
 
216
233
  def _create_next_table(self) -> Union[pyarrow.Table, None]:
234
+ logger.debug(
235
+ "CloudFetchQueue: Trying to get downloaded file for row {}".format(
236
+ self.start_row_index
237
+ )
238
+ )
217
239
  # Create next table by retrieving the logical next downloaded file, or return None to signal end of queue
218
240
  downloaded_file = self.download_manager.get_next_downloaded_file(
219
241
  self.start_row_index
220
242
  )
221
243
  if not downloaded_file:
244
+ logger.debug(
245
+ "CloudFetchQueue: Cannot find downloaded file for row {}".format(
246
+ self.start_row_index
247
+ )
248
+ )
222
249
  # None signals no more Arrow tables can be built from the remaining handlers if any remain
223
250
  return None
224
251
  arrow_table = create_arrow_table_from_arrow_file(
@@ -228,12 +255,18 @@ class CloudFetchQueue(ResultSetQueue):
228
255
  # The server rarely prepares the exact number of rows requested by the client in cloud fetch.
229
256
  # Subsequently, we drop the extraneous rows in the last file if more rows are retrieved than requested
230
257
  if arrow_table.num_rows > downloaded_file.row_count:
231
- self.start_row_index += downloaded_file.row_count
232
- return arrow_table.slice(0, downloaded_file.row_count)
258
+ arrow_table = arrow_table.slice(0, downloaded_file.row_count)
233
259
 
234
260
  # At this point, whether the file has extraneous rows or not, the arrow table should have the correct num rows
235
261
  assert downloaded_file.row_count == arrow_table.num_rows
236
262
  self.start_row_index += arrow_table.num_rows
263
+
264
+ logger.debug(
265
+ "CloudFetchQueue: Found downloaded file, row count: {}, new start offset: {}".format(
266
+ arrow_table.num_rows, self.start_row_index
267
+ )
268
+ )
269
+
237
270
  return arrow_table
238
271
 
239
272
  def _create_empty_table(self) -> pyarrow.Table:
@@ -16,13 +16,15 @@ class DatabricksIdentifierPreparer(compiler.IdentifierPreparer):
16
16
 
17
17
  class DatabricksDDLCompiler(compiler.DDLCompiler):
18
18
  def post_create_table(self, table):
19
- post = " USING DELTA"
19
+ post = [" USING DELTA"]
20
20
  if table.comment:
21
21
  comment = self.sql_compiler.render_literal_value(
22
22
  table.comment, sqltypes.String()
23
23
  )
24
- post += " COMMENT " + comment
25
- return post
24
+ post.append("COMMENT " + comment)
25
+
26
+ post.append("TBLPROPERTIES('delta.feature.allowColumnDefaults' = 'enabled')")
27
+ return "\n".join(post)
26
28
 
27
29
  def visit_unique_constraint(self, constraint, **kw):
28
30
  logger.warning("Databricks does not support unique constraints")
@@ -79,7 +79,8 @@ class TestTableCommentDDL(DDLTestBase):
79
79
  def test_create_table_with_comment(self, table_with_comment):
80
80
  stmt = CreateTable(table_with_comment)
81
81
  output = self.compile(stmt)
82
- assert "USING DELTA COMMENT 'foobar'" in output
82
+ assert "USING DELTA" in output
83
+ assert "COMMENT 'foobar'" in output
83
84
 
84
85
  def test_alter_table_add_comment(self, table_without_comment: Table):
85
86
  table_without_comment.comment = "wireless mechanical keyboard"