Skip to content

Commit aa92717

Browse files
Reject decoded streaming PUT sources in native execution
Signed-off-by: Aakash Saravanan <aakash.saravanan@databricks.com>
1 parent 1171efc commit aa92717

4 files changed

Lines changed: 33 additions & 29 deletions

File tree

‎src/databricks/sql/backend/kernel/_errors.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
from databricks.sql.exc import (
4949
DatabaseError,
5050
Error,
51+
NotSupportedError,
5152
OperationalError,
5253
ProgrammingError,
5354
ServerOperationError,
@@ -135,6 +136,8 @@ def reraise_kernel_error(
135136
code = getattr(exc, "code", "Unknown")
136137
cls = _CODE_TO_EXCEPTION.get(code, DatabaseError)
137138
error_code = getattr(exc, "error_code", None)
139+
if error_code == "FILE_TRANSFER_UNSUPPORTED_STREAM":
140+
cls = NotSupportedError
138141
file_transfer = error_code in (
139142
"FILE_TRANSFER_ERROR",
140143
"FILE_TRANSFER_PERMISSION_ERROR",

‎src/databricks/sql/backend/kernel/client.py‎

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -572,13 +572,6 @@ def execute_command(
572572
try:
573573
stripped_sql = _strip_leading_sql_comments(operation)
574574
staging_verb = _staging_verb(stripped_sql)
575-
if staging_verb == "PUT" and re.match(
576-
r"""(['"])__input_stream__\1""",
577-
_strip_leading_sql_comments(stripped_sql[3:]),
578-
):
579-
raise NotSupportedError(
580-
"Streaming PUT is not supported by the kernel backend."
581-
)
582575
stmt.set_sql(operation)
583576
allowed_paths = (
584577
cursor.connection.staging_allowed_local_path
@@ -601,7 +594,7 @@ def execute_command(
601594
raise TypeError("Local paths must resolve to strings")
602595
except (TypeError, ValueError) as exc:
603596
raise ProgrammingError(_STAGING_PATHS_TYPE_ERROR) from exc
604-
set_paths(normalized_paths)
597+
set_paths(normalized_paths, input_stream_token="__input_stream__")
605598
elif staging_verb is not None:
606599
raise NotSupportedError(
607600
"Volume transfers require a databricks-sql-kernel wheel "

‎tests/e2e/test_kernel_backend.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,8 @@ def execute(query):
817817
execute(f"-- upload\nPUT '{source}' INTO '{remote}' OVERWRITE")
818818
== []
819819
)
820+
with pytest.raises(NotSupportedError, match="Streaming PUT"):
821+
execute(f"PUT r'__input_stream__' INTO '{remote}' OVERWRITE")
820822
assert execute(f"GET '{remote}' TO '{destination}'") == []
821823
assert destination.read_bytes() == payload
822824
with pytest.raises(ProgrammingError):

‎tests/unit/test_kernel_client.py‎

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ def test_execute_command_forwards_allowed_local_paths(
862862
enforce_embedded_schema_correctness=False,
863863
)
864864
stmt.set_staging_allowed_local_paths.assert_called_once_with(
865-
expected if uses_paths else []
865+
expected if uses_paths else [], input_stream_token="__input_stream__"
866866
)
867867
(stmt.submit if async_op else stmt.execute).assert_called_once()
868868

@@ -912,7 +912,9 @@ def execute():
912912
stmt.set_staging_allowed_local_paths.assert_not_called()
913913
else:
914914
execute()
915-
stmt.set_staging_allowed_local_paths.assert_called_once_with([])
915+
stmt.set_staging_allowed_local_paths.assert_called_once_with(
916+
[], input_stream_token="__input_stream__"
917+
)
916918
stmt.execute.assert_called_once()
917919

918920

@@ -947,30 +949,34 @@ def test_staging_verb_matches_only_the_leading_statement(operation, expected):
947949
assert kernel_client._staging_verb(stripped) == expected
948950

949951

950-
@pytest.mark.parametrize(
951-
"query",
952-
[
953-
"PUT '__input_stream__' INTO '/Volumes/c/s/v/file'",
954-
"put '__input_stream__' INTO '/Volumes/c/s/v/file'",
955-
"PUT '__input_stream__'INTO '/Volumes/c/s/v/file'",
956-
'PUT"__input_stream__"INTO "/Volumes/c/s/v/file"',
957-
"/* upload */ PUT '__input_stream__' INTO '/Volumes/c/s/v/file'",
958-
"/* outer /* inner */ outer */ PUT '__input_stream__' INTO '/Volumes/c/s/v/file'",
959-
"PUT'__input_stream__' INTO '/Volumes/c/s/v/file'",
960-
"PUT /* upload */ '__input_stream__' INTO '/Volumes/c/s/v/file'",
961-
"PUT\n-- upload\n'__input_stream__' INTO '/Volumes/c/s/v/file'",
962-
"/* upload */ PUT\"__input_stream__\" INTO '/Volumes/c/s/v/file'",
963-
],
964-
)
965-
def test_kernel_rejects_streaming_put(query):
952+
def test_kernel_rejects_decoded_streaming_put():
966953
c = _make_client()
967954
c._kernel_session = MagicMock()
955+
stmt = c._kernel_session.statement.return_value
956+
error = _FakeKernelError(
957+
code="InvalidArgument", message="Streaming PUT is not supported"
958+
)
959+
error.error_code = "FILE_TRANSFER_UNSUPPORTED_STREAM"
960+
stmt.execute.side_effect = error
961+
cursor = MagicMock()
962+
cursor.connection.staging_allowed_local_path = None
968963
with pytest.raises(NotSupportedError, match="Streaming PUT"):
969964
c.execute_command(
970-
query, MagicMock(), 1, 1, False, MagicMock(), False, [], False, False
965+
"PUT r'__input_stream__' INTO '/Volumes/c/s/v/file'",
966+
MagicMock(),
967+
1,
968+
1,
969+
False,
970+
cursor,
971+
False,
972+
[],
973+
False,
974+
False,
971975
)
972-
stmt = c._kernel_session.statement.return_value
973-
stmt.execute.assert_not_called()
976+
stmt.set_staging_allowed_local_paths.assert_called_once_with(
977+
[], input_stream_token="__input_stream__"
978+
)
979+
stmt.execute.assert_called_once()
974980
stmt.close.assert_called_once()
975981

976982

0 commit comments

Comments
 (0)