Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/simple hooks #147

Merged
merged 3 commits into from
Sep 19, 2016
Merged
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
13 changes: 11 additions & 2 deletions dbt/compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from collections import defaultdict
import dbt.project
from dbt.source import Source
from dbt.utils import find_model_by_fqn, find_model_by_name, dependency_projects
from dbt.utils import find_model_by_fqn, find_model_by_name, dependency_projects, This
from dbt.linker import Linker
import sqlparse

Expand Down Expand Up @@ -102,7 +102,9 @@ def do_ref(*args):
ref_fqn = ".".join(other_model_fqn)
raise RuntimeError("Model '{}' depends on model '{}' which is disabled in the project config".format(src_fqn, ref_fqn))

linker.dependency(source_model, other_model_fqn)
# this creates a trivial cycle -- should this be a compiler error?
if source_model != other_model_fqn:
linker.dependency(source_model, other_model_fqn)

if other_model.is_ephemeral:
linker.inject_cte(model, other_model)
Expand All @@ -129,6 +131,13 @@ def compile_model(self, linker, model, models):
context = self.project.context()
context['ref'] = self.__ref(linker, context, model, models)
context['config'] = self.__model_config(model, linker)
context['this'] = This(context['env']['schema'], model.name)

hook_keys = ['pre-hook', 'post-hook']
for key in hook_keys:
hooks = model.config.get(key, [])
rendered_hooks = [jinja2.Environment().from_string(hook).render(context) for hook in hooks]
model.update_in_model_config({key: rendered_hooks})

rendered = template.render(context)
return rendered
Expand Down
43 changes: 37 additions & 6 deletions dbt/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
from dbt.templates import BaseCreateTemplate, DryCreateTemplate
import dbt.schema_tester
import dbt.project
from dbt.utils import This

class SourceConfig(object):
Materializations = ['view', 'table', 'incremental', 'ephemeral']
ConfigKeys = ['enabled', 'materialized', 'dist', 'sort', 'sql_where', 'unique_key', 'sort_type']
ConfigKeys = ['enabled', 'materialized', 'dist', 'sort', 'sql_where', 'unique_key', 'sort_type', 'pre-hook', 'post-hook']

def __init__(self, active_project, own_project, fqn):
self.active_project = active_project
Expand Down Expand Up @@ -51,17 +52,45 @@ def config(self):
def update_in_model_config(self, config):
self.in_model_config.update(config)

def __get_hooks(self, relevant_configs, key):
hooks = []

if key not in relevant_configs:
return []

new_hooks = relevant_configs[key]
if type(new_hooks) not in [list, tuple]:
new_hooks = [new_hooks]

for hook in new_hooks:
if type(hook) not in [str, unicode]:
name = ".".join(self.fqn)
raise RuntimeError("{} for model {} is not a string!".format(key, name))

hooks.append(hook)
return hooks

def get_project_config(self, project):
config = {}
# most configs are overwritten by a more specific config, but pre/post hooks are appended!
hook_fields = ['pre-hook', 'post-hook']
config = {k:[] for k in hook_fields}

model_configs = project['models']

fqn = self.fqn[:]
for level in fqn:
level_config = model_configs.get(level, None)
if level_config is None:
break

relevant_configs = {key: level_config[key] for key in level_config if key in self.ConfigKeys}
config.update(relevant_configs)

for key in hook_fields:
new_hooks = self.__get_hooks(relevant_configs, key)
config[key].extend([h for h in new_hooks if h not in config[key]])

clobber_configs = {k:v for (k,v) in relevant_configs.items() if k not in hook_fields}
config.update(clobber_configs)
model_configs = model_configs[level]

return config
Expand Down Expand Up @@ -246,7 +275,7 @@ def compile(self, rendered_query, project, create_template):

if self.materialization == 'incremental':
identifier = self.name
ctx['this'] = '"{}"."{}"'.format(schema, identifier)
ctx['this'] = This(schema, identifier)
if 'sql_where' not in model_config:
raise RuntimeError("sql_where not specified in model materialized as incremental: {}".format(self))
raw_sql_where = model_config['sql_where']
Expand All @@ -255,7 +284,7 @@ def compile(self, rendered_query, project, create_template):
unique_key = model_config.get('unique_key', None)
else:
identifier = self.tmp_name()
ctx['this'] = '"{}"."{}"'.format(schema, identifier)
ctx['this'] = This(schema, identifier)
sql_where = None
unique_key = None

Expand All @@ -268,7 +297,9 @@ def compile(self, rendered_query, project, create_template):
"sort_qualifier": sort_qualifier,
"sql_where": sql_where,
"prologue": self.get_prologue_string(),
"unique_key" : unique_key
"unique_key" : unique_key,
"pre-hooks" : self.config['pre-hook'],
"post-hooks" : self.config['post-hook']
}

return create_template.wrap(opts)
Expand Down
23 changes: 22 additions & 1 deletion dbt/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ class BaseCreateTemplate(object):
select ({unique_key}) from dbt_delete_sbq
where ({sql_where}) or ({sql_where}) is null
);
"""

extras_template = """
{prologue}

{pre_hooks};

{sql}

{post_hooks};
"""

label = "build"
Expand All @@ -44,6 +54,16 @@ class BaseCreateTemplate(object):
def model_name(cls, base_name):
return base_name

def add_extras(self, opts, sql):
extras = {
'prologue': opts['prologue'],
'pre_hooks': ';\n'.join(opts['pre-hooks']),
'sql': sql,
'post_hooks': ';\n'.join(opts['post-hooks']),
}

return self.extras_template.format(**extras)

def wrap(self, opts):
sql = ""
if opts['materialization'] in ('table', 'view'):
Expand All @@ -63,7 +83,8 @@ def wrap(self, opts):
else:
raise RuntimeError("Invalid materialization parameter ({})".format(opts['materialization']))

return "{}\n\n{}".format(opts['prologue'], sql)
return self.add_extras(opts, sql)


class DryCreateTemplate(object):
base_template = """
Expand Down
8 changes: 8 additions & 0 deletions dbt/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
import os
import dbt.project

class This(object):
def __init__(self, schema, table):
self.schema = schema
self.table = table

def __repr__(self):
return '"{}"."{}"'.format(self.schema, self.table)

def find_model_by_name(models, name, package_namespace=None):
found = []
for model in models:
Expand Down