diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json
index c626b70d76d06..d1eed070437ff 100644
--- a/frontend/src/queries/schema.json
+++ b/frontend/src/queries/schema.json
@@ -1541,38 +1541,51 @@
"additionalProperties": false,
"properties": {
"clickhouse": {
+ "description": "Executed ClickHouse query",
"type": "string"
},
"columns": {
+ "description": "Returned columns",
"items": {},
"type": "array"
},
+ "error": {
+ "description": "Query error. Returned only if 'explain' is true. Throws an error otherwise.",
+ "type": "string"
+ },
"explain": {
+ "description": "Query explanation output",
"items": {
"type": "string"
},
"type": "array"
},
"hogql": {
+ "description": "Generated HogQL query",
"type": "string"
},
"modifiers": {
- "$ref": "#/definitions/HogQLQueryModifiers"
+ "$ref": "#/definitions/HogQLQueryModifiers",
+ "description": "Modifiers used when performing the query"
},
"query": {
+ "description": "Input query string",
"type": "string"
},
"results": {
+ "description": "Query results",
"items": {},
"type": "array"
},
"timings": {
+ "description": "Measured timings for different parts of the query generation process",
"items": {
"$ref": "#/definitions/QueryTiming"
},
"type": "array"
},
"types": {
+ "description": "Types of returned columns",
"items": {},
"type": "array"
}
diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts
index 6e8fab0a961c9..fdba8908a3a71 100644
--- a/frontend/src/queries/schema.ts
+++ b/frontend/src/queries/schema.ts
@@ -139,14 +139,25 @@ export interface HogQLQueryModifiers {
}
export interface HogQLQueryResponse {
+ /** Input query string */
query?: string
+ /** Generated HogQL query */
hogql?: string
+ /** Executed ClickHouse query */
clickhouse?: string
+ /** Query results */
results?: any[]
- types?: any[]
+ /** Query error. Returned only if 'explain' is true. Throws an error otherwise. */
+ error?: string
+ /** Returned columns */
columns?: any[]
+ /** Types of returned columns */
+ types?: any[]
+ /** Measured timings for different parts of the query generation process */
timings?: QueryTiming[]
+ /** Query explanation output */
explain?: string[]
+ /** Modifiers used when performing the query */
modifiers?: HogQLQueryModifiers
}
diff --git a/frontend/src/scenes/debug/HogQLDebug.tsx b/frontend/src/scenes/debug/HogQLDebug.tsx
index cf5b662a3f8f1..21cc7b0b13104 100644
--- a/frontend/src/scenes/debug/HogQLDebug.tsx
+++ b/frontend/src/scenes/debug/HogQLDebug.tsx
@@ -91,6 +91,14 @@ export function HogQLDebug({ query, setQuery, queryKey }: HogQLDebugProps): JSX.
>
) : (
<>
+ {response?.error ? (
+ <>
+
Error Running Query!
+
+ {response.error}
+
+ >
+ ) : null}
{response?.hogql ? (
<>
Executed HogQL
diff --git a/posthog/hogql/query.py b/posthog/hogql/query.py
index 697305d0ae964..c7e8c82713b15 100644
--- a/posthog/hogql/query.py
+++ b/posthog/hogql/query.py
@@ -1,6 +1,7 @@
from typing import Dict, Optional, Union, cast
from posthog.clickhouse.client.connection import Workload
+from posthog.errors import ExposedCHQueryError
from posthog.hogql import ast
from posthog.hogql.constants import HogQLGlobalSettings
from posthog.hogql.errors import HogQLException
@@ -143,16 +144,27 @@ def execute_hogql_query(
timings=timings_dict,
)
- results, types = sync_execute(
- clickhouse_sql,
- clickhouse_context.values,
- with_column_types=True,
- workload=workload,
- team_id=team.pk,
- readonly=True,
- )
+ error = None
+ try:
+ results, types = sync_execute(
+ clickhouse_sql,
+ clickhouse_context.values,
+ with_column_types=True,
+ workload=workload,
+ team_id=team.pk,
+ readonly=True,
+ )
+ except Exception as e:
+ if explain:
+ results, types = None, None
+ if isinstance(e, ExposedCHQueryError) or isinstance(e, HogQLException):
+ error = str(e)
+ else:
+ error = "Unknown error"
+ else:
+ raise e
- if explain:
+ if explain and error is None: # If the query errored, explain will fail as well.
with timings.measure("explain"):
explain_results = sync_execute(
f"EXPLAIN {clickhouse_sql}",
@@ -170,6 +182,7 @@ def execute_hogql_query(
query=query,
hogql=hogql,
clickhouse=clickhouse_sql,
+ error=error,
timings=timings.to_list(),
results=results,
columns=print_columns,
diff --git a/posthog/hogql_queries/hogql_query_runner.py b/posthog/hogql_queries/hogql_query_runner.py
index 576419fdff967..4326a2ba7dbee 100644
--- a/posthog/hogql_queries/hogql_query_runner.py
+++ b/posthog/hogql_queries/hogql_query_runner.py
@@ -65,6 +65,7 @@ def calculate(self) -> HogQLQueryResponse:
workload=Workload.ONLINE,
timings=self.timings,
in_export_context=self.in_export_context,
+ explain=bool(self.query.explain),
)
def _is_stale(self, cached_result_package):
diff --git a/posthog/schema.py b/posthog/schema.py
index 77bbd8cb01cb3..3b86559f8fc78 100644
--- a/posthog/schema.py
+++ b/posthog/schema.py
@@ -830,15 +830,22 @@ class HogQLQueryResponse(BaseModel):
model_config = ConfigDict(
extra="forbid",
)
- clickhouse: Optional[str] = None
- columns: Optional[List] = None
- explain: Optional[List[str]] = None
- hogql: Optional[str] = None
- modifiers: Optional[HogQLQueryModifiers] = None
- query: Optional[str] = None
- results: Optional[List] = None
- timings: Optional[List[QueryTiming]] = None
- types: Optional[List] = None
+ clickhouse: Optional[str] = Field(default=None, description="Executed ClickHouse query")
+ columns: Optional[List] = Field(default=None, description="Returned columns")
+ error: Optional[str] = Field(
+ default=None, description="Query error. Returned only if 'explain' is true. Throws an error otherwise."
+ )
+ explain: Optional[List[str]] = Field(default=None, description="Query explanation output")
+ hogql: Optional[str] = Field(default=None, description="Generated HogQL query")
+ modifiers: Optional[HogQLQueryModifiers] = Field(
+ default=None, description="Modifiers used when performing the query"
+ )
+ query: Optional[str] = Field(default=None, description="Input query string")
+ results: Optional[List] = Field(default=None, description="Query results")
+ timings: Optional[List[QueryTiming]] = Field(
+ default=None, description="Measured timings for different parts of the query generation process"
+ )
+ types: Optional[List] = Field(default=None, description="Types of returned columns")
class LifecycleFilter(BaseModel):