Repository: dbt-labs/dbt-core

Issue:
Remove the non-destructive flag and all associated provisions
## Feature

### Feature description

Let's delete some code!

The `--non-destructive` flag was originally intended to work around Redshift's bound views. With the advent of late binding views, this is no longer necessary. Moreover, `--non-destructive` never worked in an ideal way.

Some problems:
- it just skips views, making it difficult to keep the database in sync with code changes
- it uses a `truncate` for tables which commits the open transaction. This can lead to query inconsistency from external queriers.
- it makes materialization code super complicated on pretty much every database dbt supports

### Caveats
We'll need to:
- remove the `--non-destructive` flag
- cleanse materializations of non-destructive logic (might be tricky, but will be a huge improvement)
- note specific places where we've needed to make weird workarounds for non-destructive builds, like [here](https://github.com/fishtown-analytics/dbt/blob/dev/wilt-chamberlain/core/dbt/include/global_project/macros/materializations/view/view.sql#L33)

This is one of the oldest and most neglected features in dbt, so we should take extra care to see if there are any lingering code paths that become irrelevant once non-destructive mode doesn't exist anymore.

### Who will this benefit?
dbt maintainers, folks that want to implement custom materializations



Discussion:

Resolving patch:
diff --git a/core/dbt/flags.py b/core/dbt/flags.py
--- a/core/dbt/flags.py
+++ b/core/dbt/flags.py
@@ -1,5 +1,4 @@
 STRICT_MODE = False
-NON_DESTRUCTIVE = False
 FULL_REFRESH = False
 USE_CACHE = True
 WARN_ERROR = False
@@ -7,11 +6,9 @@
 
 
 def reset():
-    global STRICT_MODE, NON_DESTRUCTIVE, FULL_REFRESH, USE_CACHE, WARN_ERROR, \
-        TEST_NEW_PARSER
+    global STRICT_MODE, FULL_REFRESH, USE_CACHE, WARN_ERROR, TEST_NEW_PARSER
 
     STRICT_MODE = False
-    NON_DESTRUCTIVE = False
     FULL_REFRESH = False
     USE_CACHE = True
     WARN_ERROR = False
diff --git a/core/dbt/main.py b/core/dbt/main.py
--- a/core/dbt/main.py
+++ b/core/dbt/main.py
@@ -204,7 +204,6 @@ def run_from_args(parsed):
 
 
 def update_flags(parsed):
-    flags.NON_DESTRUCTIVE = getattr(parsed, 'non_destructive', False)
     flags.USE_CACHE = getattr(parsed, 'use_cache', True)
 
     arg_drop_existing = getattr(parsed, 'drop_existing', False)
@@ -421,14 +420,6 @@ def _add_selection_arguments(*subparsers):
 
 def _add_table_mutability_arguments(*subparsers):
     for sub in subparsers:
-        sub.add_argument(
-            '--non-destructive',
-            action='store_true',
-            help="""
-            If specified, DBT will not drop views. Tables will be truncated
-            instead of dropped.
-            """
-        )
         sub.add_argument(
             '--full-refresh',
             action='store_true',
@@ -675,7 +666,7 @@ def parse_args(args):
     # --models, --exclude
     _add_selection_arguments(run_sub, compile_sub, generate_sub, test_sub,
                              archive_sub)
-    # --full-refresh, --non-destructive
+    # --full-refresh
     _add_table_mutability_arguments(run_sub, compile_sub)
 
     _build_seed_subparser(subs, base_subparser)


Test patch:
diff --git a/test/integration/017_runtime_materialization_tests/test_runtime_materialization.py b/test/integration/017_runtime_materialization_tests/test_runtime_materialization.py
--- a/test/integration/017_runtime_materialization_tests/test_runtime_materialization.py
+++ b/test/integration/017_runtime_materialization_tests/test_runtime_materialization.py
@@ -43,47 +43,6 @@ def test_postgres_full_refresh(self):
         self.assertTablesEqual("seed","incremental")
         self.assertTablesEqual("seed","materialized")
 
-    @use_profile('postgres')
-    def test_postgres_non_destructive(self):
-        results = self.run_dbt(['run', '--non-destructive'])
-        self.assertEqual(len(results), 3)
-
-        self.assertTablesEqual("seed","view")
-        self.assertTablesEqual("seed","incremental")
-        self.assertTablesEqual("seed","materialized")
-        self.assertTableDoesNotExist('dependent_view')
-
-        self.run_sql_file("test/integration/017_runtime_materialization_tests/update.sql")
-
-        results = self.run_dbt(['run', '--non-destructive'])
-        self.assertEqual(len(results), 3)
-
-        self.assertTableDoesExist('dependent_view')
-        self.assertTablesEqual("seed","view")
-        self.assertTablesEqual("seed","incremental")
-        self.assertTablesEqual("seed","materialized")
-
-    @use_profile('postgres')
-    def test_postgres_full_refresh_and_non_destructive(self):
-        results = self.run_dbt(['run', '--full-refresh', '--non-destructive'])
-        self.assertEqual(len(results), 3)
-
-        self.assertTablesEqual("seed","view")
-        self.assertTablesEqual("seed","incremental")
-        self.assertTablesEqual("seed","materialized")
-        self.assertTableDoesNotExist('dependent_view')
-
-        self.run_sql_file("test/integration/017_runtime_materialization_tests/invalidate_incremental.sql")
-        self.run_sql_file("test/integration/017_runtime_materialization_tests/update.sql")
-
-        results = self.run_dbt(['run', '--full-refresh', '--non-destructive'])
-        self.assertEqual(len(results), 3)
-
-        self.assertTableDoesExist('dependent_view')
-        self.assertTablesEqual("seed","view")
-        self.assertTablesEqual("seed","incremental")
-        self.assertTablesEqual("seed","materialized")
-
     @use_profile('postgres')
     def test_postgres_delete__dbt_tmp_relation(self):
         # This creates a __dbt_tmp view - make sure it doesn't interfere with the dbt run
diff --git a/test/integration/017_runtime_materialization_tests/update.sql b/test/integration/017_runtime_materialization_tests/update.sql
--- a/test/integration/017_runtime_materialization_tests/update.sql
+++ b/test/integration/017_runtime_materialization_tests/update.sql
@@ -1,7 +1,5 @@
 
--- create a view on top of the models. Since they shouldn't be drop-cascaded,
--- this view will still be around after the dbt run --non-destructive
-
+-- create a view on top of the models
 create view {schema}.dependent_view as (
 
     select count(*) from {schema}.materialized
diff --git a/test/integration/022_bigquery_test/test_simple_bigquery_view.py b/test/integration/022_bigquery_test/test_simple_bigquery_view.py
--- a/test/integration/022_bigquery_test/test_simple_bigquery_view.py
+++ b/test/integration/022_bigquery_test/test_simple_bigquery_view.py
@@ -41,7 +41,6 @@ def assert_nondupes_pass(self):
                 self.assertEqual(result.status, 0)
 
 
-
 class TestSimpleBigQueryRun(TestBaseBigQueryRun):
 
     @use_profile('bigquery')
@@ -53,15 +52,6 @@ def test__bigquery_simple_run(self):
         self.assertEqual(len(results), 5)
         self.assert_nondupes_pass()
 
-    @use_profile('bigquery')
-    def test__bigquery_exists_non_destructive(self):
-        self.run_dbt(['seed'])
-        # first run dbt. this should work
-        self.assertEqual(len(self.run_dbt()), 5)
-        # then run dbt with --non-destructive. The view should still exist.
-        self.run_dbt(['run', '--non-destructive'])
-        self.assert_nondupes_pass()
-
 
 class TestUnderscoreBigQueryRun(TestBaseBigQueryRun):
     prefix = "_test{}{:04}".format(int(time.time()), random.randint(0, 9999))