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

get_relation returns none in hook context #2938

Closed
jtcohen6 opened this issue Dec 4, 2020 · 9 comments
Closed

get_relation returns none in hook context #2938

jtcohen6 opened this issue Dec 4, 2020 · 9 comments
Labels
bug Something isn't working

Comments

@jtcohen6
Copy link
Contributor

jtcohen6 commented Dec 4, 2020

Describe the bug

The adapter.get_relation(...) method seems to return None within the post-hook context.

Why does this matter? Well, {{ this }} always has its type defined as None, and it's fairly common that a user would want to use get_relation (perform a cache lookup) to get its relational type for the purposes of templating a post hook:

alter {{ relation.type }} {{ relation }} ... do something ...

Steps To Reproduce

macros/say_hi.sql

{% macro say_hi(relation) %}

    {% set rel = adapter.get_relation(this.database, this.schema, this.name) %}
    
   select 'say hi to my two best friends: the {{ relation.type }} {{ relation }} and the {{ rel.type }} {{ rel }}'

{% endmacro %}

models/my_model.sql

{{ config(
    post_hook = say_hi(this)
) }}

{{ say_hi(this) }}

dbt.log

create view "jerco"."dbt_jcohen"."my_model__dbt_tmp" as (
    select 'say hi to my two best friends: the None "jerco"."dbt_jcohen"."my_model" and the view "jerco"."dbt_jcohen"."my_model"'
  );
...
        select 'say hi to my two best friends: the None "jerco"."dbt_jcohen"."my_model" and the  None'
...

Expected behavior

I expected the result of get_relation to be defined for both the model and post-hook contexts.

The output of dbt --version:

$ dbt --version
installed version: 0.18.1
   latest version: 0.18.1

Additional context

In the meantime, it's mostly possible to work around this by inferring the relational object type from the configuration of the model we're post-hooking:

{% set relation_type='view' if model.config.materialized == 'view' else 'table' %}
@jtcohen6 jtcohen6 added the bug Something isn't working label Dec 4, 2020
@aaronsteers
Copy link
Contributor

aaronsteers commented Dec 11, 2020

@jtcohen6 - Thanks for raising this in our slack thread. I'm seeing the same from +post-hook when attempting to apply permissions in Snowflake.

It seems this must have been working at some point - is it possible this is a recent regression, and would it be worthwhile for me to try this in a previous version of DBT?

@aaronsteers
Copy link
Contributor

@jtcohen6 - Do you know if the model.config.materialized == 'view' work with this? In other words, can I pass this in a macro and then apply that test within the macro?

@jtcohen6
Copy link
Contributor Author

It seems this must have been working at some point - is it possible this is a recent regression, and would it be worthwhile for me to try this in a previous version of DBT?

@aaronsteers Hm... I haven't looked into this, but I'd be curious to know if it works in older versions!

Do you know if the model.config.materialized == 'view' work with this? In other words, can I pass this in a macro and then apply that test within the macro?

During a model's execution, model reflects the node object of the current model, and this reflects the Relation object of the current model. So config is only stored on the node config, but they ultimately represent "the same thing."

@aaronsteers
Copy link
Contributor

aaronsteers commented Dec 11, 2020

@jtcohen6 - This is super helpful - thank you! The expression model.config.materlialized is correctly returning table or view.

Do you know if I can use the model object also to get the fully qualified object name that I would otherwise get from {{ this }}?

OR (asked another way) can I get a relation object from a node object or vice versa?

@jtcohen6
Copy link
Contributor Author

It's possible to do something like this (docs):

{% set rel = api.Relation.create(
    database = model.database,
    schema = model.schema,
    identifier = model.identifier,
    type = 'view' if model.config.materialized == 'view' else 'table'
) %}

Effectively, it's the same as this, but with the type defined. {{ rel }} and {{ this }} should render the same.

@aaronsteers
Copy link
Contributor

aaronsteers commented Dec 11, 2020

Thanks for sharing this - and for the link. I had checked that page but it doesn't say anything directly about the model object. I did also try {{ debug() }} but the command failed for some reason.

This might be unrelated, but is there a known reason why I wouldn't be able to access a macro attached to a specific model from a +post-hook in schema.yml which I'm otherwise able to access just fine in dbt_project.yml.

In schema.yml, I get either Could not render {{ data_platform_core.grant_select(this, 'PEOPLEDATA_RO') }}: 'data_platform_core' is undefined or Could not render {{ grant_select(this, 'PEOPLEDATA_RO') }}: 'grant_select' is undefined. Again, the confusing thing is that it works just fine in dbt_project.yml (without project name prefix).

Sample that throws the error:

version: 2
models:
  - name: dim_work_group
    +post-hook:
      - "{{ grant_select(this, 'PEOPLEDATA_RO') }}"

@jtcohen6
Copy link
Contributor Author

jtcohen6 commented Dec 11, 2020

Right, that docs link details how to instantiate Relation objects. There are some (admittedly) sparse docs on the model node object here.

As far as setting post-hook in schema.yml: Today there's a distinction between node configs and resource properties. Ultimately (before v1.0), I'd like to reconcile the two as much as possible (#2401). Today, because post-hook is a node config, it can only be set in dbt_project.yml or within a model file's config() block. By contrast, description, tests, meta, etc. are resource properties, so they can only be set in models/*.yml files, not in dbt_project.yml or in-file config() blocks.

The specific error you're seeing is the result of another limitation that, in an ideal world, I'd like to make possible: You cannot call custom macros from .yml files, except in hooks, which are late-rendered. There was an issue about just this a few days ago (#2942), which I closed only because there's some foundational work that lies between us and enabling that pattern.

@aaronsteers
Copy link
Contributor

That is super helpful context and I think it gives me my path forward. Thanks so much!

@jtcohen6
Copy link
Contributor Author

jtcohen6 commented Jul 2, 2021

Coming back to this, and having revisited #2370, I'm afraid to report that this does work if the hook is re-parsed at execute time, instead of set at parse time. Namely:

{{ config(
    post_hook = "{{ say_hi(this) }}"
) }}

{{ say_hi(this) }}

This is so not my favorite thing, but I'm going to close the issue. The real question is bigger than get_relation, and it's whether hooks should always be re-parsed at execute time, without the need to nest a macro in an extra set of curlies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants