dbt-labs / dbt-labs/dbt-adapters

feat(redshift): add profile option to drop relations without CASCADE

Open
#1,772 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
233
Forks
362
Avg merge
3d 22h
Merged PRs (30d)
9

Description

## Summary

Add a profile credentials option (`drop_without_cascade` or similar) to omit `CASCADE` from `DROP TABLE/VIEW/MATERIALIZED VIEW` statements in dbt-redshift.

## Background

All three dbt-redshift drop macros unconditionally append `CASCADE`:

**`macros/relations/table/drop.sql`** (line 2):
```sql
drop table if exists {{ relation }} cascade
```

**`macros/relations/view/drop.sql`** (line 2):
```sql
drop view if exists {{ relation }} cascade
```

**`macros/relations/materialized_view/drop.sql`** (line 2):
```sql
drop materialized view if exists {{ relation }} cascade
```

`CASCADE` instructs Redshift to automatically drop all dependent objects (views, etc.). On clusters with many objects, Redshift must walk the full dependency graph to resolve each `DROP ... CASCADE`, which is expensive. For users who exclusively use **unbound views** (or have no downstream views at all), there are no dependents to cascade to — the overhead is pure waste.

Additionally, `CASCADE` is the root cause of the concurrent-transaction race condition that requires the global thread lock (see related issue). Removing `CASCADE` also opens the door to removing the lock.

## Proposed Change

Add a boolean profile credential field `drop_without_cascade` (default `False`) that, when `True`, emits `DROP ... RESTRICT` (or simply omits `CASCADE`) instead.

### Files to change

**`dbt-redshift/src/dbt/adapters/redshift/connections.py`**

Add to `RedshiftCredentials` dataclass (~line 190):
```python
# Drop behavior
drop_without_cascade: bool = False
```

Add to `_connection_keys()` (~line 228):
```python
"drop_without_cascade",
```

**`macros/relations/table/drop.sql`**:
```sql
{%- macro redshift__drop_table(relation) -%}
drop table if exists {{ relation }} {% if not adapter.config.credentials.drop_without_cascade %}cascade{% endif %}
{%- endmacro -%}
```

**`macros/relations/view/drop.sql`**:
```sql
{%- macro redshift__drop_view(relation) -%}
drop view if exists {{ relation }} {% if not adapter.config.credentials.drop_without_cascade %}cascade{% endif %}
{%- endmacro -%}
```

**`macros/relations/materialized_view/drop.sql`**:
```sql
{% macro redshift__drop_materialized_view(relation) -%}
drop materialized view if exists {{ relation }} {% if not adapter.config.credentials.drop_without_cascade %}cascade{% endif %}
{%- endmacro %}
```

> **Note:** The exact mechanism for passing credentials into Jinja macros should follow the existing pattern used by `redshift__use_show_apis()` in `macros/metadata/helpers.sql`. Alternatively this could be implemented as a Python-level override of the drop macros using `execute()` directly in `impl.py`.

## Safety

- Default is `False` — no change in behavior for existing users.
- Safe only for projects that don't have dependent views downstream of dropped relations (e.g., projects using only unbound views, or where dbt manages all dependencies).
- Must be documented with a clear warning: if a dependent object exists and `CASCADE` is omitted, Redshift will raise an error rather than silently dropping it.
- Pairs naturally with `drop_without_lock` (see related issue): users who set both get maximum DROP performance with minimal risk when their project has no downstream view dependencies.

## Related

- Related to the `drop_without_lock` feature request — the thread lock exists specifically because of `CASCADE` race conditions.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.