Repository: dbt-labs/dbt-core

Issue:
[CT-1977] [Regression] `'BaseResult' object has no attribute 'node'`
### Is this a new bug in dbt-core?

- [X] I believe this is a new bug in dbt-core
- [X] I have searched the existing issues, and I could not find an existing issue for this bug

### Current Behavior

Hi,

I recently upgraded to dbt core 1.4.1 and dbt bigquery 1.4.0 and I've started receiving a python error using exceptions.raise_compile_error in a on-run-start hook macro. Everything was working fine before the upgrade.

Macro and logs attached below.

I tried to substitute the exceptions.raise_compile_error with a log() message just to check if it was indeed the exception causing the issue, and I didn't bump into any errors.

### Expected Behavior

This setup worked before. If a single row was returned by the query, an error would be raised and dbt run would not start at all.

### Steps To Reproduce

In macros folder, create an export_table_check.sql file with the following content (change table name and column name based on your own table in bigq).

```

{% macro export_table_check() %}

    {% set table = '12_exported_records' %}

    {% set query %}
        SELECT column_name
        FROM {{ref(table)}}
        LIMIT 1
    {% endset %}

    {%- if flags.WHICH in ('run', 'build') -%}
        {% set results = run_query(query) %}
        {% if execute %}
            {%- if results.rows -%}
                {{ exceptions.raise_compiler_error("ON_RUN_START_CHECK_NOT_PASSED: Data already exported. DBT Run aborted.") }}
            {% else -%}
                {{ log("No data found in " ~ table ~ " for current day and runtime region. Proceeding...", true) }}
            {%- endif -%}
        {%- endif -%}
    {%- endif -%}
{% endmacro %}


```

In dbt_project:

```
on-run-start: 
- "{{ export_table_check() }}"
```




### Relevant log output

```shell
15:41:26  Running with dbt=1.4.1
15:41:26  Found 31 models, 96 tests, 0 snapshots, 0 analyses, 349 macros, 1 operation, 3 seed files, 31 sources, 0 exposures, 0 metrics
15:41:26  
15:41:28  
15:41:28  Running 1 on-run-start hook
15:41:28  Database error while running on-run-start
15:41:28  Concurrency: 8 threads (target='dev')
15:41:28  
15:41:28  1 of 1 START sql table model prioritization.00a_program_specification_exploded . [RUN]
15:41:31  1 of 1 OK created sql table model prioritization.00a_program_specification_exploded  [CREATE TABLE (213.0 rows, 62.9 KB processed) in 2.81s]
15:41:31  
15:41:31  Finished running 1 table model in 0 hours 0 minutes and 4.82 seconds (4.82s).
15:41:32  Encountered an error:
'BaseResult' object has no attribute 'node'
15:41:32  Traceback (most recent call last):
  File "/home/deleof/anaconda3/envs/prio/lib/python3.9/site-packages/dbt/main.py", line 135, in main
    results, succeeded = handle_and_check(args)
  File "/home/deleof/anaconda3/envs/prio/lib/python3.9/site-packages/dbt/main.py", line 198, in handle_and_check
    task, res = run_from_args(parsed)
  File "/home/deleof/anaconda3/envs/prio/lib/python3.9/site-packages/dbt/main.py", line 245, in run_from_args
    results = task.run()
  File "/home/deleof/anaconda3/envs/prio/lib/python3.9/site-packages/dbt/task/runnable.py", line 472, in run
    result = self.execute_with_hooks(selected_uids)
  File "/home/deleof/anaconda3/envs/prio/lib/python3.9/site-packages/dbt/task/runnable.py", line 436, in execute_with_hooks
    self.after_run(adapter, res)
  File "/home/deleof/anaconda3/envs/prio/lib/python3.9/site-packages/dbt/task/run.py", line 443, in after_run
    database_schema_set: Set[Tuple[Optional[str], str]] = {
  File "/home/deleof/anaconda3/envs/prio/lib/python3.9/site-packages/dbt/task/run.py", line 446, in <setcomp>
    if r.node.is_relational
AttributeError: 'BaseResult' object has no attribute 'node'
```


### Environment

```markdown
- OS: Ubuntu 20.04 LTS
- Python: 3.9.0, 3.10.0
- dbt: 1.4.1
```


### Which database adapter are you using with dbt?

bigquery

### Additional Context

_No response_


Discussion:
Thanks for opening @fradeleo!

**Lame fix:** Change [`task/run.py:446`](https://github.com/dbt-labs/dbt-core/blob/3fbfa6e6f382e59e6fdb06e2d2a570de3f7db4b6/core/dbt/task/run.py#L443-L448) to check if the result has an attribute `node` first.
```python
        database_schema_set: Set[Tuple[Optional[str], str]] = {
            (r.node.database, r.node.schema)
            for r in results
            if (hasattr(r, "node") and r.node.is_relational)
            and r.status not in (NodeStatus.Error, NodeStatus.Fail, NodeStatus.Skipped)
        }
```

**Better fix:** Figure out what we changed in v1.4 that actually causes this hook result to pass through `RunTask.after_run`, in a way that it wasn't previously. I tagged this `Team:Language` and `logging` because I have a hunch that it's to do with a change to our exception classes... but it's possible that the presence of `exceptions.raise_compiler_error` is a red herring
We got the same issue and reverted our upgrades of dbt-core 1.4.1 and dbt-bigquery 1.4.0 since we found it hard to retrace what was causing the issue to be triggered.. 

Resolving patch:
diff --git a/core/dbt/task/run.py b/core/dbt/task/run.py
--- a/core/dbt/task/run.py
+++ b/core/dbt/task/run.py
@@ -443,7 +443,7 @@ def after_run(self, adapter, results):
         database_schema_set: Set[Tuple[Optional[str], str]] = {
             (r.node.database, r.node.schema)
             for r in results
-            if r.node.is_relational
+            if (hasattr(r, "node") and r.node.is_relational)
             and r.status not in (NodeStatus.Error, NodeStatus.Fail, NodeStatus.Skipped)
         }
 


Test patch:
diff --git a/tests/functional/hooks/fixtures.py b/tests/functional/hooks/fixtures.py
--- a/tests/functional/hooks/fixtures.py
+++ b/tests/functional/hooks/fixtures.py
@@ -1,3 +1,31 @@
+macros_missing_column = """
+{% macro export_table_check() %}
+
+    {% set table = 'test_column' %}
+
+    {% set query %}
+        SELECT column_name
+        FROM {{ref(table)}}
+        LIMIT 1
+    {% endset %}
+
+    {%- if flags.WHICH in ('run', 'build') -%}
+        {% set results = run_query(query) %}
+        {% if execute %}
+            {%- if results.rows -%}
+                {{ exceptions.raise_compiler_error("ON_RUN_START_CHECK_NOT_PASSED: Data already exported. DBT Run aborted.") }}
+            {% else -%}
+                {{ log("No data found in " ~ table ~ " for current day and runtime region. Proceeding...", true) }}
+            {%- endif -%}
+        {%- endif -%}
+    {%- endif -%}
+{% endmacro %}
+"""
+
+models__missing_column = """
+select 1 as col
+"""
+
 macros__before_and_after = """
 {% macro custom_run_hook(state, target, run_started_at, invocation_id) %}
 
diff --git a/tests/functional/hooks/test_run_hooks.py b/tests/functional/hooks/test_run_hooks.py
--- a/tests/functional/hooks/test_run_hooks.py
+++ b/tests/functional/hooks/test_run_hooks.py
@@ -8,6 +8,8 @@
     macros__before_and_after,
     models__hooks,
     seeds__example_seed_csv,
+    macros_missing_column,
+    models__missing_column,
 )
 
 from dbt.tests.util import (
@@ -141,3 +143,25 @@ def test_pre_and_post_seed_hooks(self, setUp, project, dbt_profile_target):
         check_table_does_not_exist(project.adapter, "start_hook_order_test")
         check_table_does_not_exist(project.adapter, "end_hook_order_test")
         self.assert_used_schemas(project)
+
+
+class TestAfterRunHooks(object):
+    @pytest.fixture(scope="class")
+    def macros(self):
+        return {"temp_macro.sql": macros_missing_column}
+
+    @pytest.fixture(scope="class")
+    def models(self):
+        return {"test_column.sql": models__missing_column}
+
+    @pytest.fixture(scope="class")
+    def project_config_update(self):
+        return {
+            # The create and drop table statements here validate that these hooks run
+            # in the same order that they are defined. Drop before create is an error.
+            # Also check that the table does not exist below.
+            "on-run-start": "- {{ export_table_check() }}"
+        }
+
+    def test_missing_column_pre_hook(self, project):
+        run_dbt(["run"], expect_pass=False)