Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hogql): automatic person table joins #14286

Merged
merged 170 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 165 commits
Commits
Show all changes
170 commits
Select commit Hold shift + click to select a range
7e44195
feat(hogql): select statements
mariusandra Feb 8, 2023
a87115f
visitor
mariusandra Feb 8, 2023
c55f170
cleanup
mariusandra Feb 8, 2023
d47bc61
parse limit by
mariusandra Feb 8, 2023
839d505
parse limit by
mariusandra Feb 8, 2023
fef9463
merge limit clauses
mariusandra Feb 8, 2023
9cd96bc
Update snapshots
github-actions[bot] Feb 8, 2023
2d0d1e8
fix placeholders
mariusandra Feb 8, 2023
5064dad
resolve symbols for events table
mariusandra Feb 9, 2023
62dd464
resolve aliases
mariusandra Feb 9, 2023
6c04d20
refactor column and table aliases
mariusandra Feb 9, 2023
c550147
column resolver
mariusandra Feb 9, 2023
2253f41
make sure some things error
mariusandra Feb 9, 2023
a006872
annotate
mariusandra Feb 9, 2023
b7b5521
constants
mariusandra Feb 9, 2023
60376dd
simple sql query
mariusandra Feb 9, 2023
9683e92
Update snapshots
github-actions[bot] Feb 9, 2023
ac3048e
introduce "print name"
mariusandra Feb 10, 2023
86ddae5
visit_unknown
mariusandra Feb 10, 2023
c48024c
basic printer via a visitor
mariusandra Feb 10, 2023
afe485d
completely redo printing
mariusandra Feb 13, 2023
761a398
Merge branch 'hogql-symbol-resolution' of github.com:PostHog/posthog …
mariusandra Feb 13, 2023
fb35bc7
Merge branch 'master' into hogql-further-improvements
mariusandra Feb 13, 2023
8af1c79
Merge branch 'hogql-further-improvements' into hogql-symbol-resolution
mariusandra Feb 13, 2023
076916e
some sample queries
mariusandra Feb 13, 2023
f1cbd94
Merge branch 'master' into hogql-symbol-resolution
mariusandra Feb 13, 2023
fd271dd
query tests
mariusandra Feb 13, 2023
b41055e
test selecting from persons and distinct id table
mariusandra Feb 13, 2023
309a764
document current join ast
mariusandra Feb 13, 2023
592a18c
children, joins and aliases
mariusandra Feb 13, 2023
72da1d9
turn joins around
mariusandra Feb 13, 2023
54e5539
get some actual joins working
mariusandra Feb 13, 2023
95b6a5e
query aliases
mariusandra Feb 13, 2023
da83eec
errors, cleanup, recordings
mariusandra Feb 13, 2023
2e27c1e
resolver tests
mariusandra Feb 14, 2023
78caa57
cleanup
mariusandra Feb 14, 2023
48f684f
more tiny cleanup
mariusandra Feb 14, 2023
5cea7bd
resolver cleanup
mariusandra Feb 14, 2023
9e995fb
bit of printer cleanup
mariusandra Feb 14, 2023
0522eb5
Merge branch 'master' into hogql-symbol-resolution
mariusandra Feb 14, 2023
166e7fe
placeholders in query
mariusandra Feb 14, 2023
2287c07
changes
mariusandra Feb 14, 2023
92983fb
person properties fake table
mariusandra Feb 14, 2023
66b47ab
pass two more tests
mariusandra Feb 14, 2023
fa22ddd
legacy person properties
mariusandra Feb 15, 2023
3004248
Update snapshots
github-actions[bot] Feb 15, 2023
e7efe0a
Update snapshots
github-actions[bot] Feb 15, 2023
b075bc2
simple splash and table property printing
mariusandra Feb 15, 2023
74957e0
Merge remote-tracking branch 'origin/hogql-symbol-resolution' into ho…
mariusandra Feb 15, 2023
e59ef94
Update snapshots
github-actions[bot] Feb 15, 2023
f095fec
fix pp
mariusandra Feb 15, 2023
ff60757
Merge remote-tracking branch 'origin/hogql-symbol-resolution' into ho…
mariusandra Feb 15, 2023
4715e8a
revert what was different about hogql access logs
mariusandra Feb 15, 2023
07204aa
explicit names for person non properties
mariusandra Feb 15, 2023
1002382
consolidate into print_ast
mariusandra Feb 15, 2023
1d681f8
merge symbol printer into printer
mariusandra Feb 15, 2023
b32cb4b
move it up
mariusandra Feb 15, 2023
425a02e
Update snapshots
github-actions[bot] Feb 15, 2023
0d17eb7
Update snapshots
github-actions[bot] Feb 15, 2023
4851b8f
Update snapshots
github-actions[bot] Feb 15, 2023
d17d725
basic hogql node
mariusandra Feb 16, 2023
cc17eff
no need to discard resolved symbols... we use them again and again
mariusandra Feb 16, 2023
f3e7900
actual sql editor
mariusandra Feb 16, 2023
d731524
hide query editor if opening a hogql table
mariusandra Feb 16, 2023
0759e08
Update snapshots
github-actions[bot] Feb 16, 2023
5605a94
Update snapshots
github-actions[bot] Feb 16, 2023
1b02f02
query into snapshot data
mariusandra Feb 16, 2023
b59cf91
splash expander
mariusandra Feb 16, 2023
5cf6e77
code quality issues
mariusandra Feb 16, 2023
af86159
Merge branch 'hogql-node' into hogql-transforms
mariusandra Feb 16, 2023
44ff1a4
error if unexpected splash
mariusandra Feb 16, 2023
fdf1861
bandage
mariusandra Feb 16, 2023
a0e0dce
lazy table joins
mariusandra Feb 16, 2023
750f165
test
mariusandra Feb 16, 2023
aee30ae
pdi for session_recording_events
mariusandra Feb 16, 2023
b8f1793
Update snapshots
github-actions[bot] Feb 16, 2023
5d015fd
replace old fake table joins with lazy table joins - resolver makes s…
mariusandra Feb 19, 2023
66a0e05
access joined subtables correctly
mariusandra Feb 19, 2023
8763040
resolve lazy tables
mariusandra Feb 19, 2023
b97bcc6
fix bug
mariusandra Feb 19, 2023
1978c27
add groups table
mariusandra Feb 19, 2023
f81e9fe
support intervals
mariusandra Feb 19, 2023
4c0e109
prettier for sql
mariusandra Feb 19, 2023
3fd47f4
prettier for sql
mariusandra Feb 19, 2023
d6b8d3e
add failing test
mariusandra Feb 19, 2023
f72bf63
support dollars
mariusandra Feb 19, 2023
2f7d3df
Merge branch 'master' into hogql-symbol-resolution
mariusandra Feb 20, 2023
947e7bf
asterisk and obelisk
mariusandra Feb 20, 2023
74bf09c
yeet
mariusandra Feb 20, 2023
d8eb091
class is for internal use only
mariusandra Feb 20, 2023
9f1ae92
fix aliases
mariusandra Feb 20, 2023
c767159
not needed
mariusandra Feb 20, 2023
e496907
fix a few fields
mariusandra Feb 20, 2023
b5cd169
Merge branch 'hogql-symbol-resolution' into hogql-node
mariusandra Feb 20, 2023
4e3830c
pretty printing
mariusandra Feb 20, 2023
b25fd20
update sample query
mariusandra Feb 20, 2023
bf29398
Update snapshots
github-actions[bot] Feb 20, 2023
f21c928
update sample query
mariusandra Feb 20, 2023
b35777f
Update snapshots
github-actions[bot] Feb 20, 2023
7271431
fix webpack build for storybook
mariusandra Feb 21, 2023
edcbbb6
Merge remote-tracking branch 'origin/hogql-node' into hogql-node
mariusandra Feb 21, 2023
c05d7b0
Merge branch 'hogql-node' into hogql-transforms
mariusandra Feb 21, 2023
55ba79e
asterisks work again
mariusandra Feb 21, 2023
f739980
Update snapshots
github-actions[bot] Feb 21, 2023
02468b7
test splotch desplotcher
mariusandra Feb 21, 2023
88d1a19
Merge branch 'hogql-transforms' of github.com:PostHog/posthog into ho…
mariusandra Feb 21, 2023
2ad909c
Merge branch 'hogql-transforms' into person-table-joins
mariusandra Feb 21, 2023
837ff1d
fix test
mariusandra Feb 21, 2023
ecdc8ae
field traverser
mariusandra Feb 21, 2023
36c5491
Merge branch 'master' into hogql-node
mariusandra Feb 21, 2023
e844256
revert tiny changes lost in merge
mariusandra Feb 21, 2023
3ef7f29
Merge branch 'hogql-node' into hogql-transforms
mariusandra Feb 21, 2023
59ac65f
Merge branch 'master' into hogql-node
mariusandra Feb 21, 2023
8fc4e70
Merge branch 'hogql-node' into hogql-transforms
mariusandra Feb 21, 2023
69b78be
Merge branch 'hogql-transforms' into person-table-joins
mariusandra Feb 21, 2023
986aa91
make sure lazy tables also get resolved
mariusandra Feb 21, 2023
98e05c2
get rid of field access logs
mariusandra Feb 21, 2023
e7c38ff
fix bad merge
mariusandra Feb 21, 2023
a6836f7
switch to explicit materialised person props
mariusandra Feb 21, 2023
7c0ff20
revert
mariusandra Feb 21, 2023
aa8eed7
mat column does not make sense in this context on this field
mariusandra Feb 21, 2023
83107f5
few fixes
mariusandra Feb 21, 2023
a87a737
indicate legacy queries
mariusandra Feb 21, 2023
3150c50
move the kludge
mariusandra Feb 21, 2023
5530a5e
move the kludge
mariusandra Feb 21, 2023
b4cc8f2
Merge branch 'master' into hogql-transforms
mariusandra Feb 22, 2023
b7b60c2
Merge branch 'hogql-transforms' into person-table-joins
mariusandra Feb 22, 2023
0456203
support nested splotches
mariusandra Feb 22, 2023
4d97e25
Merge branch 'hogql-transforms' into person-table-joins
mariusandra Feb 22, 2023
873b54c
actually fix the test
mariusandra Feb 22, 2023
5112392
Merge branch 'hogql-transforms' into person-table-joins
mariusandra Feb 22, 2023
6199e2a
fix person properties if part of a legacy insight context
mariusandra Feb 22, 2023
10b9339
also if PoE is on
mariusandra Feb 22, 2023
52334ce
not exposing team_id
mariusandra Feb 22, 2023
940f294
lazy table tests and split files
mariusandra Feb 22, 2023
a2dcdd6
Merge branch 'master' into person-table-joins
mariusandra Feb 22, 2023
2e75217
simplify
mariusandra Feb 22, 2023
ef8d352
example
mariusandra Feb 22, 2023
d8a4791
feat(hogql): support intervals
mariusandra Feb 22, 2023
0e23667
Merge branch 'hogql-interval' into person-table-joins
mariusandra Feb 22, 2023
bde6e9f
rename
mariusandra Feb 22, 2023
f8669d4
this file was split in two
mariusandra Feb 22, 2023
6e3844b
not adding this yet
mariusandra Feb 22, 2023
02602c2
klean kludge
mariusandra Feb 22, 2023
d3bb2cd
explicitly no funny business with the title
mariusandra Feb 22, 2023
b40c41f
best guess JSON parsing
mariusandra Feb 22, 2023
2eddaae
remove funny changes
mariusandra Feb 22, 2023
057e654
remove funny changes
mariusandra Feb 22, 2023
b45811f
rename joined table to lazy table
mariusandra Feb 22, 2023
030c1dc
refactor
mariusandra Feb 22, 2023
d796232
rename field for clarity
mariusandra Feb 22, 2023
e7599f3
easier this way
mariusandra Feb 22, 2023
5024a42
more comments and renames
mariusandra Feb 22, 2023
fe582b4
keep name in alignment with other similar objects, remove code that's…
mariusandra Feb 22, 2023
9d1cb00
reduce duplication
mariusandra Feb 22, 2023
9a51249
put it back, too much noise to remove
mariusandra Feb 22, 2023
70a5338
add virtual tables
mariusandra Feb 22, 2023
417682a
test for selecting from "poe"
mariusandra Feb 23, 2023
30589c9
Merge branch 'master' into person-table-joins
mariusandra Feb 23, 2023
3e5b96e
Update snapshots
github-actions[bot] Feb 23, 2023
8c58dcd
Like -> Base
mariusandra Feb 23, 2023
c5e27fe
Merge branch 'master' into person-table-joins
mariusandra Feb 24, 2023
f288e68
small fixes
mariusandra Feb 24, 2023
e76a85b
Update snapshots
github-actions[bot] Feb 24, 2023
c02edcb
Merge branch 'master' into person-table-joins
mariusandra Feb 24, 2023
63fadcf
rename legacy bool to within_non_hogql_query
mariusandra Feb 27, 2023
ebe122e
Update UI snapshots for `chromium` (1)
github-actions[bot] Feb 27, 2023
4591cff
Update query snapshots
github-actions[bot] Feb 27, 2023
0b92e1e
Update UI snapshots for `chromium` (1)
github-actions[bot] Feb 27, 2023
e4a2331
Update UI snapshots for `chromium` (1)
github-actions[bot] Feb 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion frontend/src/queries/examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,21 @@ const HogQL: HogQLQuery = {
const HogQLTable: DataTableNode = {
kind: NodeKind.DataTableNode,
full: true,
source: HogQL,
source: {
kind: NodeKind.HogQLQuery,
query: ` select event,
person.properties.email,
properties.$browser,
count()
from events
where timestamp > now () - interval 1 month
and person.properties.email is not null
group by event,
properties.$browser,
person.properties.email
order by count() desc
limit 100`,
},
}

export const examples: Record<string, Node> = {
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/queries/nodes/DataTable/renderColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Property } from 'lib/components/Property'
import { urls } from 'scenes/urls'
import { PersonHeader } from 'scenes/persons/PersonHeader'
import { DataTableNode, HasPropertiesNode, QueryContext } from '~/queries/schema'
import { isEventsQuery, isPersonsNode } from '~/queries/utils'
import { isEventsQuery, isHogQLQuery, isPersonsNode } from '~/queries/utils'
import { combineUrl, router } from 'kea-router'
import { CopyToClipboardInline } from 'lib/components/CopyToClipboard'
import { DeletePersonButton } from '~/queries/nodes/PersonsNode/DeletePersonButton'
Expand All @@ -28,6 +28,18 @@ export function renderColumn(
return <Spinner />
} else if (value === errorColumn) {
return <LemonTag color="red">Error</LemonTag>
} else if (isHogQLQuery(query.source)) {
if (typeof value === 'string') {
try {
if ((value.startsWith('{') && value.endsWith('}')) || (value.startsWith('[') && value.endsWith(']'))) {
return <ReactJson src={JSON.parse(value)} name={key} collapsed={1} />
}
} catch (e) {}
if (value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}/)) {
return <TZLabel time={value} showSeconds />
}
}
return <Property value={value} />
} else if (key === 'event' && isEventsQuery(query.source)) {
const resultRow = record as any[]
const eventRecord = query.source.select.includes('*') ? resultRow[query.source.select.indexOf('*')] : null
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PropertyFilterType } from '~/types'
import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo'
import { QueryContext, DataTableNode } from '~/queries/schema'
import { isEventsQuery } from '~/queries/utils'
import { isEventsQuery, isHogQLQuery } from '~/queries/utils'
import { extractExpressionComment } from '~/queries/nodes/DataTable/utils'
import { SortingIndicator } from 'lib/lemon-ui/LemonTable/sorting'

Expand All @@ -14,7 +14,9 @@ export function renderColumnMeta(key: string, query: DataTableNode, context?: Qu
let width: number | undefined
let title: JSX.Element | string | undefined

if (key === 'timestamp') {
if (isHogQLQuery(query.source)) {
title = key
} else if (key === 'timestamp') {
title = 'Time'
} else if (key === 'created_at') {
title = 'First seen'
Expand Down
2 changes: 1 addition & 1 deletion posthog/api/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def people(self, request: request.Request, *args: Any, **kwargs: Any) -> Respons
def count(self, request: request.Request, **kwargs) -> Response:
action = self.get_object()
# NOTE: never accepts cohort parameters so no need for explicit person_id_joined_alias
hogql_context = HogQLContext()
hogql_context = HogQLContext(legacy_person_property_handling=True)
query, params = format_action_filter(team_id=action.team_id, action=action, hogql_context=hogql_context)
if query == "":
return Response({"count": 0})
Expand Down
108 changes: 76 additions & 32 deletions posthog/hogql/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
from pydantic import BaseModel, Extra
from pydantic import Field as PydanticField

from posthog.hogql.database import DatabaseField, StringJSONDatabaseField, Table
from posthog.hogql.database import (
DatabaseField,
FieldTraverser,
LazyTable,
StringJSONDatabaseField,
Table,
VirtualTable,
)

# NOTE: when you add new AST fields or nodes, add them to the Visitor classes in visitor.py as well!

Expand Down Expand Up @@ -46,36 +53,62 @@ def has_child(self, name: str) -> bool:
return self.symbol.has_child(name)


class TableSymbol(Symbol):
table: Table
class BaseTableSymbol(Symbol):
def resolve_database_table(self) -> Table:
raise NotImplementedError("BaseTableSymbol.resolve_database_table not overridden")

def has_child(self, name: str) -> bool:
return self.table.has_field(name)
return self.resolve_database_table().has_field(name)

def get_child(self, name: str) -> Symbol:
if name == "*":
return AsteriskSymbol(table=self)
if self.has_child(name):
field = self.table.get_field(name)
if isinstance(field, Table):
return TableSymbol(table=field)
field = self.resolve_database_table().get_field(name)
if isinstance(field, LazyTable):
return LazyTableSymbol(table=self, field=name, lazy_table=field)
if isinstance(field, FieldTraverser):
return FieldTraverserSymbol(table=self, chain=field.chain)
if isinstance(field, VirtualTable):
return VirtualTableSymbol(table=self, field=name, virtual_table=field)
return FieldSymbol(name=name, table=self)
raise ValueError(f'Field "{name}" not found on table {type(self.table).__name__}')
raise ValueError(f"Field not found: {name}")


class TableSymbol(BaseTableSymbol):
table: Table

def resolve_database_table(self) -> Table:
return self.table

class TableAliasSymbol(Symbol):

class TableAliasSymbol(BaseTableSymbol):
name: str
table: TableSymbol
table_symbol: TableSymbol

def has_child(self, name: str) -> bool:
return self.table.has_child(name)
def resolve_database_table(self) -> Table:
return self.table_symbol.table

def get_child(self, name: str) -> Symbol:
if name == "*":
return AsteriskSymbol(table=self)
if self.has_child(name):
return FieldSymbol(name=name, table=self)
return self.table.get_child(name)

class LazyTableSymbol(BaseTableSymbol):
table: BaseTableSymbol
field: str
lazy_table: LazyTable

def resolve_database_table(self) -> Table:
return self.lazy_table.table


class VirtualTableSymbol(BaseTableSymbol):
table: BaseTableSymbol
field: str
virtual_table: VirtualTable

def resolve_database_table(self) -> Table:
return self.virtual_table

def has_child(self, name: str) -> bool:
return self.virtual_table.has_field(name)


class SelectQuerySymbol(Symbol):
Expand All @@ -84,12 +117,21 @@ class SelectQuerySymbol(Symbol):
# all symbols a select query exports
columns: Dict[str, Symbol] = PydanticField(default_factory=dict)
# all from and join, tables and subqueries with aliases
tables: Dict[
str, Union[TableSymbol, TableAliasSymbol, "SelectQuerySymbol", "SelectQueryAliasSymbol"]
] = PydanticField(default_factory=dict)
tables: Dict[str, Union[BaseTableSymbol, "SelectQuerySymbol", "SelectQueryAliasSymbol"]] = PydanticField(
default_factory=dict
)
# all from and join subqueries without aliases
anonymous_tables: List["SelectQuerySymbol"] = PydanticField(default_factory=list)

def get_alias_for_table_symbol(
self,
table_symbol: Union[BaseTableSymbol, "SelectQuerySymbol", "SelectQueryAliasSymbol"],
) -> Optional[str]:
for key, value in self.tables.items():
if value == table_symbol:
return key
return None

def get_child(self, name: str) -> Symbol:
if name == "*":
return AsteriskSymbol(table=self)
Expand Down Expand Up @@ -129,27 +171,29 @@ class ConstantSymbol(Symbol):


class AsteriskSymbol(Symbol):
table: Union[TableSymbol, TableAliasSymbol, SelectQuerySymbol, SelectQueryAliasSymbol]
table: Union[BaseTableSymbol, SelectQuerySymbol, SelectQueryAliasSymbol]


class FieldTraverserSymbol(Symbol):
chain: List[str]
table: Union[BaseTableSymbol, SelectQuerySymbol, SelectQueryAliasSymbol]


class FieldSymbol(Symbol):
name: str
table: Union[TableSymbol, TableAliasSymbol, SelectQuerySymbol, SelectQueryAliasSymbol]

def resolve_database_field(self) -> Optional[Union[DatabaseField, Table]]:
table_symbol = self.table
while isinstance(table_symbol, TableAliasSymbol):
table_symbol = table_symbol.table
if isinstance(table_symbol, TableSymbol):
return table_symbol.table.get_field(self.name)
table: Union[BaseTableSymbol, SelectQuerySymbol, SelectQueryAliasSymbol]

def resolve_database_field(self) -> Optional[DatabaseField]:
if isinstance(self.table, BaseTableSymbol):
table = self.table.resolve_database_table()
if table is not None:
return table.get_field(self.name)
return None

def get_child(self, name: str) -> Symbol:
database_field = self.resolve_database_field()
if database_field is None:
raise ValueError(f'Can not access property "{name}" on field "{self.name}".')
if isinstance(database_field, Table):
return FieldSymbol(name=name, table=TableSymbol(table=database_field))
if isinstance(database_field, StringJSONDatabaseField):
return PropertySymbol(name=name, parent=self)
raise ValueError(
Expand Down
10 changes: 5 additions & 5 deletions posthog/hogql/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ class HogQLContext:

# If set, will save string constants to this dict. Inlines strings into the query if None.
values: Dict = field(default_factory=dict)
# List of field and property accesses found in the expression
field_access_logs: List[HogQLFieldAccess] = field(default_factory=list)
# Did the last calls to translate_hogql since setting these to False contain any of the following
found_aggregation: bool = False
# Do we need to join the persons table or not
# Are we small part of a non-HogQL query? If so, use custom syntax for accessed person properties.
legacy_person_property_handling: bool = False
# Do we need to join the persons table or not. Has effect if legacy_person_property_handling = True
using_person_on_events: bool = True
# If set, allows printing full SELECT queries in ClickHouse
select_team_id: Optional[int] = None
# Do we apply a limit of MAX_SELECT_RETURNED_ROWS=65535 to the topmost select query?
limit_top_select: bool = True
# To be removed. Did the last calls to translate_hogql since setting this to False contain an aggregation?
found_aggregation: bool = False
Loading