Repository: dbt-labs/dbt-core

Issue:
[CT-1886] Include `adapter_response` in `RunResultMsg`
`adapter_response` is being skipped during message serialization for execution results:

```bash
$ dbt --debug --log-format json run | jq .data.run_result.adapter_response
...
{}
...
```

https://github.com/dbt-labs/dbt-core/blob/7b464b8a4957ec7969f19234020e110be1987923/core/dbt/contracts/results.py#L132-L142

### Reproduction case

I drop a breakpoint in here:

https://github.com/dbt-labs/dbt-core/blob/7b464b8a4957ec7969f19234020e110be1987923/core/dbt/task/runnable.py#L232-L237

```python
ipdb> result.adapter_response
{'_message': 'CREATE VIEW (0 processed)', 'code': 'CREATE VIEW', 'bytes_processed': 0, 'location': 'US', 'project_id': 'dbt-test-env', 'job_id': 'b70d9a54-38a6-4e21-ad07-d08e75ace428', 'slot_ms': 0}
ipdb> result.to_msg()
RunResultMsg(status='success', message='CREATE VIEW (0 processed)', timing_info=[TimingInfoMsg(name='compile', started_at=datetime.datetime(2023, 1, 24, 10, 38, 9, 423443), completed_at=datetime.datetime(2023, 1, 24, 10, 38, 9, 426940)), TimingInfoMsg(name='execute', started_at=datetime.datetime(2023, 1, 24, 10, 38, 9, 427426), completed_at=datetime.datetime(2023, 1, 24, 10, 38, 10, 391500))], thread='Thread-1 (worker)', execution_time=0.9696011543273926, adapter_response={}, num_failures=0)
ipdb> result.to_msg().adapter_response
{}
```

### Previous behavior

This does represent a functional regression in v1.4, relative to v1.3, when we used different serialization (mashumaro) for the `result`:
```bash
$ dbt --debug --log-format json run | jq .data.run_result.adapter_response
...
{
  "_message": "CREATE VIEW (0 processed)",
  "bytes_processed": 0,
  "code": "CREATE VIEW",
  "job_id": "9180b859-944f-4f91-a559-d772e0697c6d",
  "location": "US",
  "project_id": "dbt-test-env",
  "slot_ms": 0
}
...
```
Same breakpoint as above:
```python
ipdb> result.to_dict()['adapter_response']
{'_message': 'CREATE VIEW', 'code': 'CREATE VIEW', 'rows_affected': -1}
```


Discussion:
I'm guessing this is because our proto representation of `adapter_response` can only be a dictionary with string keys + string values (given limited proto type system):

https://github.com/dbt-labs/dbt-core/blob/main/core/dbt/events/types.proto#L48

https://github.com/dbt-labs/dbt-core/blob/7b464b8a4957ec7969f19234020e110be1987923/core/dbt/events/proto_types.py#L64-L66

Still, this would be better than not including the data at all:
```python
     msg.timing_info = [ti.to_msg() for ti in self.timing] 
     msg.adapter_response = {k: str(v) for k, v in self.adapter_response.items()}
     return msg 
```
```bash
$ dbt --debug --log-format json run | jq .data.run_result.adapter_response
...
{
  "_message": "CREATE VIEW (0 processed)",
  "bytes_processed": "0",
  "code": "CREATE VIEW",
  "job_id": "10588bfb-7857-432a-950e-926e10c00a9e",
  "location": "US",
  "project_id": "dbt-test-env",
  "slot_ms": "0"
}
...
```

Resolving patch:
diff --git a/core/dbt/contracts/results.py b/core/dbt/contracts/results.py
--- a/core/dbt/contracts/results.py
+++ b/core/dbt/contracts/results.py
@@ -13,7 +13,7 @@
 from dbt.events.proto_types import RunResultMsg, TimingInfoMsg
 from dbt.events.contextvars import get_node_info
 from dbt.logger import TimingProcessor
-from dbt.utils import lowercase, cast_to_str, cast_to_int
+from dbt.utils import lowercase, cast_to_str, cast_to_int, cast_dict_to_dict_of_strings
 from dbt.dataclass_schema import dbtClassMixin, StrEnum
 
 import agate
@@ -130,7 +130,6 @@ def __pre_deserialize__(cls, data):
         return data
 
     def to_msg(self):
-        # TODO: add more fields
         msg = RunResultMsg()
         msg.status = str(self.status)
         msg.message = cast_to_str(self.message)
@@ -138,7 +137,7 @@ def to_msg(self):
         msg.execution_time = self.execution_time
         msg.num_failures = cast_to_int(self.failures)
         msg.timing_info = [ti.to_msg() for ti in self.timing]
-        # adapter_response
+        msg.adapter_response = cast_dict_to_dict_of_strings(self.adapter_response)
         return msg
 
 


Test patch:
diff --git a/tests/functional/logging/test_logging.py b/tests/functional/logging/test_logging.py
--- a/tests/functional/logging/test_logging.py
+++ b/tests/functional/logging/test_logging.py
@@ -44,6 +44,7 @@ def test_basic(project, logs_dir):
             node_start = True
         if log_event == "NodeFinished":
             node_finished = True
+            assert log_data["run_result"]["adapter_response"]
         if node_start and not node_finished:
             if log_event == "NodeExecuting":
                 assert "node_info" in log_data