Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/databricks/labs/lakebridge/transpiler/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ def _process_non_mime_result(context: TranspilingContext, error_list: list[Trans
output_code: str = context.transpiled_code or ""
output_path = cast(Path, context.output_path)

if any(err.kind == ErrorKind.PARSING for err in error_list):
output_code = context.source_code or ""
elif output_path.suffix == ".sql":
if output_path.suffix == ".sql":
output_code = _validate_transpiled_sql(context, output_code, error_list)
with output_path.open("w") as w:
# The above adds a java-style comment block at the top of the output file
Expand Down
155 changes: 155 additions & 0 deletions tests/unit/transpiler/test_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,3 +593,158 @@ def test_make_header_with_one_repeated_warning():
*/
"""
)


class MockTranspileEngine(TranspileEngine):
"""Mock transpiler that returns predefined transpiled code."""

def __init__(self, transpiled_code: str, errors: list[TranspileError] | None = None):
self._transpiled_code = transpiled_code
self._errors = errors or []

@property
def transpiler_name(self) -> str:
return "mock"

@property
def supported_dialects(self) -> list[str]:
return ["snowflake", "tsql"]

async def initialize(self, config: TranspileConfig) -> None:
pass

async def shutdown(self) -> None:
pass

async def transpile(
self, source_dialect: str, target_dialect: str, source_code: str, file_path: Path
) -> TranspileResult:
return TranspileResult(
transpiled_code=self._transpiled_code,
success_count=1 if not self._errors else 0,
error_list=self._errors,
)

def is_supported_file(self, file: Path) -> bool:
return file.suffix == ".sql"


def test_transpiled_code_output_on_parsing_error(tmp_path: Path, mock_workspace_client: WorkspaceClient):
"""Test that transpiled code is output even when parsing errors occur."""
input_file = tmp_path / "test.sql"
output_folder = tmp_path / "output"
output_folder.mkdir()

original_sql = "SELECT NUMBER_COL, VARCHAR_COL FROM my_table"
input_file.write_text(original_sql)
transpiled_sql = "SELECT DECIMAL_COL, STRING_COL FROM my_table"

parsing_error = TranspileError(
code="PARSE_ERROR",
kind=ErrorKind.PARSING,
severity=ErrorSeverity.ERROR,
path=input_file,
message="Parsing error: unexpected token",
range=CodeRange(start=CodePosition(0, 0), end=CodePosition(0, 10)),
)

mock_engine = MockTranspileEngine(transpiled_sql, [parsing_error])

config = TranspileConfig(
transpiler_config_path="mock",
input_source=str(input_file),
output_folder=str(output_folder),
source_dialect="snowflake",
skip_validation=True,
)

with patch("databricks.labs.lakebridge.helpers.db_sql.get_sql_backend", return_value=MockBackend()):
status, errors = transpile(mock_workspace_client, mock_engine, config)

output_file = output_folder / "test.sql"
assert output_file.exists(), "Output file was not created"
output_content = output_file.read_text()

assert output_content == transpiled_sql, f"Expected transpiled code '{transpiled_sql}' but got '{output_content}'"
assert output_content != original_sql, "Output should not be the original SQL"

assert len(errors) == 1
assert errors[0].kind == ErrorKind.PARSING
assert status["parsing_error_count"] == 1


def test_transpiled_code_output_without_errors(tmp_path: Path, mock_workspace_client: WorkspaceClient):
"""Test that transpiled code is output correctly when no errors occur."""
input_file = tmp_path / "success.sql"
output_folder = tmp_path / "output"
output_folder.mkdir()

original_sql = "CREATE TABLE test (id NUMBER, name VARCHAR(100))"
input_file.write_text(original_sql)
transpiled_sql = "CREATE TABLE test (id DECIMAL(38, 0), name STRING)"

mock_engine = MockTranspileEngine(transpiled_sql, [])

config = TranspileConfig(
transpiler_config_path="mock",
input_source=str(input_file),
output_folder=str(output_folder),
source_dialect="snowflake",
skip_validation=True,
)

with patch("databricks.labs.lakebridge.helpers.db_sql.get_sql_backend", return_value=MockBackend()):
status, errors = transpile(mock_workspace_client, mock_engine, config)

output_file = output_folder / "success.sql"
assert output_file.exists(), "Output file was not created"
output_content = output_file.read_text()

assert output_content == transpiled_sql, f"Expected '{transpiled_sql}' but got '{output_content}'"
assert output_content != original_sql, "Output should be transpiled, not original"

assert len(errors) == 0
assert status["parsing_error_count"] == 0
assert status["total_files_processed"] == 1


def test_empty_transpiled_code_with_parsing_error(tmp_path: Path, mock_workspace_client: WorkspaceClient):
"""Test handling when transpiled_code is empty/None during parsing error."""
input_file = tmp_path / "error.sql"
output_folder = tmp_path / "output"
output_folder.mkdir()

original_sql = "INVALID SQL SYNTAX !!!"
input_file.write_text(original_sql)

parsing_error = TranspileError(
code="PARSE_ERROR",
kind=ErrorKind.PARSING,
severity=ErrorSeverity.ERROR,
path=input_file,
message="Fatal parsing error",
range=None,
)

mock_engine = MockTranspileEngine("", [parsing_error])

config = TranspileConfig(
transpiler_config_path="mock",
input_source=str(input_file),
output_folder=str(output_folder),
source_dialect="snowflake",
skip_validation=True,
)

with patch("databricks.labs.lakebridge.helpers.db_sql.get_sql_backend", return_value=MockBackend()):
status, errors = transpile(mock_workspace_client, mock_engine, config)

output_file = output_folder / "error.sql"
assert output_file.exists(), "Output file should be created even with errors"
output_content = output_file.read_text()

assert output_content == "", "Output should be empty string when transpilation fails completely"

assert len(errors) == 1
assert errors[0].kind == ErrorKind.PARSING
assert status["parsing_error_count"] == 1
Loading