Repository: dbt-labs/dbt-core

Issue:
[Bug] Update command-based logic in `compiled_code` context member, to match `sql`
### 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

In https://github.com/dbt-labs/dbt-core/pull/9040, we added `defer_relation` for all tasks that perform deferral.

Previously, there was dynamic routing logic in the `sql` + `compiled_code` context members that checked for the existnece of `defer_relation` and overwrote the "compiled code" to use during `clone`.

#9040 updated the dynamic routing logic for the `sql` context member (based on the running task), but not the `compiled_code` context member.

These need to be the same. Otherwise, I might `build` a model with deferral and, depending on whether the materialization uses `{{ sql }}` or `{{ compiled_code }}`, get a different (inconsistent) result.

### Expected Behavior

There should be no difference between `{{ sql }}` and `{{ compiled_code }}` for SQL models.

### Steps To Reproduce

1. dbt-core `main` branch
2. Use a materialization that calls `compiled_code` instead of `sql`, e.g. [dbt-snowflake's incremental materialization](https://github.com/dbt-labs/dbt-snowflake/blob/4b2d93808e1b852aec3b1c281fa0af231b0cf297/dbt/include/snowflake/macros/materializations/incremental.sql#L83)

### Relevant log output

```
$ dbt build --debug
...
create or replace transient table ANALYTICS_DEV.dbt_jcohen.dim_certification_candidates
         as
        (select * from analytics.analytics.dim_certification_candidates
        );
```

That ain't right!

### Environment

```markdown
dbt: main
```


### Which database adapter are you using with dbt?

_No response_

### Additional Context

_No response_


Discussion:

Resolving patch:
diff --git a/core/dbt/context/providers.py b/core/dbt/context/providers.py
--- a/core/dbt/context/providers.py
+++ b/core/dbt/context/providers.py
@@ -1457,27 +1457,10 @@ def post_hooks(self) -> List[Dict[str, Any]]:
             h.to_dict(omit_none=True) for h in self.model.config.post_hook  # type: ignore[union-attr] # noqa
         ]
 
-    @contextproperty()
-    def sql(self) -> Optional[str]:
-        # only doing this in sql model for backward compatible
-        if self.model.language == ModelLanguage.sql:  # type: ignore[union-attr]
-            # If the model is deferred and the adapter doesn't support zero-copy cloning, then select * from the prod
-            # relation
-            # TODO: avoid routing on args.which if possible
-            if getattr(self.model, "defer_relation", None) and self.config.args.which == "clone":
-                # TODO https://github.com/dbt-labs/dbt-core/issues/7976
-                return f"select * from {self.model.defer_relation.relation_name or str(self.defer_relation)}"  # type: ignore[union-attr]
-            elif getattr(self.model, "extra_ctes_injected", None):
-                # TODO CT-211
-                return self.model.compiled_code  # type: ignore[union-attr]
-            else:
-                return None
-        else:
-            return None
-
     @contextproperty()
     def compiled_code(self) -> Optional[str]:
-        if getattr(self.model, "defer_relation", None):
+        # TODO: avoid routing on args.which if possible
+        if getattr(self.model, "defer_relation", None) and self.config.args.which == "clone":
             # TODO https://github.com/dbt-labs/dbt-core/issues/7976
             return f"select * from {self.model.defer_relation.relation_name or str(self.defer_relation)}"  # type: ignore[union-attr]
         elif getattr(self.model, "extra_ctes_injected", None):
@@ -1486,6 +1469,14 @@ def compiled_code(self) -> Optional[str]:
         else:
             return None
 
+    @contextproperty()
+    def sql(self) -> Optional[str]:
+        # only set this for sql models, for backward compatibility
+        if self.model.language == ModelLanguage.sql:  # type: ignore[union-attr]
+            return self.compiled_code
+        else:
+            return None
+
     @contextproperty()
     def database(self) -> str:
         return getattr(self.model, "database", self.config.credentials.database)


Test patch: