Repository: dbt-labs/dbt-core

Issue:
[CT-2737] [Bug] A misleading error message when contracts enabled but not implemented yet
### Is this a new bug in dbt-bigquery?

- [X] I believe this is a new bug in dbt-bigquery
- [X] I have searched the existing issues, and I could not find an existing issue for this bug

### Current Behavior
[edit] see https://github.com/dbt-labs/dbt-core/issues/7943

When `dbt run -s [model]` the query run in BigQuery is cut out resulting with an SyntaxError.

When examine GBQ run history, it's clear that the query that should be run is entirely cut out. dbt logs shows the same.

### Expected Behavior

Query should be equivalent with the compiled model.

### Steps To Reproduce

model name: `test.sql`
with file content:
```sql
select 1
```

run `dbt run -s test`


### Relevant log output

```shell
15:09:21  On model.hermod.fct__user_quiz_results: 
    select * from (
        select 1
    ) as __dbt_sbq
    where false
    limit 0
15:09:21  On model.hermod.fct__user_quiz_results: 
    select * from (
        select
    
    ) as __dbt_sbq
    where false
    limit 0
15:09:21  BigQuery adapter: Retry attempt 1 of 3 after error: BadRequest('Syntax error: Unexpected ")" at [5:5]')
...
```


### Environment

```markdown
- OS:
- Python:
- dbt-core: 1.5.1
- dbt-bigquery: 1.5.2
```


### Additional Context

this behaviour is present both in dbt cloud and when run locally.


Discussion:
Okay, I found the problem. Occurs when contracts are enabled but there is no entry in `schema.yml` yet.

So the real issue becomes a misleading error message instead. dbt should warn that the contract for the model is not implemented instead of throwing a SyntaxError
Thanks for reporting this @miloszbednarzak !

@jtcohen6 may provide additional commentary, but in the meantime, here are two different ways to reproduce what you reported:

<details>
<summary>

### Reprex 1</summary>

`dbt_project.yml`
```yaml
name: "my_project"
version: "1.0.0"
config-version: 2
profile: "sandcastle"
```

`models/my_model.sql`
```sql
{{ config(contract={"enforced": true})}}

select 1 as id
```

Run the model:
```shell
dbt run -s my_model
```
</details>

<details>
<summary>

### Reprex 2</summary>

`dbt_project.yml`
```yaml
name: "my_project"
version: "1.0.0"
config-version: 2
profile: "sandcastle"

models:
  my_project:
    +contract:
      enforced: true
```

`models/my_model.sql`
```sql
select 1 as id
```

Run the model:
```shell
dbt run -s my_model
```

</details>

Resolving patch:
diff --git a/core/dbt/exceptions.py b/core/dbt/exceptions.py
--- a/core/dbt/exceptions.py
+++ b/core/dbt/exceptions.py
@@ -2330,6 +2330,11 @@ def get_mismatches(self) -> agate.Table:
         return table_from_data_flat(mismatches_sorted, column_names)
 
     def get_message(self) -> str:
+        if not self.yaml_columns:
+            return (
+                "This model has an enforced contract, and its 'columns' specification is missing"
+            )
+
         table: agate.Table = self.get_mismatches()
         # Hack to get Agate table output as string
         output = io.StringIO()


Test patch:
diff --git a/tests/functional/configs/test_contract_configs.py b/tests/functional/configs/test_contract_configs.py
--- a/tests/functional/configs/test_contract_configs.py
+++ b/tests/functional/configs/test_contract_configs.py
@@ -471,3 +471,18 @@ def test__python_errors(self, project):
         exc_str = " ".join(str(err_info.value).split())
         expected_python_error = "Language Error: Expected 'sql' but found 'python'"
         assert expected_python_error in exc_str
+
+
+class TestModelContractMissingYAMLColumns:
+    @pytest.fixture(scope="class")
+    def models(self):
+        return {
+            "my_model.sql": my_model_contract_sql,
+        }
+
+    def test__missing_column_contract_error(self, project):
+        results = run_dbt(["run"], expect_pass=False)
+        expected_error = (
+            "This model has an enforced contract, and its 'columns' specification is missing"
+        )
+        assert expected_error in results[0].message