Skip to content

Commit

Permalink
feat(hogql): Allow a placeholder to be used in place of a select stat…
Browse files Browse the repository at this point in the history
…ement (#19767)

* Allow a placeholder to be used in place of a select statement in a union all

* Updated the hogql parser version

* Use new hogql-parser version

* Fixed tests

* Update query snapshots

* Update query snapshots

* Rejigged the g4 file

* Updated mypy

* Updated mypy

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
Gilbert09 and github-actions[bot] authored Jan 18, 2024
1 parent 9104b58 commit d946f66
Show file tree
Hide file tree
Showing 15 changed files with 1,967 additions and 1,933 deletions.
1,923 changes: 967 additions & 956 deletions hogql_parser/HogQLParser.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions hogql_parser/HogQLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ class HogQLParser : public antlr4::Parser {
antlr4::tree::TerminalNode *LPAREN();
SelectUnionStmtContext *selectUnionStmt();
antlr4::tree::TerminalNode *RPAREN();
PlaceholderContext *placeholder();


virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
Expand Down
2 changes: 1 addition & 1 deletion hogql_parser/HogQLParser.interp

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions hogql_parser/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
if (select_stmt_ctx) {
return visit(select_stmt_ctx);
}

auto placeholder_ctx = ctx->placeholder();
if (placeholder_ctx) {
return visitAsPyObject(placeholder_ctx);
}

return visit(ctx->selectUnionStmt());
}

Expand Down Expand Up @@ -344,6 +350,9 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
int extend_code = X_PyList_Extend(flattened_queries, sub_select_queries);
if (extend_code == -1) goto select_queries_loop_py_error;
Py_DECREF(sub_select_queries);
} else if (is_ast_node_instance(query, "Placeholder")) {
int append_code = PyList_Append(flattened_queries, query);
if (append_code == -1) goto select_queries_loop_py_error;
} else {
Py_DECREF(flattened_queries);
X_Py_DECREF_ALL(select_queries);
Expand Down
2 changes: 1 addition & 1 deletion hogql_parser/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

setup(
name="hogql_parser",
version="1.0.2",
version="1.0.3",
url="https://github.com/PostHog/posthog/tree/master/hogql_parser",
author="PostHog Inc.",
author_email="[email protected]",
Expand Down
1 change: 1 addition & 0 deletions mypy-baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ posthog/hogql/database/schema/persons.py:0: error: Argument "chain" to "Field" h
posthog/hogql/database/schema/persons.py:0: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
posthog/hogql/database/schema/persons.py:0: note: Consider using "Sequence" instead, which is covariant
posthog/hogql/parser.py:0: error: Key expression in dictionary comprehension has incompatible type "str"; expected type "Literal['expr', 'order_expr', 'select']" [misc]
posthog/hogql/parser.py:0: error: Statement is unreachable [unreachable]
posthog/hogql/parser.py:0: error: Item "None" of "list[Expr] | None" has no attribute "__iter__" (not iterable) [union-attr]
posthog/hogql/parser.py:0: error: "None" has no attribute "text" [attr-defined]
posthog/hogql/parser.py:0: error: "None" has no attribute "text" [attr-defined]
Expand Down
3 changes: 2 additions & 1 deletion posthog/hogql/grammar/HogQLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ options {
select: (selectUnionStmt | selectStmt | hogqlxTagElement) EOF;

selectUnionStmt: selectStmtWithParens (UNION ALL selectStmtWithParens)*;
selectStmtWithParens: selectStmt | LPAREN selectUnionStmt RPAREN;
selectStmtWithParens: selectStmt | LPAREN selectUnionStmt RPAREN | placeholder;

selectStmt:
with=withClause?
SELECT DISTINCT? topClause?
Expand Down
2 changes: 1 addition & 1 deletion posthog/hogql/grammar/HogQLParser.interp

Large diffs are not rendered by default.

1,943 changes: 976 additions & 967 deletions posthog/hogql/grammar/HogQLParser.py

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion posthog/hogql/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,16 @@ def visitSelectUnionStmt(self, ctx: HogQLParser.SelectUnionStmtContext):
flattened_queries.append(query)
elif isinstance(query, ast.SelectUnionQuery):
flattened_queries.extend(query.select_queries)
elif isinstance(query, ast.Placeholder):
flattened_queries.append(query)
else:
raise Exception(f"Unexpected query node type {type(query).__name__}")
if len(flattened_queries) == 1:
return flattened_queries[0]
return ast.SelectUnionQuery(select_queries=flattened_queries)

def visitSelectStmtWithParens(self, ctx: HogQLParser.SelectStmtWithParensContext):
return self.visit(ctx.selectStmt() or ctx.selectUnionStmt())
return self.visit(ctx.selectStmt() or ctx.selectUnionStmt() or ctx.placeholder())

def visitSelectStmt(self, ctx: HogQLParser.SelectStmtContext):
select_query = ast.SelectQuery(
Expand Down
2 changes: 1 addition & 1 deletion posthog/hogql/test/_test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1467,7 +1467,7 @@ def test_malformed_sql(self):
query = "SELEC 2"
with self.assertRaisesMessage(
SyntaxException,
"mismatched input 'SELEC' expecting {SELECT, WITH, '(', '<'}",
"mismatched input 'SELEC' expecting {SELECT, WITH, '{', '(', '<'}",
) as e:
self._select(query)
self.assertEqual(e.exception.start, 0)
Expand Down
2 changes: 1 addition & 1 deletion posthog/hogql/test/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def test_metadata_valid_expr_select(self):
"inputSelect": "timestamp",
"errors": [
{
"message": "mismatched input 'timestamp' expecting {SELECT, WITH, '(', '<'}",
"message": "mismatched input 'timestamp' expecting {SELECT, WITH, '{', '(', '<'}",
"start": 0,
"end": 9,
"fix": None,
Expand Down
2 changes: 1 addition & 1 deletion posthog/hogql_queries/insights/stickiness_query_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def to_query(self) -> List[ast.SelectQuery]: # type: ignore
SELECT 0 as aggregation_target, (number + 1) as num_intervals
FROM numbers(dateDiff({interval}, {date_from} - {interval_subtract}, {date_to}))
UNION ALL
SELECT * FROM ({events_query})
{events_query}
)
GROUP BY num_intervals
ORDER BY num_intervals
Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,5 @@ django-two-factor-auth==1.14.0
phonenumberslite==8.13.6
openai==0.27.8
nh3==0.2.14
hogql-parser==1.0.2
hogql-parser==1.0.3
urllib3[secure,socks]==1.26.18
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ h11==0.13.0
# via wsproto
hexbytes==1.0.0
# via dlt
hogql-parser==1.0.2
hogql-parser==1.0.3
# via -r requirements.in
humanize==4.9.0
# via dlt
Expand Down

0 comments on commit d946f66

Please sign in to comment.