We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error when running CSV download with InfluxDB as data source
Below is the error log for the Redash application.

Downloading CSV with InfluxDB as the data source causes an error.
The error log shows the following error
Since the test connection succeeds, we believe it is an application anomaly in Redash.

```
server_1            | KeyError: 'type'
server_1            | [2023-07-07 05:01:02,196][PID:9087][ERROR][redash.app] Exception on /api/queries/71/results/2093.csv [GET]
server_1            | Traceback (most recent call last):
server_1            |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
server_1            |     rv = self.dispatch_request()
server_1            |   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
server_1            |     return self.view_functions[rule.endpoint](**req.view_args)
server_1            |   File "/usr/local/lib/python3.7/site-packages/flask_restful/__init__.py", line 458, in wrapper
server_1            |     resp = resource(*args, **kwargs)
server_1            |   File "/usr/local/lib/python3.7/site-packages/flask_login/utils.py", line 261, in decorated_view
server_1            |     return func(*args, **kwargs)
server_1            |   File "/usr/local/lib/python3.7/site-packages/flask/views.py", line 89, in view
server_1            |     return self.dispatch_request(*args, **kwargs)
server_1            |   File "/app/redash/handlers/base.py", line 33, in dispatch_request
server_1            |     return super(BaseResource, self).dispatch_request(*args, **kwargs)
server_1            |   File "/usr/local/lib/python3.7/site-packages/flask_restful/__init__.py", line 573, in dispatch_request
server_1            |     resp = meth(*args, **kwargs)
server_1            |   File "/app/redash/permissions.py", line 71, in decorated
server_1            |     return fn(*args, **kwargs)
server_1            |   File "/app/redash/handlers/query_results.py", line 409, in get
server_1            |     response = response_builders[filetype](query_result)
server_1            |   File "/app/redash/handlers/query_results.py", line 439, in make_csv_response
server_1            |     serialize_query_result_to_dsv(query_result, ","), 200, headers
server_1            |   File "/app/redash/serializers/query_result.py", line 87, in serialize_query_result_to_dsv
server_1            |     fieldnames, special_columns = _get_column_lists(query_data["columns"] or [])
server_1            |   File "/app/redash/serializers/query_result.py", line 68, in _get_column_lists
server_1            |     if col["type"] == col_type:
server_1            | KeyError: 'type'
```
Below is the JSON data when CSV download fails.
(Some parts are excerpted due to the length of the file.)

```
{
  "query_result": {
    "id": 2093,
    "query": "SELECT\n    SUM(d3) / 60\nFROM\n    charger \nWHERE\n    chg_id = '1'\n    AND time >= '2023-04-30T15:00:00Z'\n    AND time < '2023-05-03T15:00:00Z'\nGROUP BY chg_id, time(15m) fill(linear)\nlimit 10;",
    "data": {
      "columns": [{ "name": "time" }, { "name": "sum" }, { "name": "chg_id" }],
      "rows": [
        { "time": "2023-04-30T15:00:00Z", "sum": 0, "chg_id": "1" },
      ]
    }
  }
}
```

Below is the JSON data for a successful CSV download.

```
      "columns": [
        { "name": "id", "friendly_name": "id", "type": "integer" },
        {
          "name": "project_id",
          "friendly_name": "project_id",
          "type": "integer"
        },
        { "name": "name", "friendly_name": "name", "type": "string" },
        { "name": "filters", "friendly_name": "filters", "type": null },
        { "name": "user_id", "friendly_name": "user_id", "type": "integer" },
        {
          "name": "column_names",
          "friendly_name": "column_names",
          "type": null
        },
        {
```
In the normal case, an element is generated for each column,
In the case of failed data, three columns made up one element.
Therefore, the required type element was missing, resulting in a program error.
The following is a part of the program that caused the error.
```
    for col in columns:
        fieldnames.append(col["name"])

        for col_type in special_types.keys():
            if col["type"] == col_type:
                special_columns[col["name"]] = special_types[col_type]
```
This is just a guess, I am guessing, but it seems to me that the SQL results being executed against InfluxDB are special and are not being converted properly in Redash, which is what is causing the problem.

All text translated by Google Translate.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redash/query_runner/influx_db.py`
Content:
```
1 import logging
2 
3 from redash.query_runner import BaseQueryRunner, register
4 from redash.utils import json_dumps
5 
6 logger = logging.getLogger(__name__)
7 
8 try:
9     from influxdb import InfluxDBClient
10 
11     enabled = True
12 
13 except ImportError:
14     enabled = False
15 
16 
17 def _transform_result(results):
18     result_columns = []
19     result_rows = []
20 
21     for result in results:
22         for series in result.raw.get("series", []):
23             for column in series["columns"]:
24                 if column not in result_columns:
25                     result_columns.append(column)
26             tags = series.get("tags", {})
27             for key in tags.keys():
28                 if key not in result_columns:
29                     result_columns.append(key)
30 
31     for result in results:
32         for series in result.raw.get("series", []):
33             for point in series["values"]:
34                 result_row = {}
35                 for column in result_columns:
36                     tags = series.get("tags", {})
37                     if column in tags:
38                         result_row[column] = tags[column]
39                     elif column in series["columns"]:
40                         index = series["columns"].index(column)
41                         value = point[index]
42                         result_row[column] = value
43                 result_rows.append(result_row)
44 
45     return json_dumps({"columns": [{"name": c} for c in result_columns], "rows": result_rows})
46 
47 
48 class InfluxDB(BaseQueryRunner):
49     should_annotate_query = False
50     noop_query = "show measurements limit 1"
51 
52     @classmethod
53     def configuration_schema(cls):
54         return {
55             "type": "object",
56             "properties": {"url": {"type": "string"}},
57             "required": ["url"],
58         }
59 
60     @classmethod
61     def enabled(cls):
62         return enabled
63 
64     @classmethod
65     def type(cls):
66         return "influxdb"
67 
68     def run_query(self, query, user):
69         client = InfluxDBClient.from_dsn(self.configuration["url"])
70 
71         logger.debug("influxdb url: %s", self.configuration["url"])
72         logger.debug("influxdb got query: %s", query)
73 
74         try:
75             results = client.query(query)
76             if not isinstance(results, list):
77                 results = [results]
78 
79             json_data = _transform_result(results)
80             error = None
81         except Exception as ex:
82             json_data = None
83             error = str(ex)
84 
85         return json_data, error
86 
87 
88 register(InfluxDB)
89
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/redash/query_runner/influx_db.py b/redash/query_runner/influx_db.py
--- a/redash/query_runner/influx_db.py
+++ b/redash/query_runner/influx_db.py
@@ -1,6 +1,12 @@
 import logging
 
-from redash.query_runner import BaseQueryRunner, register
+from redash.query_runner import (
+    TYPE_FLOAT,
+    TYPE_INTEGER,
+    TYPE_STRING,
+    BaseQueryRunner,
+    register,
+)
 from redash.utils import json_dumps
 
 logger = logging.getLogger(__name__)
@@ -14,25 +20,36 @@
     enabled = False
 
 
+TYPES_MAP = {
+    str: TYPE_STRING,
+    int: TYPE_INTEGER,
+    float: TYPE_FLOAT,
+}
+
+
+def _get_type(value):
+    return TYPES_MAP.get(type(value), TYPE_STRING)
+
+
 def _transform_result(results):
-    result_columns = []
+    column_names = []
     result_rows = []
 
     for result in results:
         for series in result.raw.get("series", []):
             for column in series["columns"]:
-                if column not in result_columns:
-                    result_columns.append(column)
+                if column not in column_names:
+                    column_names.append(column)
             tags = series.get("tags", {})
             for key in tags.keys():
-                if key not in result_columns:
-                    result_columns.append(key)
+                if key not in column_names:
+                    column_names.append(key)
 
     for result in results:
         for series in result.raw.get("series", []):
             for point in series["values"]:
                 result_row = {}
-                for column in result_columns:
+                for column in column_names:
                     tags = series.get("tags", {})
                     if column in tags:
                         result_row[column] = tags[column]
@@ -42,7 +59,12 @@
                         result_row[column] = value
                 result_rows.append(result_row)
 
-    return json_dumps({"columns": [{"name": c} for c in result_columns], "rows": result_rows})
+    if len(result_rows) > 0:
+        result_columns = [{"name": c, "type": _get_type(result_rows[0][c])} for c in result_rows[0].keys()]
+    else:
+        result_columns = [{"name": c, "type": TYPE_STRING} for c in column_names]
+
+    return json_dumps({"columns": result_columns, "rows": result_rows})
 
 
 class InfluxDB(BaseQueryRunner):

{"golden_diff": "diff --git a/redash/query_runner/influx_db.py b/redash/query_runner/influx_db.py\n--- a/redash/query_runner/influx_db.py\n+++ b/redash/query_runner/influx_db.py\n@@ -1,6 +1,12 @@\n import logging\n \n-from redash.query_runner import BaseQueryRunner, register\n+from redash.query_runner import (\n+    TYPE_FLOAT,\n+    TYPE_INTEGER,\n+    TYPE_STRING,\n+    BaseQueryRunner,\n+    register,\n+)\n from redash.utils import json_dumps\n \n logger = logging.getLogger(__name__)\n@@ -14,25 +20,36 @@\n     enabled = False\n \n \n+TYPES_MAP = {\n+    str: TYPE_STRING,\n+    int: TYPE_INTEGER,\n+    float: TYPE_FLOAT,\n+}\n+\n+\n+def _get_type(value):\n+    return TYPES_MAP.get(type(value), TYPE_STRING)\n+\n+\n def _transform_result(results):\n-    result_columns = []\n+    column_names = []\n     result_rows = []\n \n     for result in results:\n         for series in result.raw.get(\"series\", []):\n             for column in series[\"columns\"]:\n-                if column not in result_columns:\n-                    result_columns.append(column)\n+                if column not in column_names:\n+                    column_names.append(column)\n             tags = series.get(\"tags\", {})\n             for key in tags.keys():\n-                if key not in result_columns:\n-                    result_columns.append(key)\n+                if key not in column_names:\n+                    column_names.append(key)\n \n     for result in results:\n         for series in result.raw.get(\"series\", []):\n             for point in series[\"values\"]:\n                 result_row = {}\n-                for column in result_columns:\n+                for column in column_names:\n                     tags = series.get(\"tags\", {})\n                     if column in tags:\n                         result_row[column] = tags[column]\n@@ -42,7 +59,12 @@\n                         result_row[column] = value\n                 result_rows.append(result_row)\n \n-    return json_dumps({\"columns\": [{\"name\": c} for c in result_columns], \"rows\": result_rows})\n+    if len(result_rows) > 0:\n+        result_columns = [{\"name\": c, \"type\": _get_type(result_rows[0][c])} for c in result_rows[0].keys()]\n+    else:\n+        result_columns = [{\"name\": c, \"type\": TYPE_STRING} for c in column_names]\n+\n+    return json_dumps({\"columns\": result_columns, \"rows\": result_rows})\n \n \n class InfluxDB(BaseQueryRunner):\n", "issue": "Error when running CSV download with InfluxDB as data source\nBelow is the error log for the Redash application.\r\n\r\nDownloading CSV with InfluxDB as the data source causes an error.\r\nThe error log shows the following error\r\nSince the test connection succeeds, we believe it is an application anomaly in Redash.\r\n\r\n```\r\nserver_1            | KeyError: 'type'\r\nserver_1            | [2023-07-07 05:01:02,196][PID:9087][ERROR][redash.app] Exception on /api/queries/71/results/2093.csv [GET]\r\nserver_1            | Traceback (most recent call last):\r\nserver_1            |   File \"/usr/local/lib/python3.7/site-packages/flask/app.py\", line 1949, in full_dispatch_request\r\nserver_1            |     rv = self.dispatch_request()\r\nserver_1            |   File \"/usr/local/lib/python3.7/site-packages/flask/app.py\", line 1935, in dispatch_request\r\nserver_1            |     return self.view_functions[rule.endpoint](**req.view_args)\r\nserver_1            |   File \"/usr/local/lib/python3.7/site-packages/flask_restful/__init__.py\", line 458, in wrapper\r\nserver_1            |     resp = resource(*args, **kwargs)\r\nserver_1            |   File \"/usr/local/lib/python3.7/site-packages/flask_login/utils.py\", line 261, in decorated_view\r\nserver_1            |     return func(*args, **kwargs)\r\nserver_1            |   File \"/usr/local/lib/python3.7/site-packages/flask/views.py\", line 89, in view\r\nserver_1            |     return self.dispatch_request(*args, **kwargs)\r\nserver_1            |   File \"/app/redash/handlers/base.py\", line 33, in dispatch_request\r\nserver_1            |     return super(BaseResource, self).dispatch_request(*args, **kwargs)\r\nserver_1            |   File \"/usr/local/lib/python3.7/site-packages/flask_restful/__init__.py\", line 573, in dispatch_request\r\nserver_1            |     resp = meth(*args, **kwargs)\r\nserver_1            |   File \"/app/redash/permissions.py\", line 71, in decorated\r\nserver_1            |     return fn(*args, **kwargs)\r\nserver_1            |   File \"/app/redash/handlers/query_results.py\", line 409, in get\r\nserver_1            |     response = response_builders[filetype](query_result)\r\nserver_1            |   File \"/app/redash/handlers/query_results.py\", line 439, in make_csv_response\r\nserver_1            |     serialize_query_result_to_dsv(query_result, \",\"), 200, headers\r\nserver_1            |   File \"/app/redash/serializers/query_result.py\", line 87, in serialize_query_result_to_dsv\r\nserver_1            |     fieldnames, special_columns = _get_column_lists(query_data[\"columns\"] or [])\r\nserver_1            |   File \"/app/redash/serializers/query_result.py\", line 68, in _get_column_lists\r\nserver_1            |     if col[\"type\"] == col_type:\r\nserver_1            | KeyError: 'type'\r\n```\r\nBelow is the JSON data when CSV download fails.\r\n(Some parts are excerpted due to the length of the file.)\r\n\r\n```\r\n{\r\n  \"query_result\": {\r\n    \"id\": 2093,\r\n    \"query\": \"SELECT\\n    SUM(d3) / 60\\nFROM\\n    charger \\nWHERE\\n    chg_id = '1'\\n    AND time >= '2023-04-30T15:00:00Z'\\n    AND time < '2023-05-03T15:00:00Z'\\nGROUP BY chg_id, time(15m) fill(linear)\\nlimit 10;\",\r\n    \"data\": {\r\n      \"columns\": [{ \"name\": \"time\" }, { \"name\": \"sum\" }, { \"name\": \"chg_id\" }],\r\n      \"rows\": [\r\n        { \"time\": \"2023-04-30T15:00:00Z\", \"sum\": 0, \"chg_id\": \"1\" },\r\n      ]\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nBelow is the JSON data for a successful CSV download.\r\n\r\n```\r\n      \"columns\": [\r\n        { \"name\": \"id\", \"friendly_name\": \"id\", \"type\": \"integer\" },\r\n        {\r\n          \"name\": \"project_id\",\r\n          \"friendly_name\": \"project_id\",\r\n          \"type\": \"integer\"\r\n        },\r\n        { \"name\": \"name\", \"friendly_name\": \"name\", \"type\": \"string\" },\r\n        { \"name\": \"filters\", \"friendly_name\": \"filters\", \"type\": null },\r\n        { \"name\": \"user_id\", \"friendly_name\": \"user_id\", \"type\": \"integer\" },\r\n        {\r\n          \"name\": \"column_names\",\r\n          \"friendly_name\": \"column_names\",\r\n          \"type\": null\r\n        },\r\n        {\r\n```\r\nIn the normal case, an element is generated for each column,\r\nIn the case of failed data, three columns made up one element.\r\nTherefore, the required type element was missing, resulting in a program error.\r\nThe following is a part of the program that caused the error.\r\n```\r\n    for col in columns:\r\n        fieldnames.append(col[\"name\"])\r\n\r\n        for col_type in special_types.keys():\r\n            if col[\"type\"] == col_type:\r\n                special_columns[col[\"name\"]] = special_types[col_type]\r\n```\r\nThis is just a guess, I am guessing, but it seems to me that the SQL results being executed against InfluxDB are special and are not being converted properly in Redash, which is what is causing the problem.\r\n\r\nAll text translated by Google Translate.\n", "before_files": [{"content": "import logging\n\nfrom redash.query_runner import BaseQueryRunner, register\nfrom redash.utils import json_dumps\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    from influxdb import InfluxDBClient\n\n    enabled = True\n\nexcept ImportError:\n    enabled = False\n\n\ndef _transform_result(results):\n    result_columns = []\n    result_rows = []\n\n    for result in results:\n        for series in result.raw.get(\"series\", []):\n            for column in series[\"columns\"]:\n                if column not in result_columns:\n                    result_columns.append(column)\n            tags = series.get(\"tags\", {})\n            for key in tags.keys():\n                if key not in result_columns:\n                    result_columns.append(key)\n\n    for result in results:\n        for series in result.raw.get(\"series\", []):\n            for point in series[\"values\"]:\n                result_row = {}\n                for column in result_columns:\n                    tags = series.get(\"tags\", {})\n                    if column in tags:\n                        result_row[column] = tags[column]\n                    elif column in series[\"columns\"]:\n                        index = series[\"columns\"].index(column)\n                        value = point[index]\n                        result_row[column] = value\n                result_rows.append(result_row)\n\n    return json_dumps({\"columns\": [{\"name\": c} for c in result_columns], \"rows\": result_rows})\n\n\nclass InfluxDB(BaseQueryRunner):\n    should_annotate_query = False\n    noop_query = \"show measurements limit 1\"\n\n    @classmethod\n    def configuration_schema(cls):\n        return {\n            \"type\": \"object\",\n            \"properties\": {\"url\": {\"type\": \"string\"}},\n            \"required\": [\"url\"],\n        }\n\n    @classmethod\n    def enabled(cls):\n        return enabled\n\n    @classmethod\n    def type(cls):\n        return \"influxdb\"\n\n    def run_query(self, query, user):\n        client = InfluxDBClient.from_dsn(self.configuration[\"url\"])\n\n        logger.debug(\"influxdb url: %s\", self.configuration[\"url\"])\n        logger.debug(\"influxdb got query: %s\", query)\n\n        try:\n            results = client.query(query)\n            if not isinstance(results, list):\n                results = [results]\n\n            json_data = _transform_result(results)\n            error = None\n        except Exception as ex:\n            json_data = None\n            error = str(ex)\n\n        return json_data, error\n\n\nregister(InfluxDB)\n", "path": "redash/query_runner/influx_db.py"}], "after_files": [{"content": "import logging\n\nfrom redash.query_runner import (\n    TYPE_FLOAT,\n    TYPE_INTEGER,\n    TYPE_STRING,\n    BaseQueryRunner,\n    register,\n)\nfrom redash.utils import json_dumps\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    from influxdb import InfluxDBClient\n\n    enabled = True\n\nexcept ImportError:\n    enabled = False\n\n\nTYPES_MAP = {\n    str: TYPE_STRING,\n    int: TYPE_INTEGER,\n    float: TYPE_FLOAT,\n}\n\n\ndef _get_type(value):\n    return TYPES_MAP.get(type(value), TYPE_STRING)\n\n\ndef _transform_result(results):\n    column_names = []\n    result_rows = []\n\n    for result in results:\n        for series in result.raw.get(\"series\", []):\n            for column in series[\"columns\"]:\n                if column not in column_names:\n                    column_names.append(column)\n            tags = series.get(\"tags\", {})\n            for key in tags.keys():\n                if key not in column_names:\n                    column_names.append(key)\n\n    for result in results:\n        for series in result.raw.get(\"series\", []):\n            for point in series[\"values\"]:\n                result_row = {}\n                for column in column_names:\n                    tags = series.get(\"tags\", {})\n                    if column in tags:\n                        result_row[column] = tags[column]\n                    elif column in series[\"columns\"]:\n                        index = series[\"columns\"].index(column)\n                        value = point[index]\n                        result_row[column] = value\n                result_rows.append(result_row)\n\n    if len(result_rows) > 0:\n        result_columns = [{\"name\": c, \"type\": _get_type(result_rows[0][c])} for c in result_rows[0].keys()]\n    else:\n        result_columns = [{\"name\": c, \"type\": TYPE_STRING} for c in column_names]\n\n    return json_dumps({\"columns\": result_columns, \"rows\": result_rows})\n\n\nclass InfluxDB(BaseQueryRunner):\n    should_annotate_query = False\n    noop_query = \"show measurements limit 1\"\n\n    @classmethod\n    def configuration_schema(cls):\n        return {\n            \"type\": \"object\",\n            \"properties\": {\"url\": {\"type\": \"string\"}},\n            \"required\": [\"url\"],\n        }\n\n    @classmethod\n    def enabled(cls):\n        return enabled\n\n    @classmethod\n    def type(cls):\n        return \"influxdb\"\n\n    def run_query(self, query, user):\n        client = InfluxDBClient.from_dsn(self.configuration[\"url\"])\n\n        logger.debug(\"influxdb url: %s\", self.configuration[\"url\"])\n        logger.debug(\"influxdb got query: %s\", query)\n\n        try:\n            results = client.query(query)\n            if not isinstance(results, list):\n                results = [results]\n\n            json_data = _transform_result(results)\n            error = None\n        except Exception as ex:\n            json_data = None\n            error = str(ex)\n\n        return json_data, error\n\n\nregister(InfluxDB)\n", "path": "redash/query_runner/influx_db.py"}]}