{% materialization table, adapter='depp', supported_languages=['python', 'sql'] -%}

{%- set language = model['language'] -%}

{%- if language == 'python' -%}

  {%- set relation = this.incorporate(type='table') -%}

  {# The Python executor uses DROP TABLE ... CASCADE which silently drops
     dependent views without updating dbt's relation cache, causing
     "relation does not exist" errors in downstream view materializations.
     Collect dependent views before the executor runs so we can evict them
     from the cache afterwards. #}
  {%- set dependent_views = [] -%}
  {%- if adapter.type() in ('postgres', 'depp') and load_cached_relation(this) is not none -%}
    {%- set deps_sql -%}
      select dependent_ns.nspname as dep_schema,
             dependent_view.relname as dep_name
        from pg_depend
        join pg_rewrite on pg_depend.objid = pg_rewrite.oid
        join pg_class as dependent_view on pg_rewrite.ev_class = dependent_view.oid
        join pg_class as source_table on pg_depend.refobjid = source_table.oid
        join pg_namespace dependent_ns on dependent_view.relnamespace = dependent_ns.oid
        join pg_namespace source_ns on source_table.relnamespace = source_ns.oid
       where source_ns.nspname = '{{ relation.schema }}'
         and source_table.relname = '{{ relation.identifier }}'
         and dependent_view.oid != source_table.oid
         and dependent_view.relkind = 'v'
    {%- endset -%}
    {%- for row in run_query(deps_sql) -%}
      {%- do dependent_views.append(adapter.get_relation(
        database=relation.database,
        schema=row['dep_schema'],
        identifier=row['dep_name']
      )) -%}
    {%- endfor -%}
  {%- endif -%}

  {%- call statement('main', language=language) -%}
    {{- py_write(compiled_code, relation) }}
  {%- endcall %}

  {# Evict CASCADE-dropped views from the relation cache #}
  {%- for dep_rel in dependent_views if dep_rel is not none -%}
    {%- do adapter.drop_relation(dep_rel) -%}
  {%- endfor -%}

  {% if adapter.type() in ('postgres', 'depp') %}
  {% do create_indexes(relation) %}
  {% do create_constraints(relation) %}
  {% do run_query("COMMIT;") %}
  {% endif %}

{{- return({'relations': [relation]}) }}

{%- elif language == 'sql' -%}

  {# HACK: proxy to the db adapter table materialization #}
  {{- return(adapter.db_materialization(context, "table")) }}

{%- endif -%}

{% endmaterialization %}

{% macro py_write(code, relation) -%}
{{- compiled_code -}}

# Generated by dbt-depp

_depp_ref = ref
def ref(*args, **kwargs):
  _pn = kwargs.pop("partition_on", None)
  _pnum = kwargs.pop("partition_num", None)
  _load = kwargs.get("dbt_load_df_function")
  if _load and (_pn is not None or _pnum is not None):
    kwargs["dbt_load_df_function"] = lambda t: _load(t, partition_on=_pn, partition_num=_pnum)
  return _depp_ref(*args, **kwargs)

_depp_source = source
def source(*args, dbt_load_df_function, **kwargs):
  _pn = kwargs.pop("partition_on", None)
  _pnum = kwargs.pop("partition_num", None)
  _load = dbt_load_df_function
  if _pn is not None or _pnum is not None:
    _load = lambda t: dbt_load_df_function(t, partition_on=_pn, partition_num=_pnum)
  return _depp_source(*args, dbt_load_df_function=_load)

_DbtObj = dbtObj
class dbtObj(_DbtObj):
  def __init__(self, load_df_function):
    super().__init__(load_df_function)
    self.source = lambda *args, **kwargs: source(*args, dbt_load_df_function=load_df_function, **kwargs)

def main(read_df, write_df, fal_context=None):
  dbt_context = dbtObj(read_df)
  df = model(dbt_context, fal_context)
  return write_df(
      '{{ relation.quote(False, False, False) }}',
      df
  )
{%- endmacro %}

{% macro create_constraints(relation) -%}
  {%- set constraints = config.get('constraints', default=[]) -%}

  {% for constraint in constraints %}
    {% if constraint.type == 'primary_key' %}
      {% set columns = constraint.columns | join(', ') %}
      {% set constraint_name = constraint.get('name', relation.identifier ~ '_pkey') %}
      {% set sql %}
        ALTER TABLE {{ relation }}
        ADD CONSTRAINT "{{ constraint_name }}"
        PRIMARY KEY ({{ columns }})
      {% endset %}
      {% do run_query(sql) %}
    {% endif %}
  {% endfor %}
{%- endmacro %}
