Compare commits

...

3 Commits

Author SHA1 Message Date
lostmygithubaccount
158aa81b0c update per suggestions 2022-11-23 09:06:33 -08:00
lostmygithubaccount
5ddb088049 Merge remote-tracking branch 'origin/main' into cody/ibis 2022-11-22 21:54:05 -08:00
lostmygithubaccount
3edc9e53ad initial implementation based on prql pr 2022-11-20 17:55:34 -08:00
10 changed files with 88 additions and 12 deletions

View File

@@ -33,6 +33,8 @@ from dbt.node_types import NodeType, ModelLanguage
from dbt.events.format import pluralize
import dbt.tracking
from .language_provider import IbisProvider
graph_file_name = "graph.gpickle"
@@ -369,7 +371,23 @@ class Compiler:
)
compiled_node = _compiled_type_for(node).from_dict(data)
if compiled_node.language == ModelLanguage.python:
compiled_node.was_ibis = False
if compiled_node.language == ModelLanguage.ibis:
provider = IbisProvider()
context = self._create_node_context(compiled_node, manifest, extra_context)
sql = provider.compile(node.raw_code, context)
# lol
compiled_node.compiled_code = sql
compiled_node.language = ModelLanguage.sql
# efficiency hack?
# doesn't seem to help much, if at all
compiled_node.was_ibis = True
elif compiled_node.language == ModelLanguage.python:
# TODO could we also 'minify' this code at all? just aesthetic, not functional
# quoating seems like something very specific to sql so far
@@ -390,12 +408,13 @@ class Compiler:
self.config.quoting = original_quoting
else:
context = self._create_node_context(compiled_node, manifest, extra_context)
compiled_node.compiled_code = jinja.get_rendered(
node.raw_code,
context,
node,
)
if not compiled_node.was_ibis:
context = self._create_node_context(compiled_node, manifest, extra_context)
compiled_node.compiled_code = jinja.get_rendered(
node.raw_code,
context,
node,
)
compiled_node.relation_name = self._get_relation_name(node)

View File

@@ -22,6 +22,7 @@ class ParseFileType(StrEnum):
Documentation = "docs"
Schema = "schema"
Hook = "hook" # not a real filetype, from dbt_project.yml
language: str = "sql"
parse_file_type_to_parser = {
@@ -194,6 +195,7 @@ class SourceFile(BaseSourceFile):
docs: List[str] = field(default_factory=list)
macros: List[str] = field(default_factory=list)
env_vars: List[str] = field(default_factory=list)
language: str = "sql"
@classmethod
def big_seed(cls, path: FilePath) -> "SourceFile":

View File

@@ -0,0 +1,28 @@
"""
adapted from https://github.com/dbt-labs/dbt-core/pull/5982/files
"""
import ibis
def compile(code: str, context):
conn_params = {
"account": context["target"]["account"],
"user": context["target"]["user"],
"role": context["target"]["role"],
"warehouse": context["target"]["warehouse"],
"database": context["target"]["database"],
"schema": context["target"]["schema"],
"authenticator": "externalbrowser",
}
s = ibis.connect(
f"snowflake://{conn_params['user']}:_@{conn_params['account']}/{conn_params['database']}/{conn_params['schema']}?warehouse={conn_params['warehouse']}&role={conn_params['role']}&authenticator={conn_params['authenticator']}",
)
# the dirtiest code I've ever written?
# run the ibis code and compile the `model` variable
exec(code)
compiled = str(eval(f"ibis.{context['target']['type']}.compile(model)"))
return compiled

View File

@@ -80,7 +80,7 @@ class SelectionCriteria:
def default_method(cls, value: str) -> MethodName:
if _probably_path(value):
return MethodName.Path
elif value.lower().endswith((".sql", ".py", ".csv")):
elif value.lower().endswith((".sql", ".py", ".csv", ".ibis")):
return MethodName.File
else:
return MethodName.FQN

View File

@@ -0,0 +1,23 @@
class LanguageProvider:
# def compile(self, code: str) -> ParsedNode:
def compile(self, code: str) -> str:
"""
Compile a given block into a ParsedNode.
"""
raise NotImplementedError("compile")
class IbisProvider(LanguageProvider):
def __init__(self) -> None:
# TODO: Uncomment when dbt-ibis is released
# if not dbt_ibis:
# raise ImportError(
# "dbt_ibis is required and not found; try running `pip install dbt-ibis`"
# )
pass
def compile(self, code: str, context) -> str:
from .dbt_ibis import _dbt_ibis as dbt_ibis
return dbt_ibis.compile(code, context)

View File

@@ -68,3 +68,4 @@ class RunHookType(StrEnum):
class ModelLanguage(StrEnum):
python = "python"
sql = "sql"
ibis = "ibis"

View File

@@ -157,7 +157,7 @@ class ConfiguredParser(
config[key] = [hooks.get_hook_dict(h) for h in config[key]]
def _create_error_node(
self, name: str, path: str, original_file_path: str, raw_code: str, language: str = "sql"
self, name: str, path: str, original_file_path: str, raw_code: str, language: str
) -> UnparsedNode:
"""If we hit an error before we've actually parsed a node, provide some
level of useful information by attaching this to the exception.
@@ -189,7 +189,9 @@ class ConfiguredParser(
"""
if name is None:
name = block.name
if block.path.relative_path.endswith(".py"):
if block.path.relative_path.endswith(".ibis"):
language = ModelLanguage.ibis
elif block.path.relative_path.endswith(".py"):
language = ModelLanguage.python
else:
# this is not ideal but we have a lot of tests to adjust if don't do it
@@ -223,6 +225,7 @@ class ConfiguredParser(
path=path,
original_file_path=block.path.original_file_path,
raw_code=block.contents,
language=language,
)
raise ParsingException(msg, node=node)

View File

@@ -31,7 +31,6 @@ import ast
from dbt.dataclass_schema import ValidationError
from dbt.exceptions import ParsingException, validator_error_message, UndefinedMacroException
dbt_function_key_words = set(["ref", "source", "config", "get"])
dbt_function_full_names = set(["dbt.ref", "dbt.source", "dbt.config", "dbt.config.get"])

View File

@@ -175,7 +175,7 @@ def read_files(project, files, parser_files, saved_files):
project,
files,
project.model_paths,
[".sql", ".py"],
[".sql", ".py", ".ibis"],
ParseFileType.Model,
saved_files,
dbt_ignore_spec,

View File

@@ -270,6 +270,7 @@ class SchemaParser(SimpleParser[GenericTestBlock, ParsedGenericTestNode]):
path=path,
original_file_path=target.original_file_path,
raw_code=raw_code,
language="sql",
)
raise ParsingException(msg, node=node) from exc