databricks-labs-lakebridge 0.10.1__py3-none-any.whl → 0.10.3__py3-none-any.whl
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.
- databricks/labs/lakebridge/__about__.py +1 -1
- databricks/labs/lakebridge/cli.py +374 -179
- databricks/labs/lakebridge/config.py +3 -5
- databricks/labs/lakebridge/helpers/file_utils.py +10 -9
- databricks/labs/lakebridge/helpers/string_utils.py +0 -34
- databricks/labs/lakebridge/install.py +47 -19
- databricks/labs/lakebridge/intermediate/root_tables.py +5 -7
- databricks/labs/lakebridge/reconcile/connectors/source_adapter.py +2 -2
- databricks/labs/lakebridge/reconcile/connectors/{sql_server.py → tsql.py} +1 -1
- databricks/labs/lakebridge/reconcile/constants.py +1 -0
- databricks/labs/lakebridge/transpiler/execute.py +10 -8
- databricks/labs/lakebridge/transpiler/lsp/lsp_engine.py +13 -8
- databricks/labs/lakebridge/transpiler/sqlglot/sqlglot_engine.py +4 -0
- databricks/labs/lakebridge/transpiler/transpile_engine.py +4 -6
- {databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/METADATA +3 -3
- {databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/RECORD +22 -20
- docs/lakebridge/src/components/ReconcileTabs.tsx +86 -0
- docs/lakebridge/src/theme/DocSidebarItems/index.tsx +42 -0
- {databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/WHEEL +0 -0
- {databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/entry_points.txt +0 -0
- {databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/licenses/LICENSE +0 -0
- {databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/licenses/NOTICE +0 -0
@@ -1,5 +1,3 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
1
|
import logging
|
4
2
|
from dataclasses import dataclass
|
5
3
|
from enum import Enum, auto
|
@@ -31,11 +29,11 @@ class LSPConfigOptionV1:
|
|
31
29
|
default: Any = None
|
32
30
|
|
33
31
|
@classmethod
|
34
|
-
def parse_all(cls, data: dict[str, Any]) -> dict[str, list[LSPConfigOptionV1]]:
|
32
|
+
def parse_all(cls, data: dict[str, Any]) -> dict[str, list["LSPConfigOptionV1"]]:
|
35
33
|
return {key: list(LSPConfigOptionV1.parse(item) for item in value) for (key, value) in data.items()}
|
36
34
|
|
37
35
|
@classmethod
|
38
|
-
def parse(cls, data: Any) -> LSPConfigOptionV1:
|
36
|
+
def parse(cls, data: Any) -> "LSPConfigOptionV1":
|
39
37
|
if not isinstance(data, dict):
|
40
38
|
raise ValueError(f"Invalid transpiler config option, expecting a dict entry, got {data}")
|
41
39
|
flag: str = data.get("flag", "")
|
@@ -79,7 +77,7 @@ class TranspileConfig:
|
|
79
77
|
output_folder: str | None = None
|
80
78
|
error_file_path: str | None = None
|
81
79
|
sdk_config: dict[str, str] | None = None
|
82
|
-
skip_validation: bool
|
80
|
+
skip_validation: bool = False
|
83
81
|
catalog_name: str = "remorph"
|
84
82
|
schema_name: str = "transpiler"
|
85
83
|
transpiler_options: JsonValue = None
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import contextlib
|
2
|
+
import os
|
1
3
|
from pathlib import Path
|
2
4
|
from collections.abc import Generator
|
3
5
|
|
@@ -53,12 +55,11 @@ def get_sql_file(input_path: str | Path) -> Generator[Path, None, None]:
|
|
53
55
|
yield filename
|
54
56
|
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
:
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
return file.read()
|
58
|
+
@contextlib.contextmanager
|
59
|
+
def chdir(new_path: Path) -> Generator[None, None, None]:
|
60
|
+
saved_path = Path.cwd()
|
61
|
+
try:
|
62
|
+
os.chdir(new_path)
|
63
|
+
yield
|
64
|
+
finally:
|
65
|
+
os.chdir(saved_path)
|
@@ -1,37 +1,3 @@
|
|
1
|
-
import codecs
|
2
|
-
|
3
|
-
|
4
|
-
# Optionally check to see if a string begins with a Byte Order Mark
|
5
|
-
# such a character will cause the transpiler to fail
|
6
|
-
def remove_bom(input_string: str) -> str:
|
7
|
-
"""
|
8
|
-
Removes the Byte Order Mark (BOM) from the given string if it exists.
|
9
|
-
:param input_string: String to remove BOM from
|
10
|
-
:return: String without BOM
|
11
|
-
"""
|
12
|
-
output_string = input_string
|
13
|
-
|
14
|
-
# Check and remove UTF-16 (LE and BE) BOM
|
15
|
-
if input_string.startswith(codecs.BOM_UTF16_BE.decode("utf-16-be")):
|
16
|
-
output_string = input_string[len(codecs.BOM_UTF16_BE.decode("utf-16-be")) :]
|
17
|
-
elif input_string.startswith(codecs.BOM_UTF16_LE.decode("utf-16-le")):
|
18
|
-
output_string = input_string[len(codecs.BOM_UTF16_LE.decode("utf-16-le")) :]
|
19
|
-
elif input_string.startswith(codecs.BOM_UTF16.decode("utf-16")):
|
20
|
-
output_string = input_string[len(codecs.BOM_UTF16.decode("utf-16")) :]
|
21
|
-
# Check and remove UTF-32 (LE and BE) BOM
|
22
|
-
elif input_string.startswith(codecs.BOM_UTF32_BE.decode("utf-32-be")):
|
23
|
-
output_string = input_string[len(codecs.BOM_UTF32_BE.decode("utf-32-be")) :]
|
24
|
-
elif input_string.startswith(codecs.BOM_UTF32_LE.decode("utf-32-le")):
|
25
|
-
output_string = input_string[len(codecs.BOM_UTF32_LE.decode("utf-32-le")) :]
|
26
|
-
elif input_string.startswith(codecs.BOM_UTF32.decode("utf-32")):
|
27
|
-
output_string = input_string[len(codecs.BOM_UTF32.decode("utf-32")) :]
|
28
|
-
# Check and remove UTF-8 BOM
|
29
|
-
elif input_string.startswith(codecs.BOM_UTF8.decode("utf-8")):
|
30
|
-
output_string = input_string[len(codecs.BOM_UTF8.decode("utf-8")) :]
|
31
|
-
|
32
|
-
return output_string
|
33
|
-
|
34
|
-
|
35
1
|
def refactor_hexadecimal_chars(input_string: str) -> str:
|
36
2
|
"""
|
37
3
|
Updates the HexaDecimal characters ( \x1b[\\d+m ) in the given string as below.
|
@@ -37,6 +37,7 @@ from databricks.labs.lakebridge.config import (
|
|
37
37
|
|
38
38
|
from databricks.labs.lakebridge.deployment.configurator import ResourceConfigurator
|
39
39
|
from databricks.labs.lakebridge.deployment.installation import WorkspaceInstallation
|
40
|
+
from databricks.labs.lakebridge.helpers.file_utils import chdir
|
40
41
|
from databricks.labs.lakebridge.reconcile.constants import ReconReportType, ReconSourceType
|
41
42
|
from databricks.labs.lakebridge.transpiler.lsp.lsp_engine import LSPConfig
|
42
43
|
|
@@ -251,12 +252,8 @@ class WheelInstaller(TranspilerInstaller):
|
|
251
252
|
return self._post_install(version)
|
252
253
|
|
253
254
|
def _create_venv(self) -> None:
|
254
|
-
|
255
|
-
try:
|
256
|
-
os.chdir(self._install_path)
|
255
|
+
with chdir(self._install_path):
|
257
256
|
self._unsafe_create_venv()
|
258
|
-
finally:
|
259
|
-
os.chdir(cwd)
|
260
257
|
|
261
258
|
def _unsafe_create_venv(self) -> None:
|
262
259
|
# using the venv module doesn't work (maybe it's not possible to create a venv from a venv ?)
|
@@ -298,16 +295,12 @@ class WheelInstaller(TranspilerInstaller):
|
|
298
295
|
raise ValueError(f"Could not locate 'site-packages' for {self._venv!s}")
|
299
296
|
|
300
297
|
def _install_with_pip(self) -> None:
|
301
|
-
|
302
|
-
try:
|
303
|
-
os.chdir(self._install_path)
|
298
|
+
with chdir(self._install_path):
|
304
299
|
# the way to call pip from python is highly sensitive to os and source type
|
305
300
|
if self._artifact:
|
306
301
|
self._install_local_artifact()
|
307
302
|
else:
|
308
303
|
self._install_remote_artifact()
|
309
|
-
finally:
|
310
|
-
os.chdir(cwd)
|
311
304
|
|
312
305
|
def _install_local_artifact(self) -> None:
|
313
306
|
pip = self._locate_pip()
|
@@ -557,10 +550,9 @@ class WorkspaceInstaller:
|
|
557
550
|
|
558
551
|
@classmethod
|
559
552
|
def install_morpheus(cls, artifact: Path | None = None):
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
"This software requires Java 11 or above. Please install Java and re-run 'install-transpile'."
|
553
|
+
if not cls.is_java_version_okay():
|
554
|
+
logger.error(
|
555
|
+
"The morpheus transpiler requires Java 11 or above. Please install Java and re-run 'install-transpile'."
|
564
556
|
)
|
565
557
|
return
|
566
558
|
product_name = "databricks-morph-plugin"
|
@@ -568,6 +560,26 @@ class WorkspaceInstaller:
|
|
568
560
|
artifact_id = product_name
|
569
561
|
TranspilerInstaller.install_from_maven(product_name, group_id, artifact_id, artifact)
|
570
562
|
|
563
|
+
@classmethod
|
564
|
+
def is_java_version_okay(cls) -> bool:
|
565
|
+
detected_java = cls.find_java()
|
566
|
+
match detected_java:
|
567
|
+
case None:
|
568
|
+
logger.warning("No Java executable found in the system PATH.")
|
569
|
+
return False
|
570
|
+
case (java_executable, None):
|
571
|
+
logger.warning(f"Java found, but could not determine the version: {java_executable}.")
|
572
|
+
return False
|
573
|
+
case (java_executable, bytes(raw_version)):
|
574
|
+
logger.warning(f"Java found ({java_executable}), but could not parse the version:\n{raw_version}")
|
575
|
+
return False
|
576
|
+
case (java_executable, tuple(old_version)) if old_version < (11, 0, 0, 0):
|
577
|
+
version_str = ".".join(str(v) for v in old_version)
|
578
|
+
logger.warning(f"Java found ({java_executable}), but version {version_str} is too old.")
|
579
|
+
return False
|
580
|
+
case _:
|
581
|
+
return True
|
582
|
+
|
571
583
|
@classmethod
|
572
584
|
def install_artifact(cls, artifact: str):
|
573
585
|
path = Path(artifact)
|
@@ -582,25 +594,41 @@ class WorkspaceInstaller:
|
|
582
594
|
logger.fatal(f"Cannot install unsupported artifact: {artifact}")
|
583
595
|
|
584
596
|
@classmethod
|
585
|
-
def
|
597
|
+
def find_java(cls) -> tuple[Path, tuple[int, int, int, int] | bytes | None] | None:
|
598
|
+
"""Locate Java and return its version, as reported by `java -version`.
|
599
|
+
|
600
|
+
The java executable is currently located by searching the system PATH. Its version is parsed from the output of
|
601
|
+
the `java -version` command, which has been standardized since Java 10.
|
602
|
+
|
603
|
+
Returns:
|
604
|
+
a tuple of its path and the version as a tuple of integers (feature, interim, update, patch), if the java
|
605
|
+
executable could be located. If the version cannot be parsed, instead the raw version information is
|
606
|
+
returned, or `None` as a last resort. When no java executable is found, `None` is returned instead of a
|
607
|
+
tuple.
|
608
|
+
"""
|
586
609
|
# Platform-independent way to reliably locate the java executable.
|
587
610
|
# Reference: https://docs.python.org/3.10/library/subprocess.html#popen-constructor
|
588
611
|
java_executable = shutil.which("java")
|
589
612
|
if java_executable is None:
|
590
613
|
return None
|
614
|
+
java_executable_path = Path(java_executable)
|
615
|
+
logger.debug(f"Using java executable: {java_executable_path!r}")
|
591
616
|
try:
|
592
|
-
completed = run([
|
617
|
+
completed = run([str(java_executable_path), "-version"], shell=False, capture_output=True, check=True)
|
593
618
|
except CalledProcessError as e:
|
594
619
|
logger.debug(
|
595
620
|
f"Failed to run {e.args!r} (exit-code={e.returncode}, stdout={e.stdout!r}, stderr={e.stderr!r})",
|
596
621
|
exc_info=e,
|
597
622
|
)
|
598
|
-
return None
|
623
|
+
return java_executable_path, None
|
599
624
|
# It might not be ascii, but the bits we care about are so this will never fail.
|
600
|
-
|
625
|
+
raw_output = completed.stderr
|
626
|
+
java_version_output = raw_output.decode("ascii", errors="ignore")
|
601
627
|
java_version = cls._parse_java_version(java_version_output)
|
628
|
+
if java_version is None:
|
629
|
+
return java_executable_path, raw_output.strip()
|
602
630
|
logger.debug(f"Detected java version: {java_version}")
|
603
|
-
return java_version
|
631
|
+
return java_executable_path, java_version
|
604
632
|
|
605
633
|
# Pattern to match a Java version string, compiled at import time to ensure it's valid.
|
606
634
|
# Ref: https://docs.oracle.com/en/java/javase/11/install/version-string-format.html
|
@@ -1,11 +1,9 @@
|
|
1
1
|
import logging
|
2
2
|
from pathlib import Path
|
3
3
|
|
4
|
-
from databricks.labs.
|
5
|
-
|
6
|
-
|
7
|
-
read_file,
|
8
|
-
)
|
4
|
+
from databricks.labs.blueprint.paths import read_text
|
5
|
+
|
6
|
+
from databricks.labs.lakebridge.helpers.file_utils import get_sql_file, is_sql_file
|
9
7
|
from databricks.labs.lakebridge.intermediate.dag import DAG
|
10
8
|
|
11
9
|
from databricks.labs.lakebridge.transpiler.sqlglot.sqlglot_engine import SqlglotEngine
|
@@ -26,14 +24,14 @@ class RootTableAnalyzer:
|
|
26
24
|
# when input is sql file then parse the file
|
27
25
|
if is_sql_file(self.input_path):
|
28
26
|
logger.debug(f"Generating Lineage file: {self.input_path}")
|
29
|
-
sql_content =
|
27
|
+
sql_content = read_text(self.input_path)
|
30
28
|
self._populate_dag(sql_content, self.input_path, dag)
|
31
29
|
return dag # return after processing the file
|
32
30
|
|
33
31
|
# when the input is a directory
|
34
32
|
for path in get_sql_file(self.input_path):
|
35
33
|
logger.debug(f"Generating Lineage file: {path}")
|
36
|
-
sql_content =
|
34
|
+
sql_content = read_text(path)
|
37
35
|
self._populate_dag(sql_content, path, dag)
|
38
36
|
|
39
37
|
return dag
|
@@ -6,7 +6,7 @@ from databricks.labs.lakebridge.reconcile.connectors.data_source import DataSour
|
|
6
6
|
from databricks.labs.lakebridge.reconcile.connectors.databricks import DatabricksDataSource
|
7
7
|
from databricks.labs.lakebridge.reconcile.connectors.oracle import OracleDataSource
|
8
8
|
from databricks.labs.lakebridge.reconcile.connectors.snowflake import SnowflakeDataSource
|
9
|
-
from databricks.labs.lakebridge.reconcile.connectors.
|
9
|
+
from databricks.labs.lakebridge.reconcile.connectors.tsql import TSQLServerDataSource
|
10
10
|
from databricks.labs.lakebridge.transpiler.sqlglot.generator.databricks import Databricks
|
11
11
|
from databricks.labs.lakebridge.transpiler.sqlglot.parsers.oracle import Oracle
|
12
12
|
from databricks.labs.lakebridge.transpiler.sqlglot.parsers.snowflake import Snowflake
|
@@ -26,5 +26,5 @@ def create_adapter(
|
|
26
26
|
if isinstance(engine, Databricks):
|
27
27
|
return DatabricksDataSource(engine, spark, ws, secret_scope)
|
28
28
|
if isinstance(engine, TSQL):
|
29
|
-
return
|
29
|
+
return TSQLServerDataSource(engine, spark, ws, secret_scope)
|
30
30
|
raise ValueError(f"Unsupported source type --> {engine}")
|
@@ -9,6 +9,7 @@ from typing import cast
|
|
9
9
|
import itertools
|
10
10
|
|
11
11
|
from databricks.labs.blueprint.installation import JsonObject
|
12
|
+
from databricks.labs.blueprint.paths import read_text
|
12
13
|
from databricks.labs.lakebridge.__about__ import __version__
|
13
14
|
from databricks.labs.lakebridge.config import (
|
14
15
|
TranspileConfig,
|
@@ -28,7 +29,6 @@ from databricks.labs.lakebridge.transpiler.transpile_status import (
|
|
28
29
|
ErrorKind,
|
29
30
|
ErrorSeverity,
|
30
31
|
)
|
31
|
-
from databricks.labs.lakebridge.helpers.string_utils import remove_bom
|
32
32
|
from databricks.labs.lakebridge.helpers.validation import Validator
|
33
33
|
from databricks.labs.lakebridge.transpiler.sqlglot.sqlglot_engine import SqlglotEngine
|
34
34
|
from databricks.sdk import WorkspaceClient
|
@@ -62,15 +62,14 @@ async def _process_one_file(context: TranspilingContext) -> tuple[int, list[Tran
|
|
62
62
|
)
|
63
63
|
return 0, [error]
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
context = dataclasses.replace(context, source_code=source_code)
|
65
|
+
source_code = read_text(context.input_path)
|
66
|
+
context = dataclasses.replace(context, source_code=source_code)
|
68
67
|
|
69
68
|
transpile_result = await _transpile(
|
70
69
|
context.transpiler,
|
71
70
|
str(context.config.source_dialect),
|
72
71
|
context.config.target_dialect,
|
73
|
-
|
72
|
+
source_code,
|
74
73
|
context.input_path,
|
75
74
|
)
|
76
75
|
|
@@ -81,8 +80,9 @@ async def _process_one_file(context: TranspilingContext) -> tuple[int, list[Tran
|
|
81
80
|
error_list = list(transpile_result.error_list)
|
82
81
|
context = dataclasses.replace(context, transpiled_code=transpile_result.transpiled_code)
|
83
82
|
|
84
|
-
output_path =
|
85
|
-
output_path
|
83
|
+
output_path = context.output_path
|
84
|
+
assert output_path is not None, "Output path must be set in the context"
|
85
|
+
output_path.parent.mkdir(exist_ok=True)
|
86
86
|
|
87
87
|
if _is_combined_result(transpile_result):
|
88
88
|
_process_combined_result(context, error_list)
|
@@ -158,7 +158,9 @@ def _process_single_result(context: TranspilingContext, error_list: list[Transpi
|
|
158
158
|
|
159
159
|
output_path = cast(Path, context.output_path)
|
160
160
|
with output_path.open("w") as w:
|
161
|
-
|
161
|
+
# The above adds a java-style comment block at the top of the output file
|
162
|
+
# This would break .py or .json outputs so we disable it for now.
|
163
|
+
# w.write(make_header(context.input_path, error_list))
|
162
164
|
w.write(output_code)
|
163
165
|
|
164
166
|
logger.info(f"Processed file: {context.input_path} (errors: {len(error_list)})")
|
@@ -35,7 +35,7 @@ from pygls.lsp.client import BaseLanguageClient
|
|
35
35
|
from databricks.labs.blueprint.wheels import ProductInfo
|
36
36
|
from databricks.labs.lakebridge.config import LSPConfigOptionV1, TranspileConfig, TranspileResult
|
37
37
|
from databricks.labs.lakebridge.errors.exceptions import IllegalStateException
|
38
|
-
from databricks.labs.lakebridge.helpers.file_utils import
|
38
|
+
from databricks.labs.lakebridge.helpers.file_utils import chdir, is_dbt_project_file, is_sql_file
|
39
39
|
from databricks.labs.lakebridge.transpiler.transpile_engine import TranspileEngine
|
40
40
|
from databricks.labs.lakebridge.transpiler.transpile_status import (
|
41
41
|
CodePosition,
|
@@ -389,6 +389,14 @@ class LSPEngine(TranspileEngine):
|
|
389
389
|
self._client = _LanguageClient(name, version)
|
390
390
|
self._init_response: InitializeResult | None = None
|
391
391
|
|
392
|
+
@property
|
393
|
+
def transpiler_name(self) -> str:
|
394
|
+
return self._config.name
|
395
|
+
|
396
|
+
def options_for_dialect(self, source_dialect: str) -> list[LSPConfigOptionV1]:
|
397
|
+
"""Get the options supported when transpiling a given source dialect."""
|
398
|
+
return self._config.options_for_dialect(source_dialect)
|
399
|
+
|
392
400
|
@property
|
393
401
|
def supported_dialects(self) -> list[str]:
|
394
402
|
return self._config.remorph.dialects
|
@@ -400,15 +408,14 @@ class LSPEngine(TranspileEngine):
|
|
400
408
|
async def initialize(self, config: TranspileConfig) -> None:
|
401
409
|
if self.is_alive:
|
402
410
|
raise IllegalStateException("LSP engine is already initialized")
|
403
|
-
cwd = os.getcwd()
|
404
411
|
try:
|
405
|
-
|
406
|
-
|
412
|
+
# TODO: Avoid this by setting the working directory when launching the child process.
|
413
|
+
with chdir(self._workdir):
|
414
|
+
await self._do_initialize(config)
|
407
415
|
await self._await_for_transpile_capability()
|
408
416
|
# it is good practice to catch broad exceptions raised by launching a child process
|
409
417
|
except Exception as e: # pylint: disable=broad-exception-caught
|
410
418
|
logger.error("LSP initialization failed", exc_info=e)
|
411
|
-
os.chdir(cwd)
|
412
419
|
|
413
420
|
async def _do_initialize(self, config: TranspileConfig) -> None:
|
414
421
|
await self._start_server()
|
@@ -523,9 +530,7 @@ class LSPEngine(TranspileEngine):
|
|
523
530
|
self.close_document(file_path)
|
524
531
|
return ChangeManager.apply(source_code, response.changes, response.diagnostics, file_path)
|
525
532
|
|
526
|
-
def open_document(self, file_path: Path,
|
527
|
-
if source_code is None:
|
528
|
-
source_code = file_path.read_text(encoding)
|
533
|
+
def open_document(self, file_path: Path, source_code: str) -> None:
|
529
534
|
text_document = TextDocumentItem(
|
530
535
|
uri=file_path.as_uri(), language_id=LanguageKind.Sql, version=1, text=source_code
|
531
536
|
)
|
@@ -39,6 +39,10 @@ class SqlglotEngine(TranspileEngine):
|
|
39
39
|
def supported_dialects(self) -> list[str]:
|
40
40
|
return sorted(SQLGLOT_DIALECTS.keys())
|
41
41
|
|
42
|
+
@property
|
43
|
+
def transpiler_name(self) -> str:
|
44
|
+
return "sqlglot"
|
45
|
+
|
42
46
|
def _partial_transpile(
|
43
47
|
self,
|
44
48
|
read_dialect: Dialect,
|
@@ -37,13 +37,11 @@ class TranspileEngine(abc.ABC):
|
|
37
37
|
|
38
38
|
@property
|
39
39
|
@abc.abstractmethod
|
40
|
-
def
|
40
|
+
def transpiler_name(self) -> str: ...
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
f"Invalid value for '--source-dialect': '{source_dialect}' is not one of {self.supported_dialects}."
|
46
|
-
)
|
42
|
+
@property
|
43
|
+
@abc.abstractmethod
|
44
|
+
def supported_dialects(self) -> list[str]: ...
|
47
45
|
|
48
46
|
@abc.abstractmethod
|
49
47
|
def is_supported_file(self, file: Path) -> bool: ...
|
{databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: databricks-labs-lakebridge
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.3
|
4
4
|
Summary: Fast and predictable migrations to Databricks Lakehouse Platform. This tool is designed to help you migrate your data and workloads to the Databricks Lakehouse Platform in a fast, predictable, and reliable way. It provides a set of tools and utilities to help you reconcile your data and workloads, assess your current state, and plan your migration.
|
5
5
|
Project-URL: Documentation, https://databrickslabs.github.io/lakebridge
|
6
6
|
Project-URL: Issues, https://github.com/databrickslabs/lakebridge/issues
|
@@ -25,8 +25,8 @@ Classifier: Topic :: Software Development :: Libraries
|
|
25
25
|
Classifier: Topic :: Utilities
|
26
26
|
Requires-Python: >=3.10
|
27
27
|
Requires-Dist: cryptography<45.1.0,>=44.0.2
|
28
|
-
Requires-Dist: databricks-bb-analyzer~=0.1.
|
29
|
-
Requires-Dist: databricks-labs-blueprint[yaml]<0.12.0,>=0.11.
|
28
|
+
Requires-Dist: databricks-bb-analyzer~=0.1.9
|
29
|
+
Requires-Dist: databricks-labs-blueprint[yaml]<0.12.0,>=0.11.2
|
30
30
|
Requires-Dist: databricks-labs-lsql==0.16.0
|
31
31
|
Requires-Dist: databricks-sdk~=0.51.0
|
32
32
|
Requires-Dist: duckdb~=1.2.2
|
{databricks_labs_lakebridge-0.10.1.dist-info → databricks_labs_lakebridge-0.10.3.dist-info}/RECORD
RENAMED
@@ -1,17 +1,19 @@
|
|
1
1
|
docs/lakebridge/src/components/Button.tsx,sha256=5l_irZl4AGwK7k1e2rdOb_W2-305Q1mjwXA3iP8CqaM,3159
|
2
|
+
docs/lakebridge/src/components/ReconcileTabs.tsx,sha256=xJD0nq_raoYv70YLEnG2iuAUTSXXvDpmtmjX7X9Tw9E,2665
|
2
3
|
docs/lakebridge/src/css/custom.css,sha256=-XnDdVlHqJZXJmKarH7zCUMnnlAfpxIpZyr8FNJ4q0A,4024
|
3
4
|
docs/lakebridge/src/css/table.css,sha256=_MAyY7hyhfFrSNVAvCA2QlqdbeBi4Kr9Ue93bSyhKSE,315
|
4
5
|
docs/lakebridge/src/pages/index.tsx,sha256=fQRA9ZbKsPxZbXuSa1LMDk1xfYg2YXCFgsgzqus0NLc,1789
|
6
|
+
docs/lakebridge/src/theme/DocSidebarItems/index.tsx,sha256=3FHRUOXJtjQk-caU_xAmmJWPC3E-H_ZZySV23tzqz3A,1334
|
5
7
|
docs/lakebridge/src/theme/Footer/index.tsx,sha256=Jj8zY5WDiTLXwF_mAgld8Dh1A3MY1HFVVSYIoUs51so,1057
|
6
8
|
docs/lakebridge/src/theme/Layout/index.tsx,sha256=IkdLr13jKmLxT0jWQqrwqrjVXc8Rwd_kWNpTd1t2sc0,592
|
7
9
|
databricks/__init__.py,sha256=YqH8Hy8lHJxd0hLMZF6kWirUDdPiX90LRDX6S6yTMn0,261
|
8
10
|
databricks/labs/__init__.py,sha256=YqH8Hy8lHJxd0hLMZF6kWirUDdPiX90LRDX6S6yTMn0,261
|
9
|
-
databricks/labs/lakebridge/__about__.py,sha256=
|
11
|
+
databricks/labs/lakebridge/__about__.py,sha256=LBCN0OI_6vUqxgIo75HVdQc1TP5LOmy5HIs3OKrDIpk,49
|
10
12
|
databricks/labs/lakebridge/__init__.py,sha256=nUNECqNvyfpT0aeWwlqG0ADT8U8ScCLb8WWpLydppcA,464
|
11
13
|
databricks/labs/lakebridge/base_install.py,sha256=8NxXsNpgqXnuADKXVFh5oQL3osdvygRMY1amJwKfU08,490
|
12
|
-
databricks/labs/lakebridge/cli.py,sha256=
|
13
|
-
databricks/labs/lakebridge/config.py,sha256=
|
14
|
-
databricks/labs/lakebridge/install.py,sha256=
|
14
|
+
databricks/labs/lakebridge/cli.py,sha256=6exPUJs7c2qVo-X9VXFg5VM3XqOCdlk0_5OXfPw6nbY,31578
|
15
|
+
databricks/labs/lakebridge/config.py,sha256=IjxvphM9fRQHQ2FAxwZ23deJGgSemJ3rMV0sp1Ob6e8,5833
|
16
|
+
databricks/labs/lakebridge/install.py,sha256=EmtzbC-pOeiK7lqn4wxSRoeODlkqB_lQBJ9Mj4E0kjE,40536
|
15
17
|
databricks/labs/lakebridge/jvmproxy.py,sha256=F9pXpemzdaJXwpshHxVM9PYU_eNn4zTCUFQ5vc9WIhA,1573
|
16
18
|
databricks/labs/lakebridge/lineage.py,sha256=Q2oky4RkODRHWMwIQIwbYXSdZTmRkMWwEh6RssBiQxY,1843
|
17
19
|
databricks/labs/lakebridge/uninstall.py,sha256=hf36YgeW9XO2cRvvn6AXUZdihQ1ZMHnR38OVEF5sfRw,759
|
@@ -45,19 +47,19 @@ databricks/labs/lakebridge/errors/exceptions.py,sha256=PIj8wRJpxrBXOLMMt9HQhBfhZ
|
|
45
47
|
databricks/labs/lakebridge/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
48
|
databricks/labs/lakebridge/helpers/db_sql.py,sha256=chFHpn6XIuC0GrJ3a30_Y7tcXd4KZ5qO9zCAI4d7TR0,806
|
47
49
|
databricks/labs/lakebridge/helpers/execution_time.py,sha256=8oLEYh0AKz1fuiQMyDTWDymhxh6xUKlcFpINWzKnOy4,533
|
48
|
-
databricks/labs/lakebridge/helpers/file_utils.py,sha256=
|
50
|
+
databricks/labs/lakebridge/helpers/file_utils.py,sha256=1X3ri7_kyZibOFq36mX8fiERhE3tru_7VZIat1jjzOc,1911
|
49
51
|
databricks/labs/lakebridge/helpers/metastore.py,sha256=1SKsIfNtiu3jUFjaXZ5B1fBZigVYqS1Q2OWhdn9qa8U,6425
|
50
52
|
databricks/labs/lakebridge/helpers/recon_config_utils.py,sha256=1Nq_pIonE2tz08kdVpSDS-NVKGZ1p_kGRZBUQFFWZAs,7404
|
51
|
-
databricks/labs/lakebridge/helpers/string_utils.py,sha256=
|
53
|
+
databricks/labs/lakebridge/helpers/string_utils.py,sha256=TKW0BHmOZ2G8EebCohQRJLYglqeJajHgQ2BLehf9qsE,1169
|
52
54
|
databricks/labs/lakebridge/helpers/telemetry_utils.py,sha256=M0lqYcLdLKROnnu3KRJUlGS368IRPIACef4G1ae9cvA,435
|
53
55
|
databricks/labs/lakebridge/helpers/validation.py,sha256=97AoCcsliWKUBKAY8SwgL4Dad-r_W59L_7h2I3Pudmk,4449
|
54
56
|
databricks/labs/lakebridge/intermediate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
57
|
databricks/labs/lakebridge/intermediate/dag.py,sha256=47bgyaYaBK_ELwLE5VGgFUraSxKdMJkLmo2lfc602lI,3165
|
56
58
|
databricks/labs/lakebridge/intermediate/engine_adapter.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
-
databricks/labs/lakebridge/intermediate/root_tables.py,sha256=
|
59
|
+
databricks/labs/lakebridge/intermediate/root_tables.py,sha256=G9PFU22qJ0BgV1FGZPK5bWNdEa8Xpo_gyEvMmATHkTw,1524
|
58
60
|
databricks/labs/lakebridge/reconcile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
61
|
databricks/labs/lakebridge/reconcile/compare.py,sha256=P9ABIT95TeS7BVRYVbzjpaEsynF2h2m5M8f9-he8A3A,16136
|
60
|
-
databricks/labs/lakebridge/reconcile/constants.py,sha256=
|
62
|
+
databricks/labs/lakebridge/reconcile/constants.py,sha256=ZXhGp0hxNdCWTN0iOfaIiDvRkxMZm4E7vtL-tVDsImM,816
|
61
63
|
databricks/labs/lakebridge/reconcile/exception.py,sha256=kA-1KVAgZfWzxhcUwYha_8OapmFajJG0iY5TxPUPJyQ,1463
|
62
64
|
databricks/labs/lakebridge/reconcile/execute.py,sha256=13yDonKuOcGytIDEySgAF--8VC_zLR4-hLudD2EkE0g,35111
|
63
65
|
databricks/labs/lakebridge/reconcile/recon_capture.py,sha256=mlrKSzeTQnq3_ncbTunE1OyIFA2bLKlwiuDMicQRf5c,27317
|
@@ -73,8 +75,8 @@ databricks/labs/lakebridge/reconcile/connectors/jdbc_reader.py,sha256=SsY1rkeLo4
|
|
73
75
|
databricks/labs/lakebridge/reconcile/connectors/oracle.py,sha256=LBqlK5WbgB4XaQNJ_DomTHXazdHJNu4vkIic_z6UENw,4795
|
74
76
|
databricks/labs/lakebridge/reconcile/connectors/secrets.py,sha256=vue72BaYVaaeUfTOaqIEwP-I3TApgbPiuq69Z6I2u3k,1125
|
75
77
|
databricks/labs/lakebridge/reconcile/connectors/snowflake.py,sha256=ARooTfPo6Vvrrj1n3KQ6aW-raAkoY_Z_qHB6epa5WVI,8086
|
76
|
-
databricks/labs/lakebridge/reconcile/connectors/source_adapter.py,sha256=
|
77
|
-
databricks/labs/lakebridge/reconcile/connectors/
|
78
|
+
databricks/labs/lakebridge/reconcile/connectors/source_adapter.py,sha256=I6LBE0C8e80lMm_lVBVIrW9g9ogIgZ53J_EFRNkcSWY,1445
|
79
|
+
databricks/labs/lakebridge/reconcile/connectors/tsql.py,sha256=71ChvUvDWSp6qftl4cJ7B_ztnchpU7uXo1_zLl5cDbc,5676
|
78
80
|
databricks/labs/lakebridge/reconcile/query_builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
81
|
databricks/labs/lakebridge/reconcile/query_builder/aggregate_query.py,sha256=zCPmLBLWeKwn0E2QMs0ua2CIJ6cnxmn77mNt4lvauTw,13783
|
80
82
|
databricks/labs/lakebridge/reconcile/query_builder/base.py,sha256=J1LSemcN6bn-0K5U1PhXaQj22axOmqHUv-s9WwLQZOk,5293
|
@@ -145,16 +147,16 @@ databricks/labs/lakebridge/resources/reconcile/queries/installation/details.sql,
|
|
145
147
|
databricks/labs/lakebridge/resources/reconcile/queries/installation/main.sql,sha256=s_A0YyGSX_pCWnQsQnY65VYFcbNvq2qKJvYxU6zam6E,794
|
146
148
|
databricks/labs/lakebridge/resources/reconcile/queries/installation/metrics.sql,sha256=FdvjQp7gCwsbcu4UrOuJN-bBLJFpvUIyxH6PQvg04Wo,1006
|
147
149
|
databricks/labs/lakebridge/transpiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
148
|
-
databricks/labs/lakebridge/transpiler/execute.py,sha256=
|
149
|
-
databricks/labs/lakebridge/transpiler/transpile_engine.py,sha256=
|
150
|
+
databricks/labs/lakebridge/transpiler/execute.py,sha256=7DpeIixATOPryyt4TD93-sdwE1C_fIwuo6bKwClaF_s,17007
|
151
|
+
databricks/labs/lakebridge/transpiler/transpile_engine.py,sha256=5zC8fkpBBlt9RjE_BeA_Sd6vaRxA3mBdhTqoRGFTc_Y,1616
|
150
152
|
databricks/labs/lakebridge/transpiler/transpile_status.py,sha256=MO-Ju-ki3FCY15WxgwfPV9EC7Ma9q8aIfSTgHAmnkGU,1715
|
151
153
|
databricks/labs/lakebridge/transpiler/lsp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
152
|
-
databricks/labs/lakebridge/transpiler/lsp/lsp_engine.py,sha256=
|
154
|
+
databricks/labs/lakebridge/transpiler/lsp/lsp_engine.py,sha256=osT4RXpYqBNcAQ8mcoFt8m2dygs5TcmYnQq57KN_kw4,22580
|
153
155
|
databricks/labs/lakebridge/transpiler/sqlglot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
154
156
|
databricks/labs/lakebridge/transpiler/sqlglot/dialect_utils.py,sha256=GhXXWGA_2PlmHKjxrjryZpA5xaVZ81Vrw3b7DzjpFFI,1033
|
155
157
|
databricks/labs/lakebridge/transpiler/sqlglot/lca_utils.py,sha256=vpDLGhE-wFMah1VTXkMg6gI_QnzdzpYZf0h9DUd8zcI,5154
|
156
158
|
databricks/labs/lakebridge/transpiler/sqlglot/local_expression.py,sha256=V69eEJHyZKxmyaham6OulYnwQRqkbGUrdiWm1EWP8YE,3825
|
157
|
-
databricks/labs/lakebridge/transpiler/sqlglot/sqlglot_engine.py,sha256=
|
159
|
+
databricks/labs/lakebridge/transpiler/sqlglot/sqlglot_engine.py,sha256=1uqpYIB-6vhuFqco80lXyBqqdkVkZkk9xuqFAvf2kXI,10131
|
158
160
|
databricks/labs/lakebridge/transpiler/sqlglot/generator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
159
161
|
databricks/labs/lakebridge/transpiler/sqlglot/generator/databricks.py,sha256=tF38z3J-P0mDnGeDmCzAiowAUoShiosimM6nfR_-3Ro,30653
|
160
162
|
databricks/labs/lakebridge/transpiler/sqlglot/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -163,9 +165,9 @@ databricks/labs/lakebridge/transpiler/sqlglot/parsers/presto.py,sha256=bY6Ku8ZPW
|
|
163
165
|
databricks/labs/lakebridge/transpiler/sqlglot/parsers/snowflake.py,sha256=dZ7BdOlBZlkbiN9G9bu4l2c456265Gx9WoWUPRa7Ffg,23203
|
164
166
|
databricks/labs/lakebridge/upgrades/v0.4.0_add_main_table_operation_name_column.py,sha256=wMTbj1q5td4fa5DCk0tWFJ-OmhhzsExRLYUe4PKmk0s,3527
|
165
167
|
databricks/labs/lakebridge/upgrades/v0.6.0_alter_metrics_datatype.py,sha256=hnTHRtqzwPSF5Judzh6ss-uB5h3IFtm2ylWduwRNq5Y,2424
|
166
|
-
databricks_labs_lakebridge-0.10.
|
167
|
-
databricks_labs_lakebridge-0.10.
|
168
|
-
databricks_labs_lakebridge-0.10.
|
169
|
-
databricks_labs_lakebridge-0.10.
|
170
|
-
databricks_labs_lakebridge-0.10.
|
171
|
-
databricks_labs_lakebridge-0.10.
|
168
|
+
databricks_labs_lakebridge-0.10.3.dist-info/METADATA,sha256=e7yr--8po1oLKE8_BRRTFbv_y2fJ0Fw5F95wT2bnn8U,3078
|
169
|
+
databricks_labs_lakebridge-0.10.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
170
|
+
databricks_labs_lakebridge-0.10.3.dist-info/entry_points.txt,sha256=Idr1CT73b8wShdr287yu1hheGbDbhBvucVUlZcbpiPo,75
|
171
|
+
databricks_labs_lakebridge-0.10.3.dist-info/licenses/LICENSE,sha256=1hG0Cvw6mp9nL9qRoHFcCUk9fYqhcnj2vgJ75rt3BxA,3862
|
172
|
+
databricks_labs_lakebridge-0.10.3.dist-info/licenses/NOTICE,sha256=wtxMsNvTkw1hAEkkWHz8A8JrYySAUSt1tOTcqddkWEg,1797
|
173
|
+
databricks_labs_lakebridge-0.10.3.dist-info/RECORD,,
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import React, {useRef,useEffect} from 'react';
|
2
|
+
import Tabs from '@theme/Tabs';
|
3
|
+
import TabItem from '@theme/TabItem';
|
4
|
+
import useBaseUrl from '@docusaurus/useBaseUrl';
|
5
|
+
|
6
|
+
type FrameTabProps = {
|
7
|
+
src: string;
|
8
|
+
label: string;
|
9
|
+
};
|
10
|
+
|
11
|
+
const frameStyle: React.CSSProperties = {
|
12
|
+
width: '100%',
|
13
|
+
height: '500px',
|
14
|
+
border: '1px solid #ccc',
|
15
|
+
borderRadius: '6px',
|
16
|
+
marginBottom: '1em',
|
17
|
+
overflow: 'auto',
|
18
|
+
};
|
19
|
+
|
20
|
+
const FrameTab: React.FC<FrameTabProps> = ({ src, label }) => {
|
21
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
22
|
+
const url = useBaseUrl(src);
|
23
|
+
|
24
|
+
useEffect(() => {
|
25
|
+
const iframe = iframeRef.current;
|
26
|
+
if (!iframe) return;
|
27
|
+
|
28
|
+
const onLoad = () => {
|
29
|
+
try {
|
30
|
+
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
31
|
+
if (!doc) return;
|
32
|
+
|
33
|
+
const elementsToRemove = doc.querySelectorAll(
|
34
|
+
'div[data-testid="content-spacer"], div[data-testid="extra-whitespace"]'
|
35
|
+
);
|
36
|
+
|
37
|
+
elementsToRemove.forEach(el => {
|
38
|
+
if (!el.children.length) {
|
39
|
+
el.remove();
|
40
|
+
}
|
41
|
+
});
|
42
|
+
} catch (err) {
|
43
|
+
console.warn(err);
|
44
|
+
}
|
45
|
+
};
|
46
|
+
|
47
|
+
iframe.addEventListener('load', onLoad);
|
48
|
+
|
49
|
+
return () => {
|
50
|
+
iframe.removeEventListener('load', onLoad);
|
51
|
+
};
|
52
|
+
}, [url]);
|
53
|
+
|
54
|
+
return (
|
55
|
+
<div>
|
56
|
+
<div style={{ marginBottom: '0.5em' }}>
|
57
|
+
<a href={url} target="_blank" rel="noopener noreferrer">
|
58
|
+
Open notebook in new tab
|
59
|
+
</a>
|
60
|
+
</div>
|
61
|
+
<iframe ref={iframeRef} src={url} style={frameStyle} title={label} />
|
62
|
+
</div>
|
63
|
+
);
|
64
|
+
};
|
65
|
+
|
66
|
+
const LakebridgeTabs: React.FC = () => (
|
67
|
+
<Tabs>
|
68
|
+
{/* <TabItem value="Readme" label="Readme" default>
|
69
|
+
<FrameTab src="/lakebridge_reconcile/Readme.html" label="Readme" />
|
70
|
+
</TabItem>*/}
|
71
|
+
<TabItem value="Recon Main" label="Recon Main">
|
72
|
+
<FrameTab src="/lakebridge_reconcile/lakebridge_recon_main.html" label="Recon Main" />
|
73
|
+
</TabItem>
|
74
|
+
<TabItem value="Recon Wrapper" label="Recon Wrapper">
|
75
|
+
<FrameTab src="/lakebridge_reconcile/recon_wrapper_nb.html" label="Recon Wrapper" />
|
76
|
+
</TabItem>
|
77
|
+
<TabItem value="Snowflake Example" label="Transformation Query Generator">
|
78
|
+
<FrameTab
|
79
|
+
src="/lakebridge_reconcile/snowflake_transformation_query_generator.html"
|
80
|
+
label="Query Generator"
|
81
|
+
/>
|
82
|
+
</TabItem>
|
83
|
+
</Tabs>
|
84
|
+
);
|
85
|
+
|
86
|
+
export default LakebridgeTabs;
|