From 3a18f77fcfe78699bfa0f861dcd1e694ad5ab961 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 5 Dec 2024 13:01:44 +0100 Subject: [PATCH 1/5] chore(environments): Update filtering of product analytics resources --- posthog/api/activity_log.py | 22 +++++-- posthog/api/cohort.py | 22 ++++--- posthog/api/dashboards/dashboard.py | 4 +- posthog/api/feature_flag.py | 6 +- posthog/api/insight.py | 2 +- posthog/api/organization_feature_flag.py | 2 +- posthog/api/sharing.py | 4 +- posthog/api/survey.py | 4 +- posthog/cdp/filters.py | 6 +- posthog/demo/matrix/manager.py | 2 +- posthog/demo/products/hedgebox/matrix.py | 2 +- posthog/hogql/context.py | 10 +++ .../hogql/database/schema/cohort_people.py | 6 +- posthog/hogql/functions/action.py | 4 +- posthog/hogql/functions/cohort.py | 4 +- .../test/__snapshots__/test_cohort.ambr | 28 +++++++++ posthog/hogql/functions/test/test_cohort.py | 21 +++++++ posthog/hogql/property.py | 2 +- posthog/hogql/transforms/in_cohort.py | 20 +++--- posthog/hogql_queries/events_query_runner.py | 2 +- .../hogql_queries/insights/funnels/base.py | 6 +- .../trends/test/test_trends_query_runner.py | 62 +++++++++++++++++++ .../insights/trends/trends_query_runner.py | 5 +- .../hogql_queries/web_analytics/web_goals.py | 4 +- posthog/models/cohort/util.py | 4 +- posthog/models/event/query_event_list.py | 2 +- posthog/models/feature_flag/feature_flag.py | 4 +- .../models/feature_flag/user_blast_radius.py | 2 +- posthog/models/filters/mixins/simplify.py | 2 +- .../models/product_intent/product_intent.py | 2 +- posthog/queries/base.py | 18 +++--- posthog/queries/breakdown_props.py | 4 +- posthog/queries/event_query/event_query.py | 2 +- posthog/queries/foss_cohort_query.py | 9 ++- posthog/queries/person_query.py | 2 +- posthog/queries/stickiness/stickiness.py | 2 +- posthog/queries/trends/trends.py | 6 +- posthog/queries/trends/trends_actors.py | 2 +- 38 files changed, 237 insertions(+), 74 deletions(-) diff --git a/posthog/api/activity_log.py b/posthog/api/activity_log.py index 4730dde145ffb..d44c43073eb06 100644 --- a/posthog/api/activity_log.py +++ b/posthog/api/activity_log.py @@ -131,18 +131,30 @@ def important_changes(self, request: Request, *args: Any, **kwargs: Any) -> Resp with timer("gather_query_parts"): # first things this user created my_insights = list( - Insight.objects.filter(created_by=user, team_id=self.team.pk).values_list("id", flat=True) + Insight.objects.filter(created_by=user, team__project_id=self.team.project_id).values_list( + "id", flat=True + ) ) my_feature_flags = list( - FeatureFlag.objects.filter(created_by=user, team_id=self.team.pk).values_list("id", flat=True) + FeatureFlag.objects.filter(created_by=user, team__project_id=self.team.project_id).values_list( + "id", flat=True + ) ) my_notebooks = list( - Notebook.objects.filter(created_by=user, team_id=self.team.pk).values_list("short_id", flat=True) + Notebook.objects.filter(created_by=user, team__project_id=self.team.project_id).values_list( + "short_id", flat=True + ) ) my_comments = list( - Comment.objects.filter(created_by=user, team_id=self.team.pk).values_list("id", flat=True) + Comment.objects.filter(created_by=user, team__project_id=self.team.project_id).values_list( + "id", flat=True + ) + ) + my_cohorts = list( + Cohort.objects.filter(created_by=user, team__project_id=self.team.project_id).values_list( + "id", flat=True + ) ) - my_cohorts = list(Cohort.objects.filter(created_by=user, team_id=self.team.pk).values_list("id", flat=True)) my_hog_functions = list( HogFunction.objects.filter(created_by=user, team_id=self.team.pk).values_list("id", flat=True) ) diff --git a/posthog/api/cohort.py b/posthog/api/cohort.py index 2d5d557f52b0b..8fea44a0898b5 100644 --- a/posthog/api/cohort.py +++ b/posthog/api/cohort.py @@ -14,6 +14,7 @@ ) from posthog.models.person.person import PersonDistinctId from posthog.models.property.property import Property, PropertyGroup +from posthog.models.team.team import Team from posthog.queries.base import property_group_to_Q from posthog.metrics import LABEL_TEAM_ID from posthog.renderers import SafeJSONRenderer @@ -195,7 +196,7 @@ def validate_filters(self, request_filters: dict): instance = cast(Cohort, self.instance) cohort_id = instance.pk flags: QuerySet[FeatureFlag] = FeatureFlag.objects.filter( - team_id=self.context["team_id"], active=True, deleted=False + team__project_id=self.context["project_id"], active=True, deleted=False ) cohort_used_in_flags = len([flag for flag in flags if cohort_id in flag.get_cohort_ids()]) > 0 @@ -208,7 +209,7 @@ def validate_filters(self, request_filters: dict): ) if prop.type == "cohort": - nested_cohort = Cohort.objects.get(pk=prop.value, team_id=self.context["team_id"]) + nested_cohort = Cohort.objects.get(pk=prop.value, team__project_id=self.context["project_id"]) dependent_cohorts = get_dependent_cohorts(nested_cohort) for dependent_cohort in [nested_cohort, *dependent_cohorts]: if ( @@ -425,7 +426,7 @@ def activity(self, request: request.Request, **kwargs): page = int(request.query_params.get("page", "1")) item_id = kwargs["pk"] - if not Cohort.objects.filter(id=item_id, team_id=self.team_id).exists(): + if not Cohort.objects.filter(id=item_id, team__project_id=self.project_id).exists(): return Response("", status=status.HTTP_404_NOT_FOUND) activity_page = load_activity( @@ -480,7 +481,7 @@ class LegacyCohortViewSet(CohortViewSet): def will_create_loops(cohort: Cohort) -> bool: # Loops can only be formed when trying to update a Cohort, not when creating one - team_id = cohort.team_id + project_id = cohort.team.project_id # We can model this as a directed graph, where each node is a Cohort and each edge is a reference to another Cohort # There's a loop only if there's a cycle in the directed graph. The "directed" bit is important. @@ -501,7 +502,7 @@ def dfs_loop_helper(current_cohort: Cohort, seen_cohorts, cohorts_on_path): return True elif property.value not in seen_cohorts: try: - nested_cohort = Cohort.objects.get(pk=property.value, team_id=team_id) + nested_cohort = Cohort.objects.get(pk=property.value, team__project_id=project_id) except Cohort.DoesNotExist: raise ValidationError("Invalid Cohort ID in filter") @@ -623,16 +624,17 @@ def insert_actors_into_cohort_by_query(cohort: Cohort, query: str, params: dict[ def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int, batchsize: int = 1_000): # :TODO: Find a way to incorporate this into the same code path as feature flag evaluation + project_id = Team.objects.only("project_id").get(pk=team_id).project_id try: - feature_flag = FeatureFlag.objects.get(team_id=team_id, key=flag) + feature_flag = FeatureFlag.objects.get(team__project_id=project_id, key=flag) except FeatureFlag.DoesNotExist: return [] if not feature_flag.active or feature_flag.deleted or feature_flag.aggregation_group_type_index is not None: return [] - cohort = Cohort.objects.get(pk=cohort_id, team_id=team_id) - matcher_cache = FlagsMatcherCache(team_id) + cohort = Cohort.objects.get(pk=cohort_id, team__project_id=project_id) + matcher_cache = FlagsMatcherCache(team_id=team_id) uuids_to_add_to_cohort = [] cohorts_cache: dict[int, CohortOrEmpty] = {} @@ -640,7 +642,9 @@ def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int, # TODO: Consider disabling flags with cohorts for creating static cohorts # because this is currently a lot more inefficient for flag matching, # as we're required to go to the database for each person. - cohorts_cache = {cohort.pk: cohort for cohort in Cohort.objects.filter(team_id=team_id, deleted=False)} + cohorts_cache = { + cohort.pk: cohort for cohort in Cohort.objects.filter(team__project_id=project_id, deleted=False) + } default_person_properties = {} for condition in feature_flag.conditions: diff --git a/posthog/api/dashboards/dashboard.py b/posthog/api/dashboards/dashboard.py index 8fbbb98b58404..393df01a2d554 100644 --- a/posthog/api/dashboards/dashboard.py +++ b/posthog/api/dashboards/dashboard.py @@ -214,7 +214,9 @@ def create(self, validated_data: dict, *args: Any, **kwargs: Any) -> Dashboard: elif use_dashboard: try: - existing_dashboard = Dashboard.objects.get(id=use_dashboard, team_id=team_id) + existing_dashboard = Dashboard.objects.get( + id=use_dashboard, team__project_id=self.context["get_team"]().project_id + ) existing_tiles = ( DashboardTile.objects.filter(dashboard=existing_dashboard) .exclude(deleted=True) diff --git a/posthog/api/feature_flag.py b/posthog/api/feature_flag.py index 06727c74bcf53..4ad79f4cbcb60 100644 --- a/posthog/api/feature_flag.py +++ b/posthog/api/feature_flag.py @@ -256,7 +256,9 @@ def properties_all_match(predicate): if prop.type == "cohort": try: - initial_cohort: Cohort = Cohort.objects.get(pk=prop.value, team_id=self.context["team_id"]) + initial_cohort: Cohort = Cohort.objects.get( + pk=prop.value, team__project_id=self.context["project_id"] + ) dependent_cohorts = get_dependent_cohorts(initial_cohort) for cohort in [initial_cohort, *dependent_cohorts]: if [prop for prop in cohort.properties.flat if prop.type == "behavioral"]: @@ -666,7 +668,7 @@ def local_evaluation(self, request: request.Request, **kwargs): seen_cohorts_cache = { cohort.pk: cohort for cohort in Cohort.objects.db_manager(DATABASE_FOR_LOCAL_EVALUATION).filter( - team_id=self.team_id, deleted=False + team__project_id=self.project_id, deleted=False ) } diff --git a/posthog/api/insight.py b/posthog/api/insight.py index 32c24b3979f44..79485da247830 100644 --- a/posthog/api/insight.py +++ b/posthog/api/insight.py @@ -1184,7 +1184,7 @@ def activity(self, request: request.Request, **kwargs): page = int(request.query_params.get("page", "1")) item_id = kwargs["pk"] - if not Insight.objects.filter(id=item_id, team_id=self.team_id).exists(): + if not Insight.objects.filter(id=item_id, team__project_id=self.project_id).exists(): return Response("", status=status.HTTP_404_NOT_FOUND) activity_page = load_activity( diff --git a/posthog/api/organization_feature_flag.py b/posthog/api/organization_feature_flag.py index 3585f5f149849..46b1652d7f9b5 100644 --- a/posthog/api/organization_feature_flag.py +++ b/posthog/api/organization_feature_flag.py @@ -111,7 +111,7 @@ def copy_flags(self, request, *args, **kwargs): # search in destination project by name destination_cohort = Cohort.objects.filter( - name=original_cohort.name, team_id=target_project_id, deleted=False + name=original_cohort.name, team__project_id=target_project_id, deleted=False ).first() # create new cohort in the destination project diff --git a/posthog/api/sharing.py b/posthog/api/sharing.py index d0cf5af56bafd..ca931c526248d 100644 --- a/posthog/api/sharing.py +++ b/posthog/api/sharing.py @@ -99,12 +99,12 @@ def get_serializer_context( if dashboard_id: try: - context["dashboard"] = Dashboard.objects.get(id=dashboard_id, team=self.team) + context["dashboard"] = Dashboard.objects.get(id=dashboard_id, team__project_id=self.team.project_id) except Dashboard.DoesNotExist: raise NotFound("Dashboard not found.") if insight_id: try: - context["insight"] = Insight.objects.get(id=insight_id, team=self.team) + context["insight"] = Insight.objects.get(id=insight_id, team__project_id=self.team.project_id) except Insight.DoesNotExist: raise NotFound("Insight not found.") if recording_id: diff --git a/posthog/api/survey.py b/posthog/api/survey.py index 3d13981a867f4..ba95ae9f18c30 100644 --- a/posthog/api/survey.py +++ b/posthog/api/survey.py @@ -212,7 +212,7 @@ def validate_conditions(self, value): return value action_ids = (value.get("id") for value in values) - project_actions = Action.objects.filter(team_id=self.context["team_id"], id__in=action_ids) + project_actions = Action.objects.filter(team__project_id=self.context["project_id"], id__in=action_ids) for project_action in project_actions: for step in project_action.steps: @@ -562,7 +562,7 @@ def _associate_actions(self, instance: Survey, conditions): action_ids = (value.get("id") for value in values) - instance.actions.set(Action.objects.filter(team_id=self.context["team_id"], id__in=action_ids)) + instance.actions.set(Action.objects.filter(team__project_id=self.context["project_id"], id__in=action_ids)) instance.save() def _add_user_survey_interacted_filters(self, instance: Survey, end_date=None): diff --git a/posthog/cdp/filters.py b/posthog/cdp/filters.py index f95d3f029a0f3..3d34a4c4c29fc 100644 --- a/posthog/cdp/filters.py +++ b/posthog/cdp/filters.py @@ -43,7 +43,7 @@ def hog_function_filters_to_expr(filters: dict, team: Team, actions: dict[int, A action_id = int(filter["id"]) action = actions.get(action_id, None) if not action: - action = Action.objects.get(id=action_id, team=team) + action = Action.objects.get(id=action_id, team__project_id=team.project_id) exprs.append(action_to_expr(action)) except KeyError: # If an action doesn't exist, we want to return no events @@ -80,7 +80,9 @@ def compile_filters_expr(filters: Optional[dict], team: Team, actions: Optional[ if actions is None: # If not provided as an optimization we fetch all actions actions_list = ( - Action.objects.select_related("team").filter(team_id=team.id).filter(id__in=filter_action_ids(filters)) + Action.objects.select_related("team") + .filter(team__project_id=team.project_id) + .filter(id__in=filter_action_ids(filters)) ) actions = {action.id: action for action in actions_list} diff --git a/posthog/demo/matrix/manager.py b/posthog/demo/matrix/manager.py index 0abc17f32ca08..4a164d3650241 100644 --- a/posthog/demo/matrix/manager.py +++ b/posthog/demo/matrix/manager.py @@ -139,7 +139,7 @@ def run_on_team(self, team: Team, user: User): print( f"Inferred {event_definition_count} event definitions, {property_definition_count} property definitions, and {event_properties_count} event-property pairs." ) - for cohort in Cohort.objects.filter(team=team): + for cohort in Cohort.objects.filter(team__project_id=team.project_id): cohort.calculate_people_ch(pending_version=0) team.project.save() team.save() diff --git a/posthog/demo/products/hedgebox/matrix.py b/posthog/demo/products/hedgebox/matrix.py index 5990f7a744235..f14c3de924cda 100644 --- a/posthog/demo/products/hedgebox/matrix.py +++ b/posthog/demo/products/hedgebox/matrix.py @@ -769,7 +769,7 @@ def set_project_up(self, team, user): ) ), ) - for insight in Insight.objects.filter(team=team) + for insight in Insight.objects.filter(team__project_id=team.project_id) ), ) except IntegrityError: diff --git a/posthog/hogql/context.py b/posthog/hogql/context.py index 615aedc8d2f6c..104125aa9c5bc 100644 --- a/posthog/hogql/context.py +++ b/posthog/hogql/context.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from functools import cached_property from typing import TYPE_CHECKING, Any, Literal, Optional from posthog.hogql.timings import HogQLTimings @@ -94,3 +95,12 @@ def add_error( ): if not any(n.start == start and n.end == end and n.message == message and n.fix == fix for n in self.errors): self.errors.append(HogQLNotice(start=start, end=end, message=message, fix=fix)) + + @cached_property + def project_id(self) -> Optional[int]: + from posthog.models import Team + + if not self.team and not self.team_id: + return None + team = self.team or Team.objects.only("project_id").get(id=self.team_id) + return team.project_id diff --git a/posthog/hogql/database/schema/cohort_people.py b/posthog/hogql/database/schema/cohort_people.py index 1768aa55b8a50..20886a048f17b 100644 --- a/posthog/hogql/database/schema/cohort_people.py +++ b/posthog/hogql/database/schema/cohort_people.py @@ -21,12 +21,12 @@ } -def select_from_cohort_people_table(requested_fields: dict[str, list[str | int]], team_id: int): +def select_from_cohort_people_table(requested_fields: dict[str, list[str | int]], project_id: int): from posthog.hogql import ast from posthog.models import Cohort cohort_tuples = list( - Cohort.objects.filter(is_static=False, team_id=team_id) + Cohort.objects.filter(is_static=False, team__project_id=project_id) .exclude(version__isnull=True) .values_list("id", "version") ) @@ -76,7 +76,7 @@ class CohortPeople(LazyTable): fields: dict[str, FieldOrTable] = COHORT_PEOPLE_FIELDS def lazy_select(self, table_to_add: LazyTableToAdd, context, node): - return select_from_cohort_people_table(table_to_add.fields_accessed, context.team_id) + return select_from_cohort_people_table(table_to_add.fields_accessed, context.project_id) def to_printed_clickhouse(self, context): return "cohortpeople" diff --git a/posthog/hogql/functions/action.py b/posthog/hogql/functions/action.py index 5ed8a156e393b..d1f7fe85223d8 100644 --- a/posthog/hogql/functions/action.py +++ b/posthog/hogql/functions/action.py @@ -15,7 +15,7 @@ def matches_action(node: ast.Expr, args: list[ast.Expr], context: HogQLContext) from posthog.hogql.property import action_to_expr if (isinstance(arg.value, int) or isinstance(arg.value, float)) and not isinstance(arg.value, bool): - actions = Action.objects.filter(id=int(arg.value), team_id=context.team_id).all() + actions = Action.objects.filter(id=int(arg.value), team__project_id=context.project_id).all() if len(actions) == 1: context.add_notice( start=arg.start, @@ -27,7 +27,7 @@ def matches_action(node: ast.Expr, args: list[ast.Expr], context: HogQLContext) raise QueryError(f"Could not find cohort with ID {arg.value}", node=arg) if isinstance(arg.value, str): - actions = Action.objects.filter(name=arg.value, team_id=context.team_id).all() + actions = Action.objects.filter(name=arg.value, team__project_id=context.project_id).all() if len(actions) == 1: context.add_notice( start=arg.start, diff --git a/posthog/hogql/functions/cohort.py b/posthog/hogql/functions/cohort.py index 2b0992c6e7ef9..bb01604fddcde 100644 --- a/posthog/hogql/functions/cohort.py +++ b/posthog/hogql/functions/cohort.py @@ -31,7 +31,7 @@ def cohort(node: ast.Expr, args: list[ast.Expr], context: HogQLContext) -> ast.E from posthog.models import Cohort if (isinstance(arg.value, int) or isinstance(arg.value, float)) and not isinstance(arg.value, bool): - cohorts1 = Cohort.objects.filter(id=int(arg.value), team_id=context.team_id).values_list( + cohorts1 = Cohort.objects.filter(id=int(arg.value), team__project_id=context.project_id).values_list( "id", "is_static", "version", "name" ) if len(cohorts1) == 1: @@ -45,7 +45,7 @@ def cohort(node: ast.Expr, args: list[ast.Expr], context: HogQLContext) -> ast.E raise QueryError(f"Could not find cohort with ID {arg.value}", node=arg) if isinstance(arg.value, str): - cohorts2 = Cohort.objects.filter(name=arg.value, team_id=context.team_id).values_list( + cohorts2 = Cohort.objects.filter(name=arg.value, team__project_id=context.project_id).values_list( "id", "is_static", "version" ) if len(cohorts2) == 1: diff --git a/posthog/hogql/functions/test/__snapshots__/test_cohort.ambr b/posthog/hogql/functions/test/__snapshots__/test_cohort.ambr index 3b0f781470789..e3f18c55f6c2e 100644 --- a/posthog/hogql/functions/test/__snapshots__/test_cohort.ambr +++ b/posthog/hogql/functions/test/__snapshots__/test_cohort.ambr @@ -27,6 +27,34 @@ LIMIT 100 ''' # --- +# name: TestCohort.test_in_cohort_dynamic_other_team_in_project + ''' + -- ClickHouse + + SELECT events.event AS event + FROM events + WHERE and(equals(events.team_id, 99999), in(events.person_id, ( + SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, XX)) + GROUP BY cohortpeople.person_id, cohortpeople.cohort_id, cohortpeople.version + HAVING ifNull(greater(sum(cohortpeople.sign), 0), 0))), equals(events.event, %(hogql_val_0)s)) + LIMIT 100 + SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1, format_csv_allow_double_quotes=0, max_ast_elements=4000000, max_expanded_ast_elements=4000000, max_bytes_before_external_group_by=0 + + -- HogQL + + SELECT event + FROM events + WHERE and(in(person_id, ( + SELECT person_id + FROM raw_cohort_people + WHERE equals(cohort_id, XX) + GROUP BY person_id, cohort_id, version + HAVING greater(sum(sign), 0))), equals(event, 'RANDOM_TEST_ID::UUID')) + LIMIT 100 + ''' +# --- # name: TestCohort.test_in_cohort_static ''' -- ClickHouse diff --git a/posthog/hogql/functions/test/test_cohort.py b/posthog/hogql/functions/test/test_cohort.py index 4ea1830a417b1..e5372e03654e2 100644 --- a/posthog/hogql/functions/test/test_cohort.py +++ b/posthog/hogql/functions/test/test_cohort.py @@ -8,6 +8,7 @@ from posthog.hogql.test.utils import pretty_print_response_in_tests from posthog.models import Cohort from posthog.models.cohort.util import recalculate_cohortpeople +from posthog.models.team.team import Team from posthog.models.utils import UUIDT from posthog.schema import HogQLQueryModifiers from posthog.test.base import ( @@ -55,6 +56,26 @@ def test_in_cohort_dynamic(self): self.assertEqual(len(response.results), 1) self.assertEqual(response.results[0][0], random_uuid) + @pytest.mark.usefixtures("unittest_snapshot") + @override_settings(PERSON_ON_EVENTS_OVERRIDE=True, PERSON_ON_EVENTS_V2_OVERRIDE=False) + def test_in_cohort_dynamic_other_team_in_project(self): + random_uuid = self._create_random_events() + other_team_in_project = Team.objects.create(organization=self.organization, project=self.project) + cohort = Cohort.objects.create( + team=other_team_in_project, # Not self.team! + groups=[{"properties": [{"key": "$os", "value": "Chrome", "type": "person"}]}], + ) + recalculate_cohortpeople(cohort, pending_version=0, initiating_user_id=None) + response = execute_hogql_query( + f"SELECT event FROM events WHERE person_id IN COHORT {cohort.pk} AND event='{random_uuid}'", + self.team, + modifiers=HogQLQueryModifiers(inCohortVia="subquery"), + pretty=False, + ) + assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot + self.assertEqual(len(response.results), 1) + self.assertEqual(response.results[0][0], random_uuid) + @pytest.mark.usefixtures("unittest_snapshot") @override_settings(PERSON_ON_EVENTS_OVERRIDE=True, PERSON_ON_EVENTS_V2_OVERRIDE=False) def test_in_cohort_static(self): diff --git a/posthog/hogql/property.py b/posthog/hogql/property.py index e372b446b5c91..01421937ea52d 100644 --- a/posthog/hogql/property.py +++ b/posthog/hogql/property.py @@ -506,7 +506,7 @@ def property_to_expr( elif property.type == "cohort" or property.type == "static-cohort" or property.type == "precalculated-cohort": if not team: raise Exception("Can not convert cohort property to expression without team") - cohort = Cohort.objects.get(team=team, id=property.value) + cohort = Cohort.objects.get(team__project_id=team.project_id, id=property.value) return ast.CompareOperation( left=ast.Field(chain=["id" if scope == "person" else "person_id"]), op=ast.CompareOperationOp.NotInCohort diff --git a/posthog/hogql/transforms/in_cohort.py b/posthog/hogql/transforms/in_cohort.py index 418b097e1d07d..98673f00bb392 100644 --- a/posthog/hogql/transforms/in_cohort.py +++ b/posthog/hogql/transforms/in_cohort.py @@ -94,9 +94,9 @@ def _resolve_cohorts( raise QueryError("IN COHORT only works with constant arguments", node=arg) if (isinstance(arg.value, int) or isinstance(arg.value, float)) and not isinstance(arg.value, bool): - int_cohorts = Cohort.objects.filter(id=int(arg.value), team_id=self.context.team_id).values_list( - "id", "is_static", "version" - ) + int_cohorts = Cohort.objects.filter( + id=int(arg.value), team__project_id=self.context.project_id + ).values_list("id", "is_static", "version") if len(int_cohorts) == 1: if node.op == ast.CompareOperationOp.NotInCohort: raise QueryError("NOT IN COHORT is not supported by this cohort mode") @@ -110,9 +110,9 @@ def _resolve_cohorts( raise QueryError(f"Could not find cohort with ID {arg.value}", node=arg) if isinstance(arg.value, str): - str_cohorts = Cohort.objects.filter(name=arg.value, team_id=self.context.team_id).values_list( - "id", "is_static", "version" - ) + str_cohorts = Cohort.objects.filter( + name=arg.value, team__project_id=self.context.project_id + ).values_list("id", "is_static", "version") if len(str_cohorts) == 1: if node.op == ast.CompareOperationOp.NotInCohort: raise QueryError("NOT IN COHORT is not supported by this cohort mode") @@ -288,9 +288,9 @@ def visit_compare_operation(self, node: ast.CompareOperation): from posthog.models import Cohort if (isinstance(arg.value, int) or isinstance(arg.value, float)) and not isinstance(arg.value, bool): - cohorts = Cohort.objects.filter(id=int(arg.value), team_id=self.context.team_id).values_list( - "id", "is_static", "version", "name" - ) + cohorts = Cohort.objects.filter( + id=int(arg.value), team__project_id=self.context.project_id + ).values_list("id", "is_static", "version", "name") if len(cohorts) == 1: self.context.add_notice( start=arg.start, @@ -310,7 +310,7 @@ def visit_compare_operation(self, node: ast.CompareOperation): raise QueryError(f"Could not find cohort with ID {arg.value}", node=arg) if isinstance(arg.value, str): - cohorts2 = Cohort.objects.filter(name=arg.value, team_id=self.context.team_id).values_list( + cohorts2 = Cohort.objects.filter(name=arg.value, team__project_id=self.context.project_id).values_list( "id", "is_static", "version" ) if len(cohorts2) == 1: diff --git a/posthog/hogql_queries/events_query_runner.py b/posthog/hogql_queries/events_query_runner.py index cf8435c0bb485..a91e92d38c564 100644 --- a/posthog/hogql_queries/events_query_runner.py +++ b/posthog/hogql_queries/events_query_runner.py @@ -101,7 +101,7 @@ def to_query(self) -> ast.SelectQuery: if self.query.actionId: with self.timings.measure("action_id"): try: - action = Action.objects.get(pk=self.query.actionId, team_id=self.team.pk) + action = Action.objects.get(pk=self.query.actionId, team__project_id=self.team.project_id) except Action.DoesNotExist: raise Exception("Action does not exist") if not action.steps: diff --git a/posthog/hogql_queries/insights/funnels/base.py b/posthog/hogql_queries/insights/funnels/base.py index 189bfab8b109a..98d0cedea69b9 100644 --- a/posthog/hogql_queries/insights/funnels/base.py +++ b/posthog/hogql_queries/insights/funnels/base.py @@ -115,9 +115,11 @@ def breakdown_cohorts(self) -> list[Cohort]: team, breakdown = self.context.team, self.context.breakdown if isinstance(breakdown, list): - cohorts = Cohort.objects.filter(team_id=team.pk, pk__in=[b for b in breakdown if b != "all"]) + cohorts = Cohort.objects.filter( + team__project_id=team.project_id, pk__in=[b for b in breakdown if b != "all"] + ) else: - cohorts = Cohort.objects.filter(team_id=team.pk, pk=breakdown) + cohorts = Cohort.objects.filter(team__project_id=team.project_id, pk=breakdown) return list(cohorts) diff --git a/posthog/hogql_queries/insights/trends/test/test_trends_query_runner.py b/posthog/hogql_queries/insights/trends/test/test_trends_query_runner.py index 1f22f88de60a9..e72c4b66b4d79 100644 --- a/posthog/hogql_queries/insights/trends/test/test_trends_query_runner.py +++ b/posthog/hogql_queries/insights/trends/test/test_trends_query_runner.py @@ -30,6 +30,7 @@ from posthog.models.cohort.cohort import Cohort from posthog.models.group.util import create_group from posthog.models.property_definition import PropertyDefinition +from posthog.models.team.team import Team from posthog.schema import ( ActionsNode, BaseMathType, @@ -859,6 +860,67 @@ def test_formula_with_breakdown_and_compare_total_value(self): assert response.results[2]["breakdown_value"] == "Chrome" assert response.results[2]["aggregated_value"] == 9 + def test_trends_with_cohort_filter(self): + self._create_test_events() + cohort = Cohort.objects.create( + team=self.team, + groups=[ + { + "properties": [ + { + "key": "name", + "value": "p1", + "type": "person", + } + ] + } + ], + name="cohort p1", + ) + cohort.calculate_people_ch(pending_version=0) + + response = self._run_trends_query( + "2020-01-09", + "2020-01-20", + IntervalType.DAY, + [EventsNode(event="$pageview", properties=[{"key": "id", "value": cohort.pk, "type": "cohort"}])], + ) + + assert len(response.results) == 1 + assert response.results[0]["count"] == 6 + assert response.results[0]["data"] == [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0] + + def test_trends_with_cohort_filter_other_team_in_project(self): + self._create_test_events() + other_team_in_project = Team.objects.create(organization=self.organization, project=self.project) + cohort = Cohort.objects.create( + team=other_team_in_project, # Not self.team! + groups=[ + { + "properties": [ + { + "key": "name", + "value": "p1", + "type": "person", + } + ] + } + ], + name="cohort p1 other team", + ) + cohort.calculate_people_ch(pending_version=0) + + response = self._run_trends_query( + "2020-01-09", + "2020-01-20", + IntervalType.DAY, + [EventsNode(event="$pageview", properties=[{"key": "id", "value": cohort.pk, "type": "cohort"}])], + ) + + assert len(response.results) == 1 + assert response.results[0]["count"] == 6 + assert response.results[0]["data"] == [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0] + def test_formula_with_multi_cohort_breakdown(self): self._create_test_events() cohort1 = Cohort.objects.create( diff --git a/posthog/hogql_queries/insights/trends/trends_query_runner.py b/posthog/hogql_queries/insights/trends/trends_query_runner.py index 668cd8b2afb48..c7de458195b2f 100644 --- a/posthog/hogql_queries/insights/trends/trends_query_runner.py +++ b/posthog/hogql_queries/insights/trends/trends_query_runner.py @@ -219,7 +219,10 @@ def to_actors_query_options(self) -> InsightActorsQueryOptionsResponse: for value in self.query.breakdownFilter.breakdown: if value != "all" and str(value) != "0": res_breakdown.append( - BreakdownItem(label=Cohort.objects.get(pk=int(value), team=self.team).name, value=value) + BreakdownItem( + label=Cohort.objects.get(pk=int(value), team__project_id=self.team.project_id).name, + value=value, + ) ) else: res_breakdown.append(BreakdownItem(label="all users", value="all")) diff --git a/posthog/hogql_queries/web_analytics/web_goals.py b/posthog/hogql_queries/web_analytics/web_goals.py index 04e13ab6d508e..7bd36573340fb 100644 --- a/posthog/hogql_queries/web_analytics/web_goals.py +++ b/posthog/hogql_queries/web_analytics/web_goals.py @@ -29,7 +29,9 @@ def to_query(self) -> ast.SelectQuery | ast.SelectSetQuery: start = self.query_date_range.date_from_as_hogql() end = self.query_date_range.date_to_as_hogql() - actions = Action.objects.filter(team=self.team, deleted=False).order_by("pinned_at", "-last_calculated_at")[:5] + actions = Action.objects.filter(team__project_id=self.team.project_id, deleted=False).order_by( + "pinned_at", "-last_calculated_at" + )[:5] if not actions: raise NoActionsError("No actions found") diff --git a/posthog/models/cohort/util.py b/posthog/models/cohort/util.py index fe589236fa62e..fd2feb30c21d1 100644 --- a/posthog/models/cohort/util.py +++ b/posthog/models/cohort/util.py @@ -173,7 +173,7 @@ def get_entity_query( if event_id: return f"event = %({f'event_{group_idx}'})s", {f"event_{group_idx}": event_id} elif action_id: - action = Action.objects.get(pk=action_id, team_id=team_id) + action = Action.objects.get(pk=action_id) action_filter_query, action_params = format_action_filter( team_id=team_id, action=action, @@ -545,7 +545,7 @@ def get_dependent_cohorts( continue else: current_cohort = Cohort.objects.db_manager(using_database).get( - pk=cohort_id, team_id=cohort.team_id, deleted=False + pk=cohort_id, team__project_id=cohort.team.project_id, deleted=False ) seen_cohorts_cache[cohort_id] = current_cohort if current_cohort.id not in seen_cohort_ids: diff --git a/posthog/models/event/query_event_list.py b/posthog/models/event/query_event_list.py index 0ea9b3242a298..7780b4f9980b8 100644 --- a/posthog/models/event/query_event_list.py +++ b/posthog/models/event/query_event_list.py @@ -115,7 +115,7 @@ def query_events_list( if action_id: try: - action = Action.objects.get(pk=action_id, team_id=team.pk) + action = Action.objects.get(pk=action_id, team__project_id=team.project_id) if not action.steps: return [] except Action.DoesNotExist: diff --git a/posthog/models/feature_flag/feature_flag.py b/posthog/models/feature_flag/feature_flag.py index c21af6a397117..beca926b7fbac 100644 --- a/posthog/models/feature_flag/feature_flag.py +++ b/posthog/models/feature_flag/feature_flag.py @@ -187,7 +187,7 @@ def transform_cohort_filters_for_easy_evaluation( return self.conditions else: cohort = Cohort.objects.db_manager(using_database).get( - pk=cohort_id, team_id=self.team_id, deleted=False + pk=cohort_id, team__project_id=self.team.project_id, deleted=False ) seen_cohorts_cache[cohort_id] = cohort except Cohort.DoesNotExist: @@ -291,7 +291,7 @@ def get_cohort_ids( continue else: cohort = Cohort.objects.db_manager(using_database).get( - pk=cohort_id, team_id=self.team_id, deleted=False + pk=cohort_id, team__project_id=self.team.project_id, deleted=False ) seen_cohorts_cache[cohort_id] = cohort diff --git a/posthog/models/feature_flag/user_blast_radius.py b/posthog/models/feature_flag/user_blast_radius.py index 712df09ed5002..bf08e8eed950a 100644 --- a/posthog/models/feature_flag/user_blast_radius.py +++ b/posthog/models/feature_flag/user_blast_radius.py @@ -77,7 +77,7 @@ def get_user_blast_radius( if len(cohort_filters) == 1: try: - target_cohort = Cohort.objects.get(id=cohort_filters[0].value, team=team) + target_cohort = Cohort.objects.get(id=cohort_filters[0].value, team__project_id=team.project_id) except Cohort.DoesNotExist: pass finally: diff --git a/posthog/models/filters/mixins/simplify.py b/posthog/models/filters/mixins/simplify.py index b152e07113f11..01f3d2c4d4745 100644 --- a/posthog/models/filters/mixins/simplify.py +++ b/posthog/models/filters/mixins/simplify.py @@ -108,7 +108,7 @@ def _simplify_property(self, team: "Team", property: "Property", **kwargs) -> "P from posthog.models.cohort.util import simplified_cohort_filter_properties try: - cohort = Cohort.objects.get(pk=property.value, team_id=team.pk) + cohort = Cohort.objects.get(pk=property.value, team__project_id=team.project_id) except Cohort.DoesNotExist: # :TODO: Handle non-existing resource in-query instead return PropertyGroup(type=PropertyOperatorType.AND, values=[property]) diff --git a/posthog/models/product_intent/product_intent.py b/posthog/models/product_intent/product_intent.py index 85ec938eb1129..08bc428ed081b 100644 --- a/posthog/models/product_intent/product_intent.py +++ b/posthog/models/product_intent/product_intent.py @@ -58,7 +58,7 @@ def __str__(self): def has_activated_data_warehouse(self) -> bool: insights = Insight.objects.filter( - team=self.team, + team__project_id=self.team.project_id, created_at__gte=datetime(2024, 6, 1, tzinfo=UTC), query__kind="DataVisualizationNode", ) diff --git a/posthog/queries/base.py b/posthog/queries/base.py index 34713cad4a1d9..907b741eafc76 100644 --- a/posthog/queries/base.py +++ b/posthog/queries/base.py @@ -279,7 +279,7 @@ def lookup_q(key: str, value: Any) -> Q: def property_to_Q( - team_id: int, + project_id: int, property: Property, override_property_values: Optional[dict[str, Any]] = None, cohorts_cache: Optional[dict[int, CohortOrEmpty]] = None, @@ -298,7 +298,7 @@ def property_to_Q( if cohorts_cache.get(cohort_id) is None: queried_cohort = ( Cohort.objects.db_manager(using_database) - .filter(pk=cohort_id, team_id=team_id, deleted=False) + .filter(pk=cohort_id, team__project_id=project_id, deleted=False) .first() ) cohorts_cache[cohort_id] = queried_cohort or "" @@ -306,7 +306,9 @@ def property_to_Q( cohort = cohorts_cache[cohort_id] else: cohort = ( - Cohort.objects.db_manager(using_database).filter(pk=cohort_id, team_id=team_id, deleted=False).first() + Cohort.objects.db_manager(using_database) + .filter(pk=cohort_id, team__project_id=project_id, deleted=False) + .first() ) if not cohort: @@ -329,7 +331,7 @@ def property_to_Q( # :TRICKY: This has potential to create an infinite loop if the cohort is recursive. # But, this shouldn't happen because we check for cyclic cohorts on creation. return property_group_to_Q( - team_id, + project_id, cohort.properties, override_property_values, cohorts_cache, @@ -389,7 +391,7 @@ def property_to_Q( def property_group_to_Q( - team_id: int, + project_id: int, property_group: PropertyGroup, override_property_values: Optional[dict[str, Any]] = None, cohorts_cache: Optional[dict[int, CohortOrEmpty]] = None, @@ -405,7 +407,7 @@ def property_group_to_Q( if isinstance(property_group.values[0], PropertyGroup): for group in property_group.values: group_filter = property_group_to_Q( - team_id, + project_id, cast(PropertyGroup, group), override_property_values, cohorts_cache, @@ -418,7 +420,9 @@ def property_group_to_Q( else: for property in property_group.values: property = cast(Property, property) - property_filter = property_to_Q(team_id, property, override_property_values, cohorts_cache, using_database) + property_filter = property_to_Q( + project_id, property, override_property_values, cohorts_cache, using_database + ) if property_group.type == PropertyOperatorType.OR: if property.negation: filters |= ~property_filter diff --git a/posthog/queries/breakdown_props.py b/posthog/queries/breakdown_props.py index 18fa862b8333a..c4d82984f8e84 100644 --- a/posthog/queries/breakdown_props.py +++ b/posthog/queries/breakdown_props.py @@ -359,9 +359,9 @@ def _format_all_query(team: Team, filter: Filter, **kwargs) -> tuple[str, dict]: def format_breakdown_cohort_join_query(team: Team, filter: Filter, **kwargs) -> tuple[str, list, dict]: entity = kwargs.pop("entity", None) cohorts = ( - Cohort.objects.filter(team_id=team.pk, pk__in=[b for b in filter.breakdown if b != "all"]) + Cohort.objects.filter(team__project_id=team.project_id, pk__in=[b for b in filter.breakdown if b != "all"]) if isinstance(filter.breakdown, list) - else Cohort.objects.filter(team_id=team.pk, pk=filter.breakdown) + else Cohort.objects.filter(team__project_id=team.project_id, pk=filter.breakdown) ) cohort_queries, params = _parse_breakdown_cohorts(list(cohorts), filter.hogql_context) ids = [cohort.pk for cohort in cohorts] diff --git a/posthog/queries/event_query/event_query.py b/posthog/queries/event_query/event_query.py index 5458929aa5ae6..8bb19d73dbcd7 100644 --- a/posthog/queries/event_query/event_query.py +++ b/posthog/queries/event_query/event_query.py @@ -191,7 +191,7 @@ def _determine_should_join_sessions(self) -> None: def _does_cohort_need_persons(self, prop: Property) -> bool: try: - cohort: Cohort = Cohort.objects.get(pk=prop.value, team_id=self._team_id) + cohort: Cohort = Cohort.objects.get(pk=prop.value) except Cohort.DoesNotExist: return False if is_precalculated_query(cohort): diff --git a/posthog/queries/foss_cohort_query.py b/posthog/queries/foss_cohort_query.py index a7e020158872e..f11e53abb528d 100644 --- a/posthog/queries/foss_cohort_query.py +++ b/posthog/queries/foss_cohort_query.py @@ -179,7 +179,10 @@ def __init__( @staticmethod def unwrap_cohort(filter: Filter, team_id: int) -> Filter: + team: Optional[Team] = None + def _unwrap(property_group: PropertyGroup, negate_group: bool = False) -> PropertyGroup: + nonlocal team if len(property_group.values): if isinstance(property_group.values[0], PropertyGroup): # dealing with a list of property groups, so unwrap each one @@ -211,7 +214,11 @@ def _unwrap(property_group: PropertyGroup, negate_group: bool = False) -> Proper negation_value = not current_negation if negate_group else current_negation if prop.type in ["cohort", "precalculated-cohort"]: try: - prop_cohort: Cohort = Cohort.objects.get(pk=prop.value, team_id=team_id) + if team is None: # This ensures we only fetch team if needed, but never more than once + team = Team.objects.get(pk=team_id) + prop_cohort: Cohort = Cohort.objects.get( + pk=prop.value, team__project_id=team.project_id + ) if prop_cohort.is_static: new_property_group_list.append( PropertyGroup( diff --git a/posthog/queries/person_query.py b/posthog/queries/person_query.py index b9abde6524f76..69db14fb1bbb6 100644 --- a/posthog/queries/person_query.py +++ b/posthog/queries/person_query.py @@ -261,7 +261,7 @@ def _get_multiple_cohorts_clause(self, prepend: str = "") -> tuple[str, dict]: # TODO: doesn't support non-caclculated cohorts for index, property in enumerate(self._cohort_filters): try: - cohort = Cohort.objects.get(pk=property.value, team_id=self._team_id) + cohort = Cohort.objects.get(pk=property.value) if property.type == "static-cohort": subquery, subquery_params = format_static_cohort_query(cohort, index, prepend) else: diff --git a/posthog/queries/stickiness/stickiness.py b/posthog/queries/stickiness/stickiness.py index 26204b8e9964f..5e316afae3957 100644 --- a/posthog/queries/stickiness/stickiness.py +++ b/posthog/queries/stickiness/stickiness.py @@ -23,7 +23,7 @@ def run(self, filter: StickinessFilter, team: Team, *args, **kwargs) -> list[dic response = [] for entity in filter.entities: if entity.type == TREND_FILTER_TYPE_ACTIONS and entity.id is not None: - entity.name = Action.objects.only("name").get(team=team, pk=entity.id).name + entity.name = Action.objects.only("name").get(team__project_id=team.project_id, pk=entity.id).name entity_resp = handle_compare(filter=filter, func=self._serialize_entity, team=team, entity=entity) response.extend(entity_resp) diff --git a/posthog/queries/trends/trends.py b/posthog/queries/trends/trends.py index 175813696e8a1..0f0cf911f0865 100644 --- a/posthog/queries/trends/trends.py +++ b/posthog/queries/trends/trends.py @@ -249,9 +249,11 @@ def _run_parallel(self, filter: Filter, team: Team) -> list[dict[str, Any]]: def run(self, filter: Filter, team: Team, is_csv_export: bool = False, *args, **kwargs) -> list[dict[str, Any]]: self.is_csv_export = is_csv_export - actions = Action.objects.filter(team_id=team.pk).order_by("-id") + actions = Action.objects.filter(team__project_id=team.project_id).order_by("-id") if len(filter.actions) > 0: - actions = Action.objects.filter(pk__in=[entity.id for entity in filter.actions], team_id=team.pk) + actions = Action.objects.filter( + pk__in=[entity.id for entity in filter.actions], team__project_id=team.project_id + ) if filter.formula: return handle_compare(filter, self._run_formula_query, team) diff --git a/posthog/queries/trends/trends_actors.py b/posthog/queries/trends/trends_actors.py index f69db981d309a..c04c7378f8057 100644 --- a/posthog/queries/trends/trends_actors.py +++ b/posthog/queries/trends/trends_actors.py @@ -39,7 +39,7 @@ def aggregation_group_type_index(self): def actor_query(self, limit_actors: Optional[bool] = True) -> tuple[str, dict]: if self._filter.breakdown_type == "cohort" and self._filter.breakdown_value != "all": - cohort = Cohort.objects.get(pk=self._filter.breakdown_value, team_id=self._team.pk) + cohort = Cohort.objects.get(pk=self._filter.breakdown_value, team__project_id=self._team.project_id) self._filter = self._filter.shallow_clone( { "properties": self._filter.property_groups.combine_properties( From 8290baa4dac43221658cdb7dcb4b41b97764915a Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Fri, 6 Dec 2024 00:00:17 +0100 Subject: [PATCH 2/5] Make `HogQLContext.project_id` throw --- mypy-baseline.txt | 9 --------- posthog/hogql/context.py | 4 ++-- posthog/hogql/functions/test/test_cohort.py | 8 ++++---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/mypy-baseline.txt b/mypy-baseline.txt index 96984826e9c5a..6e7760d51b733 100644 --- a/mypy-baseline.txt +++ b/mypy-baseline.txt @@ -108,8 +108,6 @@ posthog/hogql/parser.py:0: error: Item "None" of "list[Expr] | None" has no 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] posthog/hogql/parser.py:0: error: Statement is unreachable [unreachable] -posthog/hogql/functions/cohort.py:0: error: Incompatible type for lookup 'team_id': (got "int | None", expected "str | int") [misc] -posthog/hogql/functions/cohort.py:0: error: Incompatible type for lookup 'team_id': (got "int | None", expected "str | int") [misc] posthog/hogql/database/schema/persons_pdi.py:0: error: Incompatible types in assignment (expression has type "Organization | None", variable has type "Organization") [assignment] posthog/hogql/database/schema/groups.py:0: error: Incompatible types in assignment (expression has type "dict[str, DatabaseField]", variable has type "dict[str, FieldOrTable]") [assignment] posthog/hogql/database/schema/groups.py:0: note: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance @@ -220,13 +218,9 @@ posthog/hogql/transforms/lazy_tables.py:0: error: Incompatible types in assignme posthog/hogql/transforms/in_cohort.py:0: error: Incompatible default for argument "context" (default has type "None", argument has type "HogQLContext") [assignment] posthog/hogql/transforms/in_cohort.py:0: note: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True posthog/hogql/transforms/in_cohort.py:0: note: Use https://github.com/hauntsaninja/no_implicit_optional to automatically upgrade your codebase -posthog/hogql/transforms/in_cohort.py:0: error: Incompatible type for lookup 'team_id': (got "int | None", expected "str | int") [misc] -posthog/hogql/transforms/in_cohort.py:0: error: Incompatible type for lookup 'team_id': (got "int | None", expected "str | int") [misc] posthog/hogql/transforms/in_cohort.py:0: error: Incompatible default for argument "context" (default has type "None", argument has type "HogQLContext") [assignment] posthog/hogql/transforms/in_cohort.py:0: note: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True posthog/hogql/transforms/in_cohort.py:0: note: Use https://github.com/hauntsaninja/no_implicit_optional to automatically upgrade your codebase -posthog/hogql/transforms/in_cohort.py:0: error: Incompatible type for lookup 'team_id': (got "int | None", expected "str | int") [misc] -posthog/hogql/transforms/in_cohort.py:0: error: Incompatible type for lookup 'team_id': (got "int | None", expected "str | int") [misc] posthog/hogql/transforms/in_cohort.py:0: error: Argument "table" to "JoinExpr" has incompatible type "Expr"; expected "SelectQuery | SelectSetQuery | Field | None" [arg-type] posthog/hogql/transforms/in_cohort.py:0: error: List item 0 has incompatible type "SelectQueryType | None"; expected "SelectQueryType" [list-item] posthog/hogql/transforms/in_cohort.py:0: error: Item "None" of "JoinConstraint | None" has no attribute "expr" [union-attr] @@ -710,9 +704,6 @@ posthog/hogql/test/test_parser_python.py:0: error: Unsupported dynamic base clas posthog/hogql/test/test_parser_cpp.py:0: error: Unsupported dynamic base class "parser_test_factory" [misc] posthog/hogql/test/test_parse_string_python.py:0: error: Unsupported dynamic base class "parse_string_test_factory" [misc] posthog/hogql/test/test_parse_string_cpp.py:0: error: Unsupported dynamic base class "parse_string_test_factory" [misc] -posthog/hogql/functions/test/test_cohort.py:0: error: "TestCohort" has no attribute "snapshot" [attr-defined] -posthog/hogql/functions/test/test_cohort.py:0: error: "TestCohort" has no attribute "snapshot" [attr-defined] -posthog/hogql/functions/test/test_cohort.py:0: error: "TestCohort" has no attribute "snapshot" [attr-defined] posthog/hogql/database/test/test_view.py:0: error: Argument "dialect" to "print_ast" has incompatible type "str"; expected "Literal['hogql', 'clickhouse']" [arg-type] posthog/hogql/database/test/test_s3_table.py:0: error: Argument "dialect" to "print_ast" has incompatible type "str"; expected "Literal['hogql', 'clickhouse']" [arg-type] posthog/helpers/full_text_search.py:0: error: Incompatible return value type (got "SearchVector", expected "CombinedExpression") [return-value] diff --git a/posthog/hogql/context.py b/posthog/hogql/context.py index 104125aa9c5bc..5f5f8a3666752 100644 --- a/posthog/hogql/context.py +++ b/posthog/hogql/context.py @@ -97,10 +97,10 @@ def add_error( self.errors.append(HogQLNotice(start=start, end=end, message=message, fix=fix)) @cached_property - def project_id(self) -> Optional[int]: + def project_id(self) -> int: from posthog.models import Team if not self.team and not self.team_id: - return None + raise ValueError("Either team or team_id must be set to determine project_id") team = self.team or Team.objects.only("project_id").get(id=self.team_id) return team.project_id diff --git a/posthog/hogql/functions/test/test_cohort.py b/posthog/hogql/functions/test/test_cohort.py index e5372e03654e2..dbedef9739fd4 100644 --- a/posthog/hogql/functions/test/test_cohort.py +++ b/posthog/hogql/functions/test/test_cohort.py @@ -52,7 +52,7 @@ def test_in_cohort_dynamic(self): modifiers=HogQLQueryModifiers(inCohortVia="subquery"), pretty=False, ) - assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot + assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore self.assertEqual(len(response.results), 1) self.assertEqual(response.results[0][0], random_uuid) @@ -72,7 +72,7 @@ def test_in_cohort_dynamic_other_team_in_project(self): modifiers=HogQLQueryModifiers(inCohortVia="subquery"), pretty=False, ) - assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot + assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore self.assertEqual(len(response.results), 1) self.assertEqual(response.results[0][0], random_uuid) @@ -89,7 +89,7 @@ def test_in_cohort_static(self): modifiers=HogQLQueryModifiers(inCohortVia="subquery"), pretty=False, ) - assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot + assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore @pytest.mark.usefixtures("unittest_snapshot") @override_settings(PERSON_ON_EVENTS_OVERRIDE=True, PERSON_ON_EVENTS_V2_OVERRIDE=False) @@ -105,7 +105,7 @@ def test_in_cohort_strings(self): modifiers=HogQLQueryModifiers(inCohortVia="subquery"), pretty=False, ) - assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot + assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore @override_settings(PERSON_ON_EVENTS_OVERRIDE=True, PERSON_ON_EVENTS_V2_OVERRIDE=True) def test_in_cohort_error(self): From cb3091ce1dbd6bad4b127e3d0ee3a21f2e2c795a Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 23:59:33 +0000 Subject: [PATCH 3/5] Update query snapshots --- .../test/__snapshots__/test_filter.ambr | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/posthog/models/filters/test/__snapshots__/test_filter.ambr b/posthog/models/filters/test/__snapshots__/test_filter.ambr index 38d231d6b6360..773730e93858c 100644 --- a/posthog/models/filters/test/__snapshots__/test_filter.ambr +++ b/posthog/models/filters/test/__snapshots__/test_filter.ambr @@ -440,9 +440,10 @@ "posthog_cohort"."is_static", "posthog_cohort"."groups" FROM "posthog_cohort" + INNER JOIN "posthog_team" ON ("posthog_cohort"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_cohort"."deleted" AND "posthog_cohort"."id" = 99999 - AND "posthog_cohort"."team_id" = 99999) + AND "posthog_team"."project_id" = 99999) ORDER BY "posthog_cohort"."id" ASC LIMIT 1 ''' @@ -517,9 +518,10 @@ "posthog_cohort"."is_static", "posthog_cohort"."groups" FROM "posthog_cohort" + INNER JOIN "posthog_team" ON ("posthog_cohort"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_cohort"."deleted" AND "posthog_cohort"."id" = 99999 - AND "posthog_cohort"."team_id" = 99999) + AND "posthog_team"."project_id" = 99999) ORDER BY "posthog_cohort"."id" ASC LIMIT 1 ''' @@ -585,9 +587,10 @@ "posthog_cohort"."is_static", "posthog_cohort"."groups" FROM "posthog_cohort" + INNER JOIN "posthog_team" ON ("posthog_cohort"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_cohort"."deleted" AND "posthog_cohort"."id" = 99999 - AND "posthog_cohort"."team_id" = 99999) + AND "posthog_team"."project_id" = 99999) ORDER BY "posthog_cohort"."id" ASC LIMIT 1 ''' @@ -613,9 +616,10 @@ "posthog_cohort"."is_static", "posthog_cohort"."groups" FROM "posthog_cohort" + INNER JOIN "posthog_team" ON ("posthog_cohort"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_cohort"."deleted" AND "posthog_cohort"."id" = 99999 - AND "posthog_cohort"."team_id" = 99999) + AND "posthog_team"."project_id" = 99999) ORDER BY "posthog_cohort"."id" ASC LIMIT 1 ''' @@ -641,9 +645,10 @@ "posthog_cohort"."is_static", "posthog_cohort"."groups" FROM "posthog_cohort" + INNER JOIN "posthog_team" ON ("posthog_cohort"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_cohort"."deleted" AND "posthog_cohort"."id" = 99999 - AND "posthog_cohort"."team_id" = 99999) + AND "posthog_team"."project_id" = 99999) ORDER BY "posthog_cohort"."id" ASC LIMIT 1 ''' @@ -669,9 +674,10 @@ "posthog_cohort"."is_static", "posthog_cohort"."groups" FROM "posthog_cohort" + INNER JOIN "posthog_team" ON ("posthog_cohort"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_cohort"."deleted" AND "posthog_cohort"."id" = 99999 - AND "posthog_cohort"."team_id" = 99999) + AND "posthog_team"."project_id" = 99999) ORDER BY "posthog_cohort"."id" ASC LIMIT 1 ''' From 4d6c01c7459de177575cbed5673089a975902693 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Fri, 6 Dec 2024 11:11:08 +0100 Subject: [PATCH 4/5] Update insight.py --- posthog/api/insight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/api/insight.py b/posthog/api/insight.py index 79485da247830..22b53f07e73af 100644 --- a/posthog/api/insight.py +++ b/posthog/api/insight.py @@ -1184,7 +1184,7 @@ def activity(self, request: request.Request, **kwargs): page = int(request.query_params.get("page", "1")) item_id = kwargs["pk"] - if not Insight.objects.filter(id=item_id, team__project_id=self.project_id).exists(): + if not Insight.objects.filter(id=item_id, team__project_id=self.team.project_id).exists(): return Response("", status=status.HTTP_404_NOT_FOUND) activity_page = load_activity( From ddc0cc830ef33731bc924bf9def2ffa16079a4cf Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:26:06 +0000 Subject: [PATCH 5/5] Update query snapshots --- .../test/__snapshots__/test_feature_flag.ambr | 336 +----------------- 1 file changed, 6 insertions(+), 330 deletions(-) diff --git a/posthog/api/test/__snapshots__/test_feature_flag.ambr b/posthog/api/test/__snapshots__/test_feature_flag.ambr index 8250f4f667393..a00efc8ba764b 100644 --- a/posthog/api/test/__snapshots__/test_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_feature_flag.ambr @@ -338,61 +338,7 @@ # name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator ''' SELECT "posthog_team"."id", - "posthog_team"."uuid", - "posthog_team"."organization_id", - "posthog_team"."project_id", - "posthog_team"."api_token", - "posthog_team"."app_urls", - "posthog_team"."name", - "posthog_team"."slack_incoming_webhook", - "posthog_team"."created_at", - "posthog_team"."updated_at", - "posthog_team"."anonymize_ips", - "posthog_team"."completed_snippet_onboarding", - "posthog_team"."has_completed_onboarding_for", - "posthog_team"."ingested_event", - "posthog_team"."autocapture_opt_out", - "posthog_team"."autocapture_web_vitals_opt_in", - "posthog_team"."autocapture_web_vitals_allowed_metrics", - "posthog_team"."autocapture_exceptions_opt_in", - "posthog_team"."autocapture_exceptions_errors_to_ignore", - "posthog_team"."person_processing_opt_out", - "posthog_team"."session_recording_opt_in", - "posthog_team"."session_recording_sample_rate", - "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_linked_flag", - "posthog_team"."session_recording_network_payload_capture_config", - "posthog_team"."session_recording_url_trigger_config", - "posthog_team"."session_recording_url_blocklist_config", - "posthog_team"."session_recording_event_trigger_config", - "posthog_team"."session_replay_config", - "posthog_team"."survey_config", - "posthog_team"."capture_console_log_opt_in", - "posthog_team"."capture_performance_opt_in", - "posthog_team"."capture_dead_clicks", - "posthog_team"."surveys_opt_in", - "posthog_team"."heatmaps_opt_in", - "posthog_team"."session_recording_version", - "posthog_team"."signup_token", - "posthog_team"."is_demo", - "posthog_team"."access_control", - "posthog_team"."week_start_day", - "posthog_team"."inject_web_apps", - "posthog_team"."test_account_filters", - "posthog_team"."test_account_filters_default_checked", - "posthog_team"."path_cleaning_filters", - "posthog_team"."timezone", - "posthog_team"."data_attributes", - "posthog_team"."person_display_name_properties", - "posthog_team"."live_events_columns", - "posthog_team"."recording_domains", - "posthog_team"."primary_dashboard_id", - "posthog_team"."extra_settings", - "posthog_team"."modifiers", - "posthog_team"."correlation_config", - "posthog_team"."session_recording_retention_period_days", - "posthog_team"."external_data_workspace_id", - "posthog_team"."external_data_workspace_last_synced_at" + "posthog_team"."project_id" FROM "posthog_team" WHERE "posthog_team"."id" = 99999 LIMIT 21 @@ -586,61 +532,7 @@ # name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too ''' SELECT "posthog_team"."id", - "posthog_team"."uuid", - "posthog_team"."organization_id", - "posthog_team"."project_id", - "posthog_team"."api_token", - "posthog_team"."app_urls", - "posthog_team"."name", - "posthog_team"."slack_incoming_webhook", - "posthog_team"."created_at", - "posthog_team"."updated_at", - "posthog_team"."anonymize_ips", - "posthog_team"."completed_snippet_onboarding", - "posthog_team"."has_completed_onboarding_for", - "posthog_team"."ingested_event", - "posthog_team"."autocapture_opt_out", - "posthog_team"."autocapture_web_vitals_opt_in", - "posthog_team"."autocapture_web_vitals_allowed_metrics", - "posthog_team"."autocapture_exceptions_opt_in", - "posthog_team"."autocapture_exceptions_errors_to_ignore", - "posthog_team"."person_processing_opt_out", - "posthog_team"."session_recording_opt_in", - "posthog_team"."session_recording_sample_rate", - "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_linked_flag", - "posthog_team"."session_recording_network_payload_capture_config", - "posthog_team"."session_recording_url_trigger_config", - "posthog_team"."session_recording_url_blocklist_config", - "posthog_team"."session_recording_event_trigger_config", - "posthog_team"."session_replay_config", - "posthog_team"."survey_config", - "posthog_team"."capture_console_log_opt_in", - "posthog_team"."capture_performance_opt_in", - "posthog_team"."capture_dead_clicks", - "posthog_team"."surveys_opt_in", - "posthog_team"."heatmaps_opt_in", - "posthog_team"."session_recording_version", - "posthog_team"."signup_token", - "posthog_team"."is_demo", - "posthog_team"."access_control", - "posthog_team"."week_start_day", - "posthog_team"."inject_web_apps", - "posthog_team"."test_account_filters", - "posthog_team"."test_account_filters_default_checked", - "posthog_team"."path_cleaning_filters", - "posthog_team"."timezone", - "posthog_team"."data_attributes", - "posthog_team"."person_display_name_properties", - "posthog_team"."live_events_columns", - "posthog_team"."recording_domains", - "posthog_team"."primary_dashboard_id", - "posthog_team"."extra_settings", - "posthog_team"."modifiers", - "posthog_team"."correlation_config", - "posthog_team"."session_recording_retention_period_days", - "posthog_team"."external_data_workspace_id", - "posthog_team"."external_data_workspace_last_synced_at" + "posthog_team"."project_id" FROM "posthog_team" WHERE "posthog_team"."id" = 99999 LIMIT 21 @@ -918,61 +810,7 @@ # name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment ''' SELECT "posthog_team"."id", - "posthog_team"."uuid", - "posthog_team"."organization_id", - "posthog_team"."project_id", - "posthog_team"."api_token", - "posthog_team"."app_urls", - "posthog_team"."name", - "posthog_team"."slack_incoming_webhook", - "posthog_team"."created_at", - "posthog_team"."updated_at", - "posthog_team"."anonymize_ips", - "posthog_team"."completed_snippet_onboarding", - "posthog_team"."has_completed_onboarding_for", - "posthog_team"."ingested_event", - "posthog_team"."autocapture_opt_out", - "posthog_team"."autocapture_web_vitals_opt_in", - "posthog_team"."autocapture_web_vitals_allowed_metrics", - "posthog_team"."autocapture_exceptions_opt_in", - "posthog_team"."autocapture_exceptions_errors_to_ignore", - "posthog_team"."person_processing_opt_out", - "posthog_team"."session_recording_opt_in", - "posthog_team"."session_recording_sample_rate", - "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_linked_flag", - "posthog_team"."session_recording_network_payload_capture_config", - "posthog_team"."session_recording_url_trigger_config", - "posthog_team"."session_recording_url_blocklist_config", - "posthog_team"."session_recording_event_trigger_config", - "posthog_team"."session_replay_config", - "posthog_team"."survey_config", - "posthog_team"."capture_console_log_opt_in", - "posthog_team"."capture_performance_opt_in", - "posthog_team"."capture_dead_clicks", - "posthog_team"."surveys_opt_in", - "posthog_team"."heatmaps_opt_in", - "posthog_team"."session_recording_version", - "posthog_team"."signup_token", - "posthog_team"."is_demo", - "posthog_team"."access_control", - "posthog_team"."week_start_day", - "posthog_team"."inject_web_apps", - "posthog_team"."test_account_filters", - "posthog_team"."test_account_filters_default_checked", - "posthog_team"."path_cleaning_filters", - "posthog_team"."timezone", - "posthog_team"."data_attributes", - "posthog_team"."person_display_name_properties", - "posthog_team"."live_events_columns", - "posthog_team"."recording_domains", - "posthog_team"."primary_dashboard_id", - "posthog_team"."extra_settings", - "posthog_team"."modifiers", - "posthog_team"."correlation_config", - "posthog_team"."session_recording_retention_period_days", - "posthog_team"."external_data_workspace_id", - "posthog_team"."external_data_workspace_last_synced_at" + "posthog_team"."project_id" FROM "posthog_team" WHERE "posthog_team"."id" = 99999 LIMIT 21 @@ -1183,61 +1021,7 @@ # name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.7 ''' SELECT "posthog_team"."id", - "posthog_team"."uuid", - "posthog_team"."organization_id", - "posthog_team"."project_id", - "posthog_team"."api_token", - "posthog_team"."app_urls", - "posthog_team"."name", - "posthog_team"."slack_incoming_webhook", - "posthog_team"."created_at", - "posthog_team"."updated_at", - "posthog_team"."anonymize_ips", - "posthog_team"."completed_snippet_onboarding", - "posthog_team"."has_completed_onboarding_for", - "posthog_team"."ingested_event", - "posthog_team"."autocapture_opt_out", - "posthog_team"."autocapture_web_vitals_opt_in", - "posthog_team"."autocapture_web_vitals_allowed_metrics", - "posthog_team"."autocapture_exceptions_opt_in", - "posthog_team"."autocapture_exceptions_errors_to_ignore", - "posthog_team"."person_processing_opt_out", - "posthog_team"."session_recording_opt_in", - "posthog_team"."session_recording_sample_rate", - "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_linked_flag", - "posthog_team"."session_recording_network_payload_capture_config", - "posthog_team"."session_recording_url_trigger_config", - "posthog_team"."session_recording_url_blocklist_config", - "posthog_team"."session_recording_event_trigger_config", - "posthog_team"."session_replay_config", - "posthog_team"."survey_config", - "posthog_team"."capture_console_log_opt_in", - "posthog_team"."capture_performance_opt_in", - "posthog_team"."capture_dead_clicks", - "posthog_team"."surveys_opt_in", - "posthog_team"."heatmaps_opt_in", - "posthog_team"."session_recording_version", - "posthog_team"."signup_token", - "posthog_team"."is_demo", - "posthog_team"."access_control", - "posthog_team"."week_start_day", - "posthog_team"."inject_web_apps", - "posthog_team"."test_account_filters", - "posthog_team"."test_account_filters_default_checked", - "posthog_team"."path_cleaning_filters", - "posthog_team"."timezone", - "posthog_team"."data_attributes", - "posthog_team"."person_display_name_properties", - "posthog_team"."live_events_columns", - "posthog_team"."recording_domains", - "posthog_team"."primary_dashboard_id", - "posthog_team"."extra_settings", - "posthog_team"."modifiers", - "posthog_team"."correlation_config", - "posthog_team"."session_recording_retention_period_days", - "posthog_team"."external_data_workspace_id", - "posthog_team"."external_data_workspace_last_synced_at" + "posthog_team"."project_id" FROM "posthog_team" WHERE "posthog_team"."id" = 99999 LIMIT 21 @@ -1297,61 +1081,7 @@ # name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag ''' SELECT "posthog_team"."id", - "posthog_team"."uuid", - "posthog_team"."organization_id", - "posthog_team"."project_id", - "posthog_team"."api_token", - "posthog_team"."app_urls", - "posthog_team"."name", - "posthog_team"."slack_incoming_webhook", - "posthog_team"."created_at", - "posthog_team"."updated_at", - "posthog_team"."anonymize_ips", - "posthog_team"."completed_snippet_onboarding", - "posthog_team"."has_completed_onboarding_for", - "posthog_team"."ingested_event", - "posthog_team"."autocapture_opt_out", - "posthog_team"."autocapture_web_vitals_opt_in", - "posthog_team"."autocapture_web_vitals_allowed_metrics", - "posthog_team"."autocapture_exceptions_opt_in", - "posthog_team"."autocapture_exceptions_errors_to_ignore", - "posthog_team"."person_processing_opt_out", - "posthog_team"."session_recording_opt_in", - "posthog_team"."session_recording_sample_rate", - "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_linked_flag", - "posthog_team"."session_recording_network_payload_capture_config", - "posthog_team"."session_recording_url_trigger_config", - "posthog_team"."session_recording_url_blocklist_config", - "posthog_team"."session_recording_event_trigger_config", - "posthog_team"."session_replay_config", - "posthog_team"."survey_config", - "posthog_team"."capture_console_log_opt_in", - "posthog_team"."capture_performance_opt_in", - "posthog_team"."capture_dead_clicks", - "posthog_team"."surveys_opt_in", - "posthog_team"."heatmaps_opt_in", - "posthog_team"."session_recording_version", - "posthog_team"."signup_token", - "posthog_team"."is_demo", - "posthog_team"."access_control", - "posthog_team"."week_start_day", - "posthog_team"."inject_web_apps", - "posthog_team"."test_account_filters", - "posthog_team"."test_account_filters_default_checked", - "posthog_team"."path_cleaning_filters", - "posthog_team"."timezone", - "posthog_team"."data_attributes", - "posthog_team"."person_display_name_properties", - "posthog_team"."live_events_columns", - "posthog_team"."recording_domains", - "posthog_team"."primary_dashboard_id", - "posthog_team"."extra_settings", - "posthog_team"."modifiers", - "posthog_team"."correlation_config", - "posthog_team"."session_recording_retention_period_days", - "posthog_team"."external_data_workspace_id", - "posthog_team"."external_data_workspace_last_synced_at" + "posthog_team"."project_id" FROM "posthog_team" WHERE "posthog_team"."id" = 99999 LIMIT 21 @@ -2076,61 +1806,7 @@ # name: TestFeatureFlag.test_creating_static_cohort.8 ''' SELECT "posthog_team"."id", - "posthog_team"."uuid", - "posthog_team"."organization_id", - "posthog_team"."project_id", - "posthog_team"."api_token", - "posthog_team"."app_urls", - "posthog_team"."name", - "posthog_team"."slack_incoming_webhook", - "posthog_team"."created_at", - "posthog_team"."updated_at", - "posthog_team"."anonymize_ips", - "posthog_team"."completed_snippet_onboarding", - "posthog_team"."has_completed_onboarding_for", - "posthog_team"."ingested_event", - "posthog_team"."autocapture_opt_out", - "posthog_team"."autocapture_web_vitals_opt_in", - "posthog_team"."autocapture_web_vitals_allowed_metrics", - "posthog_team"."autocapture_exceptions_opt_in", - "posthog_team"."autocapture_exceptions_errors_to_ignore", - "posthog_team"."person_processing_opt_out", - "posthog_team"."session_recording_opt_in", - "posthog_team"."session_recording_sample_rate", - "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_linked_flag", - "posthog_team"."session_recording_network_payload_capture_config", - "posthog_team"."session_recording_url_trigger_config", - "posthog_team"."session_recording_url_blocklist_config", - "posthog_team"."session_recording_event_trigger_config", - "posthog_team"."session_replay_config", - "posthog_team"."survey_config", - "posthog_team"."capture_console_log_opt_in", - "posthog_team"."capture_performance_opt_in", - "posthog_team"."capture_dead_clicks", - "posthog_team"."surveys_opt_in", - "posthog_team"."heatmaps_opt_in", - "posthog_team"."session_recording_version", - "posthog_team"."signup_token", - "posthog_team"."is_demo", - "posthog_team"."access_control", - "posthog_team"."week_start_day", - "posthog_team"."inject_web_apps", - "posthog_team"."test_account_filters", - "posthog_team"."test_account_filters_default_checked", - "posthog_team"."path_cleaning_filters", - "posthog_team"."timezone", - "posthog_team"."data_attributes", - "posthog_team"."person_display_name_properties", - "posthog_team"."live_events_columns", - "posthog_team"."recording_domains", - "posthog_team"."primary_dashboard_id", - "posthog_team"."extra_settings", - "posthog_team"."modifiers", - "posthog_team"."correlation_config", - "posthog_team"."session_recording_retention_period_days", - "posthog_team"."external_data_workspace_id", - "posthog_team"."external_data_workspace_last_synced_at" + "posthog_team"."project_id" FROM "posthog_team" WHERE "posthog_team"."id" = 99999 LIMIT 21