Skip to content
Open
Show file tree
Hide file tree
Changes from all 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