Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitcbf63f9

Browse files
Merge branch 'main' into sea-migration
2 parentsa74d279 +7696316 commitcbf63f9

File tree

9 files changed

+126
-264
lines changed

9 files changed

+126
-264
lines changed

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#Release History
22

3+
#4.0.5 (2025-06-24)
4+
- Fix: Reverted change in cursor close handling which led to errors impacting users (databricks/databricks-sql-python#613 by@madhav-db)
5+
36
#4.0.4 (2025-06-16)
47

58
- Update thrift client library after cleaning up unused fields and structs (databricks/databricks-sql-python#553 by@vikrantpuppala)

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name ="databricks-sql-connector"
3-
version ="4.0.4"
3+
version ="4.0.5"
44
description ="Databricks SQL Connector for Python"
55
authors = ["Databricks <databricks-sql-connector-maintainers@databricks.com>"]
66
license ="Apache-2.0"

‎src/databricks/sql/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def __repr__(self):
6868
DATE=DBAPITypeObject("date")
6969
ROWID=DBAPITypeObject()
7070

71-
__version__="4.0.4"
71+
__version__="4.0.5"
7272
USER_AGENT_NAME="PyDatabricksSqlConnector"
7373

7474
# These two functions are pyhive legacy

‎src/databricks/sql/client.py‎

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -281,13 +281,7 @@ def __enter__(self) -> "Connection":
281281
returnself
282282

283283
def__exit__(self,exc_type,exc_value,traceback):
284-
try:
285-
self.close()
286-
exceptBaseExceptionase:
287-
logger.warning(f"Exception during connection close in __exit__:{e}")
288-
ifexc_typeisNone:
289-
raise
290-
returnFalse
284+
self.close()
291285

292286
def__del__(self):
293287
ifself.open:
@@ -417,14 +411,7 @@ def __enter__(self) -> "Cursor":
417411
returnself
418412

419413
def__exit__(self,exc_type,exc_value,traceback):
420-
try:
421-
logger.debug("Cursor context manager exiting, calling close()")
422-
self.close()
423-
exceptBaseExceptionase:
424-
logger.warning(f"Exception during cursor close in __exit__:{e}")
425-
ifexc_typeisNone:
426-
raise
427-
returnFalse
414+
self.close()
428415

429416
def__iter__(self):
430417
ifself.active_result_set:
@@ -1091,21 +1078,7 @@ def cancel(self) -> None:
10911078
defclose(self)->None:
10921079
"""Close cursor"""
10931080
self.open=False
1094-
1095-
# Close active operation handle if it exists
1096-
ifself.active_command_id:
1097-
try:
1098-
self.backend.close_command(self.active_command_id)
1099-
exceptRequestErrorase:
1100-
ifisinstance(e.args[1],CursorAlreadyClosedError):
1101-
logger.info("Operation was canceled by a prior request")
1102-
else:
1103-
logging.warning(f"Error closing operation handle:{e}")
1104-
exceptExceptionase:
1105-
logging.warning(f"Error closing operation handle:{e}")
1106-
finally:
1107-
self.active_command_id=None
1108-
1081+
self.active_op_handle=None
11091082
ifself.active_result_set:
11101083
self._close_and_clear_active_result_set()
11111084

‎tests/e2e/common/large_queries_mixin.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ def test_query_with_large_narrow_result_set(self):
8383
assertrow[0]==row_id
8484

8585
deftest_long_running_query(self):
86-
"""Incrementally increase query size until it takes at least5 minutes,
86+
"""Incrementally increase query size until it takes at least3 minutes,
8787
and asserts that the query completes successfully.
8888
"""
8989
minutes=60
90-
min_duration=5*minutes
90+
min_duration=3*minutes
9191

9292
duration=-1
9393
scale0=10000
@@ -113,5 +113,5 @@ def test_long_running_query(self):
113113
duration=time.time()-start
114114
current_fraction=duration/min_duration
115115
print("Took {} s with scale factor={}".format(duration,scale_factor))
116-
# Extrapolate linearly to reach5 min and add 50% padding to push over the limit
116+
# Extrapolate linearly to reach3 min and add 50% padding to push over the limit
117117
scale_factor=math.ceil(1.5*scale_factor/current_fraction)

‎tests/e2e/common/staging_ingestion_tests.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def test_staging_ingestion_life_cycle(self, ingestion_user):
4646
)asconn:
4747

4848
cursor=conn.cursor()
49-
query=f"PUT '{temp_path}' INTO 'stage://tmp/{ingestion_user}/tmp/11/15/file1.csv' OVERWRITE"
49+
query=f"PUT '{temp_path}' INTO 'stage://tmp/{ingestion_user}/tmp/11/16/file1.csv' OVERWRITE"
5050
cursor.execute(query)
5151

5252
# GET should succeed
@@ -57,7 +57,7 @@ def test_staging_ingestion_life_cycle(self, ingestion_user):
5757
extra_params={"staging_allowed_local_path":new_temp_path}
5858
)asconn:
5959
cursor=conn.cursor()
60-
query=f"GET 'stage://tmp/{ingestion_user}/tmp/11/15/file1.csv' TO '{new_temp_path}'"
60+
query=f"GET 'stage://tmp/{ingestion_user}/tmp/11/16/file1.csv' TO '{new_temp_path}'"
6161
cursor.execute(query)
6262

6363
withopen(new_fh,"rb")asfp:
@@ -67,7 +67,7 @@ def test_staging_ingestion_life_cycle(self, ingestion_user):
6767

6868
# REMOVE should succeed
6969

70-
remove_query=f"REMOVE 'stage://tmp/{ingestion_user}/tmp/11/15/file1.csv'"
70+
remove_query=f"REMOVE 'stage://tmp/{ingestion_user}/tmp/11/16/file1.csv'"
7171

7272
withself.connection(extra_params={"staging_allowed_local_path":"/"})asconn:
7373
cursor=conn.cursor()
@@ -79,7 +79,7 @@ def test_staging_ingestion_life_cycle(self, ingestion_user):
7979
Error,match="Staging operation over HTTP was unsuccessful: 404"
8080
):
8181
cursor=conn.cursor()
82-
query=f"GET 'stage://tmp/{ingestion_user}/tmp/11/15/file1.csv' TO '{new_temp_path}'"
82+
query=f"GET 'stage://tmp/{ingestion_user}/tmp/11/16/file1.csv' TO '{new_temp_path}'"
8383
cursor.execute(query)
8484

8585
os.remove(temp_path)

‎tests/e2e/test_driver.py‎

Lines changed: 1 addition & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151

5252
fromtests.e2e.common.uc_volume_testsimportPySQLUCVolumeTestSuiteMixin
5353

54-
fromdatabricks.sql.excimportSessionAlreadyClosedError,CursorAlreadyClosedError
54+
fromdatabricks.sql.excimportSessionAlreadyClosedError
5555

5656
log=logging.getLogger(__name__)
5757

@@ -809,142 +809,6 @@ def test_catalogs_returns_arrow_table(self):
809809
results=cursor.fetchall_arrow()
810810
assertisinstance(results,pyarrow.Table)
811811

812-
deftest_close_connection_closes_cursors(self):
813-
814-
fromdatabricks.sql.thrift_api.TCLIServiceimportttypes
815-
816-
withself.connection()asconn:
817-
cursor=conn.cursor()
818-
cursor.execute(
819-
"SELECT id, id `id2`, id `id3` FROM RANGE(1000000) order by RANDOM()"
820-
)
821-
ars=cursor.active_result_set
822-
823-
# We must manually run this check because thrift_backend always forces `has_been_closed_server_side` to True
824-
# Cursor op state should be open before connection is closed
825-
status_request=ttypes.TGetOperationStatusReq(
826-
operationHandle=ars.command_id.to_thrift_handle(),
827-
getProgressUpdate=False,
828-
)
829-
op_status_at_server=ars.backend._client.GetOperationStatus(status_request)
830-
assertop_status_at_server.operationState!=CommandState.CLOSED
831-
832-
conn.close()
833-
834-
# When connection closes, any cursor operations should no longer exist at the server
835-
withpytest.raises(SessionAlreadyClosedError)ascm:
836-
op_status_at_server=ars.backend._client.GetOperationStatus(
837-
status_request
838-
)
839-
840-
deftest_closing_a_closed_connection_doesnt_fail(self,caplog):
841-
caplog.set_level(logging.DEBUG)
842-
# Second .close() call is when this context manager exits
843-
withself.connection()asconn:
844-
# First .close() call is explicit here
845-
conn.close()
846-
assert"Session appears to have been closed already"incaplog.text
847-
848-
conn=None
849-
try:
850-
withpytest.raises(KeyboardInterrupt):
851-
withself.connection()asc:
852-
conn=c
853-
raiseKeyboardInterrupt("Simulated interrupt")
854-
finally:
855-
ifconnisnotNone:
856-
assert (
857-
notconn.open
858-
),"Connection should be closed after KeyboardInterrupt"
859-
860-
deftest_cursor_close_properly_closes_operation(self):
861-
"""Test that Cursor.close() properly closes the active operation handle on the server."""
862-
withself.connection()asconn:
863-
cursor=conn.cursor()
864-
try:
865-
cursor.execute("SELECT 1 AS test")
866-
assertcursor.active_command_idisnotNone
867-
cursor.close()
868-
assertcursor.active_command_idisNone
869-
assertnotcursor.open
870-
finally:
871-
ifcursor.open:
872-
cursor.close()
873-
874-
conn=None
875-
cursor=None
876-
try:
877-
withself.connection()asc:
878-
conn=c
879-
withpytest.raises(KeyboardInterrupt):
880-
withconn.cursor()ascur:
881-
cursor=cur
882-
raiseKeyboardInterrupt("Simulated interrupt")
883-
finally:
884-
ifcursorisnotNone:
885-
assert (
886-
notcursor.open
887-
),"Cursor should be closed after KeyboardInterrupt"
888-
889-
deftest_nested_cursor_context_managers(self):
890-
"""Test that nested cursor context managers properly close operations on the server."""
891-
withself.connection()asconn:
892-
withconn.cursor()ascursor1:
893-
cursor1.execute("SELECT 1 AS test1")
894-
assertcursor1.active_command_idisnotNone
895-
896-
withconn.cursor()ascursor2:
897-
cursor2.execute("SELECT 2 AS test2")
898-
assertcursor2.active_command_idisnotNone
899-
900-
# After inner context manager exit, cursor2 should be not open
901-
assertnotcursor2.open
902-
assertcursor2.active_command_idisNone
903-
904-
# After outer context manager exit, cursor1 should be not open
905-
assertnotcursor1.open
906-
assertcursor1.active_command_idisNone
907-
908-
deftest_cursor_error_handling(self):
909-
"""Test that cursor close handles errors properly to prevent orphaned operations."""
910-
withself.connection()asconn:
911-
cursor=conn.cursor()
912-
913-
cursor.execute("SELECT 1 AS test")
914-
915-
op_handle=cursor.active_command_id
916-
917-
assertop_handleisnotNone
918-
919-
# Manually close the operation to simulate server-side closure
920-
conn.session.backend.close_command(op_handle)
921-
922-
cursor.close()
923-
924-
assertnotcursor.open
925-
926-
deftest_result_set_close(self):
927-
"""Test that ResultSet.close() properly closes operations on the server and handles state correctly."""
928-
withself.connection()asconn:
929-
cursor=conn.cursor()
930-
try:
931-
cursor.execute("SELECT * FROM RANGE(10)")
932-
933-
result_set=cursor.active_result_set
934-
assertresult_setisnotNone
935-
936-
initial_op_state=result_set.status
937-
938-
result_set.close()
939-
940-
assertresult_set.status==CommandState.CLOSED
941-
assertresult_set.status!=initial_op_state
942-
943-
# Closing the result set again should be a no-op and not raise exceptions
944-
result_set.close()
945-
finally:
946-
cursor.close()
947-
948812

949813
# use a RetrySuite to encapsulate these tests which we'll typically want to run together; however keep
950814
# the 429/503 subsuites separate since they execute under different circumstances.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp