diff --git a/.github/workflows/ci-plugin-server.yml b/.github/workflows/ci-plugin-server.yml index b4d6cb0a17f36..a62bd4a66851a 100644 --- a/.github/workflows/ci-plugin-server.yml +++ b/.github/workflows/ci-plugin-server.yml @@ -151,9 +151,11 @@ jobs: if: needs.changes.outputs.plugin-server == 'true' run: cd plugin-server && pnpm i - - name: Wait for Clickhouse & Kafka + - name: Wait for Clickhouse, Redis & Kafka if: needs.changes.outputs.plugin-server == 'true' - run: bin/check_kafka_clickhouse_up + run: | + docker compose -f docker-compose.dev.yml up kafka redis clickhouse -d --wait + bin/check_kafka_clickhouse_up - name: Set up databases if: needs.changes.outputs.plugin-server == 'true' @@ -173,16 +175,11 @@ jobs: run: cd plugin-server && pnpm test -- --runInBand --forceExit tests/ --shard=${{matrix.shard}} functional-tests: - name: Functional tests (POE=${{matrix.POE_EMBRACE_JOIN_FOR_TEAMS}},RDK=${{matrix.KAFKA_CONSUMPTION_USE_RDKAFKA}}) + name: Functional tests needs: changes if: needs.changes.outputs.plugin-server == 'true' runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - POE_EMBRACE_JOIN_FOR_TEAMS: ['', '*'] - env: REDIS_URL: 'redis://localhost' CLICKHOUSE_HOST: 'localhost' @@ -190,7 +187,6 @@ jobs: KAFKA_HOSTS: 'kafka:9092' DATABASE_URL: 'postgres://posthog:posthog@localhost:5432/posthog' RELOAD_PLUGIN_JITTER_MAX_MS: 0 - POE_EMBRACE_JOIN_FOR_TEAMS: ${{matrix.POE_EMBRACE_JOIN_FOR_TEAMS}} steps: - name: Code check out @@ -241,8 +237,10 @@ jobs: pnpm install --frozen-lockfile pnpm build - - name: Wait for Clickhouse & Kafka - run: bin/check_kafka_clickhouse_up + - name: Wait for Clickhouse, Redis & Kafka + run: | + docker compose -f docker-compose.dev.yml up kafka redis clickhouse -d --wait + bin/check_kafka_clickhouse_up - name: Set up databases env: diff --git a/cypress/e2e/surveys.cy.ts b/cypress/e2e/surveys.cy.ts index 3df925f425a4d..42c2fc6bc11cb 100644 --- a/cypress/e2e/surveys.cy.ts +++ b/cypress/e2e/surveys.cy.ts @@ -165,7 +165,7 @@ describe('Surveys', () => { cy.get('tbody').should('not.exist') }) - it('Delete survey', () => { + it('deletes a survey', () => { cy.get('h1').should('contain', 'Surveys') cy.get('[data-attr=new-survey]').click() cy.get('[data-attr=new-blank-survey]').click() @@ -278,4 +278,154 @@ describe('Surveys', () => { cy.reload() cy.contains('The survey will be stopped once 228 responses are received.').should('be.visible') }) + + it('creates a new survey with branching logic', () => { + cy.window().then((win) => { + win.POSTHOG_APP_CONTEXT.current_user.organization.available_product_features = [ + { + key: 'surveys_multiple_questions', + name: 'Multiple questions', + description: 'Ask up to 10 questions in a single survey.', + unit: null, + limit: null, + note: null, + }, + ] + + cy.get('h1').should('contain', 'Surveys') + cy.title().should('equal', 'Surveys • PostHog') + + cy.get('[data-attr="new-survey"]').click() + cy.get('[data-attr="new-blank-survey"]').click() + cy.get('[data-attr="survey-name"]').type(name) + + // Prepare questions + cy.get('[data-attr=survey-question-label-0]') + .focus() + .clear() + .type('How happy are you?', { delay: 0 }) + .should('have.value', 'How happy are you?') + cy.get('[data-attr=survey-question-type-0]').click() + cy.get('[data-attr=survey-question-type-0-rating]').click() + cy.get('[data-attr="add-question"]').click() + + cy.get('[data-attr=survey-question-label-1]') + .focus() + .clear() + .type('Sorry to hear that. Please tell us more!', { delay: 0 }) + .should('have.value', 'Sorry to hear that. Please tell us more!') + cy.get('[data-attr="add-question"]').click() + + cy.get('[data-attr=survey-question-label-2]') + .focus() + .clear() + .type('Seems you are not completely happy. Please tell us more!', { delay: 0 }) + .should('have.value', 'Seems you are not completely happy. Please tell us more!') + cy.get('[data-attr="add-question"]').click() + + cy.get('[data-attr=survey-question-label-3]') + .focus() + .clear() + .type('Glad to hear that! Please tell us more', { delay: 0 }) + .should('have.value', 'Glad to hear that! Please tell us more') + cy.get('[data-attr="add-question"]').click() + + cy.get('[data-attr=survey-question-label-4]') + .focus() + .clear() + .type('Would you like to leave us a review?', { delay: 0 }) + .should('have.value', 'Would you like to leave us a review?') + cy.get('[data-attr=survey-question-type-4]').click() + cy.get('[data-attr=survey-question-type-4-single_choice]').click() + cy.get('[data-attr="add-question"]').click() + + cy.get('[data-attr=survey-question-label-5]') + .focus() + .clear() + .type('Please write your review here', { delay: 0 }) + .should('have.value', 'Please write your review here') + + // Set branching + // Question 1 - How happy are you? + cy.get('[data-attr=survey-question-panel-0]').click() + // Default branching is always "Next question" + cy.get('button[data-attr="survey-question-0-branching-select"]').find('span').contains('Next question') + cy.get('[data-attr=survey-question-0-branching-select]').click() + cy.get('.Popover__box button').contains('span', 'Specific question based on answer').click() + cy.get('button[data-attr="survey-question-0-branching-select"]') + .find('span') + .contains('Specific question based on answer') + + cy.get('[data-attr=survey-question-0-branching-response_based-select-0]').click() + cy.get('.Popover__box button').contains('span', '2.').click() + cy.get('button[data-attr="survey-question-0-branching-response_based-select-0"]') + .find('span') + .contains('2.') + + cy.get('[data-attr=survey-question-0-branching-response_based-select-1]').click() + cy.get('.Popover__box button').contains('span', '3.').click() + cy.get('button[data-attr="survey-question-0-branching-response_based-select-1"]') + .find('span') + .contains('3.') + + cy.get('[data-attr=survey-question-0-branching-response_based-select-2]').click() + cy.get('.Popover__box button').contains('span', '4.').click() + cy.get('button[data-attr="survey-question-0-branching-response_based-select-2"]') + .find('span') + .contains('4.') + + // Question 2 - Sorry to hear that... + cy.get('[data-attr=survey-question-panel-1]').click() + cy.get('[data-attr=survey-question-1-branching-select]').click() + cy.get('.Popover__box button').contains('span', 'Confirmation message').click() + cy.get('button[data-attr="survey-question-1-branching-select"]') + .find('span') + .contains('Confirmation message') + + // Question 3 - Seems you are not completely happy... + cy.get('[data-attr=survey-question-panel-2]').click() + cy.get('[data-attr=survey-question-2-branching-select]').click() + cy.get('.Popover__box button').contains('span', 'Confirmation message').click() + cy.get('button[data-attr="survey-question-2-branching-select"]') + .find('span') + .contains('Confirmation message') + + // Question 4 - Glad to hear that... + cy.get('[data-attr=survey-question-panel-3]').click() + cy.get('[data-attr=survey-question-3-branching-select]').click() + // "Would you like to leave us a review?" + cy.get('.Popover__box button').contains('span', '5.').click() + cy.get('button[data-attr="survey-question-3-branching-select"]').find('span').contains('5.') + + // Question 5 - Would you like to leave us a review? + cy.get('[data-attr=survey-question-panel-4]').click() + cy.get('[data-attr=survey-question-4-branching-select]').click() + cy.get('.Popover__box button').contains('span', 'Specific question based on answer').click() + cy.get('button[data-attr="survey-question-4-branching-select"]') + .find('span') + .contains('Specific question based on answer') + + cy.get('[data-attr=survey-question-4-branching-response_based-select-0]').click() + cy.get('.Popover__box button').contains('span', 'Next question').click() + cy.get('button[data-attr="survey-question-4-branching-response_based-select-0"]') + .find('span') + .contains('Next question') + + cy.get('[data-attr=survey-question-4-branching-response_based-select-1]').click() + cy.get('.Popover__box button').contains('span', 'Confirmation message').click() + cy.get('button[data-attr="survey-question-4-branching-response_based-select-1"]') + .find('span') + .contains('Confirmation message') + + // Question 6 - Please write your review here + cy.get('[data-attr=survey-question-panel-5]').click() + cy.get('button[data-attr="survey-question-5-branching-select"]') + .find('span') + .contains('Confirmation message') + + // Save + cy.get('[data-attr="save-survey"]').eq(0).click() + cy.get('[data-attr=success-toast]').contains('created').should('exist') + }) + }) }) diff --git a/docker-compose.base.yml b/docker-compose.base.yml index 15adbe9d5febe..4303d86a91350 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -19,6 +19,11 @@ services: image: redis:6.2.7-alpine restart: on-failure command: redis-server --maxmemory-policy allkeys-lru --maxmemory 200mb + healthcheck: + test: ['CMD', 'redis-cli', 'ping'] + interval: 3s + timeout: 10s + retries: 10 clickhouse: # @@ -42,6 +47,11 @@ services: KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 KAFKA_CFG_ZOOKEEPER_CONNECT: zookeeper:2181 ALLOW_PLAINTEXT_LISTENER: 'true' + healthcheck: + test: kafka-cluster.sh cluster-id --bootstrap-server localhost:9092 || exit 1 + interval: 3s + timeout: 10s + retries: 10 kafka_ui: image: provectuslabs/kafka-ui:latest diff --git a/ee/clickhouse/materialized_columns/analyze.py b/ee/clickhouse/materialized_columns/analyze.py index aaacab23e702a..d19a282117e79 100644 --- a/ee/clickhouse/materialized_columns/analyze.py +++ b/ee/clickhouse/materialized_columns/analyze.py @@ -50,13 +50,6 @@ def event_properties(self, team_id: str) -> set[str]: def person_on_events_properties(self, team_id: str) -> set[str]: return self._get_properties(GET_EVENT_PROPERTIES_COUNT.format(column_name="person_properties"), team_id) - @instance_memoize - def group_on_events_properties(self, group_type_index: int, team_id: str) -> set[str]: - return self._get_properties( - GET_EVENT_PROPERTIES_COUNT.format(column_name=f"group{group_type_index}_properties"), - team_id, - ) - def _get_properties(self, query, team_id) -> set[str]: rows = sync_execute(query, {"team_id": team_id}) return {name for name, _ in rows} @@ -99,11 +92,6 @@ def properties( person_props = team_manager.person_properties(self.team_id) event_props = team_manager.event_properties(self.team_id) person_on_events_props = team_manager.person_on_events_properties(self.team_id) - group0_props = team_manager.group_on_events_properties(0, self.team_id) - group1_props = team_manager.group_on_events_properties(1, self.team_id) - group2_props = team_manager.group_on_events_properties(2, self.team_id) - group3_props = team_manager.group_on_events_properties(3, self.team_id) - group4_props = team_manager.group_on_events_properties(4, self.team_id) for table_column, property in self._all_properties: if property in event_props: @@ -113,16 +101,6 @@ def properties( if property in person_on_events_props and "person_properties" in table_column: yield "events", "person_properties", property - if property in group0_props and "group0_properties" in table_column: - yield "events", "group0_properties", property - if property in group1_props and "group1_properties" in table_column: - yield "events", "group1_properties", property - if property in group2_props and "group2_properties" in table_column: - yield "events", "group2_properties", property - if property in group3_props and "group3_properties" in table_column: - yield "events", "group3_properties", property - if property in group4_props and "group4_properties" in table_column: - yield "events", "group4_properties", property def _analyze(since_hours_ago: int, min_query_time: int, team_id: Optional[int] = None) -> list[Suggestion]: diff --git a/ee/clickhouse/materialized_columns/columns.py b/ee/clickhouse/materialized_columns/columns.py index 00da36d698d4d..3dc21ccde59f6 100644 --- a/ee/clickhouse/materialized_columns/columns.py +++ b/ee/clickhouse/materialized_columns/columns.py @@ -200,7 +200,7 @@ def _materialized_column_name( ) -> str: "Returns a sanitized and unique column name to use for materialized column" - prefix = "mat_" if table == "events" or table == "groups" else "pmat_" + prefix = "pmat_" if table == "person" else "mat_" if table_column != DEFAULT_TABLE_COLUMN: prefix += f"{SHORT_TABLE_COLUMN_NAME[table_column]}_" diff --git a/ee/clickhouse/queries/column_optimizer.py b/ee/clickhouse/queries/column_optimizer.py index b1bf142aa3d1e..df7873a7ce131 100644 --- a/ee/clickhouse/queries/column_optimizer.py +++ b/ee/clickhouse/queries/column_optimizer.py @@ -1,7 +1,6 @@ from collections import Counter as TCounter from typing import cast -from posthog.clickhouse.materialized_columns.column import ColumnName from posthog.constants import TREND_FILTER_TYPE_ACTIONS, FunnelCorrelationType from posthog.models.action.util import get_action_tables_and_properties from posthog.models.filters.mixins.utils import cached_property @@ -24,24 +23,6 @@ def group_types_to_query(self) -> set[GroupTypeIndex]: used_properties = self.used_properties_with_type("group") return {cast(GroupTypeIndex, group_type_index) for _, _, group_type_index in used_properties} - @cached_property - def group_on_event_columns_to_query(self) -> set[ColumnName]: - "Returns a list of event table group columns containing materialized properties that this query needs" - used_properties = self.used_properties_with_type("group") - - columns_to_query: set[ColumnName] = set() - - for group_type_index in range(5): - columns_to_query = columns_to_query.union( - self.columns_to_query( - "events", - {property for property in used_properties if property[2] == group_type_index}, - f"group{group_type_index}_properties", - ) - ) - - return columns_to_query - @cached_property def properties_used_in_filter(self) -> TCounter[PropertyIdentifier]: "Returns collection of properties + types that this query would use" diff --git a/ee/clickhouse/queries/funnels/test/breakdown_cases.py b/ee/clickhouse/queries/funnels/test/breakdown_cases.py index 7a1b2076776d0..f5f68078620cd 100644 --- a/ee/clickhouse/queries/funnels/test/breakdown_cases.py +++ b/ee/clickhouse/queries/funnels/test/breakdown_cases.py @@ -13,7 +13,6 @@ ) from posthog.test.base import ( APIBaseTest, - also_test_with_materialized_columns, snapshot_clickhouse_queries, also_test_with_person_on_events_v2, ) @@ -307,10 +306,6 @@ def test_funnel_aggregate_by_groups_breakdown_group(self): ], ) - @also_test_with_materialized_columns( - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @also_test_with_person_on_events_v2 @snapshot_clickhouse_queries def test_funnel_aggregate_by_groups_breakdown_group_person_on_events(self): diff --git a/ee/clickhouse/queries/funnels/test/test_funnel_correlation.py b/ee/clickhouse/queries/funnels/test/test_funnel_correlation.py index 2aa9aafa9a324..979c84d85478f 100644 --- a/ee/clickhouse/queries/funnels/test/test_funnel_correlation.py +++ b/ee/clickhouse/queries/funnels/test/test_funnel_correlation.py @@ -819,7 +819,6 @@ def test_funnel_correlation_with_properties_and_groups(self): @also_test_with_materialized_columns( event_properties=[], person_properties=["$browser"], - group_properties=[(0, "industry")], verify_no_jsonextract=False, ) @also_test_with_person_on_events_v2 diff --git a/ee/clickhouse/queries/test/test_column_optimizer.py b/ee/clickhouse/queries/test/test_column_optimizer.py index 2d2651b6980de..296f3d18b355b 100644 --- a/ee/clickhouse/queries/test/test_column_optimizer.py +++ b/ee/clickhouse/queries/test/test_column_optimizer.py @@ -229,20 +229,6 @@ def test_materialized_columns_checks_person_on_events(self): BASE_FILTER.shallow_clone( { "properties": [ - { - "key": "group_prop", - "value": ["value"], - "operator": "exact", - "type": "group", - "group_type_index": 2, - }, - { - "key": "group_prop", - "value": ["value"], - "operator": "exact", - "type": "group", - "group_type_index": 0, - }, { "key": "person_prop", "value": ["value"], @@ -256,97 +242,15 @@ def test_materialized_columns_checks_person_on_events(self): ) self.assertEqual(optimizer().person_on_event_columns_to_query, {"person_properties"}) - self.assertEqual( - optimizer().group_on_event_columns_to_query, - {"group0_properties", "group2_properties"}, - ) - # materialising the props on `person` or `group` table should make no difference + # materialising the props on `person` table should make no difference materialize("person", "person_prop") - materialize("groups", "group_prop", table_column="group_properties") self.assertEqual(optimizer().person_on_event_columns_to_query, {"person_properties"}) - self.assertEqual( - optimizer().group_on_event_columns_to_query, - {"group0_properties", "group2_properties"}, - ) materialize("events", "person_prop", table_column="person_properties") - materialize("events", "group_prop", table_column="group0_properties") self.assertEqual(optimizer().person_on_event_columns_to_query, {"mat_pp_person_prop"}) - self.assertEqual( - optimizer().group_on_event_columns_to_query, - {"mat_gp0_group_prop", "group2_properties"}, - ) - - materialize("events", "group_prop", table_column="group2_properties") - self.assertEqual( - optimizer().group_on_event_columns_to_query, - {"mat_gp0_group_prop", "mat_gp2_group_prop"}, - ) - - def test_should_query_element_chain_column(self): - should_query_elements_chain_column = lambda filter: EnterpriseColumnOptimizer( - filter, self.team.id - ).should_query_elements_chain_column - - self.assertEqual(should_query_elements_chain_column(BASE_FILTER), False) - self.assertEqual(should_query_elements_chain_column(FILTER_WITH_PROPERTIES), True) - self.assertEqual(should_query_elements_chain_column(FILTER_WITH_GROUPS), True) - - filter = Filter( - data={ - "events": [ - { - "id": "$pageview", - "type": "events", - "order": 0, - "properties": PROPERTIES_OF_ALL_TYPES, - } - ] - } - ) - self.assertEqual(should_query_elements_chain_column(filter), True) - - def test_should_query_element_chain_column_with_actions(self): - action = Action.objects.create( - team=self.team, - steps_json=[ - { - "event": "$autocapture", - "url": "https://example.com/donate", - "url_matching": "exact", - } - ], - ) - - filter = Filter(data={"actions": [{"id": action.id, "math": "dau"}]}) - self.assertEqual( - EnterpriseColumnOptimizer(filter, self.team.id).should_query_elements_chain_column, - False, - ) - - action.steps = [ - { - "event": "$autocapture", - "url": "https://example.com/donate", - "url_matching": "exact", - }, - {"event": "$autocapture", "tag_name": "button", "text": "Pay $10"}, - ] - action.save() - - self.assertEqual( - EnterpriseColumnOptimizer(filter, self.team.id).should_query_elements_chain_column, - True, - ) - - filter = BASE_FILTER.shallow_clone({"exclusions": [{"id": action.id, "type": "actions"}]}) - self.assertEqual( - EnterpriseColumnOptimizer(filter, self.team.id).should_query_elements_chain_column, - True, - ) def test_group_types_to_query(self): group_types_to_query = lambda filter: EnterpriseColumnOptimizer(filter, self.team.id).group_types_to_query diff --git a/ee/clickhouse/queries/test/test_paths.py b/ee/clickhouse/queries/test/test_paths.py index 69f673e4489ca..1a17f578b7eae 100644 --- a/ee/clickhouse/queries/test/test_paths.py +++ b/ee/clickhouse/queries/test/test_paths.py @@ -3740,11 +3740,6 @@ def test_groups_filtering(self): ], ) - @also_test_with_materialized_columns( - ["$current_url", "$screen_name"], - group_properties=[(0, "industry"), (1, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_groups_filtering_person_on_events(self): self._create_groups() diff --git a/ee/clickhouse/queries/test/test_retention.py b/ee/clickhouse/queries/test/test_retention.py index 91354c4e8caf6..45e6503fc2853 100644 --- a/ee/clickhouse/queries/test/test_retention.py +++ b/ee/clickhouse/queries/test/test_retention.py @@ -17,7 +17,6 @@ from posthog.test.base import ( APIBaseTest, ClickhouseTestMixin, - also_test_with_materialized_columns, create_person_id_override_by_distinct_id, snapshot_clickhouse_queries, ) @@ -233,9 +232,6 @@ def test_groups_in_period(self): self.assertEqual(actor_result[1]["person"]["id"], "org:6") self.assertEqual(actor_result[1]["appearances"], [1, 1, 0, 1, 1, 0, 1]) - @also_test_with_materialized_columns( - group_properties=[(0, "industry")], materialize_only_with_person_on_events=True - ) @snapshot_clickhouse_queries def test_groups_filtering_person_on_events(self): self._create_groups_and_events() @@ -450,7 +446,6 @@ def test_groups_filtering_person_on_events_v2(self): ], ) - @also_test_with_materialized_columns(group_properties=[(0, "industry")]) @snapshot_clickhouse_queries def test_groups_aggregating_person_on_events(self): self._create_groups_and_events() @@ -508,7 +503,6 @@ def test_groups_aggregating_person_on_events(self): ], ) - @also_test_with_materialized_columns(group_properties=[(0, "industry")]) @snapshot_clickhouse_queries def test_groups_in_period_person_on_events(self): from posthog.models.team import util diff --git a/ee/clickhouse/views/test/__snapshots__/test_clickhouse_retention.ambr b/ee/clickhouse/views/test/__snapshots__/test_clickhouse_retention.ambr index 4a0e871b022e5..174634e37403c 100644 --- a/ee/clickhouse/views/test/__snapshots__/test_clickhouse_retention.ambr +++ b/ee/clickhouse/views/test/__snapshots__/test_clickhouse_retention.ambr @@ -227,10 +227,10 @@ (SELECT id FROM person WHERE team_id = 2 - AND (NOT (replaceRegexpAll(JSONExtractRaw(properties, 'email'), '^"|"$', '') ILIKE '%posthog.com%')) ) + AND (NOT ("pmat_email" ILIKE '%posthog.com%')) ) GROUP BY id HAVING max(is_deleted) = 0 - AND (NOT (replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'email'), '^"|"$', '') ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id + AND (NOT (argMax(person."pmat_email", version) ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id WHERE team_id = 2 AND e.event = 'target event' AND toDateTime(e.timestamp) >= toDateTime('2020-01-01 00:00:00', 'UTC') @@ -263,10 +263,10 @@ (SELECT id FROM person WHERE team_id = 2 - AND (NOT (replaceRegexpAll(JSONExtractRaw(properties, 'email'), '^"|"$', '') ILIKE '%posthog.com%')) ) + AND (NOT ("pmat_email" ILIKE '%posthog.com%')) ) GROUP BY id HAVING max(is_deleted) = 0 - AND (NOT (replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'email'), '^"|"$', '') ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id + AND (NOT (argMax(person."pmat_email", version) ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id WHERE team_id = 2 AND e.event = 'target event' GROUP BY target @@ -327,10 +327,10 @@ (SELECT id FROM person WHERE team_id = 2 - AND (NOT (replaceRegexpAll(JSONExtractRaw(properties, 'email'), '^"|"$', '') ILIKE '%posthog.com%')) ) + AND (NOT ("pmat_email" ILIKE '%posthog.com%')) ) GROUP BY id HAVING max(is_deleted) = 0 - AND (NOT (replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'email'), '^"|"$', '') ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id + AND (NOT (argMax(person."pmat_email", version) ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id WHERE team_id = 2 AND e.event = 'target event' AND toDateTime(e.timestamp) >= toDateTime('2020-01-01 00:00:00', 'UTC') @@ -363,10 +363,10 @@ (SELECT id FROM person WHERE team_id = 2 - AND (NOT (replaceRegexpAll(JSONExtractRaw(properties, 'email'), '^"|"$', '') ILIKE '%posthog.com%')) ) + AND (NOT ("pmat_email" ILIKE '%posthog.com%')) ) GROUP BY id HAVING max(is_deleted) = 0 - AND (NOT (replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'email'), '^"|"$', '') ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id + AND (NOT (argMax(person."pmat_email", version) ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id WHERE team_id = 2 AND e.event = 'target event' GROUP BY target @@ -423,10 +423,10 @@ (SELECT id FROM person WHERE team_id = 2 - AND (NOT (replaceRegexpAll(JSONExtractRaw(properties, 'email'), '^"|"$', '') ILIKE '%posthog.com%')) ) + AND (NOT ("pmat_email" ILIKE '%posthog.com%')) ) GROUP BY id HAVING max(is_deleted) = 0 - AND (NOT (replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'email'), '^"|"$', '') ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id + AND (NOT (argMax(person."pmat_email", version) ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id WHERE team_id = 2 AND e.event = 'target event' AND toDateTime(e.timestamp) >= toDateTime('2020-01-01 00:00:00', 'UTC') @@ -459,10 +459,10 @@ (SELECT id FROM person WHERE team_id = 2 - AND (NOT (replaceRegexpAll(JSONExtractRaw(properties, 'email'), '^"|"$', '') ILIKE '%posthog.com%')) ) + AND (NOT ("pmat_email" ILIKE '%posthog.com%')) ) GROUP BY id HAVING max(is_deleted) = 0 - AND (NOT (replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'email'), '^"|"$', '') ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id + AND (NOT (argMax(person."pmat_email", version) ILIKE '%posthog.com%')) SETTINGS optimize_aggregation_in_order = 1) person ON person.id = pdi.person_id WHERE team_id = 2 AND e.event = 'target event' GROUP BY target diff --git a/ee/clickhouse/views/test/funnel/test_clickhouse_funnel_person.py b/ee/clickhouse/views/test/funnel/test_clickhouse_funnel_person.py index 260c1ef6e1767..a230df5918950 100644 --- a/ee/clickhouse/views/test/funnel/test_clickhouse_funnel_person.py +++ b/ee/clickhouse/views/test/funnel/test_clickhouse_funnel_person.py @@ -51,7 +51,6 @@ def _create_sample_data(self, num, delete=False): team=self.team, timestamp="2021-05-05 00:00:00", properties={"$browser": "Chrome", "$group_0": "g0"}, - group0_properties={"name": "g0"}, ) if delete: person.delete() diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png index 5f6bbab56ee34..664c0d103c72e 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png and b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--dark.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--dark.png index 4f4f10f435fca..d023908198ae1 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--dark.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--light.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--light.png index fa333ce9cd387..009d03f7e7dca 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--light.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs--light.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--dark.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--dark.png index d368a3388deec..2a5f9525df9b0 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--dark.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--light.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--light.png index 37993b37f4551..1ab4188bfbec0 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--light.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-logs-batch-export--light.png differ diff --git a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png new file mode 100644 index 0000000000000..66a8c19d82e13 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png new file mode 100644 index 0000000000000..2cc8007742c86 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--dark.png b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--dark.png index d1cca86851882..ef426b28d765c 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--dark.png and b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--light.png b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--light.png index 9e53d3e7ab24e..ac0ec1971e9a9 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--light.png and b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--light.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey--dark.png b/frontend/__snapshots__/scenes-app-surveys--new-survey--dark.png index d1cca86851882..ef426b28d765c 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey--dark.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey--light.png b/frontend/__snapshots__/scenes-app-surveys--new-survey--light.png index 9e53d3e7ab24e..ac0ec1971e9a9 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey--light.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey--light.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--dark.png b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--dark.png index c113cbaa5f8ff..b8505b8953a15 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--dark.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--light.png b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--light.png index cd120ab69019d..b3a45d014e8ea 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--light.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-html-question-description--light.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--dark.png b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--dark.png index fa884f178fed5..d147deffc9e79 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--dark.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--light.png b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--light.png index d6eb10878900e..a7760e75771e4 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--light.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey-with-text-question-description-that-does-not-render-html--light.png differ diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 06dbd4a6f50b5..c4ed2014b02eb 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -14,7 +14,6 @@ import { ActivityScope, AlertType, BatchExportConfiguration, - BatchExportLogEntry, BatchExportRun, CohortType, CommentType, @@ -34,6 +33,7 @@ import { EventType, Experiment, ExportedAssetType, + ExternalDataJob, ExternalDataSourceCreatePayload, ExternalDataSourceSchema, ExternalDataSourceSyncSchema, @@ -51,6 +51,7 @@ import { IntegrationType, ListOrganizationMembersParams, LogEntry, + LogEntryRequestParams, MediaUploadResponse, NewEarlyAccessFeatureType, NotebookListItemType, @@ -323,10 +324,6 @@ class ApiRequest { return this.pluginConfigs(teamId).addPathComponent(id) } - public pluginLogs(pluginConfigId: number, teamId?: TeamType['id']): ApiRequest { - return this.pluginConfig(pluginConfigId, teamId).addPathComponent('logs') - } - public hogFunctions(teamId?: TeamType['id']): ApiRequest { return this.projectsDetail(teamId).addPathComponent('hog_functions') } @@ -753,10 +750,6 @@ class ApiRequest { return this.batchExports(teamId).addPathComponent(id) } - public batchExportLogs(id: BatchExportConfiguration['id'], teamId?: TeamType['id']): ApiRequest { - return this.batchExport(id, teamId).addPathComponent('logs') - } - public batchExportRuns(id: BatchExportConfiguration['id'], teamId?: TeamType['id']): ApiRequest { return this.batchExports(teamId).addPathComponent(id).addPathComponent('runs') } @@ -769,14 +762,6 @@ class ApiRequest { return this.batchExportRuns(id, teamId).addPathComponent(runId) } - public batchExportRunLogs( - id: BatchExportConfiguration['id'], - runId: BatchExportRun['id'], - teamId?: TeamType['id'] - ): ApiRequest { - return this.batchExportRun(id, runId, teamId).addPathComponent('logs') - } - // External Data Source public externalDataSources(teamId?: TeamType['id']): ApiRequest { return this.projectsDetail(teamId).addPathComponent('external_data_sources') @@ -1574,85 +1559,34 @@ const api = { async list(): Promise> { return await new ApiRequest().pluginConfigs().get() }, - }, - - pluginLogs: { - async search( - pluginConfigId: number, - searchTerm: string | null = null, - typeFilters: CheckboxValueType[] = [], - trailingEntry: PluginLogEntry | null = null, - leadingEntry: PluginLogEntry | null = null - ): Promise { - const params = toParams( - { - limit: LOGS_PORTION_LIMIT, - type_filter: typeFilters, - search: searchTerm || undefined, - before: trailingEntry?.timestamp, - after: leadingEntry?.timestamp, - }, - true - ) - - const response = await new ApiRequest().pluginLogs(pluginConfigId).withQueryString(params).get() - - return response.results - }, - }, - - batchExportLogs: { - async search( - batchExportId: string, - searchTerm: string | null = null, - typeFilters: CheckboxValueType[] = [], - trailingEntry: BatchExportLogEntry | null = null, - leadingEntry: BatchExportLogEntry | null = null - ): Promise { - const params = toParams( - { - limit: LOGS_PORTION_LIMIT, - level_filter: typeFilters, - search: searchTerm || undefined, - before: trailingEntry?.timestamp, - after: leadingEntry?.timestamp, - }, - true - ) - - const response = await new ApiRequest().batchExportLogs(batchExportId).withQueryString(params).get() - - return response.results - }, - }, - - batchExportRunLogs: { - async search( - batchExportId: string, - batchExportRunId: string, - currentTeamId: number | null, - searchTerm: string | null = null, - typeFilters: CheckboxValueType[] = [], - trailingEntry: BatchExportLogEntry | null = null, - leadingEntry: BatchExportLogEntry | null = null - ): Promise { - const params = toParams( - { - limit: LOGS_PORTION_LIMIT, - type_filter: typeFilters, - search: searchTerm || undefined, - before: trailingEntry?.timestamp, - after: leadingEntry?.timestamp, - }, - true - ) - + async logs(pluginConfigId: number, params: LogEntryRequestParams): Promise { + const levels = (params.level?.split(',') ?? []).filter((x) => x !== 'WARNING') const response = await new ApiRequest() - .batchExportRunLogs(batchExportId, batchExportRunId, currentTeamId || undefined) - .withQueryString(params) + .pluginConfig(pluginConfigId) + .withAction('logs') + .withQueryString( + toParams( + { + limit: LOGS_PORTION_LIMIT, + type_filter: levels, + search: params.search, + before: params.before, + after: params.after, + }, + true + ) + ) .get() - return response.results + const results = response.results.map((entry: PluginLogEntry) => ({ + log_source_id: `${entry.plugin_config_id}`, + instance_id: entry.source, + timestamp: entry.timestamp, + level: entry.type, + message: entry.message, + })) + + return results }, }, @@ -1669,9 +1603,9 @@ const api = { async update(id: HogFunctionType['id'], data: Partial): Promise { return await new ApiRequest().hogFunction(id).update({ data }) }, - async searchLogs( + async logs( id: HogFunctionType['id'], - params: Record = {} + params: LogEntryRequestParams = {} ): Promise> { return await new ApiRequest().hogFunction(id).withAction('logs').withQueryString(params).get() }, @@ -1942,6 +1876,12 @@ const api = { ): Promise { return await new ApiRequest().batchExport(id).withAction('backfill').create({ data }) }, + async logs( + id: BatchExportConfiguration['id'], + params: LogEntryRequestParams = {} + ): Promise> { + return await new ApiRequest().batchExport(id).withAction('logs').withQueryString(params).get() + }, }, earlyAccessFeatures: { @@ -2033,11 +1973,13 @@ const api = { return await new ApiRequest().dataWarehouseSavedQuery(viewId).update({ data }) }, }, - externalDataSources: { async list(options?: ApiMethodOptions | undefined): Promise> { return await new ApiRequest().externalDataSources().get(options) }, + async get(sourceId: ExternalDataStripeSource['id']): Promise { + return await new ApiRequest().externalDataSource(sourceId).get() + }, async create(data: Partial): Promise<{ id: string }> { return await new ApiRequest().externalDataSources().create({ data }) }, @@ -2071,6 +2013,9 @@ const api = { .withAction('source_prefix') .create({ data: { source_type, prefix } }) }, + async jobs(sourceId: ExternalDataStripeSource['id']): Promise { + return await new ApiRequest().externalDataSource(sourceId).withAction('jobs').get() + }, }, externalDataSchemas: { diff --git a/frontend/src/lib/components/Playlist/Playlist.scss b/frontend/src/lib/components/Playlist/Playlist.scss index e578c52028a86..5f6ab75a144b0 100644 --- a/frontend/src/lib/components/Playlist/Playlist.scss +++ b/frontend/src/lib/components/Playlist/Playlist.scss @@ -51,7 +51,7 @@ .SessionRecordingPlaylistHeightWrapper { // NOTE: Somewhat random way to offset the various headers and tabs above the playlist - height: calc(100vh - 9rem); + height: calc(100vh - 15rem); min-height: 41rem; } diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index 4a03e5398a119..12ffe885a0196 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -206,7 +206,6 @@ export const FEATURE_FLAGS = { ALERTS: 'alerts', // owner: github.com/nikitaevg ERROR_TRACKING: 'error-tracking', // owner: #team-replay SETTINGS_BOUNCE_RATE_PAGE_VIEW_MODE: 'settings-bounce-rate-page-view-mode', // owner: @robbie-c - SURVEYS_BRANCHING_LOGIC: 'surveys-branching-logic', // owner: @jurajmajerik #team-feature-success MULTIPLE_BREAKDOWNS: 'multiple-breakdowns', // owner: @skoob13 #team-product-analytics WEB_ANALYTICS_LIVE_USER_COUNT: 'web-analytics-live-user-count', // owner: @robbie-c SETTINGS_SESSION_TABLE_VERSION: 'settings-session-table-version', // owner: @robbie-c diff --git a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx index 96e8105c60133..32b569226dcaa 100644 --- a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx +++ b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx @@ -95,6 +95,7 @@ interface LemonCollapsePanelProps { size: LemonButtonProps['size'] onChange: (isExpanded: boolean) => void className?: string + dataAttr?: string } function LemonCollapsePanel({ @@ -103,6 +104,7 @@ function LemonCollapsePanel({ isExpanded, size, className, + dataAttr, onChange, }: LemonCollapsePanelProps): JSX.Element { const { height: contentHeight, ref: contentRef } = useResizeObserver({ box: 'border-box' }) @@ -113,6 +115,7 @@ function LemonCollapsePanel({ onClick={() => onChange(!isExpanded)} icon={isExpanded ? : } className="LemonCollapsePanel__header" + {...(dataAttr ? { 'data-attr': dataAttr } : {})} size={size} > {header} diff --git a/frontend/src/queries/nodes/DataNode/DateRange.tsx b/frontend/src/queries/nodes/DataNode/DateRange.tsx index 5691eee8f1ae2..af3a7bc92af39 100644 --- a/frontend/src/queries/nodes/DataNode/DateRange.tsx +++ b/frontend/src/queries/nodes/DataNode/DateRange.tsx @@ -1,13 +1,13 @@ import { DateFilter } from 'lib/components/DateFilter/DateFilter' -import { EventsQuery, HogQLQuery } from '~/queries/schema' -import { isEventsQuery, isHogQLQuery } from '~/queries/utils' +import { EventsQuery, HogQLQuery, SessionAttributionExplorerQuery } from '~/queries/schema' +import { isEventsQuery, isHogQLQuery, isSessionAttributionExplorerQuery } from '~/queries/utils' -interface DateRangeProps { +interface DateRangeProps { query: Q setQuery?: (query: Q) => void } -export function DateRange({ +export function DateRange({ query, setQuery, }: DateRangeProps): JSX.Element | null { @@ -27,7 +27,7 @@ export function DateRange({ /> ) } - if (isHogQLQuery(query)) { + if (isHogQLQuery(query) || isSessionAttributionExplorerQuery(query)) { return ( !query.hiddenColumns?.includes(column.dataIndex) && column.dataIndex !== '*') const setQuerySource = useCallback( - (source: EventsNode | EventsQuery | PersonsNode | ActorsQuery | HogQLQuery) => setQuery?.({ ...query, source }), + (source: EventsNode | EventsQuery | PersonsNode | ActorsQuery | HogQLQuery | SessionAttributionExplorerQuery) => + setQuery?.({ ...query, source }), [setQuery] ) @@ -404,7 +406,11 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults } /> ) : null, showDateRange && sourceFeatures.has(QueryFeature.dateRangePicker) ? ( - + ) : null, showEventFilter && sourceFeatures.has(QueryFeature.eventNameFilter) ? ( @@ -415,7 +421,7 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults } showPropertyFilter && sourceFeatures.has(QueryFeature.eventPropertyFilters) ? ( diff --git a/frontend/src/queries/nodes/DataTable/queryFeatures.ts b/frontend/src/queries/nodes/DataTable/queryFeatures.ts index 4a6ae92296559..c5642f1a82c5f 100644 --- a/frontend/src/queries/nodes/DataTable/queryFeatures.ts +++ b/frontend/src/queries/nodes/DataTable/queryFeatures.ts @@ -4,6 +4,7 @@ import { isEventsQuery, isHogQLQuery, isPersonsNode, + isSessionAttributionExplorerQuery, isWebOverviewQuery, isWebStatsTableQuery, isWebTopClicksQuery, @@ -29,7 +30,7 @@ export enum QueryFeature { export function getQueryFeatures(query: Node): Set { const features = new Set() - if (isHogQLQuery(query) || isEventsQuery(query)) { + if (isHogQLQuery(query) || isEventsQuery(query) || isSessionAttributionExplorerQuery(query)) { features.add(QueryFeature.dateRangePicker) features.add(QueryFeature.columnsInResponse) features.add(QueryFeature.eventPropertyFilters) diff --git a/frontend/src/queries/nodes/DataTable/renderColumn.tsx b/frontend/src/queries/nodes/DataTable/renderColumn.tsx index 9ad09e14d7642..5eddb88cba390 100644 --- a/frontend/src/queries/nodes/DataTable/renderColumn.tsx +++ b/frontend/src/queries/nodes/DataTable/renderColumn.tsx @@ -229,10 +229,14 @@ export function renderColumn( } else if (key.startsWith('context.columns.')) { const columnName = trimQuotes(key.substring(16)) // 16 = "context.columns.".length const Component = context?.columns?.[columnName]?.render - return Component ? : '' + return Component ? ( + + ) : ( + String(value) + ) } else if (context?.columns?.[key]) { const Component = context?.columns?.[key]?.render - return Component ? : '' + return Component ? : String(value) } else if (key === 'id' && (isPersonsNode(query.source) || isActorsQuery(query.source))) { return ( { +interface EventPropertyFiltersProps { query: Q setQuery?: (query: Q) => void taxonomicGroupTypes?: TaxonomicFilterGroupType[] } let uniqueNode = 0 -export function EventPropertyFilters({ - query, - setQuery, - taxonomicGroupTypes, -}: EventPropertyFiltersProps): JSX.Element { +export function EventPropertyFilters< + Q extends EventsNode | EventsQuery | HogQLQuery | SessionAttributionExplorerQuery +>({ query, setQuery, taxonomicGroupTypes }: EventPropertyFiltersProps): JSX.Element { const [id] = useState(() => uniqueNode++) - const properties = isHogQLQuery(query) ? query.filters?.properties : query.properties - const eventNames = isHogQLQuery(query) ? [] : query.event ? [query.event] : [] + const properties = + isHogQLQuery(query) || isSessionAttributionExplorerQuery(query) ? query.filters?.properties : query.properties + const eventNames = + isHogQLQuery(query) || isSessionAttributionExplorerQuery(query) ? [] : query.event ? [query.event] : [] return !properties || Array.isArray(properties) ? ( { - if (isHogQLQuery(query)) { + if (isHogQLQuery(query) || isSessionAttributionExplorerQuery(query)) { setQuery?.({ ...query, filters: { ...(query.filters ?? {}), properties: value } }) } else { setQuery?.({ ...query, properties: value }) diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index 7d1b44300637b..86509d63ccd4a 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -225,6 +225,9 @@ }, { "$ref": "#/definitions/WebTopClicksQuery" + }, + { + "$ref": "#/definitions/SessionAttributionExplorerQuery" } ] }, @@ -1372,6 +1375,86 @@ ], "type": "object" }, + "CachedSessionAttributionExplorerQueryResponse": { + "additionalProperties": false, + "properties": { + "cache_key": { + "type": "string" + }, + "cache_target_age": { + "format": "date-time", + "type": "string" + }, + "calculation_trigger": { + "description": "What triggered the calculation of the query, leave empty if user/immediate", + "type": "string" + }, + "columns": { + "items": {}, + "type": "array" + }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, + "hasMore": { + "type": "boolean" + }, + "hogql": { + "description": "Generated HogQL query.", + "type": "string" + }, + "is_cached": { + "type": "boolean" + }, + "last_refresh": { + "format": "date-time", + "type": "string" + }, + "limit": { + "type": "integer" + }, + "modifiers": { + "$ref": "#/definitions/HogQLQueryModifiers", + "description": "Modifiers used when performing the query" + }, + "next_allowed_client_refresh": { + "format": "date-time", + "type": "string" + }, + "offset": { + "type": "integer" + }, + "query_status": { + "$ref": "#/definitions/QueryStatus", + "description": "Query status indicates whether next to the provided data, a query is still running." + }, + "results": {}, + "timezone": { + "type": "string" + }, + "timings": { + "description": "Measured timings for different parts of the query generation process", + "items": { + "$ref": "#/definitions/QueryTiming" + }, + "type": "array" + }, + "types": { + "items": {}, + "type": "array" + } + }, + "required": [ + "cache_key", + "is_cached", + "last_refresh", + "next_allowed_client_refresh", + "results", + "timezone" + ], + "type": "object" + }, "CachedSessionsTimelineQueryResponse": { "additionalProperties": false, "properties": { @@ -2289,6 +2372,50 @@ }, "required": ["results"], "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "columns": { + "items": {}, + "type": "array" + }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, + "hasMore": { + "type": "boolean" + }, + "hogql": { + "description": "Generated HogQL query.", + "type": "string" + }, + "limit": { + "type": "integer" + }, + "modifiers": { + "$ref": "#/definitions/HogQLQueryModifiers", + "description": "Modifiers used when performing the query" + }, + "offset": { + "type": "integer" + }, + "results": {}, + "timings": { + "description": "Measured timings for different parts of the query generation process", + "items": { + "$ref": "#/definitions/QueryTiming" + }, + "type": "array" + }, + "types": { + "items": {}, + "type": "array" + } + }, + "required": ["results"], + "type": "object" } ] }, @@ -2391,6 +2518,9 @@ }, { "$ref": "#/definitions/WebTopClicksQuery" + }, + { + "$ref": "#/definitions/SessionAttributionExplorerQuery" } ], "description": "Source of the events" @@ -5380,6 +5510,7 @@ "FunnelsActorsQuery", "FunnelCorrelationActorsQuery", "SessionsTimelineQuery", + "SessionAttributionExplorerQuery", "DataTableNode", "DataVisualizationNode", "SavedInsightNode", @@ -6371,6 +6502,50 @@ "required": ["results"], "type": "object" }, + { + "additionalProperties": false, + "properties": { + "columns": { + "items": {}, + "type": "array" + }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, + "hasMore": { + "type": "boolean" + }, + "hogql": { + "description": "Generated HogQL query.", + "type": "string" + }, + "limit": { + "type": "integer" + }, + "modifiers": { + "$ref": "#/definitions/HogQLQueryModifiers", + "description": "Modifiers used when performing the query" + }, + "offset": { + "type": "integer" + }, + "results": {}, + "timings": { + "description": "Measured timings for different parts of the query generation process", + "items": { + "$ref": "#/definitions/QueryTiming" + }, + "type": "array" + }, + "types": { + "items": {}, + "type": "array" + } + }, + "required": ["results"], + "type": "object" + }, { "properties": {}, "type": "object" @@ -6685,6 +6860,50 @@ "required": ["results"], "type": "object" }, + { + "additionalProperties": false, + "properties": { + "columns": { + "items": {}, + "type": "array" + }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, + "hasMore": { + "type": "boolean" + }, + "hogql": { + "description": "Generated HogQL query.", + "type": "string" + }, + "limit": { + "type": "integer" + }, + "modifiers": { + "$ref": "#/definitions/HogQLQueryModifiers", + "description": "Modifiers used when performing the query" + }, + "offset": { + "type": "integer" + }, + "results": {}, + "timings": { + "description": "Measured timings for different parts of the query generation process", + "items": { + "$ref": "#/definitions/QueryTiming" + }, + "type": "array" + }, + "types": { + "items": {}, + "type": "array" + } + }, + "required": ["results"], + "type": "object" + }, { "additionalProperties": false, "properties": { @@ -7001,6 +7220,9 @@ { "$ref": "#/definitions/WebTopClicksQuery" }, + { + "$ref": "#/definitions/SessionAttributionExplorerQuery" + }, { "$ref": "#/definitions/DataVisualizationNode" }, @@ -7557,6 +7779,99 @@ "required": ["kind", "shortId"], "type": "object" }, + "SessionAttributionExplorerQuery": { + "additionalProperties": false, + "properties": { + "filters": { + "additionalProperties": false, + "properties": { + "dateRange": { + "$ref": "#/definitions/DateRange" + }, + "properties": { + "items": { + "$ref": "#/definitions/SessionPropertyFilter" + }, + "type": "array" + } + }, + "type": "object" + }, + "groupBy": { + "items": { + "$ref": "#/definitions/SessionAttributionGroupBy" + }, + "type": "array" + }, + "kind": { + "const": "SessionAttributionExplorerQuery", + "type": "string" + }, + "limit": { + "type": "integer" + }, + "modifiers": { + "$ref": "#/definitions/HogQLQueryModifiers", + "description": "Modifiers used when performing the query" + }, + "offset": { + "type": "integer" + }, + "response": { + "$ref": "#/definitions/SessionAttributionExplorerQueryResponse" + } + }, + "required": ["groupBy", "kind"], + "type": "object" + }, + "SessionAttributionExplorerQueryResponse": { + "additionalProperties": false, + "properties": { + "columns": { + "items": {}, + "type": "array" + }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, + "hasMore": { + "type": "boolean" + }, + "hogql": { + "description": "Generated HogQL query.", + "type": "string" + }, + "limit": { + "type": "integer" + }, + "modifiers": { + "$ref": "#/definitions/HogQLQueryModifiers", + "description": "Modifiers used when performing the query" + }, + "offset": { + "type": "integer" + }, + "results": {}, + "timings": { + "description": "Measured timings for different parts of the query generation process", + "items": { + "$ref": "#/definitions/QueryTiming" + }, + "type": "array" + }, + "types": { + "items": {}, + "type": "array" + } + }, + "required": ["results"], + "type": "object" + }, + "SessionAttributionGroupBy": { + "enum": ["ChannelType", "Medium", "Source", "Campaign", "AdIds", "ReferringDomain", "InitialURL"], + "type": "string" + }, "SessionPropertyFilter": { "additionalProperties": false, "properties": { diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index 40ef1076b7305..164ae8bd33536 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -63,6 +63,7 @@ export enum NodeKind { FunnelsActorsQuery = 'FunnelsActorsQuery', FunnelCorrelationActorsQuery = 'FunnelCorrelationActorsQuery', SessionsTimelineQuery = 'SessionsTimelineQuery', + SessionAttributionExplorerQuery = 'SessionAttributionExplorerQuery', // Interface nodes DataTableNode = 'DataTableNode', @@ -105,6 +106,7 @@ export type AnyDataNode = | WebOverviewQuery | WebStatsTableQuery | WebTopClicksQuery + | SessionAttributionExplorerQuery /** * @discriminator kind @@ -127,6 +129,7 @@ export type QuerySchema = | WebOverviewQuery | WebStatsTableQuery | WebTopClicksQuery + | SessionAttributionExplorerQuery // Interface nodes | DataVisualizationNode @@ -512,6 +515,7 @@ export interface DataTableNode | WebOverviewQuery | WebStatsTableQuery | WebTopClicksQuery + | SessionAttributionExplorerQuery )['response'] > >, @@ -527,6 +531,7 @@ export interface DataTableNode | WebOverviewQuery | WebStatsTableQuery | WebTopClicksQuery + | SessionAttributionExplorerQuery /** Columns shown in the table, unless the `source` provides them. */ columns?: HogQLExpression[] @@ -1229,6 +1234,35 @@ export interface WebStatsTableQueryResponse extends AnalyticsQueryResponseBase +export enum SessionAttributionGroupBy { + ChannelType = 'ChannelType', + Medium = 'Medium', + Source = 'Source', + Campaign = 'Campaign', + AdIds = 'AdIds', + ReferringDomain = 'ReferringDomain', + InitialURL = 'InitialURL', +} +export interface SessionAttributionExplorerQuery extends DataNode { + kind: NodeKind.SessionAttributionExplorerQuery + groupBy: SessionAttributionGroupBy[] + filters?: { + properties?: SessionPropertyFilter[] + dateRange?: DateRange + } + limit?: integer + offset?: integer +} + +export interface SessionAttributionExplorerQueryResponse extends AnalyticsQueryResponseBase { + hasMore?: boolean + limit?: integer + offset?: integer + types?: unknown[] + columns?: unknown[] +} +export type CachedSessionAttributionExplorerQueryResponse = CachedQueryResponse + export type InsightQueryNode = | TrendsQuery | FunnelsQuery diff --git a/frontend/src/queries/utils.ts b/frontend/src/queries/utils.ts index a059f585ae925..05e42c680b0e2 100644 --- a/frontend/src/queries/utils.ts +++ b/frontend/src/queries/utils.ts @@ -32,6 +32,7 @@ import { PersonsNode, RetentionQuery, SavedInsightNode, + SessionAttributionExplorerQuery, StickinessQuery, TrendsQuery, WebOverviewQuery, @@ -128,6 +129,12 @@ export function isWebTopClicksQuery(node?: Record | null): node is return node?.kind === NodeKind.WebTopClicksQuery } +export function isSessionAttributionExplorerQuery( + node?: Record | null +): node is SessionAttributionExplorerQuery { + return node?.kind === NodeKind.SessionAttributionExplorerQuery +} + export function containsHogQLQuery(node?: Record | null): boolean { if (!node) { return false diff --git a/frontend/src/scenes/actions/ActionEdit.tsx b/frontend/src/scenes/actions/ActionEdit.tsx index 03c4e45847570..09b38427e95b7 100644 --- a/frontend/src/scenes/actions/ActionEdit.tsx +++ b/frontend/src/scenes/actions/ActionEdit.tsx @@ -247,6 +247,9 @@ export function ActionEdit({ action: loadedAction, id }: ActionEditLogicProps): onChange={onChange} disabled={!slackEnabled || !action.post_to_slack} data-attr="edit-slack-message-format" + maxLength={ + 1200 /** Must be same as in posthog/models/action/action.py */ + } /> any> = { [Scene.DataWarehouseExternal]: () => import('./data-warehouse/external/DataWarehouseExternalScene'), [Scene.DataWarehouseSettings]: () => import('./data-warehouse/settings/DataWarehouseSettingsScene'), [Scene.DataWarehouseRedirect]: () => import('./data-warehouse/redirect/DataWarehouseRedirectScene'), + [Scene.dataWarehouseSourceSettings]: () => + import('./data-warehouse/settings/source/DataWarehouseSourceSettingsScene'), [Scene.OrganizationCreateFirst]: () => import('./organization/Create'), [Scene.OrganizationCreationConfirm]: () => import('./organization/ConfirmOrganization/ConfirmOrganization'), [Scene.ProjectHomepage]: () => import('./project-homepage/ProjectHomepage'), @@ -80,5 +82,6 @@ export const appScenes: Record any> = { [Scene.Settings]: () => import('./settings/SettingsScene'), [Scene.MoveToPostHogCloud]: () => import('./moveToPostHogCloud/MoveToPostHogCloud'), [Scene.Heatmaps]: () => import('./heatmaps/HeatmapsScene'), - [Scene.SessionAttributionExplorer]: () => import('./web-analytics/SessionDebugger/SessionAttributionExplorerScene'), + [Scene.SessionAttributionExplorer]: () => + import('scenes/web-analytics/SessionAttributionExplorer/SessionAttributionExplorerScene'), } diff --git a/frontend/src/scenes/batch_exports/BatchExportScene.tsx b/frontend/src/scenes/batch_exports/BatchExportScene.tsx index 8caa9614da613..d7530dc36f5f5 100644 --- a/frontend/src/scenes/batch_exports/BatchExportScene.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportScene.tsx @@ -24,15 +24,15 @@ import { capitalizeFirstLetter, identifierToHuman } from 'lib/utils' import { pluralize } from 'lib/utils' import { useEffect, useState } from 'react' import { BatchExportRunsGrouped } from 'scenes/pipeline/BatchExportRuns' -import { PipelineLogLevel } from 'scenes/pipeline/pipelineNodeLogsLogic' +import { LogLevelDisplay } from 'scenes/pipeline/utils' import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { BatchExportLogEntry } from '~/types' +import { LogEntry } from '~/types' import { BatchExportBackfillModal } from './BatchExportBackfillModal' import { batchExportLogic, BatchExportLogicProps, BatchExportTab } from './batchExportLogic' -import { batchExportLogsLogic, BatchExportLogsProps, LOGS_PORTION_LIMIT } from './batchExportLogsLogic' +import { ALL_LOG_LEVELS, batchExportLogsLogic, BatchExportLogsProps, LOGS_PORTION_LIMIT } from './batchExportLogsLogic' import { BatchExportTag } from './components' import { humanizeDestination, intervalToFrequency, showBatchExports } from './utils' @@ -114,52 +114,27 @@ export function RunsTab(): JSX.Element { ) } -function BatchExportLogEntryLevelDisplay(type: PipelineLogLevel): JSX.Element { - let color: string | undefined - switch (type) { - case PipelineLogLevel.Debug: - color = 'var(--muted)' - break - case PipelineLogLevel.Log: - color = 'var(--text-3000)' - break - case PipelineLogLevel.Info: - color = 'var(--blue)' - break - case PipelineLogLevel.Warning: - color = 'var(--warning)' - break - case PipelineLogLevel.Error: - color = 'var(--danger)' - break - default: - break - } - // eslint-disable-next-line react/forbid-dom-props - return {type} -} - -const columns: LemonTableColumns = [ +const columns: LemonTableColumns = [ { title: 'Timestamp', key: 'timestamp', dataIndex: 'timestamp', width: 1, - render: (_, entry: BatchExportLogEntry) => dayjs(entry.timestamp).format('YYYY-MM-DD HH:mm:ss.SSS UTC'), + render: (_, entry) => dayjs(entry.timestamp).format('YYYY-MM-DD HH:mm:ss.SSS UTC'), }, { title: 'Level', key: 'level', dataIndex: 'level', width: 1, - render: (_, entry: BatchExportLogEntry) => BatchExportLogEntryLevelDisplay(entry.level), + render: (_, entry) => LogLevelDisplay(entry.level), }, { title: 'Run ID', key: 'run_id', - dataIndex: 'run_id', + dataIndex: 'instance_id', width: 1, - render: (_, entry) => entry.run_id, + render: (_, entry) => entry.instance_id, }, { title: 'Message', @@ -197,7 +172,8 @@ export function LogsTab({ batchExportId }: BatchExportLogsProps): JSX.Element { />
Show logs of type:  - {Object.values(PipelineLogLevel).map((type) => { + + {ALL_LOG_LEVELS.map((type) => { return ( ([ }), loaders(({ props: { batchExportId }, values, actions, cache }) => ({ batchExportLogs: { - __default: [] as BatchExportLogEntry[], + __default: [] as LogEntry[], loadBatchExportLogs: async () => { - const results = await api.batchExportLogs.search(batchExportId, values.searchTerm, values.typeFilters) + const response = await api.batchExports.logs(batchExportId, { + search: values.searchTerm, + level: values.typeFilters.join(','), + }) if (!cache.pollingInterval) { cache.pollingInterval = setInterval(actions.loadBatchExportLogsBackgroundPoll, 2000) } actions.clearBatchExportLogsBackground() - return results + return response.results }, loadBatchExportLogsMore: async () => { - const results = await api.batchExportLogs.search( - batchExportId, - values.searchTerm, - values.typeFilters, - values.trailingEntry - ) + const response = await api.batchExports.logs(batchExportId, { + search: values.searchTerm, + level: values.typeFilters.join(','), + before: values.trailingEntry?.timestamp, + }) - if (results.length < LOGS_PORTION_LIMIT) { + if (response.results.length < LOGS_PORTION_LIMIT) { actions.markLogsEnd() } - return [...values.batchExportLogs, ...results] + return [...values.batchExportLogs, ...response.results] }, revealBackground: () => { const newArray = [...values.batchExportLogsBackground, ...values.batchExportLogs] @@ -57,33 +61,31 @@ export const batchExportLogsLogic = kea([ }, }, batchExportLogsBackground: { - __default: [] as BatchExportLogEntry[], + __default: [] as LogEntry[], loadBatchExportLogsBackgroundPoll: async () => { if (values.batchExportLogsLoading) { return values.batchExportLogsBackground } - const results = await api.batchExportLogs.search( - batchExportId, - values.searchTerm, - values.typeFilters, - null, - values.leadingEntry - ) + const response = await api.batchExports.logs(batchExportId, { + search: values.searchTerm, + level: values.typeFilters.join(','), + after: values.leadingEntry?.timestamp, + }) - return [...results, ...values.batchExportLogsBackground] + return [...response.results, ...values.batchExportLogsBackground] }, }, })), reducers({ batchExportLogsTypes: [ - Object.values(PipelineLogLevel).filter((type) => type !== 'DEBUG'), + DEFAULT_LOG_LEVELS, { - setBatchExportLogsTypes: (_, { typeFilters }) => typeFilters.map((tf) => tf as PipelineLogLevel), + setBatchExportLogsTypes: (_, { typeFilters }) => typeFilters.map((tf) => tf as LogEntryLevel), }, ], batchExportLogsBackground: [ - [] as BatchExportLogEntry[], + [] as LogEntry[], { clearBatchExportLogsBackground: () => [], }, @@ -95,7 +97,7 @@ export const batchExportLogsLogic = kea([ }, ], typeFilters: [ - Object.values(PipelineLogLevel).filter((type) => type !== 'DEBUG') as CheckboxValueType[], + DEFAULT_LOG_LEVELS as CheckboxValueType[], { setBatchExportLogsTypes: (_, { typeFilters }) => typeFilters || [], }, @@ -108,13 +110,10 @@ export const batchExportLogsLogic = kea([ }, ], }), - selectors(({ selectors }) => ({ + selectors(() => ({ leadingEntry: [ - () => [selectors.batchExportLogs, selectors.batchExportLogsBackground], - ( - batchExportLogs: BatchExportLogEntry[], - batchExportLogsBackground: BatchExportLogEntry[] - ): BatchExportLogEntry | null => { + (s) => [s.batchExportLogs, s.batchExportLogsBackground], + (batchExportLogs, batchExportLogsBackground): LogEntry | null => { if (batchExportLogsBackground.length) { return batchExportLogsBackground[0] } @@ -125,11 +124,8 @@ export const batchExportLogsLogic = kea([ }, ], trailingEntry: [ - () => [selectors.batchExportLogs, selectors.batchExportLogsBackground], - ( - batchExportLogs: BatchExportLogEntry[], - batchExportLogsBackground: BatchExportLogEntry[] - ): BatchExportLogEntry | null => { + (s) => [s.batchExportLogs, s.batchExportLogsBackground], + (batchExportLogs, batchExportLogsBackground): LogEntry | null => { if (batchExportLogs.length) { return batchExportLogs[batchExportLogs.length - 1] } diff --git a/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx b/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx index 489c61a360e77..eb4ec4e8172ac 100644 --- a/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx +++ b/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx @@ -1,21 +1,10 @@ import { TZLabel } from '@posthog/apps-common' -import { - LemonButton, - LemonDialog, - LemonModal, - LemonSelect, - LemonSkeleton, - LemonSwitch, - LemonTable, - LemonTag, - Link, - Spinner, - Tooltip, -} from '@posthog/lemon-ui' +import { LemonButton, LemonDialog, LemonTable, LemonTag, Link, Spinner, Tooltip } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { router } from 'kea-router' import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink' import cloudflareLogo from 'public/cloudflare-logo.svg' import googleStorageLogo from 'public/google-cloud-storage-logo.png' import hubspotLogo from 'public/hubspot-logo.svg' @@ -24,21 +13,11 @@ import s3Logo from 'public/s3-logo.png' import snowflakeLogo from 'public/snowflake-logo.svg' import stripeLogo from 'public/stripe-logo.svg' import zendeskLogo from 'public/zendesk-logo.svg' -import { useEffect } from 'react' import { urls } from 'scenes/urls' -import { - DataWarehouseSyncInterval, - ExternalDataSourceSchema, - ExternalDataStripeSource, - manualLinkSources, - ProductKey, -} from '~/types' +import { manualLinkSources, ProductKey } from '~/types' -import { SyncMethodForm } from '../external/forms/SyncMethodForm' -import { defaultQuery } from '../utils' import { dataWarehouseSettingsLogic } from './dataWarehouseSettingsLogic' -import { dataWarehouseSourcesTableSyncMethodModalLogic } from './dataWarehouseSourcesTableSyncMethodModalLogic' const StatusTagSetting = { Running: 'primary', @@ -48,21 +27,9 @@ const StatusTagSetting = { } export function DataWarehouseManagedSourcesTable(): JSX.Element { - const { dataWarehouseSources, dataWarehouseSourcesLoading, sourceReloadingById } = + const { dataWarehouseSources, dataWarehouseSourcesLoading, sourceReloadingById, currentTab } = useValues(dataWarehouseSettingsLogic) - const { deleteSource, reloadSource, updateSource } = useActions(dataWarehouseSettingsLogic) - - const renderExpandable = (source: ExternalDataStripeSource): JSX.Element => { - return ( -
-
-
- -
-
-
- ) - } + const { deleteSource, reloadSource } = useActions(dataWarehouseSettingsLogic) if (!dataWarehouseSourcesLoading && dataWarehouseSources?.results.length === 0) { return ( @@ -91,35 +58,14 @@ export function DataWarehouseManagedSourcesTable(): JSX.Element { }, }, { - title: 'Source Type', + title: 'Source', key: 'name', render: function RenderName(_, source) { - return source.source_type - }, - }, - { - title: 'Table prefix', - key: 'prefix', - render: function RenderPrefix(_, source) { - return source.prefix - }, - }, - { - title: 'Sync Frequency', - key: 'frequency', - render: function RenderFrequency(_, source) { return ( - - updateSource({ ...source, sync_frequency: value as DataWarehouseSyncInterval }) - } - options={[ - { value: 'day' as DataWarehouseSyncInterval, label: 'Daily' }, - { value: 'week' as DataWarehouseSyncInterval, label: 'Weekly' }, - { value: 'month' as DataWarehouseSyncInterval, label: 'Monthly' }, - ]} + ) }, @@ -214,11 +160,6 @@ export function DataWarehouseManagedSourcesTable(): JSX.Element { }, }, ]} - expandable={{ - expandedRowRender: renderExpandable, - rowExpandable: () => true, - noIndent: true, - }} /> ) } @@ -269,270 +210,3 @@ export function RenderDataWarehouseSourceIcon({
) } - -interface SchemaTableProps { - schemas: ExternalDataSourceSchema[] -} - -const SchemaTable = ({ schemas }: SchemaTableProps): JSX.Element => { - const { updateSchema, reloadSchema, resyncSchema } = useActions(dataWarehouseSettingsLogic) - const { schemaReloadingById } = useValues(dataWarehouseSettingsLogic) - - return ( - <> - {schema.name} - }, - }, - { - title: 'Sync method', - key: 'incremental', - render: function RenderIncremental(_, schema) { - const { openSyncMethodModal } = useActions( - dataWarehouseSourcesTableSyncMethodModalLogic({ schema }) - ) - - if (!schema.sync_type) { - return ( - <> - openSyncMethodModal(schema)} - > - Set up - - - - ) - } - - return ( - <> - openSyncMethodModal(schema)} - > - {schema.sync_type == 'incremental' ? 'Incremental' : 'Full refresh'} - - - - ) - }, - }, - { - title: 'Enabled', - key: 'should_sync', - render: function RenderShouldSync(_, schema) { - return ( - { - updateSchema({ ...schema, should_sync: active }) - }} - /> - ) - }, - }, - { - title: 'Synced Table', - key: 'table', - render: function RenderTable(_, schema) { - if (schema.table) { - const query = defaultQuery(schema.table.name, schema.table.columns) - return ( - - {schema.table.name} - - ) - } - return
Not yet synced
- }, - }, - { - title: 'Last Synced At', - key: 'last_synced_at', - render: function Render(_, schema) { - return schema.last_synced_at ? ( - <> - - - ) : null - }, - }, - { - title: 'Rows Synced', - key: 'rows_synced', - render: function Render(_, schema) { - return (schema.table?.row_count ?? 0).toLocaleString() - }, - }, - { - title: 'Status', - key: 'status', - render: function RenderStatus(_, schema) { - if (!schema.status) { - return null - } - - return ( - {schema.status} - ) - }, - }, - { - key: 'actions', - width: 0, - render: function RenderActions(_, schema) { - if (schemaReloadingById[schema.id]) { - return ( -
- -
- ) - } - - return ( -
-
- - { - reloadSchema(schema) - }} - > - Reload - - {schema.incremental && ( - - { - resyncSchema(schema) - }} - status="danger" - > - Resync - - - )} - - } - /> -
-
- ) - }, - }, - ]} - /> - - ) -} - -const SyncMethodModal = ({ schema }: { schema: ExternalDataSourceSchema }): JSX.Element => { - const { - syncMethodModalIsOpen, - currentSyncMethodModalSchema, - schemaIncrementalFields, - schemaIncrementalFieldsLoading, - saveButtonIsLoading, - } = useValues(dataWarehouseSourcesTableSyncMethodModalLogic({ schema })) - const { closeSyncMethodModal, loadSchemaIncrementalFields, resetSchemaIncrementalFields, updateSchema } = - useActions(dataWarehouseSourcesTableSyncMethodModalLogic({ schema })) - - useEffect(() => { - if (currentSyncMethodModalSchema?.id) { - resetSchemaIncrementalFields() - loadSchemaIncrementalFields(currentSyncMethodModalSchema.id) - } - }, [currentSyncMethodModalSchema?.id]) - - const schemaLoading = schemaIncrementalFieldsLoading || !schemaIncrementalFields - const showForm = !schemaLoading && schemaIncrementalFields - - if (!currentSyncMethodModalSchema) { - return <> - } - - return ( - - - - - ) - } - > - {schemaLoading && ( -
- - -
- )} - {showForm && ( - { - resetSchemaIncrementalFields() - closeSyncMethodModal() - }} - onSave={(syncType, incrementalField, incrementalFieldType) => { - if (syncType === 'full_refresh') { - updateSchema({ - ...currentSyncMethodModalSchema, - should_sync: true, - sync_type: syncType, - incremental_field: null, - incremental_field_type: null, - }) - } else { - updateSchema({ - ...currentSyncMethodModalSchema, - should_sync: true, - sync_type: syncType, - incremental_field: incrementalField, - incremental_field_type: incrementalFieldType, - }) - } - }} - /> - )} -
- ) -} diff --git a/frontend/src/scenes/data-warehouse/settings/source/DataWarehouseSourceSettingsScene.tsx b/frontend/src/scenes/data-warehouse/settings/source/DataWarehouseSourceSettingsScene.tsx new file mode 100644 index 0000000000000..f3635b8df718f --- /dev/null +++ b/frontend/src/scenes/data-warehouse/settings/source/DataWarehouseSourceSettingsScene.tsx @@ -0,0 +1,72 @@ +import { LemonButton, LemonTabs } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { PageHeader } from 'lib/components/PageHeader' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { DataWarehouseSettingsTab } from '~/types' + +import { + dataWarehouseSourceSettingsLogic, + DataWarehouseSourceSettingsLogicProps, + DataWarehouseSourceSettingsTabs, +} from './dataWarehouseSourceSettingsLogic' +import { Schemas } from './Schemas' +import { Syncs } from './Syncs' + +const paramsToProps = ({ + params: { id, tab }, +}: { + params: { id?: string; tab?: string } +}): DataWarehouseSourceSettingsLogicProps => { + if (!id || !tab) { + throw new Error('Loaded DataWarehouseSourceSettings without eother `id` or `tab`') + } + + return { + id, + parentSettingsTab: tab as DataWarehouseSettingsTab, + } +} + +export const scene: SceneExport = { + component: DataWarehouseSourceSettingsScene, + logic: dataWarehouseSourceSettingsLogic, + paramsToProps, +} + +const TabContent: Record JSX.Element> = { + [DataWarehouseSourceSettingsTabs.Schemas]: Schemas, + [DataWarehouseSourceSettingsTabs.Syncs]: Syncs, +} + +const FriendlyTabNames: Record = { + [DataWarehouseSourceSettingsTabs.Schemas]: 'Schemas', + [DataWarehouseSourceSettingsTabs.Syncs]: 'Syncs', +} + +export function DataWarehouseSourceSettingsScene(): JSX.Element { + const { parentSettingsTab, currentTab } = useValues(dataWarehouseSourceSettingsLogic) + const { setCurrentTab } = useActions(dataWarehouseSourceSettingsLogic) + + return ( +
+ + Cancel + + } + /> + setCurrentTab(tab as DataWarehouseSourceSettingsTabs)} + tabs={Object.entries(TabContent).map(([tab, ContentComponent]) => ({ + label: FriendlyTabNames[tab], + key: tab, + content: , + }))} + /> +
+ ) +} diff --git a/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx b/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx new file mode 100644 index 0000000000000..f106fb91bff2c --- /dev/null +++ b/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx @@ -0,0 +1,331 @@ +import { TZLabel } from '@posthog/apps-common' +import { + LemonButton, + LemonModal, + LemonSelect, + LemonSkeleton, + LemonSwitch, + LemonTable, + LemonTag, + Link, + Spinner, + Tooltip, +} from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { useEffect } from 'react' +import { defaultQuery } from 'scenes/data-warehouse/utils' +import { urls } from 'scenes/urls' + +import { DataWarehouseSyncInterval, ExternalDataSourceSchema } from '~/types' + +import { SyncMethodForm } from '../../external/forms/SyncMethodForm' +import { dataWarehouseSettingsLogic } from '../dataWarehouseSettingsLogic' +import { dataWarehouseSourcesTableSyncMethodModalLogic } from '../dataWarehouseSourcesTableSyncMethodModalLogic' +import { dataWarehouseSourceSettingsLogic } from './dataWarehouseSourceSettingsLogic' + +export const Schemas = (): JSX.Element => { + const { source, sourceLoading } = useValues(dataWarehouseSourceSettingsLogic) + return ( + <> + + + ) +} + +interface SchemaTableProps { + schemas: ExternalDataSourceSchema[] + isLoading: boolean +} + +const StatusTagSetting = { + Running: 'primary', + Completed: 'success', + Error: 'danger', + Failed: 'danger', +} + +export const SchemaTable = ({ schemas, isLoading }: SchemaTableProps): JSX.Element => { + const { reloadSchema, resyncSchema } = useActions(dataWarehouseSettingsLogic) + const { updateSchema } = useActions(dataWarehouseSourceSettingsLogic) + const { schemaReloadingById } = useValues(dataWarehouseSettingsLogic) + + return ( + <> + {schema.name} + }, + }, + { + title: 'Sync Frequency', + key: 'frequency', + render: function RenderFrequency(_, schema) { + return ( + + updateSchema({ ...schema, sync_frequency: value as DataWarehouseSyncInterval }) + } + options={[ + { value: 'day' as DataWarehouseSyncInterval, label: 'Daily' }, + { value: 'week' as DataWarehouseSyncInterval, label: 'Weekly' }, + { value: 'month' as DataWarehouseSyncInterval, label: 'Monthly' }, + ]} + /> + ) + }, + }, + { + title: 'Sync method', + key: 'incremental', + render: function RenderIncremental(_, schema) { + const { openSyncMethodModal } = useActions( + dataWarehouseSourcesTableSyncMethodModalLogic({ schema }) + ) + + if (!schema.sync_type) { + return ( + <> + openSyncMethodModal(schema)} + > + Set up + + + + ) + } + + return ( + <> + openSyncMethodModal(schema)} + > + {schema.sync_type == 'incremental' ? 'Incremental' : 'Full refresh'} + + + + ) + }, + }, + { + title: 'Enabled', + key: 'should_sync', + render: function RenderShouldSync(_, schema) { + return ( + { + updateSchema({ ...schema, should_sync: active }) + }} + /> + ) + }, + }, + { + title: 'Synced Table', + key: 'table', + render: function RenderTable(_, schema) { + if (schema.table) { + const query = defaultQuery(schema.table.name, schema.table.columns) + return ( + + {schema.table.name} + + ) + } + return
Not yet synced
+ }, + }, + { + title: 'Last Synced At', + key: 'last_synced_at', + render: function Render(_, schema) { + return schema.last_synced_at ? ( + <> + + + ) : null + }, + }, + { + title: 'Rows Synced', + key: 'rows_synced', + render: function Render(_, schema) { + return (schema.table?.row_count ?? 0).toLocaleString() + }, + }, + { + title: 'Status', + key: 'status', + render: function RenderStatus(_, schema) { + if (!schema.status) { + return null + } + + return ( + {schema.status} + ) + }, + }, + { + key: 'actions', + width: 0, + render: function RenderActions(_, schema) { + if (schemaReloadingById[schema.id]) { + return ( +
+ +
+ ) + } + + return ( +
+
+ + { + reloadSchema(schema) + }} + > + Reload + + {schema.incremental && ( + + { + resyncSchema(schema) + }} + status="danger" + > + Resync + + + )} + + } + /> +
+
+ ) + }, + }, + ]} + /> + + ) +} + +const SyncMethodModal = ({ schema }: { schema: ExternalDataSourceSchema }): JSX.Element => { + const { + syncMethodModalIsOpen, + currentSyncMethodModalSchema, + schemaIncrementalFields, + schemaIncrementalFieldsLoading, + saveButtonIsLoading, + } = useValues(dataWarehouseSourcesTableSyncMethodModalLogic({ schema })) + const { closeSyncMethodModal, loadSchemaIncrementalFields, resetSchemaIncrementalFields, updateSchema } = + useActions(dataWarehouseSourcesTableSyncMethodModalLogic({ schema })) + + useEffect(() => { + if (currentSyncMethodModalSchema?.id) { + resetSchemaIncrementalFields() + loadSchemaIncrementalFields(currentSyncMethodModalSchema.id) + } + }, [currentSyncMethodModalSchema?.id]) + + const schemaLoading = schemaIncrementalFieldsLoading || !schemaIncrementalFields + const showForm = !schemaLoading && schemaIncrementalFields + + if (!currentSyncMethodModalSchema) { + return <> + } + + return ( + + + + + ) + } + > + {schemaLoading && ( +
+ + +
+ )} + {showForm && ( + { + resetSchemaIncrementalFields() + closeSyncMethodModal() + }} + onSave={(syncType, incrementalField, incrementalFieldType) => { + if (syncType === 'full_refresh') { + updateSchema({ + ...currentSyncMethodModalSchema, + should_sync: true, + sync_type: syncType, + incremental_field: null, + incremental_field_type: null, + }) + } else { + updateSchema({ + ...currentSyncMethodModalSchema, + should_sync: true, + sync_type: syncType, + incremental_field: incrementalField, + incremental_field_type: incrementalFieldType, + }) + } + }} + /> + )} +
+ ) +} diff --git a/frontend/src/scenes/data-warehouse/settings/source/Syncs.tsx b/frontend/src/scenes/data-warehouse/settings/source/Syncs.tsx new file mode 100644 index 0000000000000..c53f1a3d4c096 --- /dev/null +++ b/frontend/src/scenes/data-warehouse/settings/source/Syncs.tsx @@ -0,0 +1,51 @@ +import { TZLabel } from '@posthog/apps-common' +import { LemonTable, LemonTag, LemonTagType } from '@posthog/lemon-ui' +import { useValues } from 'kea' + +import { ExternalDataJob } from '~/types' + +import { dataWarehouseSourceSettingsLogic } from './dataWarehouseSourceSettingsLogic' + +const StatusTagSetting: Record = { + Running: 'primary', + Completed: 'success', + Failed: 'danger', + Cancelled: 'default', +} + +export const Syncs = (): JSX.Element => { + const { jobs, jobsLoading } = useValues(dataWarehouseSourceSettingsLogic) + + return ( + { + return job.schema.name + }, + }, + { + title: 'Status', + render: (_, job) => { + return {job.status} + }, + }, + { + title: 'Rows synced', + render: (_, job) => { + return job.rows_synced + }, + }, + { + title: 'Synced at', + render: (_, job) => { + return + }, + }, + ]} + /> + ) +} diff --git a/frontend/src/scenes/data-warehouse/settings/source/dataWarehouseSourceSettingsLogic.ts b/frontend/src/scenes/data-warehouse/settings/source/dataWarehouseSourceSettingsLogic.ts new file mode 100644 index 0000000000000..aa869b87c3680 --- /dev/null +++ b/frontend/src/scenes/data-warehouse/settings/source/dataWarehouseSourceSettingsLogic.ts @@ -0,0 +1,123 @@ +import { actions, afterMount, kea, key, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { urlToAction } from 'kea-router' +import api from 'lib/api' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { + Breadcrumb, + DataWarehouseSettingsTab, + ExternalDataJob, + ExternalDataSourceSchema, + ExternalDataStripeSource, +} from '~/types' + +import type { dataWarehouseSourceSettingsLogicType } from './dataWarehouseSourceSettingsLogicType' + +export enum DataWarehouseSourceSettingsTabs { + Schemas = 'schemas', + Syncs = 'syncs', +} + +export interface DataWarehouseSourceSettingsLogicProps { + id: string + parentSettingsTab: DataWarehouseSettingsTab +} + +export const dataWarehouseSourceSettingsLogic = kea([ + path(['scenes', 'data-warehouse', 'settings', 'source', 'dataWarehouseSourceSettingsLogic']), + props({} as DataWarehouseSourceSettingsLogicProps), + key(({ id }) => id), + actions({ + setCurrentTab: (tab: DataWarehouseSourceSettingsTabs) => ({ tab }), + setParentSettingsTab: (tab: DataWarehouseSettingsTab) => ({ tab }), + setSourceId: (id: string) => ({ id }), + }), + loaders(({ values }) => ({ + source: [ + null as ExternalDataStripeSource | null, + { + loadSource: async () => { + return await api.externalDataSources.get(values.sourceId) + }, + updateSchema: async (schema: ExternalDataSourceSchema) => { + const updatedSchema = await api.externalDataSchemas.update(schema.id, schema) + + const source = values.source + const schemaIndex = source?.schemas.findIndex((n) => n.id === schema.id) + if (schemaIndex !== undefined) { + source!.schemas[schemaIndex] = updatedSchema + } + + return source + }, + }, + ], + jobs: [ + [] as ExternalDataJob[], + { + loadJobs: async () => { + return await api.externalDataSources.jobs(values.sourceId) + }, + }, + ], + })), + reducers({ + currentTab: [ + DataWarehouseSourceSettingsTabs.Schemas as DataWarehouseSourceSettingsTabs, + { + setCurrentTab: (_, { tab }) => tab, + }, + ], + parentSettingsTab: [ + DataWarehouseSettingsTab.Managed as DataWarehouseSettingsTab, + { + setParentSettingsTab: (_, { tab }) => tab, + }, + ], + sourceId: [ + '' as string, + { + setSourceId: (_, { id }) => id, + }, + ], + }), + selectors({ + breadcrumbs: [ + (s) => [s.parentSettingsTab, s.sourceId], + (parentSettingsTab, sourceId): Breadcrumb[] => [ + { + key: Scene.DataWarehouse, + name: 'Data Warehouse', + path: urls.dataWarehouse(), + }, + { + key: Scene.DataWarehouseSettings, + name: 'Data Warehouse Settings', + path: urls.dataWarehouseSettings(parentSettingsTab), + }, + { + key: Scene.dataWarehouseSourceSettings, + name: 'Data Warehouse Source Settings', + path: urls.dataWarehouseSourceSettings(sourceId, parentSettingsTab), + }, + ], + ], + }), + urlToAction(({ actions, values }) => ({ + '/data-warehouse/settings/:parentTab/:id': ({ parentTab, id }) => { + if (id) { + actions.setSourceId(id) + } + + if (parentTab !== values.parentSettingsTab) { + actions.setParentSettingsTab(parentTab as DataWarehouseSettingsTab) + } + }, + })), + afterMount(({ actions }) => { + actions.loadSource() + actions.loadJobs() + }), +]) diff --git a/frontend/src/scenes/experiments/ExperimentView/components.tsx b/frontend/src/scenes/experiments/ExperimentView/components.tsx index 23ff276af4475..d439681fa4d6b 100644 --- a/frontend/src/scenes/experiments/ExperimentView/components.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/components.tsx @@ -173,7 +173,7 @@ export function NoResultsEmptyState(): JSX.Element { function ChecklistItem({ failureReason, checked }: { failureReason: string; checked: boolean }): JSX.Element { const failureReasonToText = { - 'no-events': 'Events have been received', + 'no-events': 'Experiment events have been received', 'no-flag-info': 'Feature flag information is present on the events', 'no-control-variant': 'Events with the control variant received', 'no-test-variant': 'Events with at least one test variant received', diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx index 9419842092131..2a1befb09c01e 100644 --- a/frontend/src/scenes/experiments/experimentLogic.tsx +++ b/frontend/src/scenes/experiments/experimentLogic.tsx @@ -581,6 +581,10 @@ export const experimentLogic = kea([ if (values.changingSecondaryMetrics) { actions.loadSecondaryMetricResults() } + + if (values.experiment?.start_date) { + actions.loadExperimentResults() + } }, setExperiment: async ({ experiment }) => { const experimentEntitiesChanged = diff --git a/frontend/src/scenes/onboarding/Onboarding.tsx b/frontend/src/scenes/onboarding/Onboarding.tsx index a2ba4d97bac32..0c5d05b3699a8 100644 --- a/frontend/src/scenes/onboarding/Onboarding.tsx +++ b/frontend/src/scenes/onboarding/Onboarding.tsx @@ -178,6 +178,14 @@ const SessionReplayOnboarding = (): JSX.Element => { value: currentTeam?.capture_performance_opt_in ?? true, visible: true, }, + { + type: 'toggle', + title: 'Record user sessions', + description: 'Watch recordings of how users interact with your web app to see what can be improved.', + teamProperty: 'session_recording_opt_in', + value: true, + visible: false, + }, ] if (hasAvailableFeature(AvailableFeature.REPLAY_RECORDING_DURATION_MINIMUM)) { diff --git a/frontend/src/scenes/pipeline/PipelineNodeLogs.tsx b/frontend/src/scenes/pipeline/PipelineNodeLogs.tsx index ca8ad55303768..6b0962ddbaced 100644 --- a/frontend/src/scenes/pipeline/PipelineNodeLogs.tsx +++ b/frontend/src/scenes/pipeline/PipelineNodeLogs.tsx @@ -5,7 +5,7 @@ import { LOGS_PORTION_LIMIT } from 'lib/constants' import { pluralize } from 'lib/utils' import { PipelineNodeLogicProps } from './pipelineNodeLogic' -import { PipelineLogLevel, pipelineNodeLogsLogic } from './pipelineNodeLogsLogic' +import { ALL_LOG_LEVELS, pipelineNodeLogsLogic } from './pipelineNodeLogsLogic' export function PipelineNodeLogs({ id, stage }: PipelineNodeLogicProps): JSX.Element { const logic = pipelineNodeLogsLogic({ id, stage }) @@ -32,7 +32,7 @@ export function PipelineNodeLogs({ id, stage }: PipelineNodeLogicProps): JSX.Ele />
Show logs of level: - {Object.values(PipelineLogLevel).map((level) => { + {ALL_LOG_LEVELS.map((level) => { return ( `${record.log_source_id}:${record.instance_id}:${record.timestamp}`} pagination={{ pageSize: 200, hideOnSinglePage: true }} /> {!!logs.length && ( diff --git a/frontend/src/scenes/pipeline/__mocks__/batchExportLogs.json b/frontend/src/scenes/pipeline/__mocks__/batchExportLogs.json index 8bd35cab9d8cc..00c1763a600ec 100644 --- a/frontend/src/scenes/pipeline/__mocks__/batchExportLogs.json +++ b/frontend/src/scenes/pipeline/__mocks__/batchExportLogs.json @@ -4,33 +4,29 @@ "previous": null, "results": [ { - "team_id": 2, - "batch_export_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", - "run_id": "62acf018-c2e4-47a8-88ff-20839ca84816", + "log_source_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", + "instance_id": "62acf018-c2e4-47a8-88ff-20839ca84816", "timestamp": "2024-01-11T09:09:07.849132Z", "level": "INFO", "message": "Successfully finished exporting batch 2024-01-11 08:00:00+00:00 - 2024-01-11 09:00:00+00:00" }, { - "team_id": 2, - "batch_export_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", - "run_id": "62acf018-c2e4-47a8-88ff-20839ca84816", + "log_source_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", + "instance_id": "62acf018-c2e4-47a8-88ff-20839ca84816", "timestamp": "2024-01-11T09:03:23.257635Z", "level": "INFO", "message": "BatchExporting 109171 rows" }, { - "team_id": 2, - "batch_export_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", - "run_id": "62acf018-c2e4-47a8-88ff-20839ca84816", + "log_source_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", + "instance_id": "62acf018-c2e4-47a8-88ff-20839ca84816", "timestamp": "2024-01-11T09:03:23.053067Z", "level": "INFO", "message": "Exporting batch 2024-01-11T08:00:00+00:00 - 2024-01-11T09:00:00+00:00" }, { - "team_id": 2, - "batch_export_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", - "run_id": "62acf018-c2e4-47a8-88ff-20839ca84816", + "log_source_id": "018cf79f-a9e5-0001-cd6a-edc4886d939d", + "instance_id": "62acf018-c2e4-47a8-88ff-20839ca84816", "timestamp": "2024-01-11T09:03:23.011577Z", "level": "INFO", "message": "Creating batch export for range 2024-01-11T08:00:00+00:00 - 2024-01-11T09:00:00+00:00" diff --git a/frontend/src/scenes/pipeline/hogfunctions/HogFunctionConfiguration.tsx b/frontend/src/scenes/pipeline/hogfunctions/HogFunctionConfiguration.tsx index 343c97e569aef..8de4129d20c8f 100644 --- a/frontend/src/scenes/pipeline/hogfunctions/HogFunctionConfiguration.tsx +++ b/frontend/src/scenes/pipeline/hogfunctions/HogFunctionConfiguration.tsx @@ -45,6 +45,7 @@ export function HogFunctionConfiguration({ templateId, id }: { templateId?: stri loaded, hogFunction, willReEnableOnSave, + invocationGlobals, } = useValues(logic) const { submitConfiguration, @@ -333,6 +334,7 @@ export function HogFunctionConfiguration({ templateId, id }: { templateId?: stri language="hog" value={value ?? ''} onChange={(v) => onChange(v ?? '')} + globals={invocationGlobals} options={{ minimap: { enabled: false, diff --git a/frontend/src/scenes/pipeline/hogfunctions/HogFunctionInputs.tsx b/frontend/src/scenes/pipeline/hogfunctions/HogFunctionInputs.tsx index f2715855a8a59..dc166ff9434f8 100644 --- a/frontend/src/scenes/pipeline/hogfunctions/HogFunctionInputs.tsx +++ b/frontend/src/scenes/pipeline/hogfunctions/HogFunctionInputs.tsx @@ -180,8 +180,8 @@ function JsonConfigField(props: { } function HogFunctionTemplateInput(props: Omit): JSX.Element { - const { globalVars } = useValues(hogFunctionConfigurationLogic) - return + const { inputGlobals } = useValues(hogFunctionConfigurationLogic) + return } function DictionaryField({ onChange, value }: { onChange?: (value: any) => void; value: any }): JSX.Element { diff --git a/frontend/src/scenes/pipeline/hogfunctions/hogFunctionConfigurationLogic.tsx b/frontend/src/scenes/pipeline/hogfunctions/hogFunctionConfigurationLogic.tsx index 5b79dde55aab2..063eb4a84f667 100644 --- a/frontend/src/scenes/pipeline/hogfunctions/hogFunctionConfigurationLogic.tsx +++ b/frontend/src/scenes/pipeline/hogfunctions/hogFunctionConfigurationLogic.tsx @@ -277,8 +277,20 @@ export const hogFunctionConfigurationLogic = kea= 3 }, ], - // TODO: connect to the actual globals - globalVars: [(s) => [s.hogFunction], (): Record => createExampleEvent()], + inputGlobals: [(s) => [s.configuration], (): Record => createExampleEvent()], + invocationGlobals: [ + (s) => [s.inputGlobals, s.configuration], + (inputGlobals, configuration): Record => { + const event = { + ...inputGlobals, + inputs: {}, + } + for (const input of configuration?.inputs_schema || []) { + event.inputs[input.key] = input.type + } + return event + }, + ], })), listeners(({ actions, values, cache }) => ({ diff --git a/frontend/src/scenes/pipeline/hogfunctions/hogFunctionTestLogic.tsx b/frontend/src/scenes/pipeline/hogfunctions/hogFunctionTestLogic.tsx index 4ae6514c471ca..60f8092be8257 100644 --- a/frontend/src/scenes/pipeline/hogfunctions/hogFunctionTestLogic.tsx +++ b/frontend/src/scenes/pipeline/hogfunctions/hogFunctionTestLogic.tsx @@ -8,7 +8,6 @@ import { LogEntry } from '~/types' import { hogFunctionConfigurationLogic, sanitizeConfiguration } from './hogFunctionConfigurationLogic' import type { hogFunctionTestLogicType } from './hogFunctionTestLogicType' -import { createExampleEvent } from './utils/event-conversion' export interface HogFunctionTestLogicProps { id: string @@ -29,7 +28,10 @@ export const hogFunctionTestLogic = kea([ key((props) => props.id), path((id) => ['scenes', 'pipeline', 'hogfunctions', 'hogFunctionTestLogic', id]), connect((props: HogFunctionTestLogicProps) => ({ - values: [hogFunctionConfigurationLogic({ id: props.id }), ['configuration', 'configurationHasErrors']], + values: [ + hogFunctionConfigurationLogic({ id: props.id }), + ['configuration', 'configurationHasErrors', 'invocationGlobals'], + ], actions: [hogFunctionConfigurationLogic({ id: props.id }), ['touchConfigurationField']], })), actions({ @@ -89,7 +91,7 @@ export const hogFunctionTestLogic = kea([ }, }, })), - afterMount(({ actions }) => { - actions.setTestInvocationValue('globals', JSON.stringify(createExampleEvent(), null, 2)) + afterMount(({ actions, values }) => { + actions.setTestInvocationValue('globals', JSON.stringify(values.invocationGlobals, null, 2)) }), ]) diff --git a/frontend/src/scenes/pipeline/pipelineNodeLogsLogic.tsx b/frontend/src/scenes/pipeline/pipelineNodeLogsLogic.tsx index fdf15ae7b39aa..382038edf7e62 100644 --- a/frontend/src/scenes/pipeline/pipelineNodeLogsLogic.tsx +++ b/frontend/src/scenes/pipeline/pipelineNodeLogsLogic.tsx @@ -7,22 +7,15 @@ import { dayjs } from 'lib/dayjs' import { pipelineNodeLogic, PipelineNodeLogicProps } from 'scenes/pipeline/pipelineNodeLogic' import api from '~/lib/api' -import { BatchExportLogEntry, LogEntry, PluginLogEntry } from '~/types' +import { LogEntry, LogEntryLevel, LogEntryRequestParams } from '~/types' import { teamLogic } from '../teamLogic' import type { pipelineNodeLogsLogicType } from './pipelineNodeLogsLogicType' import { PipelineBackend } from './types' -import { LogLevelDisplay, logLevelsToTypeFilters, LogTypeDisplay } from './utils' +import { LogLevelDisplay } from './utils' -export type PipelineNodeLogEntry = BatchExportLogEntry | PluginLogEntry | LogEntry - -export enum PipelineLogLevel { - Debug = 'DEBUG', - Log = 'LOG', - Info = 'INFO', - Warning = 'WARNING', - Error = 'ERROR', -} +export const ALL_LOG_LEVELS: LogEntryLevel[] = ['DEBUG', 'LOG', 'INFO', 'WARNING', 'ERROR'] +export const DEFAULT_LOG_LEVELS: LogEntryLevel[] = ['LOG', 'INFO', 'WARNING', 'ERROR'] export const pipelineNodeLogsLogic = kea([ props({} as PipelineNodeLogicProps), // TODO: Remove `stage` from props, it isn't needed here for anything @@ -32,7 +25,7 @@ export const pipelineNodeLogsLogic = kea([ values: [teamLogic(), ['currentTeamId'], pipelineNodeLogic(props), ['node']], })), actions({ - setSelectedLogLevels: (levels: PipelineLogLevel[]) => ({ + setSelectedLogLevels: (levels: LogEntryLevel[]) => ({ levels, }), setSearchTerm: (searchTerm: string) => ({ searchTerm }), @@ -40,33 +33,27 @@ export const pipelineNodeLogsLogic = kea([ clearBackgroundLogs: true, markLogsEnd: true, }), - loaders(({ props: { id }, values, actions, cache }) => ({ + loaders(({ values, actions, cache }) => ({ logs: [ - [] as PipelineNodeLogEntry[], + [] as LogEntry[], { loadLogs: async () => { - let results: PipelineNodeLogEntry[] + let results: LogEntry[] = [] + const logParams: LogEntryRequestParams = { + search: values.searchTerm, + level: values.selectedLogLevelsForAPI.join(','), + limit: LOGS_PORTION_LIMIT, + instance_id: values.instanceId ?? undefined, + } + if (values.node.backend === PipelineBackend.BatchExport) { - results = await api.batchExportLogs.search( - values.node.id, - values.searchTerm, - values.selectedLogLevels - ) + const res = await api.batchExports.logs(values.node.id, logParams) + results = res.results } else if (values.node.backend === PipelineBackend.HogFunction) { - const res = await api.hogFunctions.searchLogs(values.node.id, { - search: values.searchTerm, - levels: values.selectedLogLevels, - limit: LOGS_PORTION_LIMIT, - instance_id: values.instanceId, - }) - + const res = await api.hogFunctions.logs(values.node.id, logParams) results = res.results } else { - results = await api.pluginLogs.search( - values.node.id, - values.searchTerm, - logLevelsToTypeFilters(values.selectedLogLevels) - ) + results = await api.pluginConfigs.logs(values.node.id, logParams) } if (!cache.pollingInterval) { @@ -76,31 +63,22 @@ export const pipelineNodeLogsLogic = kea([ return results }, loadMoreLogs: async () => { - let results: PipelineNodeLogEntry[] + let results: LogEntry[] + const logParams: LogEntryRequestParams = { + search: values.searchTerm, + level: values.selectedLogLevels.join(','), + limit: LOGS_PORTION_LIMIT, + before: values.trailingEntry?.timestamp, + instance_id: values.instanceId ?? undefined, + } if (values.node.backend === PipelineBackend.BatchExport) { - results = await api.batchExportLogs.search( - id as string, - values.searchTerm, - values.selectedLogLevels, - values.trailingEntry as BatchExportLogEntry | null - ) + const res = await api.batchExports.logs(values.node.id, logParams) + results = res.results } else if (values.node.backend === PipelineBackend.HogFunction) { - const res = await api.hogFunctions.searchLogs(values.node.id, { - search: values.searchTerm, - levels: values.selectedLogLevels, - limit: LOGS_PORTION_LIMIT, - before: values.trailingEntry?.timestamp, - instance_id: values.instanceId, - }) - + const res = await api.hogFunctions.logs(values.node.id, logParams) results = res.results } else { - results = await api.pluginLogs.search( - id as number, - values.searchTerm, - logLevelsToTypeFilters(values.selectedLogLevels), - values.trailingEntry as PluginLogEntry | null - ) + results = await api.pluginConfigs.logs(values.node.id, logParams) } if (results.length < LOGS_PORTION_LIMIT) { @@ -116,7 +94,7 @@ export const pipelineNodeLogsLogic = kea([ }, ], backgroundLogs: [ - [] as PipelineNodeLogEntry[], + [] as LogEntry[], { pollBackgroundLogs: async () => { // we fetch new logs in the background and allow the user to expand @@ -125,33 +103,23 @@ export const pipelineNodeLogsLogic = kea([ return values.backgroundLogs } - let results: PipelineNodeLogEntry[] + let results: LogEntry[] + const logParams: LogEntryRequestParams = { + search: values.searchTerm, + level: values.selectedLogLevels.join(','), + limit: LOGS_PORTION_LIMIT, + after: values.leadingEntry?.timestamp, + instance_id: values.instanceId ?? undefined, + } + if (values.node.backend === PipelineBackend.BatchExport) { - results = await api.batchExportLogs.search( - id as string, - values.searchTerm, - values.selectedLogLevels, - null, - values.leadingEntry as BatchExportLogEntry | null - ) + const res = await api.batchExports.logs(values.node.id, logParams) + results = res.results } else if (values.node.backend === PipelineBackend.HogFunction) { - const res = await api.hogFunctions.searchLogs(values.node.id, { - search: values.searchTerm, - levels: values.selectedLogLevels, - limit: LOGS_PORTION_LIMIT, - after: values.leadingEntry?.timestamp, - instance_id: values.instanceId, - }) - + const res = await api.hogFunctions.logs(values.node.id, logParams) results = res.results } else { - results = await api.pluginLogs.search( - id as number, - values.searchTerm, - logLevelsToTypeFilters(values.selectedLogLevels), - null, - values.leadingEntry as PluginLogEntry | null - ) + results = await api.pluginConfigs.logs(values.node.id, logParams) } return [...results, ...values.backgroundLogs] @@ -161,13 +129,13 @@ export const pipelineNodeLogsLogic = kea([ })), reducers({ selectedLogLevels: [ - Object.values(PipelineLogLevel).filter((level) => level !== 'DEBUG'), + DEFAULT_LOG_LEVELS, { setSelectedLogLevels: (_, { levels }) => levels, }, ], backgroundLogs: [ - [] as PipelineNodeLogEntry[], + [] as LogEntry[], { clearBackgroundLogs: () => [], }, @@ -192,10 +160,10 @@ export const pipelineNodeLogsLogic = kea([ }, ], }), - selectors(({ actions }) => ({ + selectors(({ actions, values }) => ({ leadingEntry: [ (s) => [s.logs, s.backgroundLogs], - (logs: PipelineNodeLogEntry[], backgroundLogs: PipelineNodeLogEntry[]): PipelineNodeLogEntry | null => { + (logs: LogEntry[], backgroundLogs: LogEntry[]): LogEntry | null => { if (backgroundLogs.length) { return backgroundLogs[0] } @@ -207,7 +175,7 @@ export const pipelineNodeLogsLogic = kea([ ], trailingEntry: [ (s) => [s.logs, s.backgroundLogs], - (logs: PipelineNodeLogEntry[], backgroundLogs: PipelineNodeLogEntry[]): PipelineNodeLogEntry | null => { + (logs: LogEntry[], backgroundLogs: LogEntry[]): LogEntry | null => { if (logs.length) { return logs[logs.length - 1] } @@ -219,14 +187,13 @@ export const pipelineNodeLogsLogic = kea([ ], columns: [ (s) => [s.node], - (node): LemonTableColumns => { + (node): LemonTableColumns => { return [ { title: 'Timestamp', key: 'timestamp', dataIndex: 'timestamp', - sorter: (a: PipelineNodeLogEntry, b: PipelineNodeLogEntry) => - dayjs(a.timestamp).unix() - dayjs(b.timestamp).unix(), + sorter: (a: LogEntry, b: LogEntry) => dayjs(a.timestamp).unix() - dayjs(b.timestamp).unix(), render: (timestamp: string) => , width: 0, }, @@ -238,26 +205,19 @@ export const pipelineNodeLogsLogic = kea([ : node.backend == PipelineBackend.BatchExport ? 'Run Id' : 'Source', - dataIndex: - node.backend == PipelineBackend.HogFunction - ? 'instance_id' - : node.backend == PipelineBackend.BatchExport - ? 'run_id' - : 'source', - key: - node.backend == PipelineBackend.HogFunction - ? 'instance_id' - : node.backend == PipelineBackend.BatchExport - ? 'run_id' - : 'source', - + dataIndex: 'instance_id', + key: 'instance_id', render: (instanceId: string) => ( - {node.backend === PipelineBackend.HogFunction ? ( + {node.backend !== PipelineBackend.Plugin ? ( { - actions.setInstanceId(instanceId) + if (values.instanceId === instanceId) { + actions.setInstanceId(null) + } else { + actions.setInstanceId(instanceId) + } }} > {instanceId} @@ -271,24 +231,9 @@ export const pipelineNodeLogsLogic = kea([ { width: 100, title: 'Level', - key: - node.backend == PipelineBackend.HogFunction - ? 'level' - : node.backend == PipelineBackend.BatchExport - ? 'level' - : 'type', - dataIndex: - node.backend == PipelineBackend.HogFunction - ? 'level' - : node.backend == PipelineBackend.BatchExport - ? 'level' - : 'type', - render: - node.backend == PipelineBackend.HogFunction - ? LogLevelDisplay - : node.backend == PipelineBackend.BatchExport - ? LogLevelDisplay - : LogTypeDisplay, + key: 'level', + dataIndex: 'level', + render: LogLevelDisplay, }, { title: 'Message', @@ -296,7 +241,21 @@ export const pipelineNodeLogsLogic = kea([ dataIndex: 'message', render: (message: string) => {message}, }, - ] as LemonTableColumns + ] as LemonTableColumns + }, + ], + + selectedLogLevelsForAPI: [ + (s) => [s.selectedLogLevels], + (logLevels): LogEntryLevel[] => { + const uniqueLevels = new Set(logLevels) + if (uniqueLevels.has('WARN')) { + uniqueLevels.add('WARNING') + } + if (uniqueLevels.has('WARNING')) { + uniqueLevels.add('WARN') + } + return Array.from(uniqueLevels) }, ], })), diff --git a/frontend/src/scenes/pipeline/utils.tsx b/frontend/src/scenes/pipeline/utils.tsx index 4f7e1ed393cce..9a587b5d84aa8 100644 --- a/frontend/src/scenes/pipeline/utils.tsx +++ b/frontend/src/scenes/pipeline/utils.tsx @@ -18,15 +18,14 @@ import { urls } from 'scenes/urls' import { BatchExportConfiguration, BatchExportService, + LogEntryLevel, PipelineNodeTab, PipelineStage, PluginConfigTypeNew, - PluginLogEntryType, PluginType, } from '~/types' import { pipelineAccessLogic } from './pipelineAccessLogic' -import { PipelineLogLevel } from './pipelineNodeLogsLogic' import { Destination, ImportApp, @@ -190,59 +189,23 @@ export function RenderBatchExportIcon({ ) } -export const logLevelToTypeFilter = (level: PipelineLogLevel): PluginLogEntryType => { - switch (level) { - case PipelineLogLevel.Debug: - return PluginLogEntryType.Debug - case PipelineLogLevel.Error: - return PluginLogEntryType.Error - case PipelineLogLevel.Info: - return PluginLogEntryType.Info - case PipelineLogLevel.Log: - return PluginLogEntryType.Log - case PipelineLogLevel.Warning: - return PluginLogEntryType.Warn - default: - throw new Error('unknown log level') - } -} - -export const logLevelsToTypeFilters = (levels: PipelineLogLevel[]): PluginLogEntryType[] => - levels.map((l) => logLevelToTypeFilter(l)) - -export const typeToLogLevel = (type: PluginLogEntryType): PipelineLogLevel => { - switch (type) { - case PluginLogEntryType.Debug: - return PipelineLogLevel.Debug - case PluginLogEntryType.Error: - return PipelineLogLevel.Error - case PluginLogEntryType.Info: - return PipelineLogLevel.Info - case PluginLogEntryType.Log: - return PipelineLogLevel.Log - case PluginLogEntryType.Warn: - return PipelineLogLevel.Warning - default: - throw new Error('unknown log type') - } -} - -export function LogLevelDisplay(level: PipelineLogLevel): JSX.Element { +export function LogLevelDisplay(level: LogEntryLevel): JSX.Element { let color: string | undefined switch (level) { - case PipelineLogLevel.Debug: + case 'DEBUG': color = 'text-muted' break - case PipelineLogLevel.Log: + case 'LOG': color = 'text-text-3000' break - case PipelineLogLevel.Info: + case 'INFO': color = 'text-primary' break - case PipelineLogLevel.Warning: + case 'WARNING': + case 'WARN': color = 'text-warning' break - case PipelineLogLevel.Error: + case 'ERROR': color = 'text-danger' break default: @@ -251,23 +214,6 @@ export function LogLevelDisplay(level: PipelineLogLevel): JSX.Element { return {level} } -export function LogTypeDisplay(type: PluginLogEntryType): JSX.Element { - return LogLevelDisplay(typeToLogLevel(type)) -} - -export const humanFriendlyFrequencyName = (frequency: Destination['interval']): string => { - switch (frequency) { - case 'realtime': - return 'Realtime' - case 'day': - return 'Daily' - case 'hour': - return 'Hourly' - case 'every 5 minutes': - return '5 min' - } -} - export function nameColumn< T extends { stage: PipelineStage; id: number; name: string; description?: string } >(): LemonTableColumn { diff --git a/frontend/src/scenes/saved-insights/SavedInsights.tsx b/frontend/src/scenes/saved-insights/SavedInsights.tsx index 3c79822d2c8ec..7eae17f38e8db 100644 --- a/frontend/src/scenes/saved-insights/SavedInsights.tsx +++ b/frontend/src/scenes/saved-insights/SavedInsights.tsx @@ -9,6 +9,7 @@ import { IconHogQL, IconLifecycle, IconPerson, + IconPieChart, IconPlusSmall, IconRetention, IconStar, @@ -288,19 +289,19 @@ export const QUERY_TYPES_METADATA: Record = { [NodeKind.WebOverviewQuery]: { name: 'Overview Stats', description: 'View overview stats for a website', - icon: IconTrends, + icon: IconPieChart, inMenu: true, }, [NodeKind.WebStatsTableQuery]: { name: 'Web Table', description: 'A table of results from web analytics, with a breakdown', - icon: IconTrends, + icon: IconPieChart, inMenu: true, }, [NodeKind.WebTopClicksQuery]: { name: 'Top Clicks', description: 'View top clicks for a website', - icon: IconTrends, + icon: IconPieChart, inMenu: true, }, [NodeKind.HogQuery]: { @@ -309,6 +310,12 @@ export const QUERY_TYPES_METADATA: Record = { icon: IconHogQL, inMenu: true, }, + [NodeKind.SessionAttributionExplorerQuery]: { + name: 'Session Attribution', + description: 'Session Attribution Explorer', + icon: IconPieChart, + inMenu: true, + }, } export const INSIGHT_TYPE_OPTIONS: LemonSelectOptions = [ diff --git a/frontend/src/scenes/sceneTypes.ts b/frontend/src/scenes/sceneTypes.ts index eef8381063270..56ed570a68a10 100644 --- a/frontend/src/scenes/sceneTypes.ts +++ b/frontend/src/scenes/sceneTypes.ts @@ -50,6 +50,7 @@ export enum Scene { DataWarehouseExternal = 'DataWarehouseExternal', DataWarehouseTable = 'DataWarehouseTable', DataWarehouseSettings = 'DataWarehouseSettings', + dataWarehouseSourceSettings = 'DataWarehouseSourceSettings', DataWarehouseRedirect = 'DataWarehouseRedirect', OrganizationCreateFirst = 'OrganizationCreate', ProjectHomepage = 'ProjectHomepage', diff --git a/frontend/src/scenes/scenes.ts b/frontend/src/scenes/scenes.ts index 6133cf6fa1afe..eadcb5aaf6e24 100644 --- a/frontend/src/scenes/scenes.ts +++ b/frontend/src/scenes/scenes.ts @@ -251,6 +251,11 @@ export const sceneConfigurations: Record = { name: 'Data warehouse settings', defaultDocsPath: '/docs/data-warehouse', }, + [Scene.dataWarehouseSourceSettings]: { + projectBased: true, + name: 'Data warehouse source settings', + defaultDocsPath: '/docs/data-warehouse', + }, [Scene.DataWarehouseRedirect]: { name: 'Data warehouse redirect', }, @@ -561,6 +566,7 @@ export const routes: Record = { [urls.dataWarehouseTable()]: Scene.DataWarehouseTable, [urls.dataWarehouseSettings(':tab')]: Scene.DataWarehouseSettings, [urls.dataWarehouseRedirect(':kind')]: Scene.DataWarehouseRedirect, + [urls.dataWarehouseSourceSettings(':id', ':tab')]: Scene.dataWarehouseSourceSettings, [urls.featureFlags()]: Scene.FeatureFlags, [urls.featureFlag(':id')]: Scene.FeatureFlag, [urls.annotations()]: Scene.DataManagement, diff --git a/frontend/src/scenes/settings/project/ManagedReverseProxy.tsx b/frontend/src/scenes/settings/project/ManagedReverseProxy.tsx index 23948a65ed44d..dc753a32a35ab 100644 --- a/frontend/src/scenes/settings/project/ManagedReverseProxy.tsx +++ b/frontend/src/scenes/settings/project/ManagedReverseProxy.tsx @@ -25,6 +25,11 @@ import { proxyLogic, ProxyRecord } from './proxyLogic' const MAX_PROXY_RECORDS = 3 +const statusText = { + valid: 'live', + timed_out: 'timed out', +} + export function ManagedReverseProxy(): JSX.Element { const { formState, proxyRecords, proxyRecordsLoading } = useValues(proxyLogic) const { showForm, deleteRecord } = useActions(proxyLogic) @@ -58,12 +63,17 @@ export function ManagedReverseProxy(): JSX.Element { )} > {status === 'issuing' && } - {status === 'valid' ? 'live' : status} + {statusText[status] || status} {status === 'waiting' && ( )} + {status === 'timed_out' && ( + + + + )}
) }, diff --git a/frontend/src/scenes/surveys/QuestionBranchingInput.tsx b/frontend/src/scenes/surveys/QuestionBranchingInput.tsx index 2a6b69f0e6dca..3935712027bc7 100644 --- a/frontend/src/scenes/surveys/QuestionBranchingInput.tsx +++ b/frontend/src/scenes/surveys/QuestionBranchingInput.tsx @@ -35,7 +35,7 @@ export function QuestionBranchingInput({ { const handleSelect = (): void => { let specificQuestionIndex @@ -164,7 +164,7 @@ function QuestionResponseBasedBranchingInput({ { let specificQuestionIndex if (nextStep.startsWith(SurveyQuestionBranchingType.SpecificQuestion)) { diff --git a/frontend/src/scenes/surveys/SurveyEdit.tsx b/frontend/src/scenes/surveys/SurveyEdit.tsx index 8eca8f3d910d4..4fc3a353f13ce 100644 --- a/frontend/src/scenes/surveys/SurveyEdit.tsx +++ b/frontend/src/scenes/surveys/SurveyEdit.tsx @@ -229,6 +229,7 @@ export default function SurveyEdit(): JSX.Element { index: number ) => ({ key: index, + dataAttr: `survey-question-panel-${index}`, header: (
} diff --git a/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx b/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx index 900f2b3d22470..e0e711b9f4e40 100644 --- a/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx +++ b/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx @@ -7,10 +7,8 @@ import { IconPlusSmall, IconTrash } from '@posthog/icons' import { LemonButton, LemonCheckbox, LemonDialog, LemonInput, LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { Group } from 'kea-forms' -import { FEATURE_FLAGS } from 'lib/constants' import { SortableDragIcon } from 'lib/lemon-ui/icons' import { LemonField } from 'lib/lemon-ui/LemonField' -import { featureFlagLogic as enabledFeaturesLogic } from 'lib/logic/featureFlagLogic' import { Survey, SurveyQuestionType } from '~/types' @@ -117,8 +115,6 @@ export function SurveyEditQuestionHeader({ export function SurveyEditQuestionGroup({ index, question }: { index: number; question: any }): JSX.Element { const { survey, descriptionContentType } = useValues(surveyLogic) const { setDefaultForQuestionType, setSurveyValue, resetBranchingForQuestion } = useActions(surveyLogic) - const { featureFlags } = useValues(enabledFeaturesLogic) - const hasBranching = featureFlags[FEATURE_FLAGS.SURVEYS_BRANCHING_LOGIC] const initialDescriptionContentType = descriptionContentType(index) ?? 'text' @@ -167,30 +163,35 @@ export function SurveyEditQuestionGroup({ index, question }: { index: number; qu { label: SurveyQuestionLabel[SurveyQuestionType.Open], value: SurveyQuestionType.Open, + 'data-attr': `survey-question-type-${index}-${SurveyQuestionType.Open}`, }, { label: 'Link/Notification', value: SurveyQuestionType.Link, + 'data-attr': `survey-question-type-${index}-${SurveyQuestionType.Link}`, }, { label: 'Rating', value: SurveyQuestionType.Rating, + 'data-attr': `survey-question-type-${index}-${SurveyQuestionType.Rating}`, }, ...[ { label: 'Single choice select', value: SurveyQuestionType.SingleChoice, + 'data-attr': `survey-question-type-${index}-${SurveyQuestionType.SingleChoice}`, }, { label: 'Multiple choice select', value: SurveyQuestionType.MultipleChoice, + 'data-attr': `survey-question-type-${index}-${SurveyQuestionType.MultipleChoice}`, }, ], ]} /> - + {({ value, onChange }) => ( @@ -383,7 +384,7 @@ export function SurveyEditQuestionGroup({ index, question }: { index: number; qu } /> - {hasBranching && } +
) diff --git a/frontend/src/scenes/urls.ts b/frontend/src/scenes/urls.ts index e7d90253b5902..73925a37f7f77 100644 --- a/frontend/src/scenes/urls.ts +++ b/frontend/src/scenes/urls.ts @@ -164,6 +164,8 @@ export const urls = { dataWarehouseSettings: (tab?: DataWarehouseSettingsTab | ':tab'): string => `/data-warehouse/settings/${tab ? tab : DataWarehouseSettingsTab.Managed}`, dataWarehouseRedirect: (kind: string): string => `/data-warehouse/${kind}/redirect`, + dataWarehouseSourceSettings: (id: string, tab?: DataWarehouseSettingsTab | ':tab'): string => + `/data-warehouse/settings/${tab ? tab : DataWarehouseSettingsTab.Managed}/${id}`, annotations: (): string => '/data-management/annotations', annotation: (id: AnnotationType['id'] | ':id'): string => `/data-management/annotations/${id}`, projectApps: (tab?: PluginTab): string => `/apps${tab ? `?tab=${tab}` : ''}`, diff --git a/frontend/src/scenes/web-analytics/SessionDebugger/SessionAttributionExplorerScene.tsx b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/SessionAttributionExplorerScene.tsx similarity index 69% rename from frontend/src/scenes/web-analytics/SessionDebugger/SessionAttributionExplorerScene.tsx rename to frontend/src/scenes/web-analytics/SessionAttributionExplorer/SessionAttributionExplorerScene.tsx index dacf2ff8caccd..3289068e33fc5 100644 --- a/frontend/src/scenes/web-analytics/SessionDebugger/SessionAttributionExplorerScene.tsx +++ b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/SessionAttributionExplorerScene.tsx @@ -1,4 +1,5 @@ -import { IconCollapse, IconExpand } from '@posthog/icons' +import { IconCollapse, IconExpand, IconPlus } from '@posthog/icons' +import { LemonMenu, LemonSwitch } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { supportLogic } from 'lib/components/Support/supportLogic' import { IconFeedback } from 'lib/lemon-ui/icons' @@ -11,7 +12,7 @@ import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { SceneExport } from 'scenes/sceneTypes' import { Query } from '~/queries/Query/Query' -import { DataTableNode, HogQLQuery } from '~/queries/schema' +import { DataTableNode, HogQLQuery, SessionAttributionGroupBy } from '~/queries/schema' import { isSessionPropertyFilters } from '~/queries/schema-guards' import { QueryContext, QueryContextColumnComponent } from '~/queries/types' @@ -103,7 +104,7 @@ const queryContext: QueryContext = { title: 'UTM campaign', render: ExpandableDataCell, }, - has_ad_id: { + ad_ids: { title: 'Ad IDs', render: ExpandableDataCell, }, @@ -112,6 +113,73 @@ const queryContext: QueryContext = { render: ExpandableDataCell, }, }, + emptyStateHeading: 'There are no matching sessions for this query', + emptyStateDetail: 'Try changing the date range, or changing the property filters.', +} + +const groupByOptions = [ + { + label: 'Channel type', + value: SessionAttributionGroupBy.ChannelType, + }, + { + label: 'Referring domain', + value: SessionAttributionGroupBy.ReferringDomain, + }, + { + label: 'UTM source', + value: SessionAttributionGroupBy.Source, + }, + { + label: 'UTM medium', + value: SessionAttributionGroupBy.Medium, + }, + { + label: 'UTM campaign', + value: SessionAttributionGroupBy.Campaign, + }, + { + label: 'Ad IDs', + value: SessionAttributionGroupBy.AdIds, + }, + { + label: 'Entry URL', + value: SessionAttributionGroupBy.InitialURL, + }, +] + +export const GroupByFilter = (): JSX.Element => { + const { groupBy } = useValues(sessionAttributionExplorerLogic) + const { enableGroupBy, disableGroupBy } = useActions(sessionAttributionExplorerLogic) + + return ( +
+ { + return { + label: () => ( + { + if (val) { + enableGroupBy(value) + } else { + disableGroupBy(value) + } + }} + fullWidth={true} + label={label} + /> + ), + } + })} + > + } size="small" type="secondary"> + Group by + + +
+ ) } export function SessionAttributionExplorer(): JSX.Element { @@ -134,9 +202,8 @@ export function SessionAttributionExplorer(): JSX.Element { Channel type.

- The table below groups sessions with the same value for Channel type, referring domain, - source, medium, and which ad ids are present. It shows the count of sessions in each group, - and some example entry URLs from that group. + The table below shows sessions that are grouped, and you can change how they are grouped. + Any columns that are not used in the grouping will show example values.

If you believe that a session is attributed incorrectly, please let us know!

@@ -159,17 +226,18 @@ export function SessionAttributionExplorer(): JSX.Element { ) : null} + context={queryContext} query={query} setQuery={(query) => { const source = query.source as HogQLQuery - if (source.filters?.properties && isSessionPropertyFilters(source.filters.properties)) { + if (source.filters && isSessionPropertyFilters(source.filters.properties)) { setProperties(source.filters.properties) + } else { + setProperties([]) } - if (source.filters?.dateRange) { - setDateRange(source.filters.dateRange) - } + setDateRange(source.filters?.dateRange ?? null) }} /> diff --git a/frontend/src/scenes/web-analytics/SessionAttributionExplorer/__mocks__/sessionAttributionQuery.json b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/__mocks__/sessionAttributionQuery.json new file mode 100644 index 0000000000000..8b45fc79953e2 --- /dev/null +++ b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/__mocks__/sessionAttributionQuery.json @@ -0,0 +1,181 @@ +{ + "cache_key": "cache_ffcae1d8084f585670caee559af58a79", + "cache_target_age": "2024-07-10T09:38:27.761334Z", + "columns": [ + "context.columns.count", + "context.columns.channel_type", + "context.columns.referring_domain", + "context.columns.utm_source", + "context.columns.utm_medium", + "context.columns.utm_campaign", + "context.columns.ad_ids", + "context.columns.example_entry_urls" + ], + "error": null, + "hasMore": false, + "hogql": "SELECT\n count() AS `context.columns.count`,\n $channel_type AS `context.columns.channel_type`,\n $entry_referring_domain AS `context.columns.referring_domain`,\n $entry_utm_source AS `context.columns.utm_source`,\n $entry_utm_medium AS `context.columns.utm_medium`,\n topK(10)($entry_utm_campaign) AS `context.columns.utm_campaign`,\n topK(10)(nullIf(arrayStringConcat([if(isNotNull($entry_gclid), 'glcid', NULL), if(isNotNull($entry_gad_source), 'gad_source', NULL)], ','), '')) AS `context.columns.ad_ids`,\n topK(10)($entry_current_url) AS `context.columns.example_entry_urls`\nFROM\n sessions\nWHERE\n and(1, 1, less($start_timestamp, toDateTime('2024-07-10 09:38:12.535618')), greaterOrEquals($start_timestamp, toDateTime('2024-07-03 09:38:12.535669')))\nGROUP BY\n `context.columns.channel_type`,\n `context.columns.referring_domain`,\n `context.columns.utm_source`,\n `context.columns.utm_medium`\nORDER BY\n `context.columns.count` DESC\nLIMIT 101\nOFFSET 0", + "is_cached": true, + "last_refresh": "2024-07-10T09:38:12.761334Z", + "limit": 100, + "modifiers": { + "bounceRatePageViewMode": "count_pageviews", + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "disabled", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "v2" + }, + "next_allowed_client_refresh": "2024-07-10T09:39:12.761339Z", + "offset": 0, + "query_status": { + "complete": false, + "end_time": null, + "error": false, + "error_message": null, + "expiration_time": null, + "id": "817cdeac-13ef-490c-b64c-9ace3990f598", + "labels": null, + "query_async": true, + "query_progress": null, + "results": null, + "start_time": "2024-07-10T10:06:19.839326Z", + "task_id": "90e54ce6-f8b1-4cb7-9af7-3d2f66116452", + "team_id": 1 + }, + "results": [ + [ + 17, + "Unknown", + "localhost:8000", + null, + null, + [], + [], + [ + "http://localhost:8000/project/1/web/session-attribution-debugger", + "http://localhost:8000/project/1/web/session-attribution-explorer", + "http://localhost:8000/project/1/insights/new", + "http://localhost:8000/project/1/web/session-attribution-explorer?filters=%5B%7B%22key%22%3A%22%24entry_pathname%22%2C%22value%22%3A%5B%22%2Ffiles%2F%22%5D%2C%22operator%22%3A%22exact%22%2C%22type%22%3A%22session%22%7D%5D", + "http://localhost:8000/login?next=/project/1/web", + "http://localhost:8000/project/1/web/session-attribution-explorer?properties=%5B%7B%22key%22%3A%22%24channel_type%22%2C%22value%22%3A%5B%22Referral%22%5D%2C%22operator%22%3A%22exact%22%2C%22type%22%3A%22session%22%7D%5D&dateRange=%7B%22date_from%22%3A%22dStart%22%7D", + "http://localhost:8000/project/1/data-management/events" + ] + ], + [ + 6, + "Direct", + "$direct", + null, + null, + [], + [], + [ + "http://localhost:8000/", + "http://localhost:8000/login?next=/", + "http://localhost:8000/project/1/web/session-attribution-explorer?dateRange=%7B%22date_from%22%3A%22-180d%22%7D" + ] + ] + ], + "timezone": "UTC", + "timings": [ + { + "k": "./session_attribution_query_runner", + "t": 0.00008904199057724327 + }, + { + "k": "./parse_select_cpp", + "t": 0.018147832990507595 + }, + { + "k": "./query", + "t": 0.0003516669967211783 + }, + { + "k": "./replace_placeholders", + "t": 0.0007442920032190159 + }, + { + "k": "./max_limit", + "t": 0.000022374995751306415 + }, + { + "k": "./hogql/prepare_ast/clone", + "t": 0.0001059580099536106 + }, + { + "k": "./hogql/prepare_ast/create_hogql_database", + "t": 0.03962483300711028 + }, + { + "k": "./hogql/prepare_ast/resolve_types", + "t": 0.0003752080083359033 + }, + { + "k": "./hogql/prepare_ast", + "t": 0.04015908399014734 + }, + { + "k": "./hogql/print_ast/printer", + "t": 0.0002745000092545524 + }, + { + "k": "./hogql/print_ast", + "t": 0.0002916249941335991 + }, + { + "k": "./hogql", + "t": 0.040463582990923896 + }, + { + "k": "./print_ast/create_hogql_database", + "t": 0.0375871249998454 + }, + { + "k": "./print_ast/resolve_types", + "t": 0.0003574169968487695 + }, + { + "k": "./print_ast/resolve_property_types", + "t": 0.00029016700864303857 + }, + { + "k": "./print_ast/resolve_lazy_tables", + "t": 0.03512458399927709 + }, + { + "k": "./print_ast/swap_properties", + "t": 0.0009043330064741895 + }, + { + "k": "./print_ast/printer", + "t": 0.0018909170030383393 + }, + { + "k": "./print_ast", + "t": 0.07628516700060572 + }, + { + "k": "./clickhouse_execute", + "t": 0.10822070798894856 + }, + { + "k": ".", + "t": 0.2550588330050232 + } + ], + "types": [ + ["context.columns.count", "UInt64"], + ["context.columns.channel_type", "String"], + ["context.columns.referring_domain", "Nullable(String)"], + ["context.columns.utm_source", "Nullable(String)"], + ["context.columns.utm_medium", "Nullable(String)"], + ["context.columns.utm_campaign", "Array(String)"], + ["context.columns.ad_ids", "Array(String)"], + ["context.columns.example_entry_urls", "Array(String)"] + ] +} diff --git a/frontend/src/scenes/web-analytics/SessionAttributionExplorer/__mocks__/sessionAttributionQueryStatus.json b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/__mocks__/sessionAttributionQueryStatus.json new file mode 100644 index 0000000000000..9eeba429b6db2 --- /dev/null +++ b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/__mocks__/sessionAttributionQueryStatus.json @@ -0,0 +1,183 @@ +{ + "query_status": { + "complete": true, + "end_time": "2024-07-10T10:06:20.116Z", + "error": false, + "error_message": null, + "expiration_time": "2024-07-10T10:16:20.116Z", + "id": "817cdeac-13ef-490c-b64c-9ace3990f598", + "labels": null, + "query_async": true, + "query_progress": null, + "results": { + "cache_key": "cache_ffcae1d8084f585670caee559af58a79", + "cache_target_age": null, + "columns": [ + "context.columns.count", + "context.columns.channel_type", + "context.columns.referring_domain", + "context.columns.utm_source", + "context.columns.utm_medium", + "context.columns.utm_campaign", + "context.columns.ad_ids", + "context.columns.example_entry_urls" + ], + "error": null, + "hasMore": false, + "hogql": "SELECT\n count() AS `context.columns.count`,\n $channel_type AS `context.columns.channel_type`,\n $entry_referring_domain AS `context.columns.referring_domain`,\n $entry_utm_source AS `context.columns.utm_source`,\n $entry_utm_medium AS `context.columns.utm_medium`,\n topK(10)($entry_utm_campaign) AS `context.columns.utm_campaign`,\n topK(10)(nullIf(arrayStringConcat([if(isNotNull($entry_gclid), 'glcid', NULL), if(isNotNull($entry_gad_source), 'gad_source', NULL)], ','), '')) AS `context.columns.ad_ids`,\n topK(10)($entry_current_url) AS `context.columns.example_entry_urls`\nFROM\n sessions\nWHERE\n and(1, 1, less($start_timestamp, toDateTime('2024-07-10 10:06:19.898500')), greaterOrEquals($start_timestamp, toDateTime('2024-07-03 10:06:19.898633')))\nGROUP BY\n `context.columns.channel_type`,\n `context.columns.referring_domain`,\n `context.columns.utm_source`,\n `context.columns.utm_medium`\nORDER BY\n `context.columns.count` DESC\nLIMIT 101\nOFFSET 0", + "is_cached": false, + "last_refresh": "2024-07-10T10:06:20.115209Z", + "limit": 100, + "modifiers": { + "bounceRatePageViewMode": "count_pageviews", + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "disabled", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "v2" + }, + "next_allowed_client_refresh": "2024-07-10T10:07:20.115214Z", + "offset": 0, + "query_status": null, + "results": [ + [ + 17, + "Unknown", + "localhost:8000", + null, + null, + [], + [], + [ + "http://localhost:8000/project/1/web/session-attribution-debugger", + "http://localhost:8000/project/1/web/session-attribution-explorer", + "http://localhost:8000/project/1/insights/new", + "http://localhost:8000/project/1/web/session-attribution-explorer?filters=%5B%7B%22key%22%3A%22%24entry_pathname%22%2C%22value%22%3A%5B%22%2Ffiles%2F%22%5D%2C%22operator%22%3A%22exact%22%2C%22type%22%3A%22session%22%7D%5D", + "http://localhost:8000/login?next=/project/1/web", + "http://localhost:8000/project/1/web/session-attribution-explorer?properties=%5B%7B%22key%22%3A%22%24channel_type%22%2C%22value%22%3A%5B%22Referral%22%5D%2C%22operator%22%3A%22exact%22%2C%22type%22%3A%22session%22%7D%5D&dateRange=%7B%22date_from%22%3A%22dStart%22%7D", + "http://localhost:8000/project/1/data-management/events" + ] + ], + [ + 6, + "Direct", + "$direct", + null, + null, + [], + [], + [ + "http://localhost:8000/", + "http://localhost:8000/login?next=/", + "http://localhost:8000/project/1/web/session-attribution-explorer?dateRange=%7B%22date_from%22%3A%22-180d%22%7D" + ] + ] + ], + "timezone": "UTC", + "timings": [ + { + "k": "./session_attribution_query_runner", + "t": 0.00018341699615120888 + }, + { + "k": "./parse_select_cpp", + "t": 0.013566417008405551 + }, + { + "k": "./query", + "t": 6.879199645482004e-5 + }, + { + "k": "./replace_placeholders", + "t": 0.0007592500041937456 + }, + { + "k": "./max_limit", + "t": 1.8708000425249338e-5 + }, + { + "k": "./hogql/prepare_ast/clone", + "t": 0.00010145900887437165 + }, + { + "k": "./hogql/prepare_ast/create_hogql_database", + "t": 0.033654708007816225 + }, + { + "k": "./hogql/prepare_ast/resolve_types", + "t": 0.0007856669981265441 + }, + { + "k": "./hogql/prepare_ast", + "t": 0.034647958003915846 + }, + { + "k": "./hogql/print_ast/printer", + "t": 0.0004404580104164779 + }, + { + "k": "./hogql/print_ast", + "t": 0.0004706659965449944 + }, + { + "k": "./hogql", + "t": 0.035132000004523434 + }, + { + "k": "./print_ast/create_hogql_database", + "t": 0.03552412499266211 + }, + { + "k": "./print_ast/resolve_types", + "t": 0.0016556659975321963 + }, + { + "k": "./print_ast/resolve_property_types", + "t": 0.0003872499946737662 + }, + { + "k": "./print_ast/resolve_lazy_tables", + "t": 0.031225833008647896 + }, + { + "k": "./print_ast/swap_properties", + "t": 0.0007154159975470975 + }, + { + "k": "./print_ast/printer", + "t": 0.0016986249975161627 + }, + { + "k": "./print_ast", + "t": 0.07132712499878835 + }, + { + "k": "./clickhouse_execute", + "t": 0.10960962499666493 + }, + { + "k": ".", + "t": 0.2371137909940444 + } + ], + "types": [ + ["context.columns.count", "UInt64"], + ["context.columns.channel_type", "String"], + ["context.columns.referring_domain", "Nullable(String)"], + ["context.columns.utm_source", "Nullable(String)"], + ["context.columns.utm_medium", "Nullable(String)"], + ["context.columns.utm_campaign", "Array(String)"], + ["context.columns.ad_ids", "Array(String)"], + ["context.columns.example_entry_urls", "Array(String)"] + ] + }, + "start_time": "2024-07-10T10:06:19.839Z", + "task_id": null, + "team_id": 1 + } +} diff --git a/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorer.stories.tsx b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorer.stories.tsx new file mode 100644 index 0000000000000..b98489824fd5f --- /dev/null +++ b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorer.stories.tsx @@ -0,0 +1,44 @@ +import { Meta, StoryFn } from '@storybook/react' +import { router } from 'kea-router' +import { useEffect } from 'react' +import { App } from 'scenes/App' +import { urls } from 'scenes/urls' + +import { mswDecorator } from '~/mocks/browser' + +const meta: Meta = { + title: 'Scenes-App/SessionAttributionExplorer', + parameters: { + layout: 'fullscreen', + viewMode: 'story', + mockDate: '2022-03-11', + }, + decorators: [ + mswDecorator({ + get: { + '/api/projects/:team_id/query/:id/': async (_, res, ctx) => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return res(ctx.json(require('./__mocks__/sessionAttributionQueryStatus.json'))) + }, + }, + post: { + '/api/projects/:team_id/query/': async (_, res, ctx) => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return res(ctx.json(require('./__mocks__/sessionAttributionQuery.json'))) + }, + }, + }), + ], +} +export default meta + +// Session Attribution Explorer +export const SessionAttributionExplorer: StoryFn = () => { + useEffect(() => { + router.actions.push(urls.sessionAttributionExplorer()) + }, []) + return +} +SessionAttributionExplorer.parameters = { + testOptions: { waitForSelector: '.LemonTable__boundary' }, +} diff --git a/frontend/src/scenes/web-analytics/SessionDebugger/sessionAttributionExplorerLogic.ts b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorerLogic.ts similarity index 66% rename from frontend/src/scenes/web-analytics/SessionDebugger/sessionAttributionExplorerLogic.ts rename to frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorerLogic.ts index 765cbd35cc89a..8465e0c32b871 100644 --- a/frontend/src/scenes/web-analytics/SessionDebugger/sessionAttributionExplorerLogic.ts +++ b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorerLogic.ts @@ -4,13 +4,25 @@ import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { urls } from 'scenes/urls' -import { DataTableNode, DateRange, HogQLQuery, NodeKind } from '~/queries/schema' +import { + DataTableNode, + DateRange, + NodeKind, + SessionAttributionExplorerQuery, + SessionAttributionGroupBy, +} from '~/queries/schema' import { isSessionPropertyFilters } from '~/queries/schema-guards' import { SessionPropertyFilter } from '~/types' import type { sessionAttributionExplorerLogicType } from './sessionAttributionExplorerLogicType' export const initialProperties = [] as SessionPropertyFilter[] +export const initialGroupBy = [ + SessionAttributionGroupBy.Source, + SessionAttributionGroupBy.Medium, + SessionAttributionGroupBy.ChannelType, + SessionAttributionGroupBy.ReferringDomain, +] export const defaultDateRange: DateRange = { date_from: '-7d', date_to: 'now' } export const sessionAttributionExplorerLogic = kea([ path(['scenes', 'webAnalytics', 'sessionDebuggerLogic']), @@ -19,10 +31,16 @@ export const sessionAttributionExplorerLogic = kea ({ properties }), - setDateRange: (dateRange: DateRange) => ({ dateRange }), + setDateRange: (dateRange: DateRange | null) => ({ dateRange }), setStateFromUrl: (state: { properties: SessionPropertyFilter[]; dateRange: DateRange | null }) => ({ state, }), + enableGroupBy: (groupBy: SessionAttributionGroupBy) => { + return { groupBy } + }, + disableGroupBy: (groupBy: SessionAttributionGroupBy) => { + return { groupBy } + }, }), reducers({ properties: [ @@ -39,44 +57,32 @@ export const sessionAttributionExplorerLogic = kea state.dateRange, }, ], + groupBy: [ + initialGroupBy, + { + enableGroupBy: (state, { groupBy }) => { + return Array.from(new Set([...state, groupBy])) + }, + disableGroupBy: (state, { groupBy }) => { + return state.filter((item) => item !== groupBy) + }, + }, + ], }), selectors({ query: [ - (s) => [s.properties, s.dateRange], - (properties: SessionPropertyFilter[], dateRange): DataTableNode => { - const source: HogQLQuery = { - kind: NodeKind.HogQLQuery, - filters: { - properties, - dateRange: dateRange ?? defaultDateRange, - }, - query: ` -SELECT - count() as "context.columns.count", - "$channel_type" as "context.columns.channel_type", - "$entry_referring_domain" as "context.columns.referring_domain", - "$entry_utm_source" as "context.columns.utm_source", - "$entry_utm_medium" as "context.columns.utm_medium", - "$entry_utm_campaign" as "context.columns.utm_campaign", - nullIf(arrayStringConcat([ - if(isNotNull($entry_gclid), 'glcid', NULL), - if(isNotNull($entry_gad_source), 'gad_source', NULL) - -- add more here if we add more ad ids - ], ','), '') as "context.columns.has_ad_id", - topK(10)($entry_current_url) as "context.columns.example_entry_urls" -FROM sessions -WHERE {filters} -GROUP BY - "context.columns.referring_domain", - "context.columns.utm_source", - "context.columns.utm_medium", - "context.columns.utm_campaign", - "context.columns.has_ad_id", - "context.columns.channel_type" -ORDER BY - "context.columns.count" DESC -`, + (s) => [s.properties, s.dateRange, s.groupBy], + (properties: SessionPropertyFilter[], dateRange, groupBy): DataTableNode => { + const filters = { + properties, + dateRange: dateRange ?? defaultDateRange, + } + const source: SessionAttributionExplorerQuery = { + kind: NodeKind.SessionAttributionExplorerQuery, + groupBy: groupBy, + filters: filters, } + return { kind: NodeKind.DataTableNode, source: source, diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 9096e172150a4..d8a29799aaf2e 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -28,7 +28,6 @@ import { LogLevel } from 'rrweb' import { BehavioralFilterKey, BehavioralFilterType } from 'scenes/cohorts/CohortFilters/types' import { AggregationAxisFormat } from 'scenes/insights/aggregationAxisFormat' import { JSONContent } from 'scenes/notebooks/Notebook/utils' -import { PipelineLogLevel } from 'scenes/pipeline/pipelineNodeLogsLogic' import { Scene } from 'scenes/sceneTypes' import { QueryContext } from '~/queries/types' @@ -1941,21 +1940,25 @@ export interface PluginErrorType { event?: Record } +export type LogEntryLevel = 'DEBUG' | 'LOG' | 'INFO' | 'WARN' | 'WARNING' | 'ERROR' + // The general log entry format that eventually everything should match export type LogEntry = { log_source_id: string instance_id: string timestamp: string - level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' + level: LogEntryLevel message: string } -export enum PluginLogEntryType { - Debug = 'DEBUG', - Log = 'LOG', - Info = 'INFO', - Warn = 'WARN', - Error = 'ERROR', +export type LogEntryRequestParams = { + limit?: number + after?: string + before?: string + // Comma separated list of log levels + level?: string + search?: string + instance_id?: string } export interface PluginLogEntry { @@ -1964,21 +1967,13 @@ export interface PluginLogEntry { plugin_id: number plugin_config_id: number timestamp: string - type: PluginLogEntryType + source: string + type: LogEntryLevel is_system: boolean message: string instance_id: string } -export interface BatchExportLogEntry { - team_id: number - batch_export_id: number - run_id: number - timestamp: string - level: PipelineLogLevel - message: string -} - export enum AnnotationScope { Insight = 'dashboard_item', Project = 'project', @@ -3868,6 +3863,16 @@ export interface ExternalDataSourceSchema extends SimpleExternalDataSourceSchema status?: string incremental_field: string | null incremental_field_type: string | null + sync_frequency: DataWarehouseSyncInterval +} + +export interface ExternalDataJob { + id: string + created_at: string + status: 'Running' | 'Failed' | 'Completed' | 'Cancelled' + schema: SimpleExternalDataSourceSchema + rows_synced: number + latest_error: string } export interface SimpleDataWarehouseTable { diff --git a/hogql_parser/HogQLParser.cpp b/hogql_parser/HogQLParser.cpp index 4a77b7b33b70d..17f52f0d28c27 100644 --- a/hogql_parser/HogQLParser.cpp +++ b/hogql_parser/HogQLParser.cpp @@ -53,12 +53,12 @@ void hogqlparserParserInitialize() { auto staticData = std::make_unique( std::vector{ "program", "declaration", "expression", "varDecl", "identifierList", - "statement", "returnStmt", "ifStmt", "whileStmt", "forStmt", "funcStmt", - "varAssignment", "exprStmt", "emptyStmt", "block", "kvPair", "kvPairList", - "select", "selectUnionStmt", "selectStmtWithParens", "selectStmt", - "withClause", "topClause", "fromClause", "arrayJoinClause", "windowClause", - "prewhereClause", "whereClause", "groupByClause", "havingClause", - "orderByClause", "projectionOrderByClause", "limitAndOffsetClause", + "statement", "returnStmt", "ifStmt", "whileStmt", "forStmt", "forInStmt", + "funcStmt", "varAssignment", "exprStmt", "emptyStmt", "block", "kvPair", + "kvPairList", "select", "selectUnionStmt", "selectStmtWithParens", + "selectStmt", "withClause", "topClause", "fromClause", "arrayJoinClause", + "windowClause", "prewhereClause", "whereClause", "groupByClause", + "havingClause", "orderByClause", "projectionOrderByClause", "limitAndOffsetClause", "offsetOnlyClause", "settingsClause", "joinExpr", "joinOp", "joinOpCross", "joinConstraintClause", "sampleClause", "orderExprList", "orderExpr", "ratioExpr", "settingExprList", "settingExpr", "windowExpr", "winPartitionByClause", @@ -114,7 +114,7 @@ void hogqlparserParserInitialize() { } ); static const int32_t serializedATNSegment[] = { - 4,1,155,1249,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6, + 4,1,155,1267,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6, 2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14, 7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21, 7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28, @@ -125,459 +125,466 @@ void hogqlparserParserInitialize() { 7,56,2,57,7,57,2,58,7,58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63, 7,63,2,64,7,64,2,65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70, 7,70,2,71,7,71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77, - 7,77,2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,1,0, - 5,0,170,8,0,10,0,12,0,173,9,0,1,0,1,0,1,1,1,1,3,1,179,8,1,1,2,1,2,1,3, - 1,3,1,3,1,3,1,3,3,3,188,8,3,1,4,1,4,1,4,5,4,193,8,4,10,4,12,4,196,9,4, - 1,4,3,4,199,8,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,210,8,5,1,6,1, - 6,3,6,214,8,6,1,6,3,6,217,8,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,3,7,226,8,7, - 1,8,1,8,1,8,1,8,1,8,1,8,3,8,234,8,8,1,9,1,9,1,9,1,9,1,9,3,9,241,8,9,1, - 9,1,9,3,9,245,8,9,1,9,1,9,1,9,1,9,3,9,251,8,9,1,9,1,9,1,9,3,9,256,8,9, - 1,10,1,10,1,10,1,10,3,10,262,8,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11, - 1,11,1,12,1,12,3,12,274,8,12,1,13,1,13,1,14,1,14,5,14,280,8,14,10,14, - 12,14,283,9,14,1,14,1,14,1,15,1,15,1,15,1,15,1,16,1,16,1,16,5,16,294, - 8,16,10,16,12,16,297,9,16,1,16,3,16,300,8,16,1,17,1,17,1,17,3,17,305, - 8,17,1,17,1,17,1,18,1,18,1,18,1,18,5,18,313,8,18,10,18,12,18,316,9,18, - 1,19,1,19,1,19,1,19,1,19,1,19,3,19,324,8,19,1,20,3,20,327,8,20,1,20,1, - 20,3,20,331,8,20,1,20,3,20,334,8,20,1,20,1,20,3,20,338,8,20,1,20,3,20, - 341,8,20,1,20,3,20,344,8,20,1,20,3,20,347,8,20,1,20,3,20,350,8,20,1,20, - 1,20,3,20,354,8,20,1,20,1,20,3,20,358,8,20,1,20,3,20,361,8,20,1,20,3, - 20,364,8,20,1,20,3,20,367,8,20,1,20,1,20,3,20,371,8,20,1,20,3,20,374, - 8,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22,3,22,383,8,22,1,23,1,23,1,23, - 1,24,3,24,389,8,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25, - 1,25,1,25,1,25,1,25,1,25,1,25,1,25,5,25,408,8,25,10,25,12,25,411,9,25, - 1,26,1,26,1,26,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28, - 3,28,427,8,28,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,31,1,31,1,31,1,31, - 1,32,1,32,1,32,1,32,3,32,444,8,32,1,32,1,32,1,32,1,32,3,32,450,8,32,1, - 32,1,32,1,32,1,32,3,32,456,8,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, - 32,1,32,3,32,467,8,32,3,32,469,8,32,1,33,1,33,1,33,1,34,1,34,1,34,1,35, - 1,35,1,35,3,35,480,8,35,1,35,3,35,483,8,35,1,35,1,35,1,35,1,35,3,35,489, - 8,35,1,35,1,35,1,35,1,35,1,35,1,35,3,35,497,8,35,1,35,1,35,1,35,1,35, - 5,35,503,8,35,10,35,12,35,506,9,35,1,36,3,36,509,8,36,1,36,1,36,1,36, - 3,36,514,8,36,1,36,3,36,517,8,36,1,36,3,36,520,8,36,1,36,1,36,3,36,524, - 8,36,1,36,1,36,3,36,528,8,36,1,36,3,36,531,8,36,3,36,533,8,36,1,36,3, - 36,536,8,36,1,36,1,36,3,36,540,8,36,1,36,1,36,3,36,544,8,36,1,36,3,36, - 547,8,36,3,36,549,8,36,3,36,551,8,36,1,37,1,37,1,37,3,37,556,8,37,1,38, - 1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,3,38,567,8,38,1,39,1,39,1,39, - 1,39,3,39,573,8,39,1,40,1,40,1,40,5,40,578,8,40,10,40,12,40,581,9,40, - 1,41,1,41,3,41,585,8,41,1,41,1,41,3,41,589,8,41,1,41,1,41,3,41,593,8, - 41,1,42,1,42,1,42,1,42,3,42,599,8,42,3,42,601,8,42,1,43,1,43,1,43,5,43, - 606,8,43,10,43,12,43,609,9,43,1,44,1,44,1,44,1,44,1,45,3,45,616,8,45, - 1,45,3,45,619,8,45,1,45,3,45,622,8,45,1,46,1,46,1,46,1,46,1,47,1,47,1, - 47,1,47,1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,3,49,641,8,49,1, - 50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,3,50,655,8, - 50,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52,5,52,669, - 8,52,10,52,12,52,672,9,52,1,52,3,52,675,8,52,1,52,1,52,1,52,1,52,1,52, - 1,52,1,52,5,52,684,8,52,10,52,12,52,687,9,52,1,52,3,52,690,8,52,1,52, - 1,52,1,52,1,52,1,52,1,52,1,52,5,52,699,8,52,10,52,12,52,702,9,52,1,52, - 3,52,705,8,52,1,52,1,52,1,52,1,52,1,52,3,52,712,8,52,1,52,1,52,3,52,716, - 8,52,1,53,1,53,1,53,5,53,721,8,53,10,53,12,53,724,9,53,1,53,3,53,727, - 8,53,1,54,1,54,1,54,3,54,732,8,54,1,54,1,54,1,54,1,54,1,54,4,54,739,8, - 54,11,54,12,54,740,1,54,1,54,3,54,745,8,54,1,54,1,54,1,54,1,54,1,54,1, - 54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1, - 54,1,54,1,54,3,54,769,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1, - 54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,786,8,54,1,54,1,54,1,54,1,54,3, - 54,792,8,54,1,54,3,54,795,8,54,1,54,3,54,798,8,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,1,54,3,54,808,8,54,1,54,1,54,1,54,1,54,3,54,814,8,54,1, - 54,3,54,817,8,54,1,54,3,54,820,8,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54, - 828,8,54,1,54,3,54,831,8,54,1,54,1,54,3,54,835,8,54,1,54,3,54,838,8,54, - 1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,852, - 8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,3,54,869,8,54,1,54,1,54,1,54,3,54,874,8,54,1,54,1,54,3,54,878, - 8,54,1,54,1,54,1,54,1,54,3,54,884,8,54,1,54,1,54,1,54,1,54,1,54,3,54, - 891,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,903,8, - 54,1,54,1,54,3,54,907,8,54,1,54,3,54,910,8,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,3,54,919,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,3,54,933,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,3,54,972,8,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,980,8,54,5,54,982, - 8,54,10,54,12,54,985,9,54,1,55,1,55,1,55,5,55,990,8,55,10,55,12,55,993, - 9,55,1,55,3,55,996,8,55,1,56,1,56,3,56,1000,8,56,1,57,1,57,1,57,1,57, - 5,57,1006,8,57,10,57,12,57,1009,9,57,1,57,3,57,1012,8,57,1,57,1,57,1, - 57,1,57,1,57,5,57,1019,8,57,10,57,12,57,1022,9,57,1,57,3,57,1025,8,57, - 3,57,1027,8,57,1,57,1,57,1,57,1,58,1,58,1,58,5,58,1035,8,58,10,58,12, - 58,1038,9,58,1,58,1,58,1,58,1,58,1,58,1,58,5,58,1046,8,58,10,58,12,58, - 1049,9,58,1,58,1,58,3,58,1053,8,58,1,58,1,58,1,58,1,58,1,58,3,58,1060, - 8,58,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1073, - 8,59,1,60,1,60,1,60,5,60,1078,8,60,10,60,12,60,1081,9,60,1,60,3,60,1084, - 8,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,3,61,1096,8,61, - 1,62,1,62,1,62,1,62,3,62,1102,8,62,1,62,3,62,1105,8,62,1,63,1,63,1,63, - 5,63,1110,8,63,10,63,12,63,1113,9,63,1,64,1,64,1,64,1,64,1,64,1,64,1, - 64,1,64,1,64,3,64,1124,8,64,1,64,1,64,1,64,1,64,3,64,1130,8,64,5,64,1132, - 8,64,10,64,12,64,1135,9,64,1,65,1,65,1,65,3,65,1140,8,65,1,65,1,65,1, - 66,1,66,1,66,3,66,1147,8,66,1,66,1,66,1,67,1,67,1,67,5,67,1154,8,67,10, - 67,12,67,1157,9,67,1,67,3,67,1160,8,67,1,68,1,68,1,69,1,69,1,69,1,69, - 1,69,1,69,3,69,1170,8,69,3,69,1172,8,69,1,70,3,70,1175,8,70,1,70,1,70, - 1,70,1,70,1,70,1,70,3,70,1183,8,70,1,71,1,71,1,71,3,71,1188,8,71,1,72, - 1,72,1,73,1,73,1,74,1,74,1,75,1,75,3,75,1198,8,75,1,76,1,76,1,76,3,76, - 1203,8,76,1,77,1,77,1,77,1,77,1,78,1,78,1,78,1,78,1,79,1,79,3,79,1215, - 8,79,1,80,1,80,5,80,1219,8,80,10,80,12,80,1222,9,80,1,80,1,80,1,81,1, - 81,1,81,1,81,1,81,3,81,1231,8,81,1,82,1,82,5,82,1235,8,82,10,82,12,82, - 1238,9,82,1,82,1,82,1,83,1,83,1,83,1,83,1,83,3,83,1247,8,83,1,83,0,3, - 70,108,128,84,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38, - 40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84, - 86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122, - 124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158, - 160,162,164,166,0,16,2,0,17,17,72,72,2,0,42,42,49,49,3,0,1,1,4,4,8,8, - 4,0,1,1,3,4,8,8,78,78,2,0,49,49,71,71,2,0,1,1,4,4,2,0,7,7,21,22,2,0,28, - 28,47,47,2,0,69,69,74,74,3,0,10,10,48,48,87,87,2,0,39,39,51,51,1,0,103, - 104,2,0,114,114,135,135,7,0,20,20,36,36,53,54,68,68,76,76,93,93,99,99, - 12,0,1,19,21,28,30,35,37,40,42,49,51,52,56,56,58,67,69,75,77,92,94,95, - 97,98,4,0,19,19,28,28,37,37,46,46,1409,0,171,1,0,0,0,2,178,1,0,0,0,4, - 180,1,0,0,0,6,182,1,0,0,0,8,189,1,0,0,0,10,209,1,0,0,0,12,211,1,0,0,0, - 14,218,1,0,0,0,16,227,1,0,0,0,18,235,1,0,0,0,20,257,1,0,0,0,22,266,1, - 0,0,0,24,271,1,0,0,0,26,275,1,0,0,0,28,277,1,0,0,0,30,286,1,0,0,0,32, - 290,1,0,0,0,34,304,1,0,0,0,36,308,1,0,0,0,38,323,1,0,0,0,40,326,1,0,0, - 0,42,375,1,0,0,0,44,378,1,0,0,0,46,384,1,0,0,0,48,388,1,0,0,0,50,394, - 1,0,0,0,52,412,1,0,0,0,54,415,1,0,0,0,56,418,1,0,0,0,58,428,1,0,0,0,60, - 431,1,0,0,0,62,435,1,0,0,0,64,468,1,0,0,0,66,470,1,0,0,0,68,473,1,0,0, - 0,70,488,1,0,0,0,72,550,1,0,0,0,74,555,1,0,0,0,76,566,1,0,0,0,78,568, - 1,0,0,0,80,574,1,0,0,0,82,582,1,0,0,0,84,600,1,0,0,0,86,602,1,0,0,0,88, - 610,1,0,0,0,90,615,1,0,0,0,92,623,1,0,0,0,94,627,1,0,0,0,96,631,1,0,0, - 0,98,640,1,0,0,0,100,654,1,0,0,0,102,656,1,0,0,0,104,715,1,0,0,0,106, - 717,1,0,0,0,108,877,1,0,0,0,110,986,1,0,0,0,112,999,1,0,0,0,114,1026, - 1,0,0,0,116,1059,1,0,0,0,118,1072,1,0,0,0,120,1074,1,0,0,0,122,1095,1, - 0,0,0,124,1104,1,0,0,0,126,1106,1,0,0,0,128,1123,1,0,0,0,130,1136,1,0, - 0,0,132,1146,1,0,0,0,134,1150,1,0,0,0,136,1161,1,0,0,0,138,1171,1,0,0, - 0,140,1174,1,0,0,0,142,1187,1,0,0,0,144,1189,1,0,0,0,146,1191,1,0,0,0, - 148,1193,1,0,0,0,150,1197,1,0,0,0,152,1202,1,0,0,0,154,1204,1,0,0,0,156, - 1208,1,0,0,0,158,1214,1,0,0,0,160,1216,1,0,0,0,162,1230,1,0,0,0,164,1232, - 1,0,0,0,166,1246,1,0,0,0,168,170,3,2,1,0,169,168,1,0,0,0,170,173,1,0, - 0,0,171,169,1,0,0,0,171,172,1,0,0,0,172,174,1,0,0,0,173,171,1,0,0,0,174, - 175,5,0,0,1,175,1,1,0,0,0,176,179,3,6,3,0,177,179,3,10,5,0,178,176,1, - 0,0,0,178,177,1,0,0,0,179,3,1,0,0,0,180,181,3,108,54,0,181,5,1,0,0,0, - 182,183,5,50,0,0,183,187,3,152,76,0,184,185,5,111,0,0,185,186,5,118,0, - 0,186,188,3,4,2,0,187,184,1,0,0,0,187,188,1,0,0,0,188,7,1,0,0,0,189,194, - 3,152,76,0,190,191,5,112,0,0,191,193,3,152,76,0,192,190,1,0,0,0,193,196, - 1,0,0,0,194,192,1,0,0,0,194,195,1,0,0,0,195,198,1,0,0,0,196,194,1,0,0, - 0,197,199,5,112,0,0,198,197,1,0,0,0,198,199,1,0,0,0,199,9,1,0,0,0,200, - 210,3,12,6,0,201,210,3,14,7,0,202,210,3,16,8,0,203,210,3,18,9,0,204,210, - 3,20,10,0,205,210,3,22,11,0,206,210,3,28,14,0,207,210,3,24,12,0,208,210, - 3,26,13,0,209,200,1,0,0,0,209,201,1,0,0,0,209,202,1,0,0,0,209,203,1,0, - 0,0,209,204,1,0,0,0,209,205,1,0,0,0,209,206,1,0,0,0,209,207,1,0,0,0,209, - 208,1,0,0,0,210,11,1,0,0,0,211,213,5,70,0,0,212,214,3,4,2,0,213,212,1, - 0,0,0,213,214,1,0,0,0,214,216,1,0,0,0,215,217,5,146,0,0,216,215,1,0,0, - 0,216,217,1,0,0,0,217,13,1,0,0,0,218,219,5,38,0,0,219,220,5,126,0,0,220, - 221,3,4,2,0,221,222,5,145,0,0,222,225,3,10,5,0,223,224,5,24,0,0,224,226, - 3,10,5,0,225,223,1,0,0,0,225,226,1,0,0,0,226,15,1,0,0,0,227,228,5,96, - 0,0,228,229,5,126,0,0,229,230,3,4,2,0,230,231,5,145,0,0,231,233,3,10, - 5,0,232,234,5,146,0,0,233,232,1,0,0,0,233,234,1,0,0,0,234,17,1,0,0,0, - 235,236,5,31,0,0,236,240,5,126,0,0,237,241,3,6,3,0,238,241,3,22,11,0, - 239,241,3,4,2,0,240,237,1,0,0,0,240,238,1,0,0,0,240,239,1,0,0,0,240,241, - 1,0,0,0,241,242,1,0,0,0,242,244,5,146,0,0,243,245,3,4,2,0,244,243,1,0, - 0,0,244,245,1,0,0,0,245,246,1,0,0,0,246,250,5,146,0,0,247,251,3,6,3,0, - 248,251,3,22,11,0,249,251,3,4,2,0,250,247,1,0,0,0,250,248,1,0,0,0,250, - 249,1,0,0,0,250,251,1,0,0,0,251,252,1,0,0,0,252,253,5,145,0,0,253,255, - 3,10,5,0,254,256,5,146,0,0,255,254,1,0,0,0,255,256,1,0,0,0,256,19,1,0, - 0,0,257,258,5,29,0,0,258,259,3,152,76,0,259,261,5,126,0,0,260,262,3,8, - 4,0,261,260,1,0,0,0,261,262,1,0,0,0,262,263,1,0,0,0,263,264,5,145,0,0, - 264,265,3,28,14,0,265,21,1,0,0,0,266,267,3,4,2,0,267,268,5,111,0,0,268, - 269,5,118,0,0,269,270,3,4,2,0,270,23,1,0,0,0,271,273,3,4,2,0,272,274, - 5,146,0,0,273,272,1,0,0,0,273,274,1,0,0,0,274,25,1,0,0,0,275,276,5,146, - 0,0,276,27,1,0,0,0,277,281,5,124,0,0,278,280,3,2,1,0,279,278,1,0,0,0, - 280,283,1,0,0,0,281,279,1,0,0,0,281,282,1,0,0,0,282,284,1,0,0,0,283,281, - 1,0,0,0,284,285,5,143,0,0,285,29,1,0,0,0,286,287,3,4,2,0,287,288,5,111, - 0,0,288,289,3,4,2,0,289,31,1,0,0,0,290,295,3,30,15,0,291,292,5,112,0, - 0,292,294,3,30,15,0,293,291,1,0,0,0,294,297,1,0,0,0,295,293,1,0,0,0,295, - 296,1,0,0,0,296,299,1,0,0,0,297,295,1,0,0,0,298,300,5,112,0,0,299,298, - 1,0,0,0,299,300,1,0,0,0,300,33,1,0,0,0,301,305,3,36,18,0,302,305,3,40, - 20,0,303,305,3,116,58,0,304,301,1,0,0,0,304,302,1,0,0,0,304,303,1,0,0, - 0,305,306,1,0,0,0,306,307,5,0,0,1,307,35,1,0,0,0,308,314,3,38,19,0,309, - 310,5,91,0,0,310,311,5,1,0,0,311,313,3,38,19,0,312,309,1,0,0,0,313,316, - 1,0,0,0,314,312,1,0,0,0,314,315,1,0,0,0,315,37,1,0,0,0,316,314,1,0,0, - 0,317,324,3,40,20,0,318,319,5,126,0,0,319,320,3,36,18,0,320,321,5,145, - 0,0,321,324,1,0,0,0,322,324,3,156,78,0,323,317,1,0,0,0,323,318,1,0,0, - 0,323,322,1,0,0,0,324,39,1,0,0,0,325,327,3,42,21,0,326,325,1,0,0,0,326, - 327,1,0,0,0,327,328,1,0,0,0,328,330,5,77,0,0,329,331,5,23,0,0,330,329, - 1,0,0,0,330,331,1,0,0,0,331,333,1,0,0,0,332,334,3,44,22,0,333,332,1,0, - 0,0,333,334,1,0,0,0,334,335,1,0,0,0,335,337,3,106,53,0,336,338,3,46,23, - 0,337,336,1,0,0,0,337,338,1,0,0,0,338,340,1,0,0,0,339,341,3,48,24,0,340, - 339,1,0,0,0,340,341,1,0,0,0,341,343,1,0,0,0,342,344,3,52,26,0,343,342, - 1,0,0,0,343,344,1,0,0,0,344,346,1,0,0,0,345,347,3,54,27,0,346,345,1,0, - 0,0,346,347,1,0,0,0,347,349,1,0,0,0,348,350,3,56,28,0,349,348,1,0,0,0, - 349,350,1,0,0,0,350,353,1,0,0,0,351,352,5,98,0,0,352,354,7,0,0,0,353, - 351,1,0,0,0,353,354,1,0,0,0,354,357,1,0,0,0,355,356,5,98,0,0,356,358, - 5,86,0,0,357,355,1,0,0,0,357,358,1,0,0,0,358,360,1,0,0,0,359,361,3,58, - 29,0,360,359,1,0,0,0,360,361,1,0,0,0,361,363,1,0,0,0,362,364,3,50,25, - 0,363,362,1,0,0,0,363,364,1,0,0,0,364,366,1,0,0,0,365,367,3,60,30,0,366, - 365,1,0,0,0,366,367,1,0,0,0,367,370,1,0,0,0,368,371,3,64,32,0,369,371, - 3,66,33,0,370,368,1,0,0,0,370,369,1,0,0,0,370,371,1,0,0,0,371,373,1,0, - 0,0,372,374,3,68,34,0,373,372,1,0,0,0,373,374,1,0,0,0,374,41,1,0,0,0, - 375,376,5,98,0,0,376,377,3,120,60,0,377,43,1,0,0,0,378,379,5,85,0,0,379, - 382,5,104,0,0,380,381,5,98,0,0,381,383,5,82,0,0,382,380,1,0,0,0,382,383, - 1,0,0,0,383,45,1,0,0,0,384,385,5,32,0,0,385,386,3,70,35,0,386,47,1,0, - 0,0,387,389,7,1,0,0,388,387,1,0,0,0,388,389,1,0,0,0,389,390,1,0,0,0,390, - 391,5,5,0,0,391,392,5,45,0,0,392,393,3,106,53,0,393,49,1,0,0,0,394,395, - 5,97,0,0,395,396,3,152,76,0,396,397,5,6,0,0,397,398,5,126,0,0,398,399, - 3,90,45,0,399,409,5,145,0,0,400,401,5,112,0,0,401,402,3,152,76,0,402, - 403,5,6,0,0,403,404,5,126,0,0,404,405,3,90,45,0,405,406,5,145,0,0,406, - 408,1,0,0,0,407,400,1,0,0,0,408,411,1,0,0,0,409,407,1,0,0,0,409,410,1, - 0,0,0,410,51,1,0,0,0,411,409,1,0,0,0,412,413,5,67,0,0,413,414,3,108,54, - 0,414,53,1,0,0,0,415,416,5,95,0,0,416,417,3,108,54,0,417,55,1,0,0,0,418, - 419,5,34,0,0,419,426,5,11,0,0,420,421,7,0,0,0,421,422,5,126,0,0,422,423, - 3,106,53,0,423,424,5,145,0,0,424,427,1,0,0,0,425,427,3,106,53,0,426,420, - 1,0,0,0,426,425,1,0,0,0,427,57,1,0,0,0,428,429,5,35,0,0,429,430,3,108, - 54,0,430,59,1,0,0,0,431,432,5,62,0,0,432,433,5,11,0,0,433,434,3,80,40, - 0,434,61,1,0,0,0,435,436,5,62,0,0,436,437,5,11,0,0,437,438,3,106,53,0, - 438,63,1,0,0,0,439,440,5,52,0,0,440,443,3,108,54,0,441,442,5,112,0,0, - 442,444,3,108,54,0,443,441,1,0,0,0,443,444,1,0,0,0,444,449,1,0,0,0,445, - 446,5,98,0,0,446,450,5,82,0,0,447,448,5,11,0,0,448,450,3,106,53,0,449, - 445,1,0,0,0,449,447,1,0,0,0,449,450,1,0,0,0,450,469,1,0,0,0,451,452,5, - 52,0,0,452,455,3,108,54,0,453,454,5,98,0,0,454,456,5,82,0,0,455,453,1, - 0,0,0,455,456,1,0,0,0,456,457,1,0,0,0,457,458,5,59,0,0,458,459,3,108, - 54,0,459,469,1,0,0,0,460,461,5,52,0,0,461,462,3,108,54,0,462,463,5,59, - 0,0,463,466,3,108,54,0,464,465,5,11,0,0,465,467,3,106,53,0,466,464,1, - 0,0,0,466,467,1,0,0,0,467,469,1,0,0,0,468,439,1,0,0,0,468,451,1,0,0,0, - 468,460,1,0,0,0,469,65,1,0,0,0,470,471,5,59,0,0,471,472,3,108,54,0,472, - 67,1,0,0,0,473,474,5,79,0,0,474,475,3,86,43,0,475,69,1,0,0,0,476,477, - 6,35,-1,0,477,479,3,128,64,0,478,480,5,27,0,0,479,478,1,0,0,0,479,480, - 1,0,0,0,480,482,1,0,0,0,481,483,3,78,39,0,482,481,1,0,0,0,482,483,1,0, - 0,0,483,489,1,0,0,0,484,485,5,126,0,0,485,486,3,70,35,0,486,487,5,145, - 0,0,487,489,1,0,0,0,488,476,1,0,0,0,488,484,1,0,0,0,489,504,1,0,0,0,490, - 491,10,3,0,0,491,492,3,74,37,0,492,493,3,70,35,4,493,503,1,0,0,0,494, - 496,10,4,0,0,495,497,3,72,36,0,496,495,1,0,0,0,496,497,1,0,0,0,497,498, - 1,0,0,0,498,499,5,45,0,0,499,500,3,70,35,0,500,501,3,76,38,0,501,503, - 1,0,0,0,502,490,1,0,0,0,502,494,1,0,0,0,503,506,1,0,0,0,504,502,1,0,0, - 0,504,505,1,0,0,0,505,71,1,0,0,0,506,504,1,0,0,0,507,509,7,2,0,0,508, - 507,1,0,0,0,508,509,1,0,0,0,509,510,1,0,0,0,510,517,5,42,0,0,511,513, - 5,42,0,0,512,514,7,2,0,0,513,512,1,0,0,0,513,514,1,0,0,0,514,517,1,0, - 0,0,515,517,7,2,0,0,516,508,1,0,0,0,516,511,1,0,0,0,516,515,1,0,0,0,517, - 551,1,0,0,0,518,520,7,3,0,0,519,518,1,0,0,0,519,520,1,0,0,0,520,521,1, - 0,0,0,521,523,7,4,0,0,522,524,5,63,0,0,523,522,1,0,0,0,523,524,1,0,0, - 0,524,533,1,0,0,0,525,527,7,4,0,0,526,528,5,63,0,0,527,526,1,0,0,0,527, - 528,1,0,0,0,528,530,1,0,0,0,529,531,7,3,0,0,530,529,1,0,0,0,530,531,1, - 0,0,0,531,533,1,0,0,0,532,519,1,0,0,0,532,525,1,0,0,0,533,551,1,0,0,0, - 534,536,7,5,0,0,535,534,1,0,0,0,535,536,1,0,0,0,536,537,1,0,0,0,537,539, - 5,33,0,0,538,540,5,63,0,0,539,538,1,0,0,0,539,540,1,0,0,0,540,549,1,0, - 0,0,541,543,5,33,0,0,542,544,5,63,0,0,543,542,1,0,0,0,543,544,1,0,0,0, - 544,546,1,0,0,0,545,547,7,5,0,0,546,545,1,0,0,0,546,547,1,0,0,0,547,549, - 1,0,0,0,548,535,1,0,0,0,548,541,1,0,0,0,549,551,1,0,0,0,550,516,1,0,0, - 0,550,532,1,0,0,0,550,548,1,0,0,0,551,73,1,0,0,0,552,553,5,16,0,0,553, - 556,5,45,0,0,554,556,5,112,0,0,555,552,1,0,0,0,555,554,1,0,0,0,556,75, - 1,0,0,0,557,558,5,60,0,0,558,567,3,106,53,0,559,560,5,92,0,0,560,561, - 5,126,0,0,561,562,3,106,53,0,562,563,5,145,0,0,563,567,1,0,0,0,564,565, - 5,92,0,0,565,567,3,106,53,0,566,557,1,0,0,0,566,559,1,0,0,0,566,564,1, - 0,0,0,567,77,1,0,0,0,568,569,5,75,0,0,569,572,3,84,42,0,570,571,5,59, - 0,0,571,573,3,84,42,0,572,570,1,0,0,0,572,573,1,0,0,0,573,79,1,0,0,0, - 574,579,3,82,41,0,575,576,5,112,0,0,576,578,3,82,41,0,577,575,1,0,0,0, - 578,581,1,0,0,0,579,577,1,0,0,0,579,580,1,0,0,0,580,81,1,0,0,0,581,579, - 1,0,0,0,582,584,3,108,54,0,583,585,7,6,0,0,584,583,1,0,0,0,584,585,1, - 0,0,0,585,588,1,0,0,0,586,587,5,58,0,0,587,589,7,7,0,0,588,586,1,0,0, - 0,588,589,1,0,0,0,589,592,1,0,0,0,590,591,5,15,0,0,591,593,5,106,0,0, - 592,590,1,0,0,0,592,593,1,0,0,0,593,83,1,0,0,0,594,601,3,156,78,0,595, - 598,3,140,70,0,596,597,5,147,0,0,597,599,3,140,70,0,598,596,1,0,0,0,598, - 599,1,0,0,0,599,601,1,0,0,0,600,594,1,0,0,0,600,595,1,0,0,0,601,85,1, - 0,0,0,602,607,3,88,44,0,603,604,5,112,0,0,604,606,3,88,44,0,605,603,1, - 0,0,0,606,609,1,0,0,0,607,605,1,0,0,0,607,608,1,0,0,0,608,87,1,0,0,0, - 609,607,1,0,0,0,610,611,3,152,76,0,611,612,5,118,0,0,612,613,3,142,71, - 0,613,89,1,0,0,0,614,616,3,92,46,0,615,614,1,0,0,0,615,616,1,0,0,0,616, - 618,1,0,0,0,617,619,3,94,47,0,618,617,1,0,0,0,618,619,1,0,0,0,619,621, - 1,0,0,0,620,622,3,96,48,0,621,620,1,0,0,0,621,622,1,0,0,0,622,91,1,0, - 0,0,623,624,5,65,0,0,624,625,5,11,0,0,625,626,3,106,53,0,626,93,1,0,0, - 0,627,628,5,62,0,0,628,629,5,11,0,0,629,630,3,80,40,0,630,95,1,0,0,0, - 631,632,7,8,0,0,632,633,3,98,49,0,633,97,1,0,0,0,634,641,3,100,50,0,635, - 636,5,9,0,0,636,637,3,100,50,0,637,638,5,2,0,0,638,639,3,100,50,0,639, - 641,1,0,0,0,640,634,1,0,0,0,640,635,1,0,0,0,641,99,1,0,0,0,642,643,5, - 18,0,0,643,655,5,73,0,0,644,645,5,90,0,0,645,655,5,66,0,0,646,647,5,90, - 0,0,647,655,5,30,0,0,648,649,3,140,70,0,649,650,5,66,0,0,650,655,1,0, - 0,0,651,652,3,140,70,0,652,653,5,30,0,0,653,655,1,0,0,0,654,642,1,0,0, - 0,654,644,1,0,0,0,654,646,1,0,0,0,654,648,1,0,0,0,654,651,1,0,0,0,655, - 101,1,0,0,0,656,657,3,108,54,0,657,658,5,0,0,1,658,103,1,0,0,0,659,716, - 3,152,76,0,660,661,3,152,76,0,661,662,5,126,0,0,662,663,3,152,76,0,663, - 670,3,104,52,0,664,665,5,112,0,0,665,666,3,152,76,0,666,667,3,104,52, - 0,667,669,1,0,0,0,668,664,1,0,0,0,669,672,1,0,0,0,670,668,1,0,0,0,670, - 671,1,0,0,0,671,674,1,0,0,0,672,670,1,0,0,0,673,675,5,112,0,0,674,673, - 1,0,0,0,674,675,1,0,0,0,675,676,1,0,0,0,676,677,5,145,0,0,677,716,1,0, - 0,0,678,679,3,152,76,0,679,680,5,126,0,0,680,685,3,154,77,0,681,682,5, - 112,0,0,682,684,3,154,77,0,683,681,1,0,0,0,684,687,1,0,0,0,685,683,1, - 0,0,0,685,686,1,0,0,0,686,689,1,0,0,0,687,685,1,0,0,0,688,690,5,112,0, - 0,689,688,1,0,0,0,689,690,1,0,0,0,690,691,1,0,0,0,691,692,5,145,0,0,692, - 716,1,0,0,0,693,694,3,152,76,0,694,695,5,126,0,0,695,700,3,104,52,0,696, - 697,5,112,0,0,697,699,3,104,52,0,698,696,1,0,0,0,699,702,1,0,0,0,700, - 698,1,0,0,0,700,701,1,0,0,0,701,704,1,0,0,0,702,700,1,0,0,0,703,705,5, - 112,0,0,704,703,1,0,0,0,704,705,1,0,0,0,705,706,1,0,0,0,706,707,5,145, - 0,0,707,716,1,0,0,0,708,709,3,152,76,0,709,711,5,126,0,0,710,712,3,106, - 53,0,711,710,1,0,0,0,711,712,1,0,0,0,712,713,1,0,0,0,713,714,5,145,0, - 0,714,716,1,0,0,0,715,659,1,0,0,0,715,660,1,0,0,0,715,678,1,0,0,0,715, - 693,1,0,0,0,715,708,1,0,0,0,716,105,1,0,0,0,717,722,3,108,54,0,718,719, - 5,112,0,0,719,721,3,108,54,0,720,718,1,0,0,0,721,724,1,0,0,0,722,720, - 1,0,0,0,722,723,1,0,0,0,723,726,1,0,0,0,724,722,1,0,0,0,725,727,5,112, - 0,0,726,725,1,0,0,0,726,727,1,0,0,0,727,107,1,0,0,0,728,729,6,54,-1,0, - 729,731,5,12,0,0,730,732,3,108,54,0,731,730,1,0,0,0,731,732,1,0,0,0,732, - 738,1,0,0,0,733,734,5,94,0,0,734,735,3,108,54,0,735,736,5,81,0,0,736, - 737,3,108,54,0,737,739,1,0,0,0,738,733,1,0,0,0,739,740,1,0,0,0,740,738, - 1,0,0,0,740,741,1,0,0,0,741,744,1,0,0,0,742,743,5,24,0,0,743,745,3,108, - 54,0,744,742,1,0,0,0,744,745,1,0,0,0,745,746,1,0,0,0,746,747,5,25,0,0, - 747,878,1,0,0,0,748,749,5,13,0,0,749,750,5,126,0,0,750,751,3,108,54,0, - 751,752,5,6,0,0,752,753,3,104,52,0,753,754,5,145,0,0,754,878,1,0,0,0, - 755,756,5,19,0,0,756,878,5,106,0,0,757,758,5,43,0,0,758,759,3,108,54, - 0,759,760,3,144,72,0,760,878,1,0,0,0,761,762,5,80,0,0,762,763,5,126,0, - 0,763,764,3,108,54,0,764,765,5,32,0,0,765,768,3,108,54,0,766,767,5,31, - 0,0,767,769,3,108,54,0,768,766,1,0,0,0,768,769,1,0,0,0,769,770,1,0,0, - 0,770,771,5,145,0,0,771,878,1,0,0,0,772,773,5,83,0,0,773,878,5,106,0, - 0,774,775,5,88,0,0,775,776,5,126,0,0,776,777,7,9,0,0,777,778,3,158,79, - 0,778,779,5,32,0,0,779,780,3,108,54,0,780,781,5,145,0,0,781,878,1,0,0, - 0,782,783,3,152,76,0,783,785,5,126,0,0,784,786,3,106,53,0,785,784,1,0, - 0,0,785,786,1,0,0,0,786,787,1,0,0,0,787,788,5,145,0,0,788,797,1,0,0,0, - 789,791,5,126,0,0,790,792,5,23,0,0,791,790,1,0,0,0,791,792,1,0,0,0,792, - 794,1,0,0,0,793,795,3,110,55,0,794,793,1,0,0,0,794,795,1,0,0,0,795,796, - 1,0,0,0,796,798,5,145,0,0,797,789,1,0,0,0,797,798,1,0,0,0,798,799,1,0, - 0,0,799,800,5,64,0,0,800,801,5,126,0,0,801,802,3,90,45,0,802,803,5,145, - 0,0,803,878,1,0,0,0,804,805,3,152,76,0,805,807,5,126,0,0,806,808,3,106, - 53,0,807,806,1,0,0,0,807,808,1,0,0,0,808,809,1,0,0,0,809,810,5,145,0, - 0,810,819,1,0,0,0,811,813,5,126,0,0,812,814,5,23,0,0,813,812,1,0,0,0, - 813,814,1,0,0,0,814,816,1,0,0,0,815,817,3,110,55,0,816,815,1,0,0,0,816, - 817,1,0,0,0,817,818,1,0,0,0,818,820,5,145,0,0,819,811,1,0,0,0,819,820, - 1,0,0,0,820,821,1,0,0,0,821,822,5,64,0,0,822,823,3,152,76,0,823,878,1, - 0,0,0,824,830,3,152,76,0,825,827,5,126,0,0,826,828,3,106,53,0,827,826, - 1,0,0,0,827,828,1,0,0,0,828,829,1,0,0,0,829,831,5,145,0,0,830,825,1,0, - 0,0,830,831,1,0,0,0,831,832,1,0,0,0,832,834,5,126,0,0,833,835,5,23,0, - 0,834,833,1,0,0,0,834,835,1,0,0,0,835,837,1,0,0,0,836,838,3,110,55,0, - 837,836,1,0,0,0,837,838,1,0,0,0,838,839,1,0,0,0,839,840,5,145,0,0,840, - 878,1,0,0,0,841,878,3,116,58,0,842,878,3,160,80,0,843,878,3,142,71,0, - 844,845,5,114,0,0,845,878,3,108,54,19,846,847,5,56,0,0,847,878,3,108, - 54,13,848,849,3,132,66,0,849,850,5,116,0,0,850,852,1,0,0,0,851,848,1, - 0,0,0,851,852,1,0,0,0,852,853,1,0,0,0,853,878,5,108,0,0,854,855,5,126, - 0,0,855,856,3,36,18,0,856,857,5,145,0,0,857,878,1,0,0,0,858,859,5,126, - 0,0,859,860,3,108,54,0,860,861,5,145,0,0,861,878,1,0,0,0,862,863,5,126, - 0,0,863,864,3,106,53,0,864,865,5,145,0,0,865,878,1,0,0,0,866,868,5,125, - 0,0,867,869,3,106,53,0,868,867,1,0,0,0,868,869,1,0,0,0,869,870,1,0,0, - 0,870,878,5,144,0,0,871,873,5,124,0,0,872,874,3,32,16,0,873,872,1,0,0, - 0,873,874,1,0,0,0,874,875,1,0,0,0,875,878,5,143,0,0,876,878,3,124,62, - 0,877,728,1,0,0,0,877,748,1,0,0,0,877,755,1,0,0,0,877,757,1,0,0,0,877, - 761,1,0,0,0,877,772,1,0,0,0,877,774,1,0,0,0,877,782,1,0,0,0,877,804,1, - 0,0,0,877,824,1,0,0,0,877,841,1,0,0,0,877,842,1,0,0,0,877,843,1,0,0,0, - 877,844,1,0,0,0,877,846,1,0,0,0,877,851,1,0,0,0,877,854,1,0,0,0,877,858, - 1,0,0,0,877,862,1,0,0,0,877,866,1,0,0,0,877,871,1,0,0,0,877,876,1,0,0, - 0,878,983,1,0,0,0,879,883,10,18,0,0,880,884,5,108,0,0,881,884,5,147,0, - 0,882,884,5,134,0,0,883,880,1,0,0,0,883,881,1,0,0,0,883,882,1,0,0,0,884, - 885,1,0,0,0,885,982,3,108,54,19,886,890,10,17,0,0,887,891,5,135,0,0,888, - 891,5,114,0,0,889,891,5,113,0,0,890,887,1,0,0,0,890,888,1,0,0,0,890,889, - 1,0,0,0,891,892,1,0,0,0,892,982,3,108,54,18,893,918,10,16,0,0,894,919, - 5,117,0,0,895,919,5,118,0,0,896,919,5,129,0,0,897,919,5,127,0,0,898,919, - 5,128,0,0,899,919,5,119,0,0,900,919,5,120,0,0,901,903,5,56,0,0,902,901, - 1,0,0,0,902,903,1,0,0,0,903,904,1,0,0,0,904,906,5,40,0,0,905,907,5,14, - 0,0,906,905,1,0,0,0,906,907,1,0,0,0,907,919,1,0,0,0,908,910,5,56,0,0, - 909,908,1,0,0,0,909,910,1,0,0,0,910,911,1,0,0,0,911,919,7,10,0,0,912, - 919,5,141,0,0,913,919,5,142,0,0,914,919,5,131,0,0,915,919,5,122,0,0,916, - 919,5,123,0,0,917,919,5,130,0,0,918,894,1,0,0,0,918,895,1,0,0,0,918,896, - 1,0,0,0,918,897,1,0,0,0,918,898,1,0,0,0,918,899,1,0,0,0,918,900,1,0,0, - 0,918,902,1,0,0,0,918,909,1,0,0,0,918,912,1,0,0,0,918,913,1,0,0,0,918, - 914,1,0,0,0,918,915,1,0,0,0,918,916,1,0,0,0,918,917,1,0,0,0,919,920,1, - 0,0,0,920,982,3,108,54,17,921,922,10,14,0,0,922,923,5,133,0,0,923,982, - 3,108,54,15,924,925,10,12,0,0,925,926,5,2,0,0,926,982,3,108,54,13,927, - 928,10,11,0,0,928,929,5,61,0,0,929,982,3,108,54,12,930,932,10,10,0,0, - 931,933,5,56,0,0,932,931,1,0,0,0,932,933,1,0,0,0,933,934,1,0,0,0,934, - 935,5,9,0,0,935,936,3,108,54,0,936,937,5,2,0,0,937,938,3,108,54,11,938, - 982,1,0,0,0,939,940,10,9,0,0,940,941,5,136,0,0,941,942,3,108,54,0,942, - 943,5,111,0,0,943,944,3,108,54,9,944,982,1,0,0,0,945,946,10,25,0,0,946, - 947,5,125,0,0,947,948,3,108,54,0,948,949,5,144,0,0,949,982,1,0,0,0,950, - 951,10,24,0,0,951,952,5,116,0,0,952,982,5,104,0,0,953,954,10,23,0,0,954, - 955,5,116,0,0,955,982,3,152,76,0,956,957,10,22,0,0,957,958,5,132,0,0, - 958,959,5,125,0,0,959,960,3,108,54,0,960,961,5,144,0,0,961,982,1,0,0, - 0,962,963,10,21,0,0,963,964,5,132,0,0,964,982,5,104,0,0,965,966,10,20, - 0,0,966,967,5,132,0,0,967,982,3,152,76,0,968,969,10,15,0,0,969,971,5, - 44,0,0,970,972,5,56,0,0,971,970,1,0,0,0,971,972,1,0,0,0,972,973,1,0,0, - 0,973,982,5,57,0,0,974,979,10,8,0,0,975,976,5,6,0,0,976,980,3,152,76, - 0,977,978,5,6,0,0,978,980,5,106,0,0,979,975,1,0,0,0,979,977,1,0,0,0,980, - 982,1,0,0,0,981,879,1,0,0,0,981,886,1,0,0,0,981,893,1,0,0,0,981,921,1, - 0,0,0,981,924,1,0,0,0,981,927,1,0,0,0,981,930,1,0,0,0,981,939,1,0,0,0, - 981,945,1,0,0,0,981,950,1,0,0,0,981,953,1,0,0,0,981,956,1,0,0,0,981,962, - 1,0,0,0,981,965,1,0,0,0,981,968,1,0,0,0,981,974,1,0,0,0,982,985,1,0,0, - 0,983,981,1,0,0,0,983,984,1,0,0,0,984,109,1,0,0,0,985,983,1,0,0,0,986, - 991,3,112,56,0,987,988,5,112,0,0,988,990,3,112,56,0,989,987,1,0,0,0,990, - 993,1,0,0,0,991,989,1,0,0,0,991,992,1,0,0,0,992,995,1,0,0,0,993,991,1, - 0,0,0,994,996,5,112,0,0,995,994,1,0,0,0,995,996,1,0,0,0,996,111,1,0,0, - 0,997,1000,3,114,57,0,998,1000,3,108,54,0,999,997,1,0,0,0,999,998,1,0, - 0,0,1000,113,1,0,0,0,1001,1002,5,126,0,0,1002,1007,3,152,76,0,1003,1004, - 5,112,0,0,1004,1006,3,152,76,0,1005,1003,1,0,0,0,1006,1009,1,0,0,0,1007, - 1005,1,0,0,0,1007,1008,1,0,0,0,1008,1011,1,0,0,0,1009,1007,1,0,0,0,1010, - 1012,5,112,0,0,1011,1010,1,0,0,0,1011,1012,1,0,0,0,1012,1013,1,0,0,0, - 1013,1014,5,145,0,0,1014,1027,1,0,0,0,1015,1020,3,152,76,0,1016,1017, - 5,112,0,0,1017,1019,3,152,76,0,1018,1016,1,0,0,0,1019,1022,1,0,0,0,1020, - 1018,1,0,0,0,1020,1021,1,0,0,0,1021,1024,1,0,0,0,1022,1020,1,0,0,0,1023, - 1025,5,112,0,0,1024,1023,1,0,0,0,1024,1025,1,0,0,0,1025,1027,1,0,0,0, - 1026,1001,1,0,0,0,1026,1015,1,0,0,0,1027,1028,1,0,0,0,1028,1029,5,107, - 0,0,1029,1030,3,108,54,0,1030,115,1,0,0,0,1031,1032,5,128,0,0,1032,1036, - 3,152,76,0,1033,1035,3,118,59,0,1034,1033,1,0,0,0,1035,1038,1,0,0,0,1036, - 1034,1,0,0,0,1036,1037,1,0,0,0,1037,1039,1,0,0,0,1038,1036,1,0,0,0,1039, - 1040,5,147,0,0,1040,1041,5,120,0,0,1041,1060,1,0,0,0,1042,1043,5,128, - 0,0,1043,1047,3,152,76,0,1044,1046,3,118,59,0,1045,1044,1,0,0,0,1046, - 1049,1,0,0,0,1047,1045,1,0,0,0,1047,1048,1,0,0,0,1048,1050,1,0,0,0,1049, - 1047,1,0,0,0,1050,1052,5,120,0,0,1051,1053,3,116,58,0,1052,1051,1,0,0, - 0,1052,1053,1,0,0,0,1053,1054,1,0,0,0,1054,1055,5,128,0,0,1055,1056,5, - 147,0,0,1056,1057,3,152,76,0,1057,1058,5,120,0,0,1058,1060,1,0,0,0,1059, - 1031,1,0,0,0,1059,1042,1,0,0,0,1060,117,1,0,0,0,1061,1062,3,152,76,0, - 1062,1063,5,118,0,0,1063,1064,3,158,79,0,1064,1073,1,0,0,0,1065,1066, - 3,152,76,0,1066,1067,5,118,0,0,1067,1068,5,124,0,0,1068,1069,3,108,54, - 0,1069,1070,5,143,0,0,1070,1073,1,0,0,0,1071,1073,3,152,76,0,1072,1061, - 1,0,0,0,1072,1065,1,0,0,0,1072,1071,1,0,0,0,1073,119,1,0,0,0,1074,1079, - 3,122,61,0,1075,1076,5,112,0,0,1076,1078,3,122,61,0,1077,1075,1,0,0,0, - 1078,1081,1,0,0,0,1079,1077,1,0,0,0,1079,1080,1,0,0,0,1080,1083,1,0,0, - 0,1081,1079,1,0,0,0,1082,1084,5,112,0,0,1083,1082,1,0,0,0,1083,1084,1, - 0,0,0,1084,121,1,0,0,0,1085,1086,3,152,76,0,1086,1087,5,6,0,0,1087,1088, - 5,126,0,0,1088,1089,3,36,18,0,1089,1090,5,145,0,0,1090,1096,1,0,0,0,1091, - 1092,3,108,54,0,1092,1093,5,6,0,0,1093,1094,3,152,76,0,1094,1096,1,0, - 0,0,1095,1085,1,0,0,0,1095,1091,1,0,0,0,1096,123,1,0,0,0,1097,1105,3, - 156,78,0,1098,1099,3,132,66,0,1099,1100,5,116,0,0,1100,1102,1,0,0,0,1101, - 1098,1,0,0,0,1101,1102,1,0,0,0,1102,1103,1,0,0,0,1103,1105,3,126,63,0, - 1104,1097,1,0,0,0,1104,1101,1,0,0,0,1105,125,1,0,0,0,1106,1111,3,152, - 76,0,1107,1108,5,116,0,0,1108,1110,3,152,76,0,1109,1107,1,0,0,0,1110, - 1113,1,0,0,0,1111,1109,1,0,0,0,1111,1112,1,0,0,0,1112,127,1,0,0,0,1113, - 1111,1,0,0,0,1114,1115,6,64,-1,0,1115,1124,3,132,66,0,1116,1124,3,130, - 65,0,1117,1118,5,126,0,0,1118,1119,3,36,18,0,1119,1120,5,145,0,0,1120, - 1124,1,0,0,0,1121,1124,3,116,58,0,1122,1124,3,156,78,0,1123,1114,1,0, - 0,0,1123,1116,1,0,0,0,1123,1117,1,0,0,0,1123,1121,1,0,0,0,1123,1122,1, - 0,0,0,1124,1133,1,0,0,0,1125,1129,10,3,0,0,1126,1130,3,150,75,0,1127, - 1128,5,6,0,0,1128,1130,3,152,76,0,1129,1126,1,0,0,0,1129,1127,1,0,0,0, - 1130,1132,1,0,0,0,1131,1125,1,0,0,0,1132,1135,1,0,0,0,1133,1131,1,0,0, - 0,1133,1134,1,0,0,0,1134,129,1,0,0,0,1135,1133,1,0,0,0,1136,1137,3,152, - 76,0,1137,1139,5,126,0,0,1138,1140,3,134,67,0,1139,1138,1,0,0,0,1139, - 1140,1,0,0,0,1140,1141,1,0,0,0,1141,1142,5,145,0,0,1142,131,1,0,0,0,1143, - 1144,3,136,68,0,1144,1145,5,116,0,0,1145,1147,1,0,0,0,1146,1143,1,0,0, - 0,1146,1147,1,0,0,0,1147,1148,1,0,0,0,1148,1149,3,152,76,0,1149,133,1, - 0,0,0,1150,1155,3,108,54,0,1151,1152,5,112,0,0,1152,1154,3,108,54,0,1153, - 1151,1,0,0,0,1154,1157,1,0,0,0,1155,1153,1,0,0,0,1155,1156,1,0,0,0,1156, - 1159,1,0,0,0,1157,1155,1,0,0,0,1158,1160,5,112,0,0,1159,1158,1,0,0,0, - 1159,1160,1,0,0,0,1160,135,1,0,0,0,1161,1162,3,152,76,0,1162,137,1,0, - 0,0,1163,1172,5,102,0,0,1164,1165,5,116,0,0,1165,1172,7,11,0,0,1166,1167, - 5,104,0,0,1167,1169,5,116,0,0,1168,1170,7,11,0,0,1169,1168,1,0,0,0,1169, - 1170,1,0,0,0,1170,1172,1,0,0,0,1171,1163,1,0,0,0,1171,1164,1,0,0,0,1171, - 1166,1,0,0,0,1172,139,1,0,0,0,1173,1175,7,12,0,0,1174,1173,1,0,0,0,1174, - 1175,1,0,0,0,1175,1182,1,0,0,0,1176,1183,3,138,69,0,1177,1183,5,103,0, - 0,1178,1183,5,104,0,0,1179,1183,5,105,0,0,1180,1183,5,41,0,0,1181,1183, - 5,55,0,0,1182,1176,1,0,0,0,1182,1177,1,0,0,0,1182,1178,1,0,0,0,1182,1179, - 1,0,0,0,1182,1180,1,0,0,0,1182,1181,1,0,0,0,1183,141,1,0,0,0,1184,1188, - 3,140,70,0,1185,1188,5,106,0,0,1186,1188,5,57,0,0,1187,1184,1,0,0,0,1187, - 1185,1,0,0,0,1187,1186,1,0,0,0,1188,143,1,0,0,0,1189,1190,7,13,0,0,1190, - 145,1,0,0,0,1191,1192,7,14,0,0,1192,147,1,0,0,0,1193,1194,7,15,0,0,1194, - 149,1,0,0,0,1195,1198,5,101,0,0,1196,1198,3,148,74,0,1197,1195,1,0,0, - 0,1197,1196,1,0,0,0,1198,151,1,0,0,0,1199,1203,5,101,0,0,1200,1203,3, - 144,72,0,1201,1203,3,146,73,0,1202,1199,1,0,0,0,1202,1200,1,0,0,0,1202, - 1201,1,0,0,0,1203,153,1,0,0,0,1204,1205,3,158,79,0,1205,1206,5,118,0, - 0,1206,1207,3,140,70,0,1207,155,1,0,0,0,1208,1209,5,124,0,0,1209,1210, - 3,152,76,0,1210,1211,5,143,0,0,1211,157,1,0,0,0,1212,1215,5,106,0,0,1213, - 1215,3,160,80,0,1214,1212,1,0,0,0,1214,1213,1,0,0,0,1215,159,1,0,0,0, - 1216,1220,5,138,0,0,1217,1219,3,162,81,0,1218,1217,1,0,0,0,1219,1222, - 1,0,0,0,1220,1218,1,0,0,0,1220,1221,1,0,0,0,1221,1223,1,0,0,0,1222,1220, - 1,0,0,0,1223,1224,5,140,0,0,1224,161,1,0,0,0,1225,1226,5,153,0,0,1226, - 1227,3,108,54,0,1227,1228,5,143,0,0,1228,1231,1,0,0,0,1229,1231,5,152, - 0,0,1230,1225,1,0,0,0,1230,1229,1,0,0,0,1231,163,1,0,0,0,1232,1236,5, - 139,0,0,1233,1235,3,166,83,0,1234,1233,1,0,0,0,1235,1238,1,0,0,0,1236, - 1234,1,0,0,0,1236,1237,1,0,0,0,1237,1239,1,0,0,0,1238,1236,1,0,0,0,1239, - 1240,5,0,0,1,1240,165,1,0,0,0,1241,1242,5,155,0,0,1242,1243,3,108,54, - 0,1243,1244,5,143,0,0,1244,1247,1,0,0,0,1245,1247,5,154,0,0,1246,1241, - 1,0,0,0,1246,1245,1,0,0,0,1247,167,1,0,0,0,160,171,178,187,194,198,209, - 213,216,225,233,240,244,250,255,261,273,281,295,299,304,314,323,326,330, - 333,337,340,343,346,349,353,357,360,363,366,370,373,382,388,409,426,443, - 449,455,466,468,479,482,488,496,502,504,508,513,516,519,523,527,530,532, - 535,539,543,546,548,550,555,566,572,579,584,588,592,598,600,607,615,618, - 621,640,654,670,674,685,689,700,704,711,715,722,726,731,740,744,768,785, - 791,794,797,807,813,816,819,827,830,834,837,851,868,873,877,883,890,902, - 906,909,918,932,971,979,981,983,991,995,999,1007,1011,1020,1024,1026, - 1036,1047,1052,1059,1072,1079,1083,1095,1101,1104,1111,1123,1129,1133, - 1139,1146,1155,1159,1169,1171,1174,1182,1187,1197,1202,1214,1220,1230, - 1236,1246 + 7,77,2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84, + 7,84,1,0,5,0,172,8,0,10,0,12,0,175,9,0,1,0,1,0,1,1,1,1,3,1,181,8,1,1, + 2,1,2,1,3,1,3,1,3,1,3,1,3,3,3,190,8,3,1,4,1,4,1,4,5,4,195,8,4,10,4,12, + 4,198,9,4,1,4,3,4,201,8,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5, + 213,8,5,1,6,1,6,3,6,217,8,6,1,6,3,6,220,8,6,1,7,1,7,1,7,1,7,1,7,1,7,1, + 7,3,7,229,8,7,1,8,1,8,1,8,1,8,1,8,1,8,3,8,237,8,8,1,9,1,9,1,9,1,9,1,9, + 3,9,244,8,9,1,9,1,9,3,9,248,8,9,1,9,1,9,1,9,1,9,3,9,254,8,9,1,9,1,9,1, + 9,3,9,259,8,9,1,10,1,10,1,10,1,10,1,10,1,10,3,10,267,8,10,1,10,1,10,1, + 10,1,10,1,10,3,10,274,8,10,1,11,1,11,1,11,1,11,3,11,280,8,11,1,11,1,11, + 1,11,1,12,1,12,1,12,1,12,1,12,1,13,1,13,3,13,292,8,13,1,14,1,14,1,15, + 1,15,5,15,298,8,15,10,15,12,15,301,9,15,1,15,1,15,1,16,1,16,1,16,1,16, + 1,17,1,17,1,17,5,17,312,8,17,10,17,12,17,315,9,17,1,17,3,17,318,8,17, + 1,18,1,18,1,18,3,18,323,8,18,1,18,1,18,1,19,1,19,1,19,1,19,5,19,331,8, + 19,10,19,12,19,334,9,19,1,20,1,20,1,20,1,20,1,20,1,20,3,20,342,8,20,1, + 21,3,21,345,8,21,1,21,1,21,3,21,349,8,21,1,21,3,21,352,8,21,1,21,1,21, + 3,21,356,8,21,1,21,3,21,359,8,21,1,21,3,21,362,8,21,1,21,3,21,365,8,21, + 1,21,3,21,368,8,21,1,21,1,21,3,21,372,8,21,1,21,1,21,3,21,376,8,21,1, + 21,3,21,379,8,21,1,21,3,21,382,8,21,1,21,3,21,385,8,21,1,21,1,21,3,21, + 389,8,21,1,21,3,21,392,8,21,1,22,1,22,1,22,1,23,1,23,1,23,1,23,3,23,401, + 8,23,1,24,1,24,1,24,1,25,3,25,407,8,25,1,25,1,25,1,25,1,25,1,26,1,26, + 1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,5,26,426,8,26, + 10,26,12,26,429,9,26,1,27,1,27,1,27,1,28,1,28,1,28,1,29,1,29,1,29,1,29, + 1,29,1,29,1,29,1,29,3,29,445,8,29,1,30,1,30,1,30,1,31,1,31,1,31,1,31, + 1,32,1,32,1,32,1,32,1,33,1,33,1,33,1,33,3,33,462,8,33,1,33,1,33,1,33, + 1,33,3,33,468,8,33,1,33,1,33,1,33,1,33,3,33,474,8,33,1,33,1,33,1,33,1, + 33,1,33,1,33,1,33,1,33,1,33,3,33,485,8,33,3,33,487,8,33,1,34,1,34,1,34, + 1,35,1,35,1,35,1,36,1,36,1,36,3,36,498,8,36,1,36,3,36,501,8,36,1,36,1, + 36,1,36,1,36,3,36,507,8,36,1,36,1,36,1,36,1,36,1,36,1,36,3,36,515,8,36, + 1,36,1,36,1,36,1,36,5,36,521,8,36,10,36,12,36,524,9,36,1,37,3,37,527, + 8,37,1,37,1,37,1,37,3,37,532,8,37,1,37,3,37,535,8,37,1,37,3,37,538,8, + 37,1,37,1,37,3,37,542,8,37,1,37,1,37,3,37,546,8,37,1,37,3,37,549,8,37, + 3,37,551,8,37,1,37,3,37,554,8,37,1,37,1,37,3,37,558,8,37,1,37,1,37,3, + 37,562,8,37,1,37,3,37,565,8,37,3,37,567,8,37,3,37,569,8,37,1,38,1,38, + 1,38,3,38,574,8,38,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,3,39, + 585,8,39,1,40,1,40,1,40,1,40,3,40,591,8,40,1,41,1,41,1,41,5,41,596,8, + 41,10,41,12,41,599,9,41,1,42,1,42,3,42,603,8,42,1,42,1,42,3,42,607,8, + 42,1,42,1,42,3,42,611,8,42,1,43,1,43,1,43,1,43,3,43,617,8,43,3,43,619, + 8,43,1,44,1,44,1,44,5,44,624,8,44,10,44,12,44,627,9,44,1,45,1,45,1,45, + 1,45,1,46,3,46,634,8,46,1,46,3,46,637,8,46,1,46,3,46,640,8,46,1,47,1, + 47,1,47,1,47,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1, + 50,1,50,3,50,659,8,50,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1, + 51,1,51,1,51,3,51,673,8,51,1,52,1,52,1,52,1,53,1,53,1,53,1,53,1,53,1, + 53,1,53,1,53,1,53,5,53,687,8,53,10,53,12,53,690,9,53,1,53,3,53,693,8, + 53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,5,53,702,8,53,10,53,12,53,705,9, + 53,1,53,3,53,708,8,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,5,53,717,8,53, + 10,53,12,53,720,9,53,1,53,3,53,723,8,53,1,53,1,53,1,53,1,53,1,53,3,53, + 730,8,53,1,53,1,53,3,53,734,8,53,1,54,1,54,1,54,5,54,739,8,54,10,54,12, + 54,742,9,54,1,54,3,54,745,8,54,1,55,1,55,1,55,3,55,750,8,55,1,55,1,55, + 1,55,1,55,1,55,4,55,757,8,55,11,55,12,55,758,1,55,1,55,3,55,763,8,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,787,8,55,1,55,1,55,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,804, + 8,55,1,55,1,55,1,55,1,55,3,55,810,8,55,1,55,3,55,813,8,55,1,55,3,55,816, + 8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,826,8,55,1,55,1,55, + 1,55,1,55,3,55,832,8,55,1,55,3,55,835,8,55,1,55,3,55,838,8,55,1,55,1, + 55,1,55,1,55,1,55,1,55,3,55,846,8,55,1,55,3,55,849,8,55,1,55,1,55,3,55, + 853,8,55,1,55,3,55,856,8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,3,55,870,8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,887,8,55,1,55,1,55,1,55,3, + 55,892,8,55,1,55,1,55,3,55,896,8,55,1,55,1,55,1,55,1,55,3,55,902,8,55, + 1,55,1,55,1,55,1,55,1,55,3,55,909,8,55,1,55,1,55,1,55,1,55,1,55,1,55, + 1,55,1,55,1,55,1,55,3,55,921,8,55,1,55,1,55,3,55,925,8,55,1,55,3,55,928, + 8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,937,8,55,1,55,1,55,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,951,8,55,1,55,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,990,8,55,1,55,1,55,1,55,1,55, + 1,55,1,55,3,55,998,8,55,5,55,1000,8,55,10,55,12,55,1003,9,55,1,56,1,56, + 1,56,5,56,1008,8,56,10,56,12,56,1011,9,56,1,56,3,56,1014,8,56,1,57,1, + 57,3,57,1018,8,57,1,58,1,58,1,58,1,58,5,58,1024,8,58,10,58,12,58,1027, + 9,58,1,58,3,58,1030,8,58,1,58,1,58,1,58,1,58,1,58,5,58,1037,8,58,10,58, + 12,58,1040,9,58,1,58,3,58,1043,8,58,3,58,1045,8,58,1,58,1,58,1,58,1,59, + 1,59,1,59,5,59,1053,8,59,10,59,12,59,1056,9,59,1,59,1,59,1,59,1,59,1, + 59,1,59,5,59,1064,8,59,10,59,12,59,1067,9,59,1,59,1,59,3,59,1071,8,59, + 1,59,1,59,1,59,1,59,1,59,3,59,1078,8,59,1,60,1,60,1,60,1,60,1,60,1,60, + 1,60,1,60,1,60,1,60,1,60,3,60,1091,8,60,1,61,1,61,1,61,5,61,1096,8,61, + 10,61,12,61,1099,9,61,1,61,3,61,1102,8,61,1,62,1,62,1,62,1,62,1,62,1, + 62,1,62,1,62,1,62,1,62,3,62,1114,8,62,1,63,1,63,1,63,1,63,3,63,1120,8, + 63,1,63,3,63,1123,8,63,1,64,1,64,1,64,5,64,1128,8,64,10,64,12,64,1131, + 9,64,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,3,65,1142,8,65,1,65, + 1,65,1,65,1,65,3,65,1148,8,65,5,65,1150,8,65,10,65,12,65,1153,9,65,1, + 66,1,66,1,66,3,66,1158,8,66,1,66,1,66,1,67,1,67,1,67,3,67,1165,8,67,1, + 67,1,67,1,68,1,68,1,68,5,68,1172,8,68,10,68,12,68,1175,9,68,1,68,3,68, + 1178,8,68,1,69,1,69,1,70,1,70,1,70,1,70,1,70,1,70,3,70,1188,8,70,3,70, + 1190,8,70,1,71,3,71,1193,8,71,1,71,1,71,1,71,1,71,1,71,1,71,3,71,1201, + 8,71,1,72,1,72,1,72,3,72,1206,8,72,1,73,1,73,1,74,1,74,1,75,1,75,1,76, + 1,76,3,76,1216,8,76,1,77,1,77,1,77,3,77,1221,8,77,1,78,1,78,1,78,1,78, + 1,79,1,79,1,79,1,79,1,80,1,80,3,80,1233,8,80,1,81,1,81,5,81,1237,8,81, + 10,81,12,81,1240,9,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,3,82,1249,8, + 82,1,83,1,83,5,83,1253,8,83,10,83,12,83,1256,9,83,1,83,1,83,1,84,1,84, + 1,84,1,84,1,84,3,84,1265,8,84,1,84,0,3,72,110,130,85,0,2,4,6,8,10,12, + 14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58, + 60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104, + 106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140, + 142,144,146,148,150,152,154,156,158,160,162,164,166,168,0,16,2,0,17,17, + 72,72,2,0,42,42,49,49,3,0,1,1,4,4,8,8,4,0,1,1,3,4,8,8,78,78,2,0,49,49, + 71,71,2,0,1,1,4,4,2,0,7,7,21,22,2,0,28,28,47,47,2,0,69,69,74,74,3,0,10, + 10,48,48,87,87,2,0,39,39,51,51,1,0,103,104,2,0,114,114,135,135,7,0,20, + 20,36,36,53,54,68,68,76,76,93,93,99,99,12,0,1,19,21,28,30,35,37,40,42, + 49,51,52,56,56,58,67,69,75,77,92,94,95,97,98,4,0,19,19,28,28,37,37,46, + 46,1429,0,173,1,0,0,0,2,180,1,0,0,0,4,182,1,0,0,0,6,184,1,0,0,0,8,191, + 1,0,0,0,10,212,1,0,0,0,12,214,1,0,0,0,14,221,1,0,0,0,16,230,1,0,0,0,18, + 238,1,0,0,0,20,260,1,0,0,0,22,275,1,0,0,0,24,284,1,0,0,0,26,289,1,0,0, + 0,28,293,1,0,0,0,30,295,1,0,0,0,32,304,1,0,0,0,34,308,1,0,0,0,36,322, + 1,0,0,0,38,326,1,0,0,0,40,341,1,0,0,0,42,344,1,0,0,0,44,393,1,0,0,0,46, + 396,1,0,0,0,48,402,1,0,0,0,50,406,1,0,0,0,52,412,1,0,0,0,54,430,1,0,0, + 0,56,433,1,0,0,0,58,436,1,0,0,0,60,446,1,0,0,0,62,449,1,0,0,0,64,453, + 1,0,0,0,66,486,1,0,0,0,68,488,1,0,0,0,70,491,1,0,0,0,72,506,1,0,0,0,74, + 568,1,0,0,0,76,573,1,0,0,0,78,584,1,0,0,0,80,586,1,0,0,0,82,592,1,0,0, + 0,84,600,1,0,0,0,86,618,1,0,0,0,88,620,1,0,0,0,90,628,1,0,0,0,92,633, + 1,0,0,0,94,641,1,0,0,0,96,645,1,0,0,0,98,649,1,0,0,0,100,658,1,0,0,0, + 102,672,1,0,0,0,104,674,1,0,0,0,106,733,1,0,0,0,108,735,1,0,0,0,110,895, + 1,0,0,0,112,1004,1,0,0,0,114,1017,1,0,0,0,116,1044,1,0,0,0,118,1077,1, + 0,0,0,120,1090,1,0,0,0,122,1092,1,0,0,0,124,1113,1,0,0,0,126,1122,1,0, + 0,0,128,1124,1,0,0,0,130,1141,1,0,0,0,132,1154,1,0,0,0,134,1164,1,0,0, + 0,136,1168,1,0,0,0,138,1179,1,0,0,0,140,1189,1,0,0,0,142,1192,1,0,0,0, + 144,1205,1,0,0,0,146,1207,1,0,0,0,148,1209,1,0,0,0,150,1211,1,0,0,0,152, + 1215,1,0,0,0,154,1220,1,0,0,0,156,1222,1,0,0,0,158,1226,1,0,0,0,160,1232, + 1,0,0,0,162,1234,1,0,0,0,164,1248,1,0,0,0,166,1250,1,0,0,0,168,1264,1, + 0,0,0,170,172,3,2,1,0,171,170,1,0,0,0,172,175,1,0,0,0,173,171,1,0,0,0, + 173,174,1,0,0,0,174,176,1,0,0,0,175,173,1,0,0,0,176,177,5,0,0,1,177,1, + 1,0,0,0,178,181,3,6,3,0,179,181,3,10,5,0,180,178,1,0,0,0,180,179,1,0, + 0,0,181,3,1,0,0,0,182,183,3,110,55,0,183,5,1,0,0,0,184,185,5,50,0,0,185, + 189,3,154,77,0,186,187,5,111,0,0,187,188,5,118,0,0,188,190,3,4,2,0,189, + 186,1,0,0,0,189,190,1,0,0,0,190,7,1,0,0,0,191,196,3,154,77,0,192,193, + 5,112,0,0,193,195,3,154,77,0,194,192,1,0,0,0,195,198,1,0,0,0,196,194, + 1,0,0,0,196,197,1,0,0,0,197,200,1,0,0,0,198,196,1,0,0,0,199,201,5,112, + 0,0,200,199,1,0,0,0,200,201,1,0,0,0,201,9,1,0,0,0,202,213,3,12,6,0,203, + 213,3,14,7,0,204,213,3,16,8,0,205,213,3,20,10,0,206,213,3,18,9,0,207, + 213,3,22,11,0,208,213,3,24,12,0,209,213,3,30,15,0,210,213,3,26,13,0,211, + 213,3,28,14,0,212,202,1,0,0,0,212,203,1,0,0,0,212,204,1,0,0,0,212,205, + 1,0,0,0,212,206,1,0,0,0,212,207,1,0,0,0,212,208,1,0,0,0,212,209,1,0,0, + 0,212,210,1,0,0,0,212,211,1,0,0,0,213,11,1,0,0,0,214,216,5,70,0,0,215, + 217,3,4,2,0,216,215,1,0,0,0,216,217,1,0,0,0,217,219,1,0,0,0,218,220,5, + 146,0,0,219,218,1,0,0,0,219,220,1,0,0,0,220,13,1,0,0,0,221,222,5,38,0, + 0,222,223,5,126,0,0,223,224,3,4,2,0,224,225,5,145,0,0,225,228,3,10,5, + 0,226,227,5,24,0,0,227,229,3,10,5,0,228,226,1,0,0,0,228,229,1,0,0,0,229, + 15,1,0,0,0,230,231,5,96,0,0,231,232,5,126,0,0,232,233,3,4,2,0,233,234, + 5,145,0,0,234,236,3,10,5,0,235,237,5,146,0,0,236,235,1,0,0,0,236,237, + 1,0,0,0,237,17,1,0,0,0,238,239,5,31,0,0,239,243,5,126,0,0,240,244,3,6, + 3,0,241,244,3,24,12,0,242,244,3,4,2,0,243,240,1,0,0,0,243,241,1,0,0,0, + 243,242,1,0,0,0,243,244,1,0,0,0,244,245,1,0,0,0,245,247,5,146,0,0,246, + 248,3,4,2,0,247,246,1,0,0,0,247,248,1,0,0,0,248,249,1,0,0,0,249,253,5, + 146,0,0,250,254,3,6,3,0,251,254,3,24,12,0,252,254,3,4,2,0,253,250,1,0, + 0,0,253,251,1,0,0,0,253,252,1,0,0,0,253,254,1,0,0,0,254,255,1,0,0,0,255, + 256,5,145,0,0,256,258,3,10,5,0,257,259,5,146,0,0,258,257,1,0,0,0,258, + 259,1,0,0,0,259,19,1,0,0,0,260,261,5,31,0,0,261,262,5,126,0,0,262,263, + 5,50,0,0,263,266,3,154,77,0,264,265,5,112,0,0,265,267,3,154,77,0,266, + 264,1,0,0,0,266,267,1,0,0,0,267,268,1,0,0,0,268,269,5,40,0,0,269,270, + 3,4,2,0,270,271,5,145,0,0,271,273,3,10,5,0,272,274,5,146,0,0,273,272, + 1,0,0,0,273,274,1,0,0,0,274,21,1,0,0,0,275,276,5,29,0,0,276,277,3,154, + 77,0,277,279,5,126,0,0,278,280,3,8,4,0,279,278,1,0,0,0,279,280,1,0,0, + 0,280,281,1,0,0,0,281,282,5,145,0,0,282,283,3,30,15,0,283,23,1,0,0,0, + 284,285,3,4,2,0,285,286,5,111,0,0,286,287,5,118,0,0,287,288,3,4,2,0,288, + 25,1,0,0,0,289,291,3,4,2,0,290,292,5,146,0,0,291,290,1,0,0,0,291,292, + 1,0,0,0,292,27,1,0,0,0,293,294,5,146,0,0,294,29,1,0,0,0,295,299,5,124, + 0,0,296,298,3,2,1,0,297,296,1,0,0,0,298,301,1,0,0,0,299,297,1,0,0,0,299, + 300,1,0,0,0,300,302,1,0,0,0,301,299,1,0,0,0,302,303,5,143,0,0,303,31, + 1,0,0,0,304,305,3,4,2,0,305,306,5,111,0,0,306,307,3,4,2,0,307,33,1,0, + 0,0,308,313,3,32,16,0,309,310,5,112,0,0,310,312,3,32,16,0,311,309,1,0, + 0,0,312,315,1,0,0,0,313,311,1,0,0,0,313,314,1,0,0,0,314,317,1,0,0,0,315, + 313,1,0,0,0,316,318,5,112,0,0,317,316,1,0,0,0,317,318,1,0,0,0,318,35, + 1,0,0,0,319,323,3,38,19,0,320,323,3,42,21,0,321,323,3,118,59,0,322,319, + 1,0,0,0,322,320,1,0,0,0,322,321,1,0,0,0,323,324,1,0,0,0,324,325,5,0,0, + 1,325,37,1,0,0,0,326,332,3,40,20,0,327,328,5,91,0,0,328,329,5,1,0,0,329, + 331,3,40,20,0,330,327,1,0,0,0,331,334,1,0,0,0,332,330,1,0,0,0,332,333, + 1,0,0,0,333,39,1,0,0,0,334,332,1,0,0,0,335,342,3,42,21,0,336,337,5,126, + 0,0,337,338,3,38,19,0,338,339,5,145,0,0,339,342,1,0,0,0,340,342,3,158, + 79,0,341,335,1,0,0,0,341,336,1,0,0,0,341,340,1,0,0,0,342,41,1,0,0,0,343, + 345,3,44,22,0,344,343,1,0,0,0,344,345,1,0,0,0,345,346,1,0,0,0,346,348, + 5,77,0,0,347,349,5,23,0,0,348,347,1,0,0,0,348,349,1,0,0,0,349,351,1,0, + 0,0,350,352,3,46,23,0,351,350,1,0,0,0,351,352,1,0,0,0,352,353,1,0,0,0, + 353,355,3,108,54,0,354,356,3,48,24,0,355,354,1,0,0,0,355,356,1,0,0,0, + 356,358,1,0,0,0,357,359,3,50,25,0,358,357,1,0,0,0,358,359,1,0,0,0,359, + 361,1,0,0,0,360,362,3,54,27,0,361,360,1,0,0,0,361,362,1,0,0,0,362,364, + 1,0,0,0,363,365,3,56,28,0,364,363,1,0,0,0,364,365,1,0,0,0,365,367,1,0, + 0,0,366,368,3,58,29,0,367,366,1,0,0,0,367,368,1,0,0,0,368,371,1,0,0,0, + 369,370,5,98,0,0,370,372,7,0,0,0,371,369,1,0,0,0,371,372,1,0,0,0,372, + 375,1,0,0,0,373,374,5,98,0,0,374,376,5,86,0,0,375,373,1,0,0,0,375,376, + 1,0,0,0,376,378,1,0,0,0,377,379,3,60,30,0,378,377,1,0,0,0,378,379,1,0, + 0,0,379,381,1,0,0,0,380,382,3,52,26,0,381,380,1,0,0,0,381,382,1,0,0,0, + 382,384,1,0,0,0,383,385,3,62,31,0,384,383,1,0,0,0,384,385,1,0,0,0,385, + 388,1,0,0,0,386,389,3,66,33,0,387,389,3,68,34,0,388,386,1,0,0,0,388,387, + 1,0,0,0,388,389,1,0,0,0,389,391,1,0,0,0,390,392,3,70,35,0,391,390,1,0, + 0,0,391,392,1,0,0,0,392,43,1,0,0,0,393,394,5,98,0,0,394,395,3,122,61, + 0,395,45,1,0,0,0,396,397,5,85,0,0,397,400,5,104,0,0,398,399,5,98,0,0, + 399,401,5,82,0,0,400,398,1,0,0,0,400,401,1,0,0,0,401,47,1,0,0,0,402,403, + 5,32,0,0,403,404,3,72,36,0,404,49,1,0,0,0,405,407,7,1,0,0,406,405,1,0, + 0,0,406,407,1,0,0,0,407,408,1,0,0,0,408,409,5,5,0,0,409,410,5,45,0,0, + 410,411,3,108,54,0,411,51,1,0,0,0,412,413,5,97,0,0,413,414,3,154,77,0, + 414,415,5,6,0,0,415,416,5,126,0,0,416,417,3,92,46,0,417,427,5,145,0,0, + 418,419,5,112,0,0,419,420,3,154,77,0,420,421,5,6,0,0,421,422,5,126,0, + 0,422,423,3,92,46,0,423,424,5,145,0,0,424,426,1,0,0,0,425,418,1,0,0,0, + 426,429,1,0,0,0,427,425,1,0,0,0,427,428,1,0,0,0,428,53,1,0,0,0,429,427, + 1,0,0,0,430,431,5,67,0,0,431,432,3,110,55,0,432,55,1,0,0,0,433,434,5, + 95,0,0,434,435,3,110,55,0,435,57,1,0,0,0,436,437,5,34,0,0,437,444,5,11, + 0,0,438,439,7,0,0,0,439,440,5,126,0,0,440,441,3,108,54,0,441,442,5,145, + 0,0,442,445,1,0,0,0,443,445,3,108,54,0,444,438,1,0,0,0,444,443,1,0,0, + 0,445,59,1,0,0,0,446,447,5,35,0,0,447,448,3,110,55,0,448,61,1,0,0,0,449, + 450,5,62,0,0,450,451,5,11,0,0,451,452,3,82,41,0,452,63,1,0,0,0,453,454, + 5,62,0,0,454,455,5,11,0,0,455,456,3,108,54,0,456,65,1,0,0,0,457,458,5, + 52,0,0,458,461,3,110,55,0,459,460,5,112,0,0,460,462,3,110,55,0,461,459, + 1,0,0,0,461,462,1,0,0,0,462,467,1,0,0,0,463,464,5,98,0,0,464,468,5,82, + 0,0,465,466,5,11,0,0,466,468,3,108,54,0,467,463,1,0,0,0,467,465,1,0,0, + 0,467,468,1,0,0,0,468,487,1,0,0,0,469,470,5,52,0,0,470,473,3,110,55,0, + 471,472,5,98,0,0,472,474,5,82,0,0,473,471,1,0,0,0,473,474,1,0,0,0,474, + 475,1,0,0,0,475,476,5,59,0,0,476,477,3,110,55,0,477,487,1,0,0,0,478,479, + 5,52,0,0,479,480,3,110,55,0,480,481,5,59,0,0,481,484,3,110,55,0,482,483, + 5,11,0,0,483,485,3,108,54,0,484,482,1,0,0,0,484,485,1,0,0,0,485,487,1, + 0,0,0,486,457,1,0,0,0,486,469,1,0,0,0,486,478,1,0,0,0,487,67,1,0,0,0, + 488,489,5,59,0,0,489,490,3,110,55,0,490,69,1,0,0,0,491,492,5,79,0,0,492, + 493,3,88,44,0,493,71,1,0,0,0,494,495,6,36,-1,0,495,497,3,130,65,0,496, + 498,5,27,0,0,497,496,1,0,0,0,497,498,1,0,0,0,498,500,1,0,0,0,499,501, + 3,80,40,0,500,499,1,0,0,0,500,501,1,0,0,0,501,507,1,0,0,0,502,503,5,126, + 0,0,503,504,3,72,36,0,504,505,5,145,0,0,505,507,1,0,0,0,506,494,1,0,0, + 0,506,502,1,0,0,0,507,522,1,0,0,0,508,509,10,3,0,0,509,510,3,76,38,0, + 510,511,3,72,36,4,511,521,1,0,0,0,512,514,10,4,0,0,513,515,3,74,37,0, + 514,513,1,0,0,0,514,515,1,0,0,0,515,516,1,0,0,0,516,517,5,45,0,0,517, + 518,3,72,36,0,518,519,3,78,39,0,519,521,1,0,0,0,520,508,1,0,0,0,520,512, + 1,0,0,0,521,524,1,0,0,0,522,520,1,0,0,0,522,523,1,0,0,0,523,73,1,0,0, + 0,524,522,1,0,0,0,525,527,7,2,0,0,526,525,1,0,0,0,526,527,1,0,0,0,527, + 528,1,0,0,0,528,535,5,42,0,0,529,531,5,42,0,0,530,532,7,2,0,0,531,530, + 1,0,0,0,531,532,1,0,0,0,532,535,1,0,0,0,533,535,7,2,0,0,534,526,1,0,0, + 0,534,529,1,0,0,0,534,533,1,0,0,0,535,569,1,0,0,0,536,538,7,3,0,0,537, + 536,1,0,0,0,537,538,1,0,0,0,538,539,1,0,0,0,539,541,7,4,0,0,540,542,5, + 63,0,0,541,540,1,0,0,0,541,542,1,0,0,0,542,551,1,0,0,0,543,545,7,4,0, + 0,544,546,5,63,0,0,545,544,1,0,0,0,545,546,1,0,0,0,546,548,1,0,0,0,547, + 549,7,3,0,0,548,547,1,0,0,0,548,549,1,0,0,0,549,551,1,0,0,0,550,537,1, + 0,0,0,550,543,1,0,0,0,551,569,1,0,0,0,552,554,7,5,0,0,553,552,1,0,0,0, + 553,554,1,0,0,0,554,555,1,0,0,0,555,557,5,33,0,0,556,558,5,63,0,0,557, + 556,1,0,0,0,557,558,1,0,0,0,558,567,1,0,0,0,559,561,5,33,0,0,560,562, + 5,63,0,0,561,560,1,0,0,0,561,562,1,0,0,0,562,564,1,0,0,0,563,565,7,5, + 0,0,564,563,1,0,0,0,564,565,1,0,0,0,565,567,1,0,0,0,566,553,1,0,0,0,566, + 559,1,0,0,0,567,569,1,0,0,0,568,534,1,0,0,0,568,550,1,0,0,0,568,566,1, + 0,0,0,569,75,1,0,0,0,570,571,5,16,0,0,571,574,5,45,0,0,572,574,5,112, + 0,0,573,570,1,0,0,0,573,572,1,0,0,0,574,77,1,0,0,0,575,576,5,60,0,0,576, + 585,3,108,54,0,577,578,5,92,0,0,578,579,5,126,0,0,579,580,3,108,54,0, + 580,581,5,145,0,0,581,585,1,0,0,0,582,583,5,92,0,0,583,585,3,108,54,0, + 584,575,1,0,0,0,584,577,1,0,0,0,584,582,1,0,0,0,585,79,1,0,0,0,586,587, + 5,75,0,0,587,590,3,86,43,0,588,589,5,59,0,0,589,591,3,86,43,0,590,588, + 1,0,0,0,590,591,1,0,0,0,591,81,1,0,0,0,592,597,3,84,42,0,593,594,5,112, + 0,0,594,596,3,84,42,0,595,593,1,0,0,0,596,599,1,0,0,0,597,595,1,0,0,0, + 597,598,1,0,0,0,598,83,1,0,0,0,599,597,1,0,0,0,600,602,3,110,55,0,601, + 603,7,6,0,0,602,601,1,0,0,0,602,603,1,0,0,0,603,606,1,0,0,0,604,605,5, + 58,0,0,605,607,7,7,0,0,606,604,1,0,0,0,606,607,1,0,0,0,607,610,1,0,0, + 0,608,609,5,15,0,0,609,611,5,106,0,0,610,608,1,0,0,0,610,611,1,0,0,0, + 611,85,1,0,0,0,612,619,3,158,79,0,613,616,3,142,71,0,614,615,5,147,0, + 0,615,617,3,142,71,0,616,614,1,0,0,0,616,617,1,0,0,0,617,619,1,0,0,0, + 618,612,1,0,0,0,618,613,1,0,0,0,619,87,1,0,0,0,620,625,3,90,45,0,621, + 622,5,112,0,0,622,624,3,90,45,0,623,621,1,0,0,0,624,627,1,0,0,0,625,623, + 1,0,0,0,625,626,1,0,0,0,626,89,1,0,0,0,627,625,1,0,0,0,628,629,3,154, + 77,0,629,630,5,118,0,0,630,631,3,144,72,0,631,91,1,0,0,0,632,634,3,94, + 47,0,633,632,1,0,0,0,633,634,1,0,0,0,634,636,1,0,0,0,635,637,3,96,48, + 0,636,635,1,0,0,0,636,637,1,0,0,0,637,639,1,0,0,0,638,640,3,98,49,0,639, + 638,1,0,0,0,639,640,1,0,0,0,640,93,1,0,0,0,641,642,5,65,0,0,642,643,5, + 11,0,0,643,644,3,108,54,0,644,95,1,0,0,0,645,646,5,62,0,0,646,647,5,11, + 0,0,647,648,3,82,41,0,648,97,1,0,0,0,649,650,7,8,0,0,650,651,3,100,50, + 0,651,99,1,0,0,0,652,659,3,102,51,0,653,654,5,9,0,0,654,655,3,102,51, + 0,655,656,5,2,0,0,656,657,3,102,51,0,657,659,1,0,0,0,658,652,1,0,0,0, + 658,653,1,0,0,0,659,101,1,0,0,0,660,661,5,18,0,0,661,673,5,73,0,0,662, + 663,5,90,0,0,663,673,5,66,0,0,664,665,5,90,0,0,665,673,5,30,0,0,666,667, + 3,142,71,0,667,668,5,66,0,0,668,673,1,0,0,0,669,670,3,142,71,0,670,671, + 5,30,0,0,671,673,1,0,0,0,672,660,1,0,0,0,672,662,1,0,0,0,672,664,1,0, + 0,0,672,666,1,0,0,0,672,669,1,0,0,0,673,103,1,0,0,0,674,675,3,110,55, + 0,675,676,5,0,0,1,676,105,1,0,0,0,677,734,3,154,77,0,678,679,3,154,77, + 0,679,680,5,126,0,0,680,681,3,154,77,0,681,688,3,106,53,0,682,683,5,112, + 0,0,683,684,3,154,77,0,684,685,3,106,53,0,685,687,1,0,0,0,686,682,1,0, + 0,0,687,690,1,0,0,0,688,686,1,0,0,0,688,689,1,0,0,0,689,692,1,0,0,0,690, + 688,1,0,0,0,691,693,5,112,0,0,692,691,1,0,0,0,692,693,1,0,0,0,693,694, + 1,0,0,0,694,695,5,145,0,0,695,734,1,0,0,0,696,697,3,154,77,0,697,698, + 5,126,0,0,698,703,3,156,78,0,699,700,5,112,0,0,700,702,3,156,78,0,701, + 699,1,0,0,0,702,705,1,0,0,0,703,701,1,0,0,0,703,704,1,0,0,0,704,707,1, + 0,0,0,705,703,1,0,0,0,706,708,5,112,0,0,707,706,1,0,0,0,707,708,1,0,0, + 0,708,709,1,0,0,0,709,710,5,145,0,0,710,734,1,0,0,0,711,712,3,154,77, + 0,712,713,5,126,0,0,713,718,3,106,53,0,714,715,5,112,0,0,715,717,3,106, + 53,0,716,714,1,0,0,0,717,720,1,0,0,0,718,716,1,0,0,0,718,719,1,0,0,0, + 719,722,1,0,0,0,720,718,1,0,0,0,721,723,5,112,0,0,722,721,1,0,0,0,722, + 723,1,0,0,0,723,724,1,0,0,0,724,725,5,145,0,0,725,734,1,0,0,0,726,727, + 3,154,77,0,727,729,5,126,0,0,728,730,3,108,54,0,729,728,1,0,0,0,729,730, + 1,0,0,0,730,731,1,0,0,0,731,732,5,145,0,0,732,734,1,0,0,0,733,677,1,0, + 0,0,733,678,1,0,0,0,733,696,1,0,0,0,733,711,1,0,0,0,733,726,1,0,0,0,734, + 107,1,0,0,0,735,740,3,110,55,0,736,737,5,112,0,0,737,739,3,110,55,0,738, + 736,1,0,0,0,739,742,1,0,0,0,740,738,1,0,0,0,740,741,1,0,0,0,741,744,1, + 0,0,0,742,740,1,0,0,0,743,745,5,112,0,0,744,743,1,0,0,0,744,745,1,0,0, + 0,745,109,1,0,0,0,746,747,6,55,-1,0,747,749,5,12,0,0,748,750,3,110,55, + 0,749,748,1,0,0,0,749,750,1,0,0,0,750,756,1,0,0,0,751,752,5,94,0,0,752, + 753,3,110,55,0,753,754,5,81,0,0,754,755,3,110,55,0,755,757,1,0,0,0,756, + 751,1,0,0,0,757,758,1,0,0,0,758,756,1,0,0,0,758,759,1,0,0,0,759,762,1, + 0,0,0,760,761,5,24,0,0,761,763,3,110,55,0,762,760,1,0,0,0,762,763,1,0, + 0,0,763,764,1,0,0,0,764,765,5,25,0,0,765,896,1,0,0,0,766,767,5,13,0,0, + 767,768,5,126,0,0,768,769,3,110,55,0,769,770,5,6,0,0,770,771,3,106,53, + 0,771,772,5,145,0,0,772,896,1,0,0,0,773,774,5,19,0,0,774,896,5,106,0, + 0,775,776,5,43,0,0,776,777,3,110,55,0,777,778,3,146,73,0,778,896,1,0, + 0,0,779,780,5,80,0,0,780,781,5,126,0,0,781,782,3,110,55,0,782,783,5,32, + 0,0,783,786,3,110,55,0,784,785,5,31,0,0,785,787,3,110,55,0,786,784,1, + 0,0,0,786,787,1,0,0,0,787,788,1,0,0,0,788,789,5,145,0,0,789,896,1,0,0, + 0,790,791,5,83,0,0,791,896,5,106,0,0,792,793,5,88,0,0,793,794,5,126,0, + 0,794,795,7,9,0,0,795,796,3,160,80,0,796,797,5,32,0,0,797,798,3,110,55, + 0,798,799,5,145,0,0,799,896,1,0,0,0,800,801,3,154,77,0,801,803,5,126, + 0,0,802,804,3,108,54,0,803,802,1,0,0,0,803,804,1,0,0,0,804,805,1,0,0, + 0,805,806,5,145,0,0,806,815,1,0,0,0,807,809,5,126,0,0,808,810,5,23,0, + 0,809,808,1,0,0,0,809,810,1,0,0,0,810,812,1,0,0,0,811,813,3,112,56,0, + 812,811,1,0,0,0,812,813,1,0,0,0,813,814,1,0,0,0,814,816,5,145,0,0,815, + 807,1,0,0,0,815,816,1,0,0,0,816,817,1,0,0,0,817,818,5,64,0,0,818,819, + 5,126,0,0,819,820,3,92,46,0,820,821,5,145,0,0,821,896,1,0,0,0,822,823, + 3,154,77,0,823,825,5,126,0,0,824,826,3,108,54,0,825,824,1,0,0,0,825,826, + 1,0,0,0,826,827,1,0,0,0,827,828,5,145,0,0,828,837,1,0,0,0,829,831,5,126, + 0,0,830,832,5,23,0,0,831,830,1,0,0,0,831,832,1,0,0,0,832,834,1,0,0,0, + 833,835,3,112,56,0,834,833,1,0,0,0,834,835,1,0,0,0,835,836,1,0,0,0,836, + 838,5,145,0,0,837,829,1,0,0,0,837,838,1,0,0,0,838,839,1,0,0,0,839,840, + 5,64,0,0,840,841,3,154,77,0,841,896,1,0,0,0,842,848,3,154,77,0,843,845, + 5,126,0,0,844,846,3,108,54,0,845,844,1,0,0,0,845,846,1,0,0,0,846,847, + 1,0,0,0,847,849,5,145,0,0,848,843,1,0,0,0,848,849,1,0,0,0,849,850,1,0, + 0,0,850,852,5,126,0,0,851,853,5,23,0,0,852,851,1,0,0,0,852,853,1,0,0, + 0,853,855,1,0,0,0,854,856,3,112,56,0,855,854,1,0,0,0,855,856,1,0,0,0, + 856,857,1,0,0,0,857,858,5,145,0,0,858,896,1,0,0,0,859,896,3,118,59,0, + 860,896,3,162,81,0,861,896,3,144,72,0,862,863,5,114,0,0,863,896,3,110, + 55,19,864,865,5,56,0,0,865,896,3,110,55,13,866,867,3,134,67,0,867,868, + 5,116,0,0,868,870,1,0,0,0,869,866,1,0,0,0,869,870,1,0,0,0,870,871,1,0, + 0,0,871,896,5,108,0,0,872,873,5,126,0,0,873,874,3,38,19,0,874,875,5,145, + 0,0,875,896,1,0,0,0,876,877,5,126,0,0,877,878,3,110,55,0,878,879,5,145, + 0,0,879,896,1,0,0,0,880,881,5,126,0,0,881,882,3,108,54,0,882,883,5,145, + 0,0,883,896,1,0,0,0,884,886,5,125,0,0,885,887,3,108,54,0,886,885,1,0, + 0,0,886,887,1,0,0,0,887,888,1,0,0,0,888,896,5,144,0,0,889,891,5,124,0, + 0,890,892,3,34,17,0,891,890,1,0,0,0,891,892,1,0,0,0,892,893,1,0,0,0,893, + 896,5,143,0,0,894,896,3,126,63,0,895,746,1,0,0,0,895,766,1,0,0,0,895, + 773,1,0,0,0,895,775,1,0,0,0,895,779,1,0,0,0,895,790,1,0,0,0,895,792,1, + 0,0,0,895,800,1,0,0,0,895,822,1,0,0,0,895,842,1,0,0,0,895,859,1,0,0,0, + 895,860,1,0,0,0,895,861,1,0,0,0,895,862,1,0,0,0,895,864,1,0,0,0,895,869, + 1,0,0,0,895,872,1,0,0,0,895,876,1,0,0,0,895,880,1,0,0,0,895,884,1,0,0, + 0,895,889,1,0,0,0,895,894,1,0,0,0,896,1001,1,0,0,0,897,901,10,18,0,0, + 898,902,5,108,0,0,899,902,5,147,0,0,900,902,5,134,0,0,901,898,1,0,0,0, + 901,899,1,0,0,0,901,900,1,0,0,0,902,903,1,0,0,0,903,1000,3,110,55,19, + 904,908,10,17,0,0,905,909,5,135,0,0,906,909,5,114,0,0,907,909,5,113,0, + 0,908,905,1,0,0,0,908,906,1,0,0,0,908,907,1,0,0,0,909,910,1,0,0,0,910, + 1000,3,110,55,18,911,936,10,16,0,0,912,937,5,117,0,0,913,937,5,118,0, + 0,914,937,5,129,0,0,915,937,5,127,0,0,916,937,5,128,0,0,917,937,5,119, + 0,0,918,937,5,120,0,0,919,921,5,56,0,0,920,919,1,0,0,0,920,921,1,0,0, + 0,921,922,1,0,0,0,922,924,5,40,0,0,923,925,5,14,0,0,924,923,1,0,0,0,924, + 925,1,0,0,0,925,937,1,0,0,0,926,928,5,56,0,0,927,926,1,0,0,0,927,928, + 1,0,0,0,928,929,1,0,0,0,929,937,7,10,0,0,930,937,5,141,0,0,931,937,5, + 142,0,0,932,937,5,131,0,0,933,937,5,122,0,0,934,937,5,123,0,0,935,937, + 5,130,0,0,936,912,1,0,0,0,936,913,1,0,0,0,936,914,1,0,0,0,936,915,1,0, + 0,0,936,916,1,0,0,0,936,917,1,0,0,0,936,918,1,0,0,0,936,920,1,0,0,0,936, + 927,1,0,0,0,936,930,1,0,0,0,936,931,1,0,0,0,936,932,1,0,0,0,936,933,1, + 0,0,0,936,934,1,0,0,0,936,935,1,0,0,0,937,938,1,0,0,0,938,1000,3,110, + 55,17,939,940,10,14,0,0,940,941,5,133,0,0,941,1000,3,110,55,15,942,943, + 10,12,0,0,943,944,5,2,0,0,944,1000,3,110,55,13,945,946,10,11,0,0,946, + 947,5,61,0,0,947,1000,3,110,55,12,948,950,10,10,0,0,949,951,5,56,0,0, + 950,949,1,0,0,0,950,951,1,0,0,0,951,952,1,0,0,0,952,953,5,9,0,0,953,954, + 3,110,55,0,954,955,5,2,0,0,955,956,3,110,55,11,956,1000,1,0,0,0,957,958, + 10,9,0,0,958,959,5,136,0,0,959,960,3,110,55,0,960,961,5,111,0,0,961,962, + 3,110,55,9,962,1000,1,0,0,0,963,964,10,25,0,0,964,965,5,125,0,0,965,966, + 3,110,55,0,966,967,5,144,0,0,967,1000,1,0,0,0,968,969,10,24,0,0,969,970, + 5,116,0,0,970,1000,5,104,0,0,971,972,10,23,0,0,972,973,5,116,0,0,973, + 1000,3,154,77,0,974,975,10,22,0,0,975,976,5,132,0,0,976,977,5,125,0,0, + 977,978,3,110,55,0,978,979,5,144,0,0,979,1000,1,0,0,0,980,981,10,21,0, + 0,981,982,5,132,0,0,982,1000,5,104,0,0,983,984,10,20,0,0,984,985,5,132, + 0,0,985,1000,3,154,77,0,986,987,10,15,0,0,987,989,5,44,0,0,988,990,5, + 56,0,0,989,988,1,0,0,0,989,990,1,0,0,0,990,991,1,0,0,0,991,1000,5,57, + 0,0,992,997,10,8,0,0,993,994,5,6,0,0,994,998,3,154,77,0,995,996,5,6,0, + 0,996,998,5,106,0,0,997,993,1,0,0,0,997,995,1,0,0,0,998,1000,1,0,0,0, + 999,897,1,0,0,0,999,904,1,0,0,0,999,911,1,0,0,0,999,939,1,0,0,0,999,942, + 1,0,0,0,999,945,1,0,0,0,999,948,1,0,0,0,999,957,1,0,0,0,999,963,1,0,0, + 0,999,968,1,0,0,0,999,971,1,0,0,0,999,974,1,0,0,0,999,980,1,0,0,0,999, + 983,1,0,0,0,999,986,1,0,0,0,999,992,1,0,0,0,1000,1003,1,0,0,0,1001,999, + 1,0,0,0,1001,1002,1,0,0,0,1002,111,1,0,0,0,1003,1001,1,0,0,0,1004,1009, + 3,114,57,0,1005,1006,5,112,0,0,1006,1008,3,114,57,0,1007,1005,1,0,0,0, + 1008,1011,1,0,0,0,1009,1007,1,0,0,0,1009,1010,1,0,0,0,1010,1013,1,0,0, + 0,1011,1009,1,0,0,0,1012,1014,5,112,0,0,1013,1012,1,0,0,0,1013,1014,1, + 0,0,0,1014,113,1,0,0,0,1015,1018,3,116,58,0,1016,1018,3,110,55,0,1017, + 1015,1,0,0,0,1017,1016,1,0,0,0,1018,115,1,0,0,0,1019,1020,5,126,0,0,1020, + 1025,3,154,77,0,1021,1022,5,112,0,0,1022,1024,3,154,77,0,1023,1021,1, + 0,0,0,1024,1027,1,0,0,0,1025,1023,1,0,0,0,1025,1026,1,0,0,0,1026,1029, + 1,0,0,0,1027,1025,1,0,0,0,1028,1030,5,112,0,0,1029,1028,1,0,0,0,1029, + 1030,1,0,0,0,1030,1031,1,0,0,0,1031,1032,5,145,0,0,1032,1045,1,0,0,0, + 1033,1038,3,154,77,0,1034,1035,5,112,0,0,1035,1037,3,154,77,0,1036,1034, + 1,0,0,0,1037,1040,1,0,0,0,1038,1036,1,0,0,0,1038,1039,1,0,0,0,1039,1042, + 1,0,0,0,1040,1038,1,0,0,0,1041,1043,5,112,0,0,1042,1041,1,0,0,0,1042, + 1043,1,0,0,0,1043,1045,1,0,0,0,1044,1019,1,0,0,0,1044,1033,1,0,0,0,1045, + 1046,1,0,0,0,1046,1047,5,107,0,0,1047,1048,3,110,55,0,1048,117,1,0,0, + 0,1049,1050,5,128,0,0,1050,1054,3,154,77,0,1051,1053,3,120,60,0,1052, + 1051,1,0,0,0,1053,1056,1,0,0,0,1054,1052,1,0,0,0,1054,1055,1,0,0,0,1055, + 1057,1,0,0,0,1056,1054,1,0,0,0,1057,1058,5,147,0,0,1058,1059,5,120,0, + 0,1059,1078,1,0,0,0,1060,1061,5,128,0,0,1061,1065,3,154,77,0,1062,1064, + 3,120,60,0,1063,1062,1,0,0,0,1064,1067,1,0,0,0,1065,1063,1,0,0,0,1065, + 1066,1,0,0,0,1066,1068,1,0,0,0,1067,1065,1,0,0,0,1068,1070,5,120,0,0, + 1069,1071,3,118,59,0,1070,1069,1,0,0,0,1070,1071,1,0,0,0,1071,1072,1, + 0,0,0,1072,1073,5,128,0,0,1073,1074,5,147,0,0,1074,1075,3,154,77,0,1075, + 1076,5,120,0,0,1076,1078,1,0,0,0,1077,1049,1,0,0,0,1077,1060,1,0,0,0, + 1078,119,1,0,0,0,1079,1080,3,154,77,0,1080,1081,5,118,0,0,1081,1082,3, + 160,80,0,1082,1091,1,0,0,0,1083,1084,3,154,77,0,1084,1085,5,118,0,0,1085, + 1086,5,124,0,0,1086,1087,3,110,55,0,1087,1088,5,143,0,0,1088,1091,1,0, + 0,0,1089,1091,3,154,77,0,1090,1079,1,0,0,0,1090,1083,1,0,0,0,1090,1089, + 1,0,0,0,1091,121,1,0,0,0,1092,1097,3,124,62,0,1093,1094,5,112,0,0,1094, + 1096,3,124,62,0,1095,1093,1,0,0,0,1096,1099,1,0,0,0,1097,1095,1,0,0,0, + 1097,1098,1,0,0,0,1098,1101,1,0,0,0,1099,1097,1,0,0,0,1100,1102,5,112, + 0,0,1101,1100,1,0,0,0,1101,1102,1,0,0,0,1102,123,1,0,0,0,1103,1104,3, + 154,77,0,1104,1105,5,6,0,0,1105,1106,5,126,0,0,1106,1107,3,38,19,0,1107, + 1108,5,145,0,0,1108,1114,1,0,0,0,1109,1110,3,110,55,0,1110,1111,5,6,0, + 0,1111,1112,3,154,77,0,1112,1114,1,0,0,0,1113,1103,1,0,0,0,1113,1109, + 1,0,0,0,1114,125,1,0,0,0,1115,1123,3,158,79,0,1116,1117,3,134,67,0,1117, + 1118,5,116,0,0,1118,1120,1,0,0,0,1119,1116,1,0,0,0,1119,1120,1,0,0,0, + 1120,1121,1,0,0,0,1121,1123,3,128,64,0,1122,1115,1,0,0,0,1122,1119,1, + 0,0,0,1123,127,1,0,0,0,1124,1129,3,154,77,0,1125,1126,5,116,0,0,1126, + 1128,3,154,77,0,1127,1125,1,0,0,0,1128,1131,1,0,0,0,1129,1127,1,0,0,0, + 1129,1130,1,0,0,0,1130,129,1,0,0,0,1131,1129,1,0,0,0,1132,1133,6,65,-1, + 0,1133,1142,3,134,67,0,1134,1142,3,132,66,0,1135,1136,5,126,0,0,1136, + 1137,3,38,19,0,1137,1138,5,145,0,0,1138,1142,1,0,0,0,1139,1142,3,118, + 59,0,1140,1142,3,158,79,0,1141,1132,1,0,0,0,1141,1134,1,0,0,0,1141,1135, + 1,0,0,0,1141,1139,1,0,0,0,1141,1140,1,0,0,0,1142,1151,1,0,0,0,1143,1147, + 10,3,0,0,1144,1148,3,152,76,0,1145,1146,5,6,0,0,1146,1148,3,154,77,0, + 1147,1144,1,0,0,0,1147,1145,1,0,0,0,1148,1150,1,0,0,0,1149,1143,1,0,0, + 0,1150,1153,1,0,0,0,1151,1149,1,0,0,0,1151,1152,1,0,0,0,1152,131,1,0, + 0,0,1153,1151,1,0,0,0,1154,1155,3,154,77,0,1155,1157,5,126,0,0,1156,1158, + 3,136,68,0,1157,1156,1,0,0,0,1157,1158,1,0,0,0,1158,1159,1,0,0,0,1159, + 1160,5,145,0,0,1160,133,1,0,0,0,1161,1162,3,138,69,0,1162,1163,5,116, + 0,0,1163,1165,1,0,0,0,1164,1161,1,0,0,0,1164,1165,1,0,0,0,1165,1166,1, + 0,0,0,1166,1167,3,154,77,0,1167,135,1,0,0,0,1168,1173,3,110,55,0,1169, + 1170,5,112,0,0,1170,1172,3,110,55,0,1171,1169,1,0,0,0,1172,1175,1,0,0, + 0,1173,1171,1,0,0,0,1173,1174,1,0,0,0,1174,1177,1,0,0,0,1175,1173,1,0, + 0,0,1176,1178,5,112,0,0,1177,1176,1,0,0,0,1177,1178,1,0,0,0,1178,137, + 1,0,0,0,1179,1180,3,154,77,0,1180,139,1,0,0,0,1181,1190,5,102,0,0,1182, + 1183,5,116,0,0,1183,1190,7,11,0,0,1184,1185,5,104,0,0,1185,1187,5,116, + 0,0,1186,1188,7,11,0,0,1187,1186,1,0,0,0,1187,1188,1,0,0,0,1188,1190, + 1,0,0,0,1189,1181,1,0,0,0,1189,1182,1,0,0,0,1189,1184,1,0,0,0,1190,141, + 1,0,0,0,1191,1193,7,12,0,0,1192,1191,1,0,0,0,1192,1193,1,0,0,0,1193,1200, + 1,0,0,0,1194,1201,3,140,70,0,1195,1201,5,103,0,0,1196,1201,5,104,0,0, + 1197,1201,5,105,0,0,1198,1201,5,41,0,0,1199,1201,5,55,0,0,1200,1194,1, + 0,0,0,1200,1195,1,0,0,0,1200,1196,1,0,0,0,1200,1197,1,0,0,0,1200,1198, + 1,0,0,0,1200,1199,1,0,0,0,1201,143,1,0,0,0,1202,1206,3,142,71,0,1203, + 1206,5,106,0,0,1204,1206,5,57,0,0,1205,1202,1,0,0,0,1205,1203,1,0,0,0, + 1205,1204,1,0,0,0,1206,145,1,0,0,0,1207,1208,7,13,0,0,1208,147,1,0,0, + 0,1209,1210,7,14,0,0,1210,149,1,0,0,0,1211,1212,7,15,0,0,1212,151,1,0, + 0,0,1213,1216,5,101,0,0,1214,1216,3,150,75,0,1215,1213,1,0,0,0,1215,1214, + 1,0,0,0,1216,153,1,0,0,0,1217,1221,5,101,0,0,1218,1221,3,146,73,0,1219, + 1221,3,148,74,0,1220,1217,1,0,0,0,1220,1218,1,0,0,0,1220,1219,1,0,0,0, + 1221,155,1,0,0,0,1222,1223,3,160,80,0,1223,1224,5,118,0,0,1224,1225,3, + 142,71,0,1225,157,1,0,0,0,1226,1227,5,124,0,0,1227,1228,3,154,77,0,1228, + 1229,5,143,0,0,1229,159,1,0,0,0,1230,1233,5,106,0,0,1231,1233,3,162,81, + 0,1232,1230,1,0,0,0,1232,1231,1,0,0,0,1233,161,1,0,0,0,1234,1238,5,138, + 0,0,1235,1237,3,164,82,0,1236,1235,1,0,0,0,1237,1240,1,0,0,0,1238,1236, + 1,0,0,0,1238,1239,1,0,0,0,1239,1241,1,0,0,0,1240,1238,1,0,0,0,1241,1242, + 5,140,0,0,1242,163,1,0,0,0,1243,1244,5,153,0,0,1244,1245,3,110,55,0,1245, + 1246,5,143,0,0,1246,1249,1,0,0,0,1247,1249,5,152,0,0,1248,1243,1,0,0, + 0,1248,1247,1,0,0,0,1249,165,1,0,0,0,1250,1254,5,139,0,0,1251,1253,3, + 168,84,0,1252,1251,1,0,0,0,1253,1256,1,0,0,0,1254,1252,1,0,0,0,1254,1255, + 1,0,0,0,1255,1257,1,0,0,0,1256,1254,1,0,0,0,1257,1258,5,0,0,1,1258,167, + 1,0,0,0,1259,1260,5,155,0,0,1260,1261,3,110,55,0,1261,1262,5,143,0,0, + 1262,1265,1,0,0,0,1263,1265,5,154,0,0,1264,1259,1,0,0,0,1264,1263,1,0, + 0,0,1265,169,1,0,0,0,162,173,180,189,196,200,212,216,219,228,236,243, + 247,253,258,266,273,279,291,299,313,317,322,332,341,344,348,351,355,358, + 361,364,367,371,375,378,381,384,388,391,400,406,427,444,461,467,473,484, + 486,497,500,506,514,520,522,526,531,534,537,541,545,548,550,553,557,561, + 564,566,568,573,584,590,597,602,606,610,616,618,625,633,636,639,658,672, + 688,692,703,707,718,722,729,733,740,744,749,758,762,786,803,809,812,815, + 825,831,834,837,845,848,852,855,869,886,891,895,901,908,920,924,927,936, + 950,989,997,999,1001,1009,1013,1017,1025,1029,1038,1042,1044,1054,1065, + 1070,1077,1090,1097,1101,1113,1119,1122,1129,1141,1147,1151,1157,1164, + 1173,1177,1187,1189,1192,1200,1205,1215,1220,1232,1238,1248,1254,1264 }; staticData->serializedATN = antlr4::atn::SerializedATNView(serializedATNSegment, sizeof(serializedATNSegment) / sizeof(serializedATNSegment[0])); @@ -671,20 +678,20 @@ HogQLParser::ProgramContext* HogQLParser::program() { }); try { enterOuterAlt(_localctx, 1); - setState(171); + setState(173); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -2) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106351341731839) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 263297) != 0)) { - setState(168); + setState(170); declaration(); - setState(173); + setState(175); _errHandler->sync(this); _la = _input->LA(1); } - setState(174); + setState(176); match(HogQLParser::EOF); } @@ -736,12 +743,12 @@ HogQLParser::DeclarationContext* HogQLParser::declaration() { exitRule(); }); try { - setState(178); + setState(180); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LET: { enterOuterAlt(_localctx, 1); - setState(176); + setState(178); varDecl(); break; } @@ -861,7 +868,7 @@ HogQLParser::DeclarationContext* HogQLParser::declaration() { case HogQLParser::QUOTE_SINGLE_TEMPLATE: case HogQLParser::SEMICOLON: { enterOuterAlt(_localctx, 2); - setState(177); + setState(179); statement(); break; } @@ -916,7 +923,7 @@ HogQLParser::ExpressionContext* HogQLParser::expression() { }); try { enterOuterAlt(_localctx, 1); - setState(180); + setState(182); columnExpr(0); } @@ -982,20 +989,20 @@ HogQLParser::VarDeclContext* HogQLParser::varDecl() { }); try { enterOuterAlt(_localctx, 1); - setState(182); + setState(184); match(HogQLParser::LET); - setState(183); + setState(185); identifier(); - setState(187); + setState(189); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COLON) { - setState(184); + setState(186); match(HogQLParser::COLON); - setState(185); + setState(187); match(HogQLParser::EQ_SINGLE); - setState(186); + setState(188); expression(); } @@ -1059,28 +1066,28 @@ HogQLParser::IdentifierListContext* HogQLParser::identifierList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(189); + setState(191); identifier(); - setState(194); + setState(196); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 3, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(190); + setState(192); match(HogQLParser::COMMA); - setState(191); + setState(193); identifier(); } - setState(196); + setState(198); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 3, _ctx); } - setState(198); + setState(200); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(197); + setState(199); match(HogQLParser::COMMA); } @@ -1112,6 +1119,10 @@ HogQLParser::WhileStmtContext* HogQLParser::StatementContext::whileStmt() { return getRuleContext(0); } +HogQLParser::ForInStmtContext* HogQLParser::StatementContext::forInStmt() { + return getRuleContext(0); +} + HogQLParser::ForStmtContext* HogQLParser::StatementContext::forStmt() { return getRuleContext(0); } @@ -1161,68 +1172,75 @@ HogQLParser::StatementContext* HogQLParser::statement() { exitRule(); }); try { - setState(209); + setState(212); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 5, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(200); + setState(202); returnStmt(); break; } case 2: { enterOuterAlt(_localctx, 2); - setState(201); + setState(203); ifStmt(); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(202); + setState(204); whileStmt(); break; } case 4: { enterOuterAlt(_localctx, 4); - setState(203); - forStmt(); + setState(205); + forInStmt(); break; } case 5: { enterOuterAlt(_localctx, 5); - setState(204); - funcStmt(); + setState(206); + forStmt(); break; } case 6: { enterOuterAlt(_localctx, 6); - setState(205); - varAssignment(); + setState(207); + funcStmt(); break; } case 7: { enterOuterAlt(_localctx, 7); - setState(206); - block(); + setState(208); + varAssignment(); break; } case 8: { enterOuterAlt(_localctx, 8); - setState(207); - exprStmt(); + setState(209); + block(); break; } case 9: { enterOuterAlt(_localctx, 9); - setState(208); + setState(210); + exprStmt(); + break; + } + + case 10: { + enterOuterAlt(_localctx, 10); + setState(211); emptyStmt(); break; } @@ -1285,14 +1303,14 @@ HogQLParser::ReturnStmtContext* HogQLParser::returnStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(211); + setState(214); match(HogQLParser::RETURN); - setState(213); + setState(216); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 6, _ctx)) { case 1: { - setState(212); + setState(215); expression(); break; } @@ -1300,12 +1318,12 @@ HogQLParser::ReturnStmtContext* HogQLParser::returnStmt() { default: break; } - setState(216); + setState(219); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 7, _ctx)) { case 1: { - setState(215); + setState(218); match(HogQLParser::SEMICOLON); break; } @@ -1384,24 +1402,24 @@ HogQLParser::IfStmtContext* HogQLParser::ifStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(218); + setState(221); match(HogQLParser::IF); - setState(219); + setState(222); match(HogQLParser::LPAREN); - setState(220); + setState(223); expression(); - setState(221); + setState(224); match(HogQLParser::RPAREN); - setState(222); - statement(); setState(225); + statement(); + setState(228); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 8, _ctx)) { case 1: { - setState(223); + setState(226); match(HogQLParser::ELSE); - setState(224); + setState(227); statement(); break; } @@ -1476,22 +1494,22 @@ HogQLParser::WhileStmtContext* HogQLParser::whileStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(227); + setState(230); match(HogQLParser::WHILE); - setState(228); + setState(231); match(HogQLParser::LPAREN); - setState(229); + setState(232); expression(); - setState(230); + setState(233); match(HogQLParser::RPAREN); - setState(231); + setState(234); statement(); - setState(233); + setState(236); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 9, _ctx)) { case 1: { - setState(232); + setState(235); match(HogQLParser::SEMICOLON); break; } @@ -1591,28 +1609,28 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(235); + setState(238); match(HogQLParser::FOR); - setState(236); + setState(239); match(HogQLParser::LPAREN); - setState(240); + setState(243); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 10, _ctx)) { case 1: { - setState(237); + setState(240); antlrcpp::downCast(_localctx)->initializerVarDeclr = varDecl(); break; } case 2: { - setState(238); + setState(241); antlrcpp::downCast(_localctx)->initializerVarAssignment = varAssignment(); break; } case 3: { - setState(239); + setState(242); antlrcpp::downCast(_localctx)->initializerExpression = expression(); break; } @@ -1620,9 +1638,9 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { default: break; } - setState(242); + setState(245); match(HogQLParser::SEMICOLON); - setState(244); + setState(247); _errHandler->sync(this); _la = _input->LA(1); @@ -1630,29 +1648,29 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(243); + setState(246); antlrcpp::downCast(_localctx)->condition = expression(); } - setState(246); + setState(249); match(HogQLParser::SEMICOLON); - setState(250); + setState(253); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 12, _ctx)) { case 1: { - setState(247); + setState(250); antlrcpp::downCast(_localctx)->incrementVarDeclr = varDecl(); break; } case 2: { - setState(248); + setState(251); antlrcpp::downCast(_localctx)->incrementVarAssignment = varAssignment(); break; } case 3: { - setState(249); + setState(252); antlrcpp::downCast(_localctx)->incrementExpression = expression(); break; } @@ -1660,16 +1678,143 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { default: break; } - setState(252); + setState(255); match(HogQLParser::RPAREN); - setState(253); + setState(256); statement(); - setState(255); + setState(258); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 13, _ctx)) { case 1: { - setState(254); + setState(257); + match(HogQLParser::SEMICOLON); + break; + } + + default: + break; + } + + } + catch (RecognitionException &e) { + _errHandler->reportError(this, e); + _localctx->exception = std::current_exception(); + _errHandler->recover(this, _localctx->exception); + } + + return _localctx; +} + +//----------------- ForInStmtContext ------------------------------------------------------------------ + +HogQLParser::ForInStmtContext::ForInStmtContext(ParserRuleContext *parent, size_t invokingState) + : ParserRuleContext(parent, invokingState) { +} + +tree::TerminalNode* HogQLParser::ForInStmtContext::FOR() { + return getToken(HogQLParser::FOR, 0); +} + +tree::TerminalNode* HogQLParser::ForInStmtContext::LPAREN() { + return getToken(HogQLParser::LPAREN, 0); +} + +tree::TerminalNode* HogQLParser::ForInStmtContext::LET() { + return getToken(HogQLParser::LET, 0); +} + +std::vector HogQLParser::ForInStmtContext::identifier() { + return getRuleContexts(); +} + +HogQLParser::IdentifierContext* HogQLParser::ForInStmtContext::identifier(size_t i) { + return getRuleContext(i); +} + +tree::TerminalNode* HogQLParser::ForInStmtContext::IN() { + return getToken(HogQLParser::IN, 0); +} + +HogQLParser::ExpressionContext* HogQLParser::ForInStmtContext::expression() { + return getRuleContext(0); +} + +tree::TerminalNode* HogQLParser::ForInStmtContext::RPAREN() { + return getToken(HogQLParser::RPAREN, 0); +} + +HogQLParser::StatementContext* HogQLParser::ForInStmtContext::statement() { + return getRuleContext(0); +} + +tree::TerminalNode* HogQLParser::ForInStmtContext::COMMA() { + return getToken(HogQLParser::COMMA, 0); +} + +tree::TerminalNode* HogQLParser::ForInStmtContext::SEMICOLON() { + return getToken(HogQLParser::SEMICOLON, 0); +} + + +size_t HogQLParser::ForInStmtContext::getRuleIndex() const { + return HogQLParser::RuleForInStmt; +} + + +std::any HogQLParser::ForInStmtContext::accept(tree::ParseTreeVisitor *visitor) { + if (auto parserVisitor = dynamic_cast(visitor)) + return parserVisitor->visitForInStmt(this); + else + return visitor->visitChildren(this); +} + +HogQLParser::ForInStmtContext* HogQLParser::forInStmt() { + ForInStmtContext *_localctx = _tracker.createInstance(_ctx, getState()); + enterRule(_localctx, 20, HogQLParser::RuleForInStmt); + size_t _la = 0; + +#if __cplusplus > 201703L + auto onExit = finally([=, this] { +#else + auto onExit = finally([=] { +#endif + exitRule(); + }); + try { + enterOuterAlt(_localctx, 1); + setState(260); + match(HogQLParser::FOR); + setState(261); + match(HogQLParser::LPAREN); + setState(262); + match(HogQLParser::LET); + setState(263); + identifier(); + setState(266); + _errHandler->sync(this); + + _la = _input->LA(1); + if (_la == HogQLParser::COMMA) { + setState(264); + match(HogQLParser::COMMA); + setState(265); + identifier(); + } + setState(268); + match(HogQLParser::IN); + setState(269); + expression(); + setState(270); + match(HogQLParser::RPAREN); + setState(271); + statement(); + setState(273); + _errHandler->sync(this); + + switch (getInterpreter()->adaptivePredict(_input, 15, _ctx)) { + case 1: { + setState(272); match(HogQLParser::SEMICOLON); break; } @@ -1733,7 +1878,7 @@ std::any HogQLParser::FuncStmtContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::FuncStmtContext* HogQLParser::funcStmt() { FuncStmtContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 20, HogQLParser::RuleFuncStmt); + enterRule(_localctx, 22, HogQLParser::RuleFuncStmt); size_t _la = 0; #if __cplusplus > 201703L @@ -1745,25 +1890,25 @@ HogQLParser::FuncStmtContext* HogQLParser::funcStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(257); + setState(275); match(HogQLParser::FN); - setState(258); + setState(276); identifier(); - setState(259); + setState(277); match(HogQLParser::LPAREN); - setState(261); + setState(279); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -181272084561788930) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 201863462911) != 0)) { - setState(260); + setState(278); identifierList(); } - setState(263); + setState(281); match(HogQLParser::RPAREN); - setState(264); + setState(282); block(); } @@ -1813,7 +1958,7 @@ std::any HogQLParser::VarAssignmentContext::accept(tree::ParseTreeVisitor *visit HogQLParser::VarAssignmentContext* HogQLParser::varAssignment() { VarAssignmentContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 22, HogQLParser::RuleVarAssignment); + enterRule(_localctx, 24, HogQLParser::RuleVarAssignment); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -1824,13 +1969,13 @@ HogQLParser::VarAssignmentContext* HogQLParser::varAssignment() { }); try { enterOuterAlt(_localctx, 1); - setState(266); + setState(284); expression(); - setState(267); + setState(285); match(HogQLParser::COLON); - setState(268); + setState(286); match(HogQLParser::EQ_SINGLE); - setState(269); + setState(287); expression(); } @@ -1872,7 +2017,7 @@ std::any HogQLParser::ExprStmtContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::ExprStmtContext* HogQLParser::exprStmt() { ExprStmtContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 24, HogQLParser::RuleExprStmt); + enterRule(_localctx, 26, HogQLParser::RuleExprStmt); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -1883,14 +2028,14 @@ HogQLParser::ExprStmtContext* HogQLParser::exprStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(271); + setState(289); expression(); - setState(273); + setState(291); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 15, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 17, _ctx)) { case 1: { - setState(272); + setState(290); match(HogQLParser::SEMICOLON); break; } @@ -1934,7 +2079,7 @@ std::any HogQLParser::EmptyStmtContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::EmptyStmtContext* HogQLParser::emptyStmt() { EmptyStmtContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 26, HogQLParser::RuleEmptyStmt); + enterRule(_localctx, 28, HogQLParser::RuleEmptyStmt); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -1945,7 +2090,7 @@ HogQLParser::EmptyStmtContext* HogQLParser::emptyStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(275); + setState(293); match(HogQLParser::SEMICOLON); } @@ -1995,7 +2140,7 @@ std::any HogQLParser::BlockContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::BlockContext* HogQLParser::block() { BlockContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 28, HogQLParser::RuleBlock); + enterRule(_localctx, 30, HogQLParser::RuleBlock); size_t _la = 0; #if __cplusplus > 201703L @@ -2007,22 +2152,22 @@ HogQLParser::BlockContext* HogQLParser::block() { }); try { enterOuterAlt(_localctx, 1); - setState(277); + setState(295); match(HogQLParser::LBRACE); - setState(281); + setState(299); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -2) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106351341731839) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 263297) != 0)) { - setState(278); + setState(296); declaration(); - setState(283); + setState(301); _errHandler->sync(this); _la = _input->LA(1); } - setState(284); + setState(302); match(HogQLParser::RBRACE); } @@ -2068,7 +2213,7 @@ std::any HogQLParser::KvPairContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::KvPairContext* HogQLParser::kvPair() { KvPairContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 30, HogQLParser::RuleKvPair); + enterRule(_localctx, 32, HogQLParser::RuleKvPair); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -2079,11 +2224,11 @@ HogQLParser::KvPairContext* HogQLParser::kvPair() { }); try { enterOuterAlt(_localctx, 1); - setState(286); + setState(304); expression(); - setState(287); + setState(305); match(HogQLParser::COLON); - setState(288); + setState(306); expression(); } @@ -2133,7 +2278,7 @@ std::any HogQLParser::KvPairListContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::KvPairListContext* HogQLParser::kvPairList() { KvPairListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 32, HogQLParser::RuleKvPairList); + enterRule(_localctx, 34, HogQLParser::RuleKvPairList); size_t _la = 0; #if __cplusplus > 201703L @@ -2146,28 +2291,28 @@ HogQLParser::KvPairListContext* HogQLParser::kvPairList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(290); + setState(308); kvPair(); - setState(295); + setState(313); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 17, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 19, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(291); + setState(309); match(HogQLParser::COMMA); - setState(292); + setState(310); kvPair(); } - setState(297); + setState(315); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 17, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 19, _ctx); } - setState(299); + setState(317); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(298); + setState(316); match(HogQLParser::COMMA); } @@ -2218,7 +2363,7 @@ std::any HogQLParser::SelectContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::SelectContext* HogQLParser::select() { SelectContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 34, HogQLParser::RuleSelect); + enterRule(_localctx, 36, HogQLParser::RuleSelect); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -2229,23 +2374,23 @@ HogQLParser::SelectContext* HogQLParser::select() { }); try { enterOuterAlt(_localctx, 1); - setState(304); + setState(322); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 19, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 21, _ctx)) { case 1: { - setState(301); + setState(319); selectUnionStmt(); break; } case 2: { - setState(302); + setState(320); selectStmt(); break; } case 3: { - setState(303); + setState(321); hogqlxTagElement(); break; } @@ -2253,7 +2398,7 @@ HogQLParser::SelectContext* HogQLParser::select() { default: break; } - setState(306); + setState(324); match(HogQLParser::EOF); } @@ -2311,7 +2456,7 @@ std::any HogQLParser::SelectUnionStmtContext::accept(tree::ParseTreeVisitor *vis HogQLParser::SelectUnionStmtContext* HogQLParser::selectUnionStmt() { SelectUnionStmtContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 36, HogQLParser::RuleSelectUnionStmt); + enterRule(_localctx, 38, HogQLParser::RuleSelectUnionStmt); size_t _la = 0; #if __cplusplus > 201703L @@ -2323,19 +2468,19 @@ HogQLParser::SelectUnionStmtContext* HogQLParser::selectUnionStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(308); + setState(326); selectStmtWithParens(); - setState(314); + setState(332); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::UNION) { - setState(309); + setState(327); match(HogQLParser::UNION); - setState(310); + setState(328); match(HogQLParser::ALL); - setState(311); + setState(329); selectStmtWithParens(); - setState(316); + setState(334); _errHandler->sync(this); _la = _input->LA(1); } @@ -2391,7 +2536,7 @@ std::any HogQLParser::SelectStmtWithParensContext::accept(tree::ParseTreeVisitor HogQLParser::SelectStmtWithParensContext* HogQLParser::selectStmtWithParens() { SelectStmtWithParensContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 38, HogQLParser::RuleSelectStmtWithParens); + enterRule(_localctx, 40, HogQLParser::RuleSelectStmtWithParens); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -2401,31 +2546,31 @@ HogQLParser::SelectStmtWithParensContext* HogQLParser::selectStmtWithParens() { exitRule(); }); try { - setState(323); + setState(341); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::SELECT: case HogQLParser::WITH: { enterOuterAlt(_localctx, 1); - setState(317); + setState(335); selectStmt(); break; } case HogQLParser::LPAREN: { enterOuterAlt(_localctx, 2); - setState(318); + setState(336); match(HogQLParser::LPAREN); - setState(319); + setState(337); selectUnionStmt(); - setState(320); + setState(338); match(HogQLParser::RPAREN); break; } case HogQLParser::LBRACE: { enterOuterAlt(_localctx, 3); - setState(322); + setState(340); placeholder(); break; } @@ -2549,7 +2694,7 @@ std::any HogQLParser::SelectStmtContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { SelectStmtContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 40, HogQLParser::RuleSelectStmt); + enterRule(_localctx, 42, HogQLParser::RuleSelectStmt); size_t _la = 0; #if __cplusplus > 201703L @@ -2561,22 +2706,22 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(326); + setState(344); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WITH) { - setState(325); + setState(343); antlrcpp::downCast(_localctx)->with = withClause(); } - setState(328); + setState(346); match(HogQLParser::SELECT); - setState(330); + setState(348); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 23, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 25, _ctx)) { case 1: { - setState(329); + setState(347); match(HogQLParser::DISTINCT); break; } @@ -2584,12 +2729,12 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(333); + setState(351); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 24, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 26, _ctx)) { case 1: { - setState(332); + setState(350); topClause(); break; } @@ -2597,57 +2742,57 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(335); + setState(353); antlrcpp::downCast(_localctx)->columns = columnExprList(); - setState(337); + setState(355); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::FROM) { - setState(336); + setState(354); antlrcpp::downCast(_localctx)->from = fromClause(); } - setState(340); + setState(358); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 567347999932448) != 0)) { - setState(339); + setState(357); arrayJoinClause(); } - setState(343); + setState(361); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::PREWHERE) { - setState(342); + setState(360); prewhereClause(); } - setState(346); + setState(364); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WHERE) { - setState(345); + setState(363); antlrcpp::downCast(_localctx)->where = whereClause(); } - setState(349); + setState(367); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::GROUP) { - setState(348); + setState(366); groupByClause(); } - setState(353); + setState(371); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 30, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 32, _ctx)) { case 1: { - setState(351); + setState(369); match(HogQLParser::WITH); - setState(352); + setState(370); _la = _input->LA(1); if (!(_la == HogQLParser::CUBE @@ -2664,51 +2809,51 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(357); + setState(375); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WITH) { - setState(355); + setState(373); match(HogQLParser::WITH); - setState(356); + setState(374); match(HogQLParser::TOTALS); } - setState(360); + setState(378); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::HAVING) { - setState(359); + setState(377); havingClause(); } - setState(363); + setState(381); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WINDOW) { - setState(362); + setState(380); windowClause(); } - setState(366); + setState(384); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ORDER) { - setState(365); + setState(383); orderByClause(); } - setState(370); + setState(388); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LIMIT: { - setState(368); + setState(386); limitAndOffsetClause(); break; } case HogQLParser::OFFSET: { - setState(369); + setState(387); offsetOnlyClause(); break; } @@ -2723,12 +2868,12 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(373); + setState(391); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::SETTINGS) { - setState(372); + setState(390); settingsClause(); } @@ -2771,7 +2916,7 @@ std::any HogQLParser::WithClauseContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::WithClauseContext* HogQLParser::withClause() { WithClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 42, HogQLParser::RuleWithClause); + enterRule(_localctx, 44, HogQLParser::RuleWithClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -2782,9 +2927,9 @@ HogQLParser::WithClauseContext* HogQLParser::withClause() { }); try { enterOuterAlt(_localctx, 1); - setState(375); + setState(393); match(HogQLParser::WITH); - setState(376); + setState(394); withExprList(); } @@ -2834,7 +2979,7 @@ std::any HogQLParser::TopClauseContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::TopClauseContext* HogQLParser::topClause() { TopClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 44, HogQLParser::RuleTopClause); + enterRule(_localctx, 46, HogQLParser::RuleTopClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -2845,18 +2990,18 @@ HogQLParser::TopClauseContext* HogQLParser::topClause() { }); try { enterOuterAlt(_localctx, 1); - setState(378); + setState(396); match(HogQLParser::TOP); - setState(379); + setState(397); match(HogQLParser::DECIMAL_LITERAL); - setState(382); + setState(400); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 37, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 39, _ctx)) { case 1: { - setState(380); + setState(398); match(HogQLParser::WITH); - setState(381); + setState(399); match(HogQLParser::TIES); break; } @@ -2904,7 +3049,7 @@ std::any HogQLParser::FromClauseContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::FromClauseContext* HogQLParser::fromClause() { FromClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 46, HogQLParser::RuleFromClause); + enterRule(_localctx, 48, HogQLParser::RuleFromClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -2915,9 +3060,9 @@ HogQLParser::FromClauseContext* HogQLParser::fromClause() { }); try { enterOuterAlt(_localctx, 1); - setState(384); + setState(402); match(HogQLParser::FROM); - setState(385); + setState(403); joinExpr(0); } @@ -2971,7 +3116,7 @@ std::any HogQLParser::ArrayJoinClauseContext::accept(tree::ParseTreeVisitor *vis HogQLParser::ArrayJoinClauseContext* HogQLParser::arrayJoinClause() { ArrayJoinClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 48, HogQLParser::RuleArrayJoinClause); + enterRule(_localctx, 50, HogQLParser::RuleArrayJoinClause); size_t _la = 0; #if __cplusplus > 201703L @@ -2983,14 +3128,14 @@ HogQLParser::ArrayJoinClauseContext* HogQLParser::arrayJoinClause() { }); try { enterOuterAlt(_localctx, 1); - setState(388); + setState(406); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::INNER || _la == HogQLParser::LEFT) { - setState(387); + setState(405); _la = _input->LA(1); if (!(_la == HogQLParser::INNER @@ -3002,11 +3147,11 @@ HogQLParser::ArrayJoinClauseContext* HogQLParser::arrayJoinClause() { consume(); } } - setState(390); + setState(408); match(HogQLParser::ARRAY); - setState(391); + setState(409); match(HogQLParser::JOIN); - setState(392); + setState(410); columnExprList(); } @@ -3092,7 +3237,7 @@ std::any HogQLParser::WindowClauseContext::accept(tree::ParseTreeVisitor *visito HogQLParser::WindowClauseContext* HogQLParser::windowClause() { WindowClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 50, HogQLParser::RuleWindowClause); + enterRule(_localctx, 52, HogQLParser::RuleWindowClause); size_t _la = 0; #if __cplusplus > 201703L @@ -3104,35 +3249,35 @@ HogQLParser::WindowClauseContext* HogQLParser::windowClause() { }); try { enterOuterAlt(_localctx, 1); - setState(394); + setState(412); match(HogQLParser::WINDOW); - setState(395); + setState(413); identifier(); - setState(396); + setState(414); match(HogQLParser::AS); - setState(397); + setState(415); match(HogQLParser::LPAREN); - setState(398); + setState(416); windowExpr(); - setState(399); + setState(417); match(HogQLParser::RPAREN); - setState(409); + setState(427); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::COMMA) { - setState(400); + setState(418); match(HogQLParser::COMMA); - setState(401); + setState(419); identifier(); - setState(402); + setState(420); match(HogQLParser::AS); - setState(403); + setState(421); match(HogQLParser::LPAREN); - setState(404); + setState(422); windowExpr(); - setState(405); + setState(423); match(HogQLParser::RPAREN); - setState(411); + setState(429); _errHandler->sync(this); _la = _input->LA(1); } @@ -3176,7 +3321,7 @@ std::any HogQLParser::PrewhereClauseContext::accept(tree::ParseTreeVisitor *visi HogQLParser::PrewhereClauseContext* HogQLParser::prewhereClause() { PrewhereClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 52, HogQLParser::RulePrewhereClause); + enterRule(_localctx, 54, HogQLParser::RulePrewhereClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -3187,9 +3332,9 @@ HogQLParser::PrewhereClauseContext* HogQLParser::prewhereClause() { }); try { enterOuterAlt(_localctx, 1); - setState(412); + setState(430); match(HogQLParser::PREWHERE); - setState(413); + setState(431); columnExpr(0); } @@ -3231,7 +3376,7 @@ std::any HogQLParser::WhereClauseContext::accept(tree::ParseTreeVisitor *visitor HogQLParser::WhereClauseContext* HogQLParser::whereClause() { WhereClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 54, HogQLParser::RuleWhereClause); + enterRule(_localctx, 56, HogQLParser::RuleWhereClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -3242,9 +3387,9 @@ HogQLParser::WhereClauseContext* HogQLParser::whereClause() { }); try { enterOuterAlt(_localctx, 1); - setState(415); + setState(433); match(HogQLParser::WHERE); - setState(416); + setState(434); columnExpr(0); } @@ -3306,7 +3451,7 @@ std::any HogQLParser::GroupByClauseContext::accept(tree::ParseTreeVisitor *visit HogQLParser::GroupByClauseContext* HogQLParser::groupByClause() { GroupByClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 56, HogQLParser::RuleGroupByClause); + enterRule(_localctx, 58, HogQLParser::RuleGroupByClause); size_t _la = 0; #if __cplusplus > 201703L @@ -3318,15 +3463,15 @@ HogQLParser::GroupByClauseContext* HogQLParser::groupByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(418); + setState(436); match(HogQLParser::GROUP); - setState(419); + setState(437); match(HogQLParser::BY); - setState(426); + setState(444); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 40, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 42, _ctx)) { case 1: { - setState(420); + setState(438); _la = _input->LA(1); if (!(_la == HogQLParser::CUBE @@ -3337,17 +3482,17 @@ HogQLParser::GroupByClauseContext* HogQLParser::groupByClause() { _errHandler->reportMatch(this); consume(); } - setState(421); + setState(439); match(HogQLParser::LPAREN); - setState(422); + setState(440); columnExprList(); - setState(423); + setState(441); match(HogQLParser::RPAREN); break; } case 2: { - setState(425); + setState(443); columnExprList(); break; } @@ -3395,7 +3540,7 @@ std::any HogQLParser::HavingClauseContext::accept(tree::ParseTreeVisitor *visito HogQLParser::HavingClauseContext* HogQLParser::havingClause() { HavingClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 58, HogQLParser::RuleHavingClause); + enterRule(_localctx, 60, HogQLParser::RuleHavingClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -3406,9 +3551,9 @@ HogQLParser::HavingClauseContext* HogQLParser::havingClause() { }); try { enterOuterAlt(_localctx, 1); - setState(428); + setState(446); match(HogQLParser::HAVING); - setState(429); + setState(447); columnExpr(0); } @@ -3454,7 +3599,7 @@ std::any HogQLParser::OrderByClauseContext::accept(tree::ParseTreeVisitor *visit HogQLParser::OrderByClauseContext* HogQLParser::orderByClause() { OrderByClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 60, HogQLParser::RuleOrderByClause); + enterRule(_localctx, 62, HogQLParser::RuleOrderByClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -3465,11 +3610,11 @@ HogQLParser::OrderByClauseContext* HogQLParser::orderByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(431); + setState(449); match(HogQLParser::ORDER); - setState(432); + setState(450); match(HogQLParser::BY); - setState(433); + setState(451); orderExprList(); } @@ -3515,7 +3660,7 @@ std::any HogQLParser::ProjectionOrderByClauseContext::accept(tree::ParseTreeVisi HogQLParser::ProjectionOrderByClauseContext* HogQLParser::projectionOrderByClause() { ProjectionOrderByClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 62, HogQLParser::RuleProjectionOrderByClause); + enterRule(_localctx, 64, HogQLParser::RuleProjectionOrderByClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -3526,11 +3671,11 @@ HogQLParser::ProjectionOrderByClauseContext* HogQLParser::projectionOrderByClaus }); try { enterOuterAlt(_localctx, 1); - setState(435); + setState(453); match(HogQLParser::ORDER); - setState(436); + setState(454); match(HogQLParser::BY); - setState(437); + setState(455); columnExprList(); } @@ -3600,7 +3745,7 @@ std::any HogQLParser::LimitAndOffsetClauseContext::accept(tree::ParseTreeVisitor HogQLParser::LimitAndOffsetClauseContext* HogQLParser::limitAndOffsetClause() { LimitAndOffsetClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 64, HogQLParser::RuleLimitAndOffsetClause); + enterRule(_localctx, 66, HogQLParser::RuleLimitAndOffsetClause); size_t _la = 0; #if __cplusplus > 201703L @@ -3611,40 +3756,40 @@ HogQLParser::LimitAndOffsetClauseContext* HogQLParser::limitAndOffsetClause() { exitRule(); }); try { - setState(468); + setState(486); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 45, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 47, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(439); + setState(457); match(HogQLParser::LIMIT); - setState(440); + setState(458); columnExpr(0); - setState(443); + setState(461); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(441); + setState(459); match(HogQLParser::COMMA); - setState(442); + setState(460); columnExpr(0); } - setState(449); + setState(467); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::WITH: { - setState(445); + setState(463); match(HogQLParser::WITH); - setState(446); + setState(464); match(HogQLParser::TIES); break; } case HogQLParser::BY: { - setState(447); + setState(465); match(HogQLParser::BY); - setState(448); + setState(466); columnExprList(); break; } @@ -3664,45 +3809,45 @@ HogQLParser::LimitAndOffsetClauseContext* HogQLParser::limitAndOffsetClause() { case 2: { enterOuterAlt(_localctx, 2); - setState(451); + setState(469); match(HogQLParser::LIMIT); - setState(452); + setState(470); columnExpr(0); - setState(455); + setState(473); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WITH) { - setState(453); + setState(471); match(HogQLParser::WITH); - setState(454); + setState(472); match(HogQLParser::TIES); } - setState(457); + setState(475); match(HogQLParser::OFFSET); - setState(458); + setState(476); columnExpr(0); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(460); + setState(478); match(HogQLParser::LIMIT); - setState(461); + setState(479); columnExpr(0); - setState(462); + setState(480); match(HogQLParser::OFFSET); - setState(463); + setState(481); columnExpr(0); - setState(466); + setState(484); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::BY) { - setState(464); + setState(482); match(HogQLParser::BY); - setState(465); + setState(483); columnExprList(); } break; @@ -3751,7 +3896,7 @@ std::any HogQLParser::OffsetOnlyClauseContext::accept(tree::ParseTreeVisitor *vi HogQLParser::OffsetOnlyClauseContext* HogQLParser::offsetOnlyClause() { OffsetOnlyClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 66, HogQLParser::RuleOffsetOnlyClause); + enterRule(_localctx, 68, HogQLParser::RuleOffsetOnlyClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -3762,9 +3907,9 @@ HogQLParser::OffsetOnlyClauseContext* HogQLParser::offsetOnlyClause() { }); try { enterOuterAlt(_localctx, 1); - setState(470); + setState(488); match(HogQLParser::OFFSET); - setState(471); + setState(489); columnExpr(0); } @@ -3806,7 +3951,7 @@ std::any HogQLParser::SettingsClauseContext::accept(tree::ParseTreeVisitor *visi HogQLParser::SettingsClauseContext* HogQLParser::settingsClause() { SettingsClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 68, HogQLParser::RuleSettingsClause); + enterRule(_localctx, 70, HogQLParser::RuleSettingsClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -3817,9 +3962,9 @@ HogQLParser::SettingsClauseContext* HogQLParser::settingsClause() { }); try { enterOuterAlt(_localctx, 1); - setState(473); + setState(491); match(HogQLParser::SETTINGS); - setState(474); + setState(492); settingExprList(); } @@ -3958,8 +4103,8 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { HogQLParser::JoinExprContext *_localctx = _tracker.createInstance(_ctx, parentState); HogQLParser::JoinExprContext *previousContext = _localctx; (void)previousContext; // Silence compiler, in case the context is not used by generated code. - size_t startState = 70; - enterRecursionRule(_localctx, 70, HogQLParser::RuleJoinExpr, precedence); + size_t startState = 72; + enterRecursionRule(_localctx, 72, HogQLParser::RuleJoinExpr, precedence); size_t _la = 0; @@ -3973,22 +4118,22 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(488); + setState(506); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 48, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 50, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(477); + setState(495); tableExpr(0); - setState(479); + setState(497); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 46, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 48, _ctx)) { case 1: { - setState(478); + setState(496); match(HogQLParser::FINAL); break; } @@ -3996,12 +4141,12 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { default: break; } - setState(482); + setState(500); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 47, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 49, _ctx)) { case 1: { - setState(481); + setState(499); sampleClause(); break; } @@ -4016,11 +4161,11 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(484); + setState(502); match(HogQLParser::LPAREN); - setState(485); + setState(503); joinExpr(0); - setState(486); + setState(504); match(HogQLParser::RPAREN); break; } @@ -4029,27 +4174,27 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { break; } _ctx->stop = _input->LT(-1); - setState(504); + setState(522); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 51, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 53, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { if (!_parseListeners.empty()) triggerExitRuleEvent(); previousContext = _localctx; - setState(502); + setState(520); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 50, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 52, _ctx)) { case 1: { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleJoinExpr); - setState(490); + setState(508); if (!(precpred(_ctx, 3))) throw FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(491); + setState(509); joinOpCross(); - setState(492); + setState(510); joinExpr(4); break; } @@ -4058,10 +4203,10 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleJoinExpr); - setState(494); + setState(512); if (!(precpred(_ctx, 4))) throw FailedPredicateException(this, "precpred(_ctx, 4)"); - setState(496); + setState(514); _errHandler->sync(this); _la = _input->LA(1); @@ -4069,14 +4214,14 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { ((1ULL << _la) & 567356589867290) != 0) || _la == HogQLParser::RIGHT || _la == HogQLParser::SEMI) { - setState(495); + setState(513); joinOp(); } - setState(498); + setState(516); match(HogQLParser::JOIN); - setState(499); + setState(517); joinExpr(0); - setState(500); + setState(518); joinConstraintClause(); break; } @@ -4085,9 +4230,9 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { break; } } - setState(506); + setState(524); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 51, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 53, _ctx); } } catch (RecognitionException &e) { @@ -4212,7 +4357,7 @@ std::any HogQLParser::JoinOpLeftRightContext::accept(tree::ParseTreeVisitor *vis } HogQLParser::JoinOpContext* HogQLParser::joinOp() { JoinOpContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 72, HogQLParser::RuleJoinOp); + enterRule(_localctx, 74, HogQLParser::RuleJoinOp); size_t _la = 0; #if __cplusplus > 201703L @@ -4223,23 +4368,23 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { exitRule(); }); try { - setState(550); + setState(568); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 65, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 67, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(516); + setState(534); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 54, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 56, _ctx)) { case 1: { - setState(508); + setState(526); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0)) { - setState(507); + setState(525); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0))) { @@ -4250,21 +4395,21 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { consume(); } } - setState(510); + setState(528); match(HogQLParser::INNER); break; } case 2: { - setState(511); + setState(529); match(HogQLParser::INNER); - setState(513); + setState(531); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0)) { - setState(512); + setState(530); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0))) { @@ -4279,7 +4424,7 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { } case 3: { - setState(515); + setState(533); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0))) { @@ -4301,17 +4446,17 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(532); + setState(550); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 59, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 61, _ctx)) { case 1: { - setState(519); + setState(537); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI) { - setState(518); + setState(536); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI)) { @@ -4322,7 +4467,7 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { consume(); } } - setState(521); + setState(539); _la = _input->LA(1); if (!(_la == HogQLParser::LEFT @@ -4333,19 +4478,19 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { _errHandler->reportMatch(this); consume(); } - setState(523); + setState(541); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(522); + setState(540); match(HogQLParser::OUTER); } break; } case 2: { - setState(525); + setState(543); _la = _input->LA(1); if (!(_la == HogQLParser::LEFT @@ -4356,21 +4501,21 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { _errHandler->reportMatch(this); consume(); } - setState(527); + setState(545); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(526); + setState(544); match(HogQLParser::OUTER); } - setState(530); + setState(548); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI) { - setState(529); + setState(547); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI)) { @@ -4393,18 +4538,18 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { case 3: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 3); - setState(548); + setState(566); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 64, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 66, _ctx)) { case 1: { - setState(535); + setState(553); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ALL || _la == HogQLParser::ANY) { - setState(534); + setState(552); _la = _input->LA(1); if (!(_la == HogQLParser::ALL @@ -4416,38 +4561,38 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { consume(); } } - setState(537); + setState(555); match(HogQLParser::FULL); - setState(539); + setState(557); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(538); + setState(556); match(HogQLParser::OUTER); } break; } case 2: { - setState(541); + setState(559); match(HogQLParser::FULL); - setState(543); + setState(561); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(542); + setState(560); match(HogQLParser::OUTER); } - setState(546); + setState(564); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ALL || _la == HogQLParser::ANY) { - setState(545); + setState(563); _la = _input->LA(1); if (!(_la == HogQLParser::ALL @@ -4515,7 +4660,7 @@ std::any HogQLParser::JoinOpCrossContext::accept(tree::ParseTreeVisitor *visitor HogQLParser::JoinOpCrossContext* HogQLParser::joinOpCross() { JoinOpCrossContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 74, HogQLParser::RuleJoinOpCross); + enterRule(_localctx, 76, HogQLParser::RuleJoinOpCross); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -4525,21 +4670,21 @@ HogQLParser::JoinOpCrossContext* HogQLParser::joinOpCross() { exitRule(); }); try { - setState(555); + setState(573); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::CROSS: { enterOuterAlt(_localctx, 1); - setState(552); + setState(570); match(HogQLParser::CROSS); - setState(553); + setState(571); match(HogQLParser::JOIN); break; } case HogQLParser::COMMA: { enterOuterAlt(_localctx, 2); - setState(554); + setState(572); match(HogQLParser::COMMA); break; } @@ -4599,7 +4744,7 @@ std::any HogQLParser::JoinConstraintClauseContext::accept(tree::ParseTreeVisitor HogQLParser::JoinConstraintClauseContext* HogQLParser::joinConstraintClause() { JoinConstraintClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 76, HogQLParser::RuleJoinConstraintClause); + enterRule(_localctx, 78, HogQLParser::RuleJoinConstraintClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -4609,36 +4754,36 @@ HogQLParser::JoinConstraintClauseContext* HogQLParser::joinConstraintClause() { exitRule(); }); try { - setState(566); + setState(584); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 67, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 69, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(557); + setState(575); match(HogQLParser::ON); - setState(558); + setState(576); columnExprList(); break; } case 2: { enterOuterAlt(_localctx, 2); - setState(559); + setState(577); match(HogQLParser::USING); - setState(560); + setState(578); match(HogQLParser::LPAREN); - setState(561); + setState(579); columnExprList(); - setState(562); + setState(580); match(HogQLParser::RPAREN); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(564); + setState(582); match(HogQLParser::USING); - setState(565); + setState(583); columnExprList(); break; } @@ -4694,7 +4839,7 @@ std::any HogQLParser::SampleClauseContext::accept(tree::ParseTreeVisitor *visito HogQLParser::SampleClauseContext* HogQLParser::sampleClause() { SampleClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 78, HogQLParser::RuleSampleClause); + enterRule(_localctx, 80, HogQLParser::RuleSampleClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -4705,18 +4850,18 @@ HogQLParser::SampleClauseContext* HogQLParser::sampleClause() { }); try { enterOuterAlt(_localctx, 1); - setState(568); + setState(586); match(HogQLParser::SAMPLE); - setState(569); + setState(587); ratioExpr(); - setState(572); + setState(590); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 68, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 70, _ctx)) { case 1: { - setState(570); + setState(588); match(HogQLParser::OFFSET); - setState(571); + setState(589); ratioExpr(); break; } @@ -4772,7 +4917,7 @@ std::any HogQLParser::OrderExprListContext::accept(tree::ParseTreeVisitor *visit HogQLParser::OrderExprListContext* HogQLParser::orderExprList() { OrderExprListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 80, HogQLParser::RuleOrderExprList); + enterRule(_localctx, 82, HogQLParser::RuleOrderExprList); size_t _la = 0; #if __cplusplus > 201703L @@ -4784,17 +4929,17 @@ HogQLParser::OrderExprListContext* HogQLParser::orderExprList() { }); try { enterOuterAlt(_localctx, 1); - setState(574); + setState(592); orderExpr(); - setState(579); + setState(597); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::COMMA) { - setState(575); + setState(593); match(HogQLParser::COMMA); - setState(576); + setState(594); orderExpr(); - setState(581); + setState(599); _errHandler->sync(this); _la = _input->LA(1); } @@ -4866,7 +5011,7 @@ std::any HogQLParser::OrderExprContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::OrderExprContext* HogQLParser::orderExpr() { OrderExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 82, HogQLParser::RuleOrderExpr); + enterRule(_localctx, 84, HogQLParser::RuleOrderExpr); size_t _la = 0; #if __cplusplus > 201703L @@ -4878,15 +5023,15 @@ HogQLParser::OrderExprContext* HogQLParser::orderExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(582); + setState(600); columnExpr(0); - setState(584); + setState(602); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 6291584) != 0)) { - setState(583); + setState(601); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 6291584) != 0))) { @@ -4897,14 +5042,14 @@ HogQLParser::OrderExprContext* HogQLParser::orderExpr() { consume(); } } - setState(588); + setState(606); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NULLS) { - setState(586); + setState(604); match(HogQLParser::NULLS); - setState(587); + setState(605); _la = _input->LA(1); if (!(_la == HogQLParser::FIRST @@ -4916,14 +5061,14 @@ HogQLParser::OrderExprContext* HogQLParser::orderExpr() { consume(); } } - setState(592); + setState(610); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COLLATE) { - setState(590); + setState(608); match(HogQLParser::COLLATE); - setState(591); + setState(609); match(HogQLParser::STRING_LITERAL); } @@ -4974,7 +5119,7 @@ std::any HogQLParser::RatioExprContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::RatioExprContext* HogQLParser::ratioExpr() { RatioExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 84, HogQLParser::RuleRatioExpr); + enterRule(_localctx, 86, HogQLParser::RuleRatioExpr); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -4984,12 +5129,12 @@ HogQLParser::RatioExprContext* HogQLParser::ratioExpr() { exitRule(); }); try { - setState(600); + setState(618); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LBRACE: { enterOuterAlt(_localctx, 1); - setState(594); + setState(612); placeholder(); break; } @@ -5004,16 +5149,16 @@ HogQLParser::RatioExprContext* HogQLParser::ratioExpr() { case HogQLParser::DOT: case HogQLParser::PLUS: { enterOuterAlt(_localctx, 2); - setState(595); + setState(613); numberLiteral(); - setState(598); + setState(616); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 73, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 75, _ctx)) { case 1: { - setState(596); + setState(614); match(HogQLParser::SLASH); - setState(597); + setState(615); numberLiteral(); break; } @@ -5075,7 +5220,7 @@ std::any HogQLParser::SettingExprListContext::accept(tree::ParseTreeVisitor *vis HogQLParser::SettingExprListContext* HogQLParser::settingExprList() { SettingExprListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 86, HogQLParser::RuleSettingExprList); + enterRule(_localctx, 88, HogQLParser::RuleSettingExprList); size_t _la = 0; #if __cplusplus > 201703L @@ -5087,17 +5232,17 @@ HogQLParser::SettingExprListContext* HogQLParser::settingExprList() { }); try { enterOuterAlt(_localctx, 1); - setState(602); + setState(620); settingExpr(); - setState(607); + setState(625); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::COMMA) { - setState(603); + setState(621); match(HogQLParser::COMMA); - setState(604); + setState(622); settingExpr(); - setState(609); + setState(627); _errHandler->sync(this); _la = _input->LA(1); } @@ -5145,7 +5290,7 @@ std::any HogQLParser::SettingExprContext::accept(tree::ParseTreeVisitor *visitor HogQLParser::SettingExprContext* HogQLParser::settingExpr() { SettingExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 88, HogQLParser::RuleSettingExpr); + enterRule(_localctx, 90, HogQLParser::RuleSettingExpr); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -5156,11 +5301,11 @@ HogQLParser::SettingExprContext* HogQLParser::settingExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(610); + setState(628); identifier(); - setState(611); + setState(629); match(HogQLParser::EQ_SINGLE); - setState(612); + setState(630); literal(); } @@ -5206,7 +5351,7 @@ std::any HogQLParser::WindowExprContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::WindowExprContext* HogQLParser::windowExpr() { WindowExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 90, HogQLParser::RuleWindowExpr); + enterRule(_localctx, 92, HogQLParser::RuleWindowExpr); size_t _la = 0; #if __cplusplus > 201703L @@ -5218,30 +5363,30 @@ HogQLParser::WindowExprContext* HogQLParser::windowExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(615); + setState(633); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::PARTITION) { - setState(614); + setState(632); winPartitionByClause(); } - setState(618); + setState(636); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ORDER) { - setState(617); + setState(635); winOrderByClause(); } - setState(621); + setState(639); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::RANGE || _la == HogQLParser::ROWS) { - setState(620); + setState(638); winFrameClause(); } @@ -5288,7 +5433,7 @@ std::any HogQLParser::WinPartitionByClauseContext::accept(tree::ParseTreeVisitor HogQLParser::WinPartitionByClauseContext* HogQLParser::winPartitionByClause() { WinPartitionByClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 92, HogQLParser::RuleWinPartitionByClause); + enterRule(_localctx, 94, HogQLParser::RuleWinPartitionByClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -5299,11 +5444,11 @@ HogQLParser::WinPartitionByClauseContext* HogQLParser::winPartitionByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(623); + setState(641); match(HogQLParser::PARTITION); - setState(624); + setState(642); match(HogQLParser::BY); - setState(625); + setState(643); columnExprList(); } @@ -5349,7 +5494,7 @@ std::any HogQLParser::WinOrderByClauseContext::accept(tree::ParseTreeVisitor *vi HogQLParser::WinOrderByClauseContext* HogQLParser::winOrderByClause() { WinOrderByClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 94, HogQLParser::RuleWinOrderByClause); + enterRule(_localctx, 96, HogQLParser::RuleWinOrderByClause); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -5360,11 +5505,11 @@ HogQLParser::WinOrderByClauseContext* HogQLParser::winOrderByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(627); + setState(645); match(HogQLParser::ORDER); - setState(628); + setState(646); match(HogQLParser::BY); - setState(629); + setState(647); orderExprList(); } @@ -5410,7 +5555,7 @@ std::any HogQLParser::WinFrameClauseContext::accept(tree::ParseTreeVisitor *visi HogQLParser::WinFrameClauseContext* HogQLParser::winFrameClause() { WinFrameClauseContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 96, HogQLParser::RuleWinFrameClause); + enterRule(_localctx, 98, HogQLParser::RuleWinFrameClause); size_t _la = 0; #if __cplusplus > 201703L @@ -5422,7 +5567,7 @@ HogQLParser::WinFrameClauseContext* HogQLParser::winFrameClause() { }); try { enterOuterAlt(_localctx, 1); - setState(631); + setState(649); _la = _input->LA(1); if (!(_la == HogQLParser::RANGE @@ -5433,7 +5578,7 @@ HogQLParser::WinFrameClauseContext* HogQLParser::winFrameClause() { _errHandler->reportMatch(this); consume(); } - setState(632); + setState(650); winFrameExtend(); } @@ -5505,7 +5650,7 @@ std::any HogQLParser::FrameBetweenContext::accept(tree::ParseTreeVisitor *visito } HogQLParser::WinFrameExtendContext* HogQLParser::winFrameExtend() { WinFrameExtendContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 98, HogQLParser::RuleWinFrameExtend); + enterRule(_localctx, 100, HogQLParser::RuleWinFrameExtend); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -5515,7 +5660,7 @@ HogQLParser::WinFrameExtendContext* HogQLParser::winFrameExtend() { exitRule(); }); try { - setState(640); + setState(658); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::CURRENT: @@ -5531,7 +5676,7 @@ HogQLParser::WinFrameExtendContext* HogQLParser::winFrameExtend() { case HogQLParser::PLUS: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(634); + setState(652); winFrameBound(); break; } @@ -5539,13 +5684,13 @@ HogQLParser::WinFrameExtendContext* HogQLParser::winFrameExtend() { case HogQLParser::BETWEEN: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(635); + setState(653); match(HogQLParser::BETWEEN); - setState(636); + setState(654); winFrameBound(); - setState(637); + setState(655); match(HogQLParser::AND); - setState(638); + setState(656); winFrameBound(); break; } @@ -5609,7 +5754,7 @@ std::any HogQLParser::WinFrameBoundContext::accept(tree::ParseTreeVisitor *visit HogQLParser::WinFrameBoundContext* HogQLParser::winFrameBound() { WinFrameBoundContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 100, HogQLParser::RuleWinFrameBound); + enterRule(_localctx, 102, HogQLParser::RuleWinFrameBound); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -5620,45 +5765,45 @@ HogQLParser::WinFrameBoundContext* HogQLParser::winFrameBound() { }); try { enterOuterAlt(_localctx, 1); - setState(654); + setState(672); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 80, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 82, _ctx)) { case 1: { - setState(642); + setState(660); match(HogQLParser::CURRENT); - setState(643); + setState(661); match(HogQLParser::ROW); break; } case 2: { - setState(644); + setState(662); match(HogQLParser::UNBOUNDED); - setState(645); + setState(663); match(HogQLParser::PRECEDING); break; } case 3: { - setState(646); + setState(664); match(HogQLParser::UNBOUNDED); - setState(647); + setState(665); match(HogQLParser::FOLLOWING); break; } case 4: { - setState(648); + setState(666); numberLiteral(); - setState(649); + setState(667); match(HogQLParser::PRECEDING); break; } case 5: { - setState(651); + setState(669); numberLiteral(); - setState(652); + setState(670); match(HogQLParser::FOLLOWING); break; } @@ -5706,7 +5851,7 @@ std::any HogQLParser::ExprContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::ExprContext* HogQLParser::expr() { ExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 102, HogQLParser::RuleExpr); + enterRule(_localctx, 104, HogQLParser::RuleExpr); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -5717,9 +5862,9 @@ HogQLParser::ExprContext* HogQLParser::expr() { }); try { enterOuterAlt(_localctx, 1); - setState(656); + setState(674); columnExpr(0); - setState(657); + setState(675); match(HogQLParser::EOF); } @@ -5912,7 +6057,7 @@ std::any HogQLParser::ColumnTypeExprEnumContext::accept(tree::ParseTreeVisitor * } HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { ColumnTypeExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 104, HogQLParser::RuleColumnTypeExpr); + enterRule(_localctx, 106, HogQLParser::RuleColumnTypeExpr); size_t _la = 0; #if __cplusplus > 201703L @@ -5924,13 +6069,13 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { }); try { size_t alt; - setState(715); + setState(733); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 88, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 90, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(659); + setState(677); identifier(); break; } @@ -5938,39 +6083,39 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(660); + setState(678); identifier(); - setState(661); + setState(679); match(HogQLParser::LPAREN); - setState(662); + setState(680); identifier(); - setState(663); + setState(681); columnTypeExpr(); - setState(670); + setState(688); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 81, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 83, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(664); + setState(682); match(HogQLParser::COMMA); - setState(665); + setState(683); identifier(); - setState(666); + setState(684); columnTypeExpr(); } - setState(672); + setState(690); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 81, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 83, _ctx); } - setState(674); + setState(692); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(673); + setState(691); match(HogQLParser::COMMA); } - setState(676); + setState(694); match(HogQLParser::RPAREN); break; } @@ -5978,35 +6123,35 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 3: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 3); - setState(678); + setState(696); identifier(); - setState(679); + setState(697); match(HogQLParser::LPAREN); - setState(680); + setState(698); enumValue(); - setState(685); + setState(703); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 83, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 85, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(681); + setState(699); match(HogQLParser::COMMA); - setState(682); + setState(700); enumValue(); } - setState(687); + setState(705); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 83, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 85, _ctx); } - setState(689); + setState(707); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(688); + setState(706); match(HogQLParser::COMMA); } - setState(691); + setState(709); match(HogQLParser::RPAREN); break; } @@ -6014,35 +6159,35 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 4: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 4); - setState(693); + setState(711); identifier(); - setState(694); + setState(712); match(HogQLParser::LPAREN); - setState(695); + setState(713); columnTypeExpr(); - setState(700); + setState(718); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 85, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 87, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(696); + setState(714); match(HogQLParser::COMMA); - setState(697); + setState(715); columnTypeExpr(); } - setState(702); + setState(720); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 85, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 87, _ctx); } - setState(704); + setState(722); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(703); + setState(721); match(HogQLParser::COMMA); } - setState(706); + setState(724); match(HogQLParser::RPAREN); break; } @@ -6050,11 +6195,11 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 5: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 5); - setState(708); + setState(726); identifier(); - setState(709); + setState(727); match(HogQLParser::LPAREN); - setState(711); + setState(729); _errHandler->sync(this); _la = _input->LA(1); @@ -6062,10 +6207,10 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(710); + setState(728); columnExprList(); } - setState(713); + setState(731); match(HogQLParser::RPAREN); break; } @@ -6121,7 +6266,7 @@ std::any HogQLParser::ColumnExprListContext::accept(tree::ParseTreeVisitor *visi HogQLParser::ColumnExprListContext* HogQLParser::columnExprList() { ColumnExprListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 106, HogQLParser::RuleColumnExprList); + enterRule(_localctx, 108, HogQLParser::RuleColumnExprList); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -6133,28 +6278,28 @@ HogQLParser::ColumnExprListContext* HogQLParser::columnExprList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(717); + setState(735); columnExpr(0); - setState(722); + setState(740); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 89, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 91, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(718); + setState(736); match(HogQLParser::COMMA); - setState(719); + setState(737); columnExpr(0); } - setState(724); + setState(742); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 89, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 91, _ctx); } - setState(726); + setState(744); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 90, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 92, _ctx)) { case 1: { - setState(725); + setState(743); match(HogQLParser::COMMA); break; } @@ -7293,8 +7438,8 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { HogQLParser::ColumnExprContext *_localctx = _tracker.createInstance(_ctx, parentState); HogQLParser::ColumnExprContext *previousContext = _localctx; (void)previousContext; // Silence compiler, in case the context is not used by generated code. - size_t startState = 108; - enterRecursionRule(_localctx, 108, HogQLParser::RuleColumnExpr, precedence); + size_t startState = 110; + enterRecursionRule(_localctx, 110, HogQLParser::RuleColumnExpr, precedence); size_t _la = 0; @@ -7308,22 +7453,22 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(877); + setState(895); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 110, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 112, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(729); + setState(747); match(HogQLParser::CASE); - setState(731); + setState(749); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 91, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 93, _ctx)) { case 1: { - setState(730); + setState(748); antlrcpp::downCast(_localctx)->caseExpr = columnExpr(0); break; } @@ -7331,33 +7476,33 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(738); + setState(756); _errHandler->sync(this); _la = _input->LA(1); do { - setState(733); + setState(751); match(HogQLParser::WHEN); - setState(734); + setState(752); antlrcpp::downCast(_localctx)->whenExpr = columnExpr(0); - setState(735); + setState(753); match(HogQLParser::THEN); - setState(736); + setState(754); antlrcpp::downCast(_localctx)->thenExpr = columnExpr(0); - setState(740); + setState(758); _errHandler->sync(this); _la = _input->LA(1); } while (_la == HogQLParser::WHEN); - setState(744); + setState(762); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ELSE) { - setState(742); + setState(760); match(HogQLParser::ELSE); - setState(743); + setState(761); antlrcpp::downCast(_localctx)->elseExpr = columnExpr(0); } - setState(746); + setState(764); match(HogQLParser::END); break; } @@ -7366,17 +7511,17 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(748); + setState(766); match(HogQLParser::CAST); - setState(749); + setState(767); match(HogQLParser::LPAREN); - setState(750); + setState(768); columnExpr(0); - setState(751); + setState(769); match(HogQLParser::AS); - setState(752); + setState(770); columnTypeExpr(); - setState(753); + setState(771); match(HogQLParser::RPAREN); break; } @@ -7385,9 +7530,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(755); + setState(773); match(HogQLParser::DATE); - setState(756); + setState(774); match(HogQLParser::STRING_LITERAL); break; } @@ -7396,11 +7541,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(757); + setState(775); match(HogQLParser::INTERVAL); - setState(758); + setState(776); columnExpr(0); - setState(759); + setState(777); interval(); break; } @@ -7409,27 +7554,27 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(761); + setState(779); match(HogQLParser::SUBSTRING); - setState(762); + setState(780); match(HogQLParser::LPAREN); - setState(763); + setState(781); columnExpr(0); - setState(764); + setState(782); match(HogQLParser::FROM); - setState(765); + setState(783); columnExpr(0); - setState(768); + setState(786); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::FOR) { - setState(766); + setState(784); match(HogQLParser::FOR); - setState(767); + setState(785); columnExpr(0); } - setState(770); + setState(788); match(HogQLParser::RPAREN); break; } @@ -7438,9 +7583,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(772); + setState(790); match(HogQLParser::TIMESTAMP); - setState(773); + setState(791); match(HogQLParser::STRING_LITERAL); break; } @@ -7449,11 +7594,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(774); + setState(792); match(HogQLParser::TRIM); - setState(775); + setState(793); match(HogQLParser::LPAREN); - setState(776); + setState(794); _la = _input->LA(1); if (!(_la == HogQLParser::BOTH @@ -7464,13 +7609,13 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _errHandler->reportMatch(this); consume(); } - setState(777); + setState(795); string(); - setState(778); + setState(796); match(HogQLParser::FROM); - setState(779); + setState(797); columnExpr(0); - setState(780); + setState(798); match(HogQLParser::RPAREN); break; } @@ -7479,12 +7624,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(782); + setState(800); identifier(); - setState(783); + setState(801); match(HogQLParser::LPAREN); - setState(785); + setState(803); _errHandler->sync(this); _la = _input->LA(1); @@ -7492,24 +7637,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(784); + setState(802); columnExprList(); } - setState(787); + setState(805); match(HogQLParser::RPAREN); - setState(797); + setState(815); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::LPAREN) { - setState(789); + setState(807); match(HogQLParser::LPAREN); - setState(791); + setState(809); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 96, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 98, _ctx)) { case 1: { - setState(790); + setState(808); match(HogQLParser::DISTINCT); break; } @@ -7517,7 +7662,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(794); + setState(812); _errHandler->sync(this); _la = _input->LA(1); @@ -7525,19 +7670,19 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(793); + setState(811); columnArgList(); } - setState(796); + setState(814); match(HogQLParser::RPAREN); } - setState(799); + setState(817); match(HogQLParser::OVER); - setState(800); + setState(818); match(HogQLParser::LPAREN); - setState(801); + setState(819); windowExpr(); - setState(802); + setState(820); match(HogQLParser::RPAREN); break; } @@ -7546,12 +7691,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(804); + setState(822); identifier(); - setState(805); + setState(823); match(HogQLParser::LPAREN); - setState(807); + setState(825); _errHandler->sync(this); _la = _input->LA(1); @@ -7559,24 +7704,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(806); + setState(824); columnExprList(); } - setState(809); + setState(827); match(HogQLParser::RPAREN); - setState(819); + setState(837); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::LPAREN) { - setState(811); + setState(829); match(HogQLParser::LPAREN); - setState(813); + setState(831); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 100, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 102, _ctx)) { case 1: { - setState(812); + setState(830); match(HogQLParser::DISTINCT); break; } @@ -7584,7 +7729,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(816); + setState(834); _errHandler->sync(this); _la = _input->LA(1); @@ -7592,15 +7737,15 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(815); + setState(833); columnArgList(); } - setState(818); + setState(836); match(HogQLParser::RPAREN); } - setState(821); + setState(839); match(HogQLParser::OVER); - setState(822); + setState(840); identifier(); break; } @@ -7609,16 +7754,16 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(824); + setState(842); identifier(); - setState(830); + setState(848); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 104, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 106, _ctx)) { case 1: { - setState(825); + setState(843); match(HogQLParser::LPAREN); - setState(827); + setState(845); _errHandler->sync(this); _la = _input->LA(1); @@ -7626,10 +7771,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(826); + setState(844); columnExprList(); } - setState(829); + setState(847); match(HogQLParser::RPAREN); break; } @@ -7637,14 +7782,14 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(832); + setState(850); match(HogQLParser::LPAREN); - setState(834); + setState(852); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 105, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 107, _ctx)) { case 1: { - setState(833); + setState(851); match(HogQLParser::DISTINCT); break; } @@ -7652,7 +7797,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(837); + setState(855); _errHandler->sync(this); _la = _input->LA(1); @@ -7660,10 +7805,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(836); + setState(854); columnArgList(); } - setState(839); + setState(857); match(HogQLParser::RPAREN); break; } @@ -7672,7 +7817,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(841); + setState(859); hogqlxTagElement(); break; } @@ -7681,7 +7826,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(842); + setState(860); templateString(); break; } @@ -7690,7 +7835,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(843); + setState(861); literal(); break; } @@ -7699,9 +7844,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(844); + setState(862); match(HogQLParser::DASH); - setState(845); + setState(863); columnExpr(19); break; } @@ -7710,9 +7855,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(846); + setState(864); match(HogQLParser::NOT); - setState(847); + setState(865); columnExpr(13); break; } @@ -7721,19 +7866,19 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(851); + setState(869); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -181272084561788930) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 201863462911) != 0)) { - setState(848); + setState(866); tableIdentifier(); - setState(849); + setState(867); match(HogQLParser::DOT); } - setState(853); + setState(871); match(HogQLParser::ASTERISK); break; } @@ -7742,11 +7887,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(854); + setState(872); match(HogQLParser::LPAREN); - setState(855); + setState(873); selectUnionStmt(); - setState(856); + setState(874); match(HogQLParser::RPAREN); break; } @@ -7755,11 +7900,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(858); + setState(876); match(HogQLParser::LPAREN); - setState(859); + setState(877); columnExpr(0); - setState(860); + setState(878); match(HogQLParser::RPAREN); break; } @@ -7768,11 +7913,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(862); + setState(880); match(HogQLParser::LPAREN); - setState(863); + setState(881); columnExprList(); - setState(864); + setState(882); match(HogQLParser::RPAREN); break; } @@ -7781,9 +7926,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(866); + setState(884); match(HogQLParser::LBRACKET); - setState(868); + setState(886); _errHandler->sync(this); _la = _input->LA(1); @@ -7791,10 +7936,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(867); + setState(885); columnExprList(); } - setState(870); + setState(888); match(HogQLParser::RBRACKET); break; } @@ -7803,9 +7948,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(871); + setState(889); match(HogQLParser::LBRACE); - setState(873); + setState(891); _errHandler->sync(this); _la = _input->LA(1); @@ -7813,10 +7958,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(872); + setState(890); kvPairList(); } - setState(875); + setState(893); match(HogQLParser::RBRACE); break; } @@ -7825,7 +7970,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(876); + setState(894); columnIdentifier(); break; } @@ -7834,42 +7979,42 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { break; } _ctx->stop = _input->LT(-1); - setState(983); + setState(1001); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 121, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 123, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { if (!_parseListeners.empty()) triggerExitRuleEvent(); previousContext = _localctx; - setState(981); + setState(999); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 120, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 122, _ctx)) { case 1: { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; newContext->left = previousContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(879); + setState(897); if (!(precpred(_ctx, 18))) throw FailedPredicateException(this, "precpred(_ctx, 18)"); - setState(883); + setState(901); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::ASTERISK: { - setState(880); + setState(898); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::ASTERISK); break; } case HogQLParser::SLASH: { - setState(881); + setState(899); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::SLASH); break; } case HogQLParser::PERCENT: { - setState(882); + setState(900); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::PERCENT); break; } @@ -7877,7 +8022,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: throw NoViableAltException(this); } - setState(885); + setState(903); antlrcpp::downCast(_localctx)->right = columnExpr(19); break; } @@ -7887,26 +8032,26 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = newContext; newContext->left = previousContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(886); + setState(904); if (!(precpred(_ctx, 17))) throw FailedPredicateException(this, "precpred(_ctx, 17)"); - setState(890); + setState(908); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::PLUS: { - setState(887); + setState(905); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::PLUS); break; } case HogQLParser::DASH: { - setState(888); + setState(906); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::DASH); break; } case HogQLParser::CONCAT: { - setState(889); + setState(907); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::CONCAT); break; } @@ -7914,7 +8059,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: throw NoViableAltException(this); } - setState(892); + setState(910); antlrcpp::downCast(_localctx)->right = columnExpr(18); break; } @@ -7924,71 +8069,71 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = newContext; newContext->left = previousContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(893); + setState(911); if (!(precpred(_ctx, 16))) throw FailedPredicateException(this, "precpred(_ctx, 16)"); - setState(918); + setState(936); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 116, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 118, _ctx)) { case 1: { - setState(894); + setState(912); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::EQ_DOUBLE); break; } case 2: { - setState(895); + setState(913); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::EQ_SINGLE); break; } case 3: { - setState(896); + setState(914); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT_EQ); break; } case 4: { - setState(897); + setState(915); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::LT_EQ); break; } case 5: { - setState(898); + setState(916); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::LT); break; } case 6: { - setState(899); + setState(917); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::GT_EQ); break; } case 7: { - setState(900); + setState(918); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::GT); break; } case 8: { - setState(902); + setState(920); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(901); + setState(919); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT); } - setState(904); + setState(922); match(HogQLParser::IN); - setState(906); + setState(924); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 114, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 116, _ctx)) { case 1: { - setState(905); + setState(923); match(HogQLParser::COHORT); break; } @@ -8000,15 +8145,15 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { } case 9: { - setState(909); + setState(927); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(908); + setState(926); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT); } - setState(911); + setState(929); _la = _input->LA(1); if (!(_la == HogQLParser::ILIKE @@ -8023,37 +8168,37 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { } case 10: { - setState(912); + setState(930); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::REGEX_SINGLE); break; } case 11: { - setState(913); + setState(931); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::REGEX_DOUBLE); break; } case 12: { - setState(914); + setState(932); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT_REGEX); break; } case 13: { - setState(915); + setState(933); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::IREGEX_SINGLE); break; } case 14: { - setState(916); + setState(934); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::IREGEX_DOUBLE); break; } case 15: { - setState(917); + setState(935); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT_IREGEX); break; } @@ -8061,7 +8206,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(920); + setState(938); antlrcpp::downCast(_localctx)->right = columnExpr(17); break; } @@ -8070,12 +8215,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(921); + setState(939); if (!(precpred(_ctx, 14))) throw FailedPredicateException(this, "precpred(_ctx, 14)"); - setState(922); + setState(940); match(HogQLParser::NULLISH); - setState(923); + setState(941); columnExpr(15); break; } @@ -8084,12 +8229,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(924); + setState(942); if (!(precpred(_ctx, 12))) throw FailedPredicateException(this, "precpred(_ctx, 12)"); - setState(925); + setState(943); match(HogQLParser::AND); - setState(926); + setState(944); columnExpr(13); break; } @@ -8098,12 +8243,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(927); + setState(945); if (!(precpred(_ctx, 11))) throw FailedPredicateException(this, "precpred(_ctx, 11)"); - setState(928); + setState(946); match(HogQLParser::OR); - setState(929); + setState(947); columnExpr(12); break; } @@ -8112,24 +8257,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(930); + setState(948); if (!(precpred(_ctx, 10))) throw FailedPredicateException(this, "precpred(_ctx, 10)"); - setState(932); + setState(950); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(931); + setState(949); match(HogQLParser::NOT); } - setState(934); + setState(952); match(HogQLParser::BETWEEN); - setState(935); + setState(953); columnExpr(0); - setState(936); + setState(954); match(HogQLParser::AND); - setState(937); + setState(955); columnExpr(11); break; } @@ -8138,16 +8283,16 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(939); + setState(957); if (!(precpred(_ctx, 9))) throw FailedPredicateException(this, "precpred(_ctx, 9)"); - setState(940); + setState(958); match(HogQLParser::QUERY); - setState(941); + setState(959); columnExpr(0); - setState(942); + setState(960); match(HogQLParser::COLON); - setState(943); + setState(961); columnExpr(9); break; } @@ -8156,14 +8301,14 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(945); + setState(963); if (!(precpred(_ctx, 25))) throw FailedPredicateException(this, "precpred(_ctx, 25)"); - setState(946); + setState(964); match(HogQLParser::LBRACKET); - setState(947); + setState(965); columnExpr(0); - setState(948); + setState(966); match(HogQLParser::RBRACKET); break; } @@ -8172,12 +8317,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(950); + setState(968); if (!(precpred(_ctx, 24))) throw FailedPredicateException(this, "precpred(_ctx, 24)"); - setState(951); + setState(969); match(HogQLParser::DOT); - setState(952); + setState(970); match(HogQLParser::DECIMAL_LITERAL); break; } @@ -8186,12 +8331,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(953); + setState(971); if (!(precpred(_ctx, 23))) throw FailedPredicateException(this, "precpred(_ctx, 23)"); - setState(954); + setState(972); match(HogQLParser::DOT); - setState(955); + setState(973); identifier(); break; } @@ -8200,16 +8345,16 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(956); + setState(974); if (!(precpred(_ctx, 22))) throw FailedPredicateException(this, "precpred(_ctx, 22)"); - setState(957); + setState(975); match(HogQLParser::NULL_PROPERTY); - setState(958); + setState(976); match(HogQLParser::LBRACKET); - setState(959); + setState(977); columnExpr(0); - setState(960); + setState(978); match(HogQLParser::RBRACKET); break; } @@ -8218,12 +8363,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(962); + setState(980); if (!(precpred(_ctx, 21))) throw FailedPredicateException(this, "precpred(_ctx, 21)"); - setState(963); + setState(981); match(HogQLParser::NULL_PROPERTY); - setState(964); + setState(982); match(HogQLParser::DECIMAL_LITERAL); break; } @@ -8232,12 +8377,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(965); + setState(983); if (!(precpred(_ctx, 20))) throw FailedPredicateException(this, "precpred(_ctx, 20)"); - setState(966); + setState(984); match(HogQLParser::NULL_PROPERTY); - setState(967); + setState(985); identifier(); break; } @@ -8246,20 +8391,20 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(968); + setState(986); if (!(precpred(_ctx, 15))) throw FailedPredicateException(this, "precpred(_ctx, 15)"); - setState(969); + setState(987); match(HogQLParser::IS); - setState(971); + setState(989); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(970); + setState(988); match(HogQLParser::NOT); } - setState(973); + setState(991); match(HogQLParser::NULL_SQL); break; } @@ -8268,24 +8413,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(974); + setState(992); if (!(precpred(_ctx, 8))) throw FailedPredicateException(this, "precpred(_ctx, 8)"); - setState(979); + setState(997); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 119, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 121, _ctx)) { case 1: { - setState(975); + setState(993); match(HogQLParser::AS); - setState(976); + setState(994); identifier(); break; } case 2: { - setState(977); + setState(995); match(HogQLParser::AS); - setState(978); + setState(996); match(HogQLParser::STRING_LITERAL); break; } @@ -8300,9 +8445,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { break; } } - setState(985); + setState(1003); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 121, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 123, _ctx); } } catch (RecognitionException &e) { @@ -8350,7 +8495,7 @@ std::any HogQLParser::ColumnArgListContext::accept(tree::ParseTreeVisitor *visit HogQLParser::ColumnArgListContext* HogQLParser::columnArgList() { ColumnArgListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 110, HogQLParser::RuleColumnArgList); + enterRule(_localctx, 112, HogQLParser::RuleColumnArgList); size_t _la = 0; #if __cplusplus > 201703L @@ -8363,28 +8508,28 @@ HogQLParser::ColumnArgListContext* HogQLParser::columnArgList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(986); + setState(1004); columnArgExpr(); - setState(991); + setState(1009); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 122, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 124, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(987); + setState(1005); match(HogQLParser::COMMA); - setState(988); + setState(1006); columnArgExpr(); } - setState(993); + setState(1011); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 122, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 124, _ctx); } - setState(995); + setState(1013); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(994); + setState(1012); match(HogQLParser::COMMA); } @@ -8427,7 +8572,7 @@ std::any HogQLParser::ColumnArgExprContext::accept(tree::ParseTreeVisitor *visit HogQLParser::ColumnArgExprContext* HogQLParser::columnArgExpr() { ColumnArgExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 112, HogQLParser::RuleColumnArgExpr); + enterRule(_localctx, 114, HogQLParser::RuleColumnArgExpr); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -8437,19 +8582,19 @@ HogQLParser::ColumnArgExprContext* HogQLParser::columnArgExpr() { exitRule(); }); try { - setState(999); + setState(1017); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 124, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 126, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(997); + setState(1015); columnLambdaExpr(); break; } case 2: { enterOuterAlt(_localctx, 2); - setState(998); + setState(1016); columnExpr(0); break; } @@ -8521,7 +8666,7 @@ std::any HogQLParser::ColumnLambdaExprContext::accept(tree::ParseTreeVisitor *vi HogQLParser::ColumnLambdaExprContext* HogQLParser::columnLambdaExpr() { ColumnLambdaExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 114, HogQLParser::RuleColumnLambdaExpr); + enterRule(_localctx, 116, HogQLParser::RuleColumnLambdaExpr); size_t _la = 0; #if __cplusplus > 201703L @@ -8534,37 +8679,37 @@ HogQLParser::ColumnLambdaExprContext* HogQLParser::columnLambdaExpr() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1026); + setState(1044); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LPAREN: { - setState(1001); + setState(1019); match(HogQLParser::LPAREN); - setState(1002); + setState(1020); identifier(); - setState(1007); + setState(1025); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 125, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 127, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1003); + setState(1021); match(HogQLParser::COMMA); - setState(1004); + setState(1022); identifier(); } - setState(1009); + setState(1027); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 125, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 127, _ctx); } - setState(1011); + setState(1029); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1010); + setState(1028); match(HogQLParser::COMMA); } - setState(1013); + setState(1031); match(HogQLParser::RPAREN); break; } @@ -8663,28 +8808,28 @@ HogQLParser::ColumnLambdaExprContext* HogQLParser::columnLambdaExpr() { case HogQLParser::WITH: case HogQLParser::YEAR: case HogQLParser::IDENTIFIER: { - setState(1015); + setState(1033); identifier(); - setState(1020); + setState(1038); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 127, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 129, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1016); + setState(1034); match(HogQLParser::COMMA); - setState(1017); + setState(1035); identifier(); } - setState(1022); + setState(1040); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 127, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 129, _ctx); } - setState(1024); + setState(1042); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1023); + setState(1041); match(HogQLParser::COMMA); } break; @@ -8693,9 +8838,9 @@ HogQLParser::ColumnLambdaExprContext* HogQLParser::columnLambdaExpr() { default: throw NoViableAltException(this); } - setState(1028); + setState(1046); match(HogQLParser::ARROW); - setState(1029); + setState(1047); columnExpr(0); } @@ -8811,7 +8956,7 @@ std::any HogQLParser::HogqlxTagElementNestedContext::accept(tree::ParseTreeVisit } HogQLParser::HogqlxTagElementContext* HogQLParser::hogqlxTagElement() { HogqlxTagElementContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 116, HogQLParser::RuleHogqlxTagElement); + enterRule(_localctx, 118, HogQLParser::RuleHogqlxTagElement); size_t _la = 0; #if __cplusplus > 201703L @@ -8822,31 +8967,31 @@ HogQLParser::HogqlxTagElementContext* HogQLParser::hogqlxTagElement() { exitRule(); }); try { - setState(1059); + setState(1077); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 133, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 135, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(1031); + setState(1049); match(HogQLParser::LT); - setState(1032); + setState(1050); identifier(); - setState(1036); + setState(1054); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -181272084561788930) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 201863462911) != 0)) { - setState(1033); + setState(1051); hogqlxTagAttribute(); - setState(1038); + setState(1056); _errHandler->sync(this); _la = _input->LA(1); } - setState(1039); + setState(1057); match(HogQLParser::SLASH); - setState(1040); + setState(1058); match(HogQLParser::GT); break; } @@ -8854,30 +8999,30 @@ HogQLParser::HogqlxTagElementContext* HogQLParser::hogqlxTagElement() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(1042); + setState(1060); match(HogQLParser::LT); - setState(1043); + setState(1061); identifier(); - setState(1047); + setState(1065); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -181272084561788930) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 201863462911) != 0)) { - setState(1044); + setState(1062); hogqlxTagAttribute(); - setState(1049); + setState(1067); _errHandler->sync(this); _la = _input->LA(1); } - setState(1050); + setState(1068); match(HogQLParser::GT); - setState(1052); + setState(1070); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 132, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 134, _ctx)) { case 1: { - setState(1051); + setState(1069); hogqlxTagElement(); break; } @@ -8885,13 +9030,13 @@ HogQLParser::HogqlxTagElementContext* HogQLParser::hogqlxTagElement() { default: break; } - setState(1054); + setState(1072); match(HogQLParser::LT); - setState(1055); + setState(1073); match(HogQLParser::SLASH); - setState(1056); + setState(1074); identifier(); - setState(1057); + setState(1075); match(HogQLParser::GT); break; } @@ -8955,7 +9100,7 @@ std::any HogQLParser::HogqlxTagAttributeContext::accept(tree::ParseTreeVisitor * HogQLParser::HogqlxTagAttributeContext* HogQLParser::hogqlxTagAttribute() { HogqlxTagAttributeContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 118, HogQLParser::RuleHogqlxTagAttribute); + enterRule(_localctx, 120, HogQLParser::RuleHogqlxTagAttribute); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -8965,38 +9110,38 @@ HogQLParser::HogqlxTagAttributeContext* HogQLParser::hogqlxTagAttribute() { exitRule(); }); try { - setState(1072); + setState(1090); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 134, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 136, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(1061); + setState(1079); identifier(); - setState(1062); + setState(1080); match(HogQLParser::EQ_SINGLE); - setState(1063); + setState(1081); string(); break; } case 2: { enterOuterAlt(_localctx, 2); - setState(1065); + setState(1083); identifier(); - setState(1066); + setState(1084); match(HogQLParser::EQ_SINGLE); - setState(1067); + setState(1085); match(HogQLParser::LBRACE); - setState(1068); + setState(1086); columnExpr(0); - setState(1069); + setState(1087); match(HogQLParser::RBRACE); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(1071); + setState(1089); identifier(); break; } @@ -9052,7 +9197,7 @@ std::any HogQLParser::WithExprListContext::accept(tree::ParseTreeVisitor *visito HogQLParser::WithExprListContext* HogQLParser::withExprList() { WithExprListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 120, HogQLParser::RuleWithExprList); + enterRule(_localctx, 122, HogQLParser::RuleWithExprList); size_t _la = 0; #if __cplusplus > 201703L @@ -9065,28 +9210,28 @@ HogQLParser::WithExprListContext* HogQLParser::withExprList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1074); + setState(1092); withExpr(); - setState(1079); + setState(1097); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 135, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 137, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1075); + setState(1093); match(HogQLParser::COMMA); - setState(1076); + setState(1094); withExpr(); } - setState(1081); + setState(1099); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 135, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 137, _ctx); } - setState(1083); + setState(1101); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1082); + setState(1100); match(HogQLParser::COMMA); } @@ -9171,7 +9316,7 @@ std::any HogQLParser::WithExprSubqueryContext::accept(tree::ParseTreeVisitor *vi } HogQLParser::WithExprContext* HogQLParser::withExpr() { WithExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 122, HogQLParser::RuleWithExpr); + enterRule(_localctx, 124, HogQLParser::RuleWithExpr); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9181,21 +9326,21 @@ HogQLParser::WithExprContext* HogQLParser::withExpr() { exitRule(); }); try { - setState(1095); + setState(1113); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 137, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 139, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(1085); + setState(1103); identifier(); - setState(1086); + setState(1104); match(HogQLParser::AS); - setState(1087); + setState(1105); match(HogQLParser::LPAREN); - setState(1088); + setState(1106); selectUnionStmt(); - setState(1089); + setState(1107); match(HogQLParser::RPAREN); break; } @@ -9203,11 +9348,11 @@ HogQLParser::WithExprContext* HogQLParser::withExpr() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(1091); + setState(1109); columnExpr(0); - setState(1092); + setState(1110); match(HogQLParser::AS); - setState(1093); + setState(1111); identifier(); break; } @@ -9263,7 +9408,7 @@ std::any HogQLParser::ColumnIdentifierContext::accept(tree::ParseTreeVisitor *vi HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { ColumnIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 124, HogQLParser::RuleColumnIdentifier); + enterRule(_localctx, 126, HogQLParser::RuleColumnIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9273,12 +9418,12 @@ HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { exitRule(); }); try { - setState(1104); + setState(1122); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LBRACE: { enterOuterAlt(_localctx, 1); - setState(1097); + setState(1115); placeholder(); break; } @@ -9378,14 +9523,14 @@ HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { case HogQLParser::YEAR: case HogQLParser::IDENTIFIER: { enterOuterAlt(_localctx, 2); - setState(1101); + setState(1119); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 138, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 140, _ctx)) { case 1: { - setState(1098); + setState(1116); tableIdentifier(); - setState(1099); + setState(1117); match(HogQLParser::DOT); break; } @@ -9393,7 +9538,7 @@ HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { default: break; } - setState(1103); + setState(1121); nestedIdentifier(); break; } @@ -9449,7 +9594,7 @@ std::any HogQLParser::NestedIdentifierContext::accept(tree::ParseTreeVisitor *vi HogQLParser::NestedIdentifierContext* HogQLParser::nestedIdentifier() { NestedIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 126, HogQLParser::RuleNestedIdentifier); + enterRule(_localctx, 128, HogQLParser::RuleNestedIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9461,21 +9606,21 @@ HogQLParser::NestedIdentifierContext* HogQLParser::nestedIdentifier() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1106); + setState(1124); identifier(); - setState(1111); + setState(1129); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 140, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 142, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1107); + setState(1125); match(HogQLParser::DOT); - setState(1108); + setState(1126); identifier(); } - setState(1113); + setState(1131); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 140, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 142, _ctx); } } @@ -9624,8 +9769,8 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { HogQLParser::TableExprContext *_localctx = _tracker.createInstance(_ctx, parentState); HogQLParser::TableExprContext *previousContext = _localctx; (void)previousContext; // Silence compiler, in case the context is not used by generated code. - size_t startState = 128; - enterRecursionRule(_localctx, 128, HogQLParser::RuleTableExpr, precedence); + size_t startState = 130; + enterRecursionRule(_localctx, 130, HogQLParser::RuleTableExpr, precedence); @@ -9639,15 +9784,15 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1123); + setState(1141); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 141, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 143, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1115); + setState(1133); tableIdentifier(); break; } @@ -9656,7 +9801,7 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1116); + setState(1134); tableFunctionExpr(); break; } @@ -9665,11 +9810,11 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1117); + setState(1135); match(HogQLParser::LPAREN); - setState(1118); + setState(1136); selectUnionStmt(); - setState(1119); + setState(1137); match(HogQLParser::RPAREN); break; } @@ -9678,7 +9823,7 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1121); + setState(1139); hogqlxTagElement(); break; } @@ -9687,7 +9832,7 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1122); + setState(1140); placeholder(); break; } @@ -9696,9 +9841,9 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { break; } _ctx->stop = _input->LT(-1); - setState(1133); + setState(1151); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 143, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 145, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { if (!_parseListeners.empty()) @@ -9707,10 +9852,10 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleTableExpr); - setState(1125); + setState(1143); if (!(precpred(_ctx, 3))) throw FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(1129); + setState(1147); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::DATE: @@ -9718,15 +9863,15 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { case HogQLParser::ID: case HogQLParser::KEY: case HogQLParser::IDENTIFIER: { - setState(1126); + setState(1144); alias(); break; } case HogQLParser::AS: { - setState(1127); + setState(1145); match(HogQLParser::AS); - setState(1128); + setState(1146); identifier(); break; } @@ -9735,9 +9880,9 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { throw NoViableAltException(this); } } - setState(1135); + setState(1153); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 143, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 145, _ctx); } } catch (RecognitionException &e) { @@ -9785,7 +9930,7 @@ std::any HogQLParser::TableFunctionExprContext::accept(tree::ParseTreeVisitor *v HogQLParser::TableFunctionExprContext* HogQLParser::tableFunctionExpr() { TableFunctionExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 130, HogQLParser::RuleTableFunctionExpr); + enterRule(_localctx, 132, HogQLParser::RuleTableFunctionExpr); size_t _la = 0; #if __cplusplus > 201703L @@ -9797,11 +9942,11 @@ HogQLParser::TableFunctionExprContext* HogQLParser::tableFunctionExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(1136); + setState(1154); identifier(); - setState(1137); + setState(1155); match(HogQLParser::LPAREN); - setState(1139); + setState(1157); _errHandler->sync(this); _la = _input->LA(1); @@ -9809,10 +9954,10 @@ HogQLParser::TableFunctionExprContext* HogQLParser::tableFunctionExpr() { ((1ULL << _la) & -1125900443713538) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 8076106347046764543) != 0) || ((((_la - 128) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 128)) & 1153) != 0)) { - setState(1138); + setState(1156); tableArgList(); } - setState(1141); + setState(1159); match(HogQLParser::RPAREN); } @@ -9858,7 +10003,7 @@ std::any HogQLParser::TableIdentifierContext::accept(tree::ParseTreeVisitor *vis HogQLParser::TableIdentifierContext* HogQLParser::tableIdentifier() { TableIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 132, HogQLParser::RuleTableIdentifier); + enterRule(_localctx, 134, HogQLParser::RuleTableIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9869,14 +10014,14 @@ HogQLParser::TableIdentifierContext* HogQLParser::tableIdentifier() { }); try { enterOuterAlt(_localctx, 1); - setState(1146); + setState(1164); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 145, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 147, _ctx)) { case 1: { - setState(1143); + setState(1161); databaseIdentifier(); - setState(1144); + setState(1162); match(HogQLParser::DOT); break; } @@ -9884,7 +10029,7 @@ HogQLParser::TableIdentifierContext* HogQLParser::tableIdentifier() { default: break; } - setState(1148); + setState(1166); identifier(); } @@ -9934,7 +10079,7 @@ std::any HogQLParser::TableArgListContext::accept(tree::ParseTreeVisitor *visito HogQLParser::TableArgListContext* HogQLParser::tableArgList() { TableArgListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 134, HogQLParser::RuleTableArgList); + enterRule(_localctx, 136, HogQLParser::RuleTableArgList); size_t _la = 0; #if __cplusplus > 201703L @@ -9947,28 +10092,28 @@ HogQLParser::TableArgListContext* HogQLParser::tableArgList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1150); + setState(1168); columnExpr(0); - setState(1155); + setState(1173); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 146, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 148, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1151); + setState(1169); match(HogQLParser::COMMA); - setState(1152); + setState(1170); columnExpr(0); } - setState(1157); + setState(1175); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 146, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 148, _ctx); } - setState(1159); + setState(1177); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1158); + setState(1176); match(HogQLParser::COMMA); } @@ -10007,7 +10152,7 @@ std::any HogQLParser::DatabaseIdentifierContext::accept(tree::ParseTreeVisitor * HogQLParser::DatabaseIdentifierContext* HogQLParser::databaseIdentifier() { DatabaseIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 136, HogQLParser::RuleDatabaseIdentifier); + enterRule(_localctx, 138, HogQLParser::RuleDatabaseIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -10018,7 +10163,7 @@ HogQLParser::DatabaseIdentifierContext* HogQLParser::databaseIdentifier() { }); try { enterOuterAlt(_localctx, 1); - setState(1161); + setState(1179); identifier(); } @@ -10072,7 +10217,7 @@ std::any HogQLParser::FloatingLiteralContext::accept(tree::ParseTreeVisitor *vis HogQLParser::FloatingLiteralContext* HogQLParser::floatingLiteral() { FloatingLiteralContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 138, HogQLParser::RuleFloatingLiteral); + enterRule(_localctx, 140, HogQLParser::RuleFloatingLiteral); size_t _la = 0; #if __cplusplus > 201703L @@ -10083,21 +10228,21 @@ HogQLParser::FloatingLiteralContext* HogQLParser::floatingLiteral() { exitRule(); }); try { - setState(1171); + setState(1189); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::FLOATING_LITERAL: { enterOuterAlt(_localctx, 1); - setState(1163); + setState(1181); match(HogQLParser::FLOATING_LITERAL); break; } case HogQLParser::DOT: { enterOuterAlt(_localctx, 2); - setState(1164); + setState(1182); match(HogQLParser::DOT); - setState(1165); + setState(1183); _la = _input->LA(1); if (!(_la == HogQLParser::OCTAL_LITERAL @@ -10113,16 +10258,16 @@ HogQLParser::FloatingLiteralContext* HogQLParser::floatingLiteral() { case HogQLParser::DECIMAL_LITERAL: { enterOuterAlt(_localctx, 3); - setState(1166); + setState(1184); match(HogQLParser::DECIMAL_LITERAL); - setState(1167); + setState(1185); match(HogQLParser::DOT); - setState(1169); + setState(1187); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 148, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 150, _ctx)) { case 1: { - setState(1168); + setState(1186); _la = _input->LA(1); if (!(_la == HogQLParser::OCTAL_LITERAL @@ -10209,7 +10354,7 @@ std::any HogQLParser::NumberLiteralContext::accept(tree::ParseTreeVisitor *visit HogQLParser::NumberLiteralContext* HogQLParser::numberLiteral() { NumberLiteralContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 140, HogQLParser::RuleNumberLiteral); + enterRule(_localctx, 142, HogQLParser::RuleNumberLiteral); size_t _la = 0; #if __cplusplus > 201703L @@ -10221,14 +10366,14 @@ HogQLParser::NumberLiteralContext* HogQLParser::numberLiteral() { }); try { enterOuterAlt(_localctx, 1); - setState(1174); + setState(1192); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::DASH || _la == HogQLParser::PLUS) { - setState(1173); + setState(1191); _la = _input->LA(1); if (!(_la == HogQLParser::DASH @@ -10240,41 +10385,41 @@ HogQLParser::NumberLiteralContext* HogQLParser::numberLiteral() { consume(); } } - setState(1182); + setState(1200); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 151, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 153, _ctx)) { case 1: { - setState(1176); + setState(1194); floatingLiteral(); break; } case 2: { - setState(1177); + setState(1195); match(HogQLParser::OCTAL_LITERAL); break; } case 3: { - setState(1178); + setState(1196); match(HogQLParser::DECIMAL_LITERAL); break; } case 4: { - setState(1179); + setState(1197); match(HogQLParser::HEXADECIMAL_LITERAL); break; } case 5: { - setState(1180); + setState(1198); match(HogQLParser::INF); break; } case 6: { - setState(1181); + setState(1199); match(HogQLParser::NAN_SQL); break; } @@ -10326,7 +10471,7 @@ std::any HogQLParser::LiteralContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::LiteralContext* HogQLParser::literal() { LiteralContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 142, HogQLParser::RuleLiteral); + enterRule(_localctx, 144, HogQLParser::RuleLiteral); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -10336,7 +10481,7 @@ HogQLParser::LiteralContext* HogQLParser::literal() { exitRule(); }); try { - setState(1187); + setState(1205); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::INF: @@ -10349,21 +10494,21 @@ HogQLParser::LiteralContext* HogQLParser::literal() { case HogQLParser::DOT: case HogQLParser::PLUS: { enterOuterAlt(_localctx, 1); - setState(1184); + setState(1202); numberLiteral(); break; } case HogQLParser::STRING_LITERAL: { enterOuterAlt(_localctx, 2); - setState(1185); + setState(1203); match(HogQLParser::STRING_LITERAL); break; } case HogQLParser::NULL_SQL: { enterOuterAlt(_localctx, 3); - setState(1186); + setState(1204); match(HogQLParser::NULL_SQL); break; } @@ -10435,7 +10580,7 @@ std::any HogQLParser::IntervalContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::IntervalContext* HogQLParser::interval() { IntervalContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 144, HogQLParser::RuleInterval); + enterRule(_localctx, 146, HogQLParser::RuleInterval); size_t _la = 0; #if __cplusplus > 201703L @@ -10447,7 +10592,7 @@ HogQLParser::IntervalContext* HogQLParser::interval() { }); try { enterOuterAlt(_localctx, 1); - setState(1189); + setState(1207); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 27021666484748288) != 0) || ((((_la - 68) & ~ 0x3fULL) == 0) && @@ -10830,7 +10975,7 @@ std::any HogQLParser::KeywordContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::KeywordContext* HogQLParser::keyword() { KeywordContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 146, HogQLParser::RuleKeyword); + enterRule(_localctx, 148, HogQLParser::RuleKeyword); size_t _la = 0; #if __cplusplus > 201703L @@ -10842,7 +10987,7 @@ HogQLParser::KeywordContext* HogQLParser::keyword() { }); try { enterOuterAlt(_localctx, 1); - setState(1191); + setState(1209); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -208293751046537218) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && @@ -10901,7 +11046,7 @@ std::any HogQLParser::KeywordForAliasContext::accept(tree::ParseTreeVisitor *vis HogQLParser::KeywordForAliasContext* HogQLParser::keywordForAlias() { KeywordForAliasContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 148, HogQLParser::RuleKeywordForAlias); + enterRule(_localctx, 150, HogQLParser::RuleKeywordForAlias); size_t _la = 0; #if __cplusplus > 201703L @@ -10913,7 +11058,7 @@ HogQLParser::KeywordForAliasContext* HogQLParser::keywordForAlias() { }); try { enterOuterAlt(_localctx, 1); - setState(1193); + setState(1211); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 70506452090880) != 0))) { @@ -10963,7 +11108,7 @@ std::any HogQLParser::AliasContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::AliasContext* HogQLParser::alias() { AliasContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 150, HogQLParser::RuleAlias); + enterRule(_localctx, 152, HogQLParser::RuleAlias); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -10973,12 +11118,12 @@ HogQLParser::AliasContext* HogQLParser::alias() { exitRule(); }); try { - setState(1197); + setState(1215); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::IDENTIFIER: { enterOuterAlt(_localctx, 1); - setState(1195); + setState(1213); match(HogQLParser::IDENTIFIER); break; } @@ -10988,7 +11133,7 @@ HogQLParser::AliasContext* HogQLParser::alias() { case HogQLParser::ID: case HogQLParser::KEY: { enterOuterAlt(_localctx, 2); - setState(1196); + setState(1214); keywordForAlias(); break; } @@ -11040,7 +11185,7 @@ std::any HogQLParser::IdentifierContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::IdentifierContext* HogQLParser::identifier() { IdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 152, HogQLParser::RuleIdentifier); + enterRule(_localctx, 154, HogQLParser::RuleIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11050,12 +11195,12 @@ HogQLParser::IdentifierContext* HogQLParser::identifier() { exitRule(); }); try { - setState(1202); + setState(1220); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::IDENTIFIER: { enterOuterAlt(_localctx, 1); - setState(1199); + setState(1217); match(HogQLParser::IDENTIFIER); break; } @@ -11069,7 +11214,7 @@ HogQLParser::IdentifierContext* HogQLParser::identifier() { case HogQLParser::WEEK: case HogQLParser::YEAR: { enterOuterAlt(_localctx, 2); - setState(1200); + setState(1218); interval(); break; } @@ -11160,7 +11305,7 @@ HogQLParser::IdentifierContext* HogQLParser::identifier() { case HogQLParser::WINDOW: case HogQLParser::WITH: { enterOuterAlt(_localctx, 3); - setState(1201); + setState(1219); keyword(); break; } @@ -11212,7 +11357,7 @@ std::any HogQLParser::EnumValueContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::EnumValueContext* HogQLParser::enumValue() { EnumValueContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 154, HogQLParser::RuleEnumValue); + enterRule(_localctx, 156, HogQLParser::RuleEnumValue); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11223,11 +11368,11 @@ HogQLParser::EnumValueContext* HogQLParser::enumValue() { }); try { enterOuterAlt(_localctx, 1); - setState(1204); + setState(1222); string(); - setState(1205); + setState(1223); match(HogQLParser::EQ_SINGLE); - setState(1206); + setState(1224); numberLiteral(); } @@ -11273,7 +11418,7 @@ std::any HogQLParser::PlaceholderContext::accept(tree::ParseTreeVisitor *visitor HogQLParser::PlaceholderContext* HogQLParser::placeholder() { PlaceholderContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 156, HogQLParser::RulePlaceholder); + enterRule(_localctx, 158, HogQLParser::RulePlaceholder); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11284,11 +11429,11 @@ HogQLParser::PlaceholderContext* HogQLParser::placeholder() { }); try { enterOuterAlt(_localctx, 1); - setState(1208); + setState(1226); match(HogQLParser::LBRACE); - setState(1209); + setState(1227); identifier(); - setState(1210); + setState(1228); match(HogQLParser::RBRACE); } @@ -11330,7 +11475,7 @@ std::any HogQLParser::StringContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::StringContext* HogQLParser::string() { StringContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 158, HogQLParser::RuleString); + enterRule(_localctx, 160, HogQLParser::RuleString); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11340,19 +11485,19 @@ HogQLParser::StringContext* HogQLParser::string() { exitRule(); }); try { - setState(1214); + setState(1232); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::STRING_LITERAL: { enterOuterAlt(_localctx, 1); - setState(1212); + setState(1230); match(HogQLParser::STRING_LITERAL); break; } case HogQLParser::QUOTE_SINGLE_TEMPLATE: { enterOuterAlt(_localctx, 2); - setState(1213); + setState(1231); templateString(); break; } @@ -11408,7 +11553,7 @@ std::any HogQLParser::TemplateStringContext::accept(tree::ParseTreeVisitor *visi HogQLParser::TemplateStringContext* HogQLParser::templateString() { TemplateStringContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 160, HogQLParser::RuleTemplateString); + enterRule(_localctx, 162, HogQLParser::RuleTemplateString); size_t _la = 0; #if __cplusplus > 201703L @@ -11420,21 +11565,21 @@ HogQLParser::TemplateStringContext* HogQLParser::templateString() { }); try { enterOuterAlt(_localctx, 1); - setState(1216); + setState(1234); match(HogQLParser::QUOTE_SINGLE_TEMPLATE); - setState(1220); + setState(1238); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::STRING_TEXT || _la == HogQLParser::STRING_ESCAPE_TRIGGER) { - setState(1217); + setState(1235); stringContents(); - setState(1222); + setState(1240); _errHandler->sync(this); _la = _input->LA(1); } - setState(1223); + setState(1241); match(HogQLParser::QUOTE_SINGLE); } @@ -11484,7 +11629,7 @@ std::any HogQLParser::StringContentsContext::accept(tree::ParseTreeVisitor *visi HogQLParser::StringContentsContext* HogQLParser::stringContents() { StringContentsContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 162, HogQLParser::RuleStringContents); + enterRule(_localctx, 164, HogQLParser::RuleStringContents); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11494,23 +11639,23 @@ HogQLParser::StringContentsContext* HogQLParser::stringContents() { exitRule(); }); try { - setState(1230); + setState(1248); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::STRING_ESCAPE_TRIGGER: { enterOuterAlt(_localctx, 1); - setState(1225); + setState(1243); match(HogQLParser::STRING_ESCAPE_TRIGGER); - setState(1226); + setState(1244); columnExpr(0); - setState(1227); + setState(1245); match(HogQLParser::RBRACE); break; } case HogQLParser::STRING_TEXT: { enterOuterAlt(_localctx, 2); - setState(1229); + setState(1247); match(HogQLParser::STRING_TEXT); break; } @@ -11566,7 +11711,7 @@ std::any HogQLParser::FullTemplateStringContext::accept(tree::ParseTreeVisitor * HogQLParser::FullTemplateStringContext* HogQLParser::fullTemplateString() { FullTemplateStringContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 164, HogQLParser::RuleFullTemplateString); + enterRule(_localctx, 166, HogQLParser::RuleFullTemplateString); size_t _la = 0; #if __cplusplus > 201703L @@ -11578,21 +11723,21 @@ HogQLParser::FullTemplateStringContext* HogQLParser::fullTemplateString() { }); try { enterOuterAlt(_localctx, 1); - setState(1232); + setState(1250); match(HogQLParser::QUOTE_SINGLE_TEMPLATE_FULL); - setState(1236); + setState(1254); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::FULL_STRING_TEXT || _la == HogQLParser::FULL_STRING_ESCAPE_TRIGGER) { - setState(1233); + setState(1251); stringContentsFull(); - setState(1238); + setState(1256); _errHandler->sync(this); _la = _input->LA(1); } - setState(1239); + setState(1257); match(HogQLParser::EOF); } @@ -11642,7 +11787,7 @@ std::any HogQLParser::StringContentsFullContext::accept(tree::ParseTreeVisitor * HogQLParser::StringContentsFullContext* HogQLParser::stringContentsFull() { StringContentsFullContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 166, HogQLParser::RuleStringContentsFull); + enterRule(_localctx, 168, HogQLParser::RuleStringContentsFull); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11652,23 +11797,23 @@ HogQLParser::StringContentsFullContext* HogQLParser::stringContentsFull() { exitRule(); }); try { - setState(1246); + setState(1264); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::FULL_STRING_ESCAPE_TRIGGER: { enterOuterAlt(_localctx, 1); - setState(1241); + setState(1259); match(HogQLParser::FULL_STRING_ESCAPE_TRIGGER); - setState(1242); + setState(1260); columnExpr(0); - setState(1243); + setState(1261); match(HogQLParser::RBRACE); break; } case HogQLParser::FULL_STRING_TEXT: { enterOuterAlt(_localctx, 2); - setState(1245); + setState(1263); match(HogQLParser::FULL_STRING_TEXT); break; } @@ -11689,9 +11834,9 @@ HogQLParser::StringContentsFullContext* HogQLParser::stringContentsFull() { bool HogQLParser::sempred(RuleContext *context, size_t ruleIndex, size_t predicateIndex) { switch (ruleIndex) { - case 35: return joinExprSempred(antlrcpp::downCast(context), predicateIndex); - case 54: return columnExprSempred(antlrcpp::downCast(context), predicateIndex); - case 64: return tableExprSempred(antlrcpp::downCast(context), predicateIndex); + case 36: return joinExprSempred(antlrcpp::downCast(context), predicateIndex); + case 55: return columnExprSempred(antlrcpp::downCast(context), predicateIndex); + case 65: return tableExprSempred(antlrcpp::downCast(context), predicateIndex); default: break; diff --git a/hogql_parser/HogQLParser.h b/hogql_parser/HogQLParser.h index 9c8b5bae23c10..bfe715d1cd82c 100644 --- a/hogql_parser/HogQLParser.h +++ b/hogql_parser/HogQLParser.h @@ -46,29 +46,29 @@ class HogQLParser : public antlr4::Parser { enum { RuleProgram = 0, RuleDeclaration = 1, RuleExpression = 2, RuleVarDecl = 3, RuleIdentifierList = 4, RuleStatement = 5, RuleReturnStmt = 6, RuleIfStmt = 7, - RuleWhileStmt = 8, RuleForStmt = 9, RuleFuncStmt = 10, RuleVarAssignment = 11, - RuleExprStmt = 12, RuleEmptyStmt = 13, RuleBlock = 14, RuleKvPair = 15, - RuleKvPairList = 16, RuleSelect = 17, RuleSelectUnionStmt = 18, RuleSelectStmtWithParens = 19, - RuleSelectStmt = 20, RuleWithClause = 21, RuleTopClause = 22, RuleFromClause = 23, - RuleArrayJoinClause = 24, RuleWindowClause = 25, RulePrewhereClause = 26, - RuleWhereClause = 27, RuleGroupByClause = 28, RuleHavingClause = 29, - RuleOrderByClause = 30, RuleProjectionOrderByClause = 31, RuleLimitAndOffsetClause = 32, - RuleOffsetOnlyClause = 33, RuleSettingsClause = 34, RuleJoinExpr = 35, - RuleJoinOp = 36, RuleJoinOpCross = 37, RuleJoinConstraintClause = 38, - RuleSampleClause = 39, RuleOrderExprList = 40, RuleOrderExpr = 41, RuleRatioExpr = 42, - RuleSettingExprList = 43, RuleSettingExpr = 44, RuleWindowExpr = 45, - RuleWinPartitionByClause = 46, RuleWinOrderByClause = 47, RuleWinFrameClause = 48, - RuleWinFrameExtend = 49, RuleWinFrameBound = 50, RuleExpr = 51, RuleColumnTypeExpr = 52, - RuleColumnExprList = 53, RuleColumnExpr = 54, RuleColumnArgList = 55, - RuleColumnArgExpr = 56, RuleColumnLambdaExpr = 57, RuleHogqlxTagElement = 58, - RuleHogqlxTagAttribute = 59, RuleWithExprList = 60, RuleWithExpr = 61, - RuleColumnIdentifier = 62, RuleNestedIdentifier = 63, RuleTableExpr = 64, - RuleTableFunctionExpr = 65, RuleTableIdentifier = 66, RuleTableArgList = 67, - RuleDatabaseIdentifier = 68, RuleFloatingLiteral = 69, RuleNumberLiteral = 70, - RuleLiteral = 71, RuleInterval = 72, RuleKeyword = 73, RuleKeywordForAlias = 74, - RuleAlias = 75, RuleIdentifier = 76, RuleEnumValue = 77, RulePlaceholder = 78, - RuleString = 79, RuleTemplateString = 80, RuleStringContents = 81, RuleFullTemplateString = 82, - RuleStringContentsFull = 83 + RuleWhileStmt = 8, RuleForStmt = 9, RuleForInStmt = 10, RuleFuncStmt = 11, + RuleVarAssignment = 12, RuleExprStmt = 13, RuleEmptyStmt = 14, RuleBlock = 15, + RuleKvPair = 16, RuleKvPairList = 17, RuleSelect = 18, RuleSelectUnionStmt = 19, + RuleSelectStmtWithParens = 20, RuleSelectStmt = 21, RuleWithClause = 22, + RuleTopClause = 23, RuleFromClause = 24, RuleArrayJoinClause = 25, RuleWindowClause = 26, + RulePrewhereClause = 27, RuleWhereClause = 28, RuleGroupByClause = 29, + RuleHavingClause = 30, RuleOrderByClause = 31, RuleProjectionOrderByClause = 32, + RuleLimitAndOffsetClause = 33, RuleOffsetOnlyClause = 34, RuleSettingsClause = 35, + RuleJoinExpr = 36, RuleJoinOp = 37, RuleJoinOpCross = 38, RuleJoinConstraintClause = 39, + RuleSampleClause = 40, RuleOrderExprList = 41, RuleOrderExpr = 42, RuleRatioExpr = 43, + RuleSettingExprList = 44, RuleSettingExpr = 45, RuleWindowExpr = 46, + RuleWinPartitionByClause = 47, RuleWinOrderByClause = 48, RuleWinFrameClause = 49, + RuleWinFrameExtend = 50, RuleWinFrameBound = 51, RuleExpr = 52, RuleColumnTypeExpr = 53, + RuleColumnExprList = 54, RuleColumnExpr = 55, RuleColumnArgList = 56, + RuleColumnArgExpr = 57, RuleColumnLambdaExpr = 58, RuleHogqlxTagElement = 59, + RuleHogqlxTagAttribute = 60, RuleWithExprList = 61, RuleWithExpr = 62, + RuleColumnIdentifier = 63, RuleNestedIdentifier = 64, RuleTableExpr = 65, + RuleTableFunctionExpr = 66, RuleTableIdentifier = 67, RuleTableArgList = 68, + RuleDatabaseIdentifier = 69, RuleFloatingLiteral = 70, RuleNumberLiteral = 71, + RuleLiteral = 72, RuleInterval = 73, RuleKeyword = 74, RuleKeywordForAlias = 75, + RuleAlias = 76, RuleIdentifier = 77, RuleEnumValue = 78, RulePlaceholder = 79, + RuleString = 80, RuleTemplateString = 81, RuleStringContents = 82, RuleFullTemplateString = 83, + RuleStringContentsFull = 84 }; explicit HogQLParser(antlr4::TokenStream *input); @@ -98,6 +98,7 @@ class HogQLParser : public antlr4::Parser { class IfStmtContext; class WhileStmtContext; class ForStmtContext; + class ForInStmtContext; class FuncStmtContext; class VarAssignmentContext; class ExprStmtContext; @@ -255,6 +256,7 @@ class HogQLParser : public antlr4::Parser { ReturnStmtContext *returnStmt(); IfStmtContext *ifStmt(); WhileStmtContext *whileStmt(); + ForInStmtContext *forInStmt(); ForStmtContext *forStmt(); FuncStmtContext *funcStmt(); VarAssignmentContext *varAssignment(); @@ -352,6 +354,29 @@ class HogQLParser : public antlr4::Parser { ForStmtContext* forStmt(); + class ForInStmtContext : public antlr4::ParserRuleContext { + public: + ForInStmtContext(antlr4::ParserRuleContext *parent, size_t invokingState); + virtual size_t getRuleIndex() const override; + antlr4::tree::TerminalNode *FOR(); + antlr4::tree::TerminalNode *LPAREN(); + antlr4::tree::TerminalNode *LET(); + std::vector identifier(); + IdentifierContext* identifier(size_t i); + antlr4::tree::TerminalNode *IN(); + ExpressionContext *expression(); + antlr4::tree::TerminalNode *RPAREN(); + StatementContext *statement(); + antlr4::tree::TerminalNode *COMMA(); + antlr4::tree::TerminalNode *SEMICOLON(); + + + virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override; + + }; + + ForInStmtContext* forInStmt(); + class FuncStmtContext : public antlr4::ParserRuleContext { public: FuncStmtContext(antlr4::ParserRuleContext *parent, size_t invokingState); diff --git a/hogql_parser/HogQLParser.interp b/hogql_parser/HogQLParser.interp index 42ddfb5536fe8..22fc706e26505 100644 --- a/hogql_parser/HogQLParser.interp +++ b/hogql_parser/HogQLParser.interp @@ -325,6 +325,7 @@ returnStmt ifStmt whileStmt forStmt +forInStmt funcStmt varAssignment exprStmt @@ -402,4 +403,4 @@ stringContentsFull atn: -[4, 1, 155, 1249, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 1, 0, 5, 0, 170, 8, 0, 10, 0, 12, 0, 173, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 179, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 188, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 193, 8, 4, 10, 4, 12, 4, 196, 9, 4, 1, 4, 3, 4, 199, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 210, 8, 5, 1, 6, 1, 6, 3, 6, 214, 8, 6, 1, 6, 3, 6, 217, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 226, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 234, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 241, 8, 9, 1, 9, 1, 9, 3, 9, 245, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 251, 8, 9, 1, 9, 1, 9, 1, 9, 3, 9, 256, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 262, 8, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 3, 12, 274, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 5, 14, 280, 8, 14, 10, 14, 12, 14, 283, 9, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 5, 16, 294, 8, 16, 10, 16, 12, 16, 297, 9, 16, 1, 16, 3, 16, 300, 8, 16, 1, 17, 1, 17, 1, 17, 3, 17, 305, 8, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 5, 18, 313, 8, 18, 10, 18, 12, 18, 316, 9, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 324, 8, 19, 1, 20, 3, 20, 327, 8, 20, 1, 20, 1, 20, 3, 20, 331, 8, 20, 1, 20, 3, 20, 334, 8, 20, 1, 20, 1, 20, 3, 20, 338, 8, 20, 1, 20, 3, 20, 341, 8, 20, 1, 20, 3, 20, 344, 8, 20, 1, 20, 3, 20, 347, 8, 20, 1, 20, 3, 20, 350, 8, 20, 1, 20, 1, 20, 3, 20, 354, 8, 20, 1, 20, 1, 20, 3, 20, 358, 8, 20, 1, 20, 3, 20, 361, 8, 20, 1, 20, 3, 20, 364, 8, 20, 1, 20, 3, 20, 367, 8, 20, 1, 20, 1, 20, 3, 20, 371, 8, 20, 1, 20, 3, 20, 374, 8, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 383, 8, 22, 1, 23, 1, 23, 1, 23, 1, 24, 3, 24, 389, 8, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 5, 25, 408, 8, 25, 10, 25, 12, 25, 411, 9, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 427, 8, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 444, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 450, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 456, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 467, 8, 32, 3, 32, 469, 8, 32, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 3, 35, 480, 8, 35, 1, 35, 3, 35, 483, 8, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 489, 8, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 497, 8, 35, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 503, 8, 35, 10, 35, 12, 35, 506, 9, 35, 1, 36, 3, 36, 509, 8, 36, 1, 36, 1, 36, 1, 36, 3, 36, 514, 8, 36, 1, 36, 3, 36, 517, 8, 36, 1, 36, 3, 36, 520, 8, 36, 1, 36, 1, 36, 3, 36, 524, 8, 36, 1, 36, 1, 36, 3, 36, 528, 8, 36, 1, 36, 3, 36, 531, 8, 36, 3, 36, 533, 8, 36, 1, 36, 3, 36, 536, 8, 36, 1, 36, 1, 36, 3, 36, 540, 8, 36, 1, 36, 1, 36, 3, 36, 544, 8, 36, 1, 36, 3, 36, 547, 8, 36, 3, 36, 549, 8, 36, 3, 36, 551, 8, 36, 1, 37, 1, 37, 1, 37, 3, 37, 556, 8, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 567, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 573, 8, 39, 1, 40, 1, 40, 1, 40, 5, 40, 578, 8, 40, 10, 40, 12, 40, 581, 9, 40, 1, 41, 1, 41, 3, 41, 585, 8, 41, 1, 41, 1, 41, 3, 41, 589, 8, 41, 1, 41, 1, 41, 3, 41, 593, 8, 41, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 599, 8, 42, 3, 42, 601, 8, 42, 1, 43, 1, 43, 1, 43, 5, 43, 606, 8, 43, 10, 43, 12, 43, 609, 9, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 3, 45, 616, 8, 45, 1, 45, 3, 45, 619, 8, 45, 1, 45, 3, 45, 622, 8, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 641, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 655, 8, 50, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 5, 52, 669, 8, 52, 10, 52, 12, 52, 672, 9, 52, 1, 52, 3, 52, 675, 8, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 5, 52, 684, 8, 52, 10, 52, 12, 52, 687, 9, 52, 1, 52, 3, 52, 690, 8, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 5, 52, 699, 8, 52, 10, 52, 12, 52, 702, 9, 52, 1, 52, 3, 52, 705, 8, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 3, 52, 712, 8, 52, 1, 52, 1, 52, 3, 52, 716, 8, 52, 1, 53, 1, 53, 1, 53, 5, 53, 721, 8, 53, 10, 53, 12, 53, 724, 9, 53, 1, 53, 3, 53, 727, 8, 53, 1, 54, 1, 54, 1, 54, 3, 54, 732, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 4, 54, 739, 8, 54, 11, 54, 12, 54, 740, 1, 54, 1, 54, 3, 54, 745, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 769, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 786, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 792, 8, 54, 1, 54, 3, 54, 795, 8, 54, 1, 54, 3, 54, 798, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 808, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 814, 8, 54, 1, 54, 3, 54, 817, 8, 54, 1, 54, 3, 54, 820, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 828, 8, 54, 1, 54, 3, 54, 831, 8, 54, 1, 54, 1, 54, 3, 54, 835, 8, 54, 1, 54, 3, 54, 838, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 852, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 869, 8, 54, 1, 54, 1, 54, 1, 54, 3, 54, 874, 8, 54, 1, 54, 1, 54, 3, 54, 878, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 884, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 891, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 903, 8, 54, 1, 54, 1, 54, 3, 54, 907, 8, 54, 1, 54, 3, 54, 910, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 919, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 933, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 972, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 980, 8, 54, 5, 54, 982, 8, 54, 10, 54, 12, 54, 985, 9, 54, 1, 55, 1, 55, 1, 55, 5, 55, 990, 8, 55, 10, 55, 12, 55, 993, 9, 55, 1, 55, 3, 55, 996, 8, 55, 1, 56, 1, 56, 3, 56, 1000, 8, 56, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 1006, 8, 57, 10, 57, 12, 57, 1009, 9, 57, 1, 57, 3, 57, 1012, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 1019, 8, 57, 10, 57, 12, 57, 1022, 9, 57, 1, 57, 3, 57, 1025, 8, 57, 3, 57, 1027, 8, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 5, 58, 1035, 8, 58, 10, 58, 12, 58, 1038, 9, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 1046, 8, 58, 10, 58, 12, 58, 1049, 9, 58, 1, 58, 1, 58, 3, 58, 1053, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 1060, 8, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1073, 8, 59, 1, 60, 1, 60, 1, 60, 5, 60, 1078, 8, 60, 10, 60, 12, 60, 1081, 9, 60, 1, 60, 3, 60, 1084, 8, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1096, 8, 61, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1102, 8, 62, 1, 62, 3, 62, 1105, 8, 62, 1, 63, 1, 63, 1, 63, 5, 63, 1110, 8, 63, 10, 63, 12, 63, 1113, 9, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 3, 64, 1124, 8, 64, 1, 64, 1, 64, 1, 64, 1, 64, 3, 64, 1130, 8, 64, 5, 64, 1132, 8, 64, 10, 64, 12, 64, 1135, 9, 64, 1, 65, 1, 65, 1, 65, 3, 65, 1140, 8, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 3, 66, 1147, 8, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 5, 67, 1154, 8, 67, 10, 67, 12, 67, 1157, 9, 67, 1, 67, 3, 67, 1160, 8, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 1170, 8, 69, 3, 69, 1172, 8, 69, 1, 70, 3, 70, 1175, 8, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 3, 70, 1183, 8, 70, 1, 71, 1, 71, 1, 71, 3, 71, 1188, 8, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 3, 75, 1198, 8, 75, 1, 76, 1, 76, 1, 76, 3, 76, 1203, 8, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 3, 79, 1215, 8, 79, 1, 80, 1, 80, 5, 80, 1219, 8, 80, 10, 80, 12, 80, 1222, 9, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 3, 81, 1231, 8, 81, 1, 82, 1, 82, 5, 82, 1235, 8, 82, 10, 82, 12, 82, 1238, 9, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 3, 83, 1247, 8, 83, 1, 83, 0, 3, 70, 108, 128, 84, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 0, 16, 2, 0, 17, 17, 72, 72, 2, 0, 42, 42, 49, 49, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 78, 78, 2, 0, 49, 49, 71, 71, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 21, 22, 2, 0, 28, 28, 47, 47, 2, 0, 69, 69, 74, 74, 3, 0, 10, 10, 48, 48, 87, 87, 2, 0, 39, 39, 51, 51, 1, 0, 103, 104, 2, 0, 114, 114, 135, 135, 7, 0, 20, 20, 36, 36, 53, 54, 68, 68, 76, 76, 93, 93, 99, 99, 12, 0, 1, 19, 21, 28, 30, 35, 37, 40, 42, 49, 51, 52, 56, 56, 58, 67, 69, 75, 77, 92, 94, 95, 97, 98, 4, 0, 19, 19, 28, 28, 37, 37, 46, 46, 1409, 0, 171, 1, 0, 0, 0, 2, 178, 1, 0, 0, 0, 4, 180, 1, 0, 0, 0, 6, 182, 1, 0, 0, 0, 8, 189, 1, 0, 0, 0, 10, 209, 1, 0, 0, 0, 12, 211, 1, 0, 0, 0, 14, 218, 1, 0, 0, 0, 16, 227, 1, 0, 0, 0, 18, 235, 1, 0, 0, 0, 20, 257, 1, 0, 0, 0, 22, 266, 1, 0, 0, 0, 24, 271, 1, 0, 0, 0, 26, 275, 1, 0, 0, 0, 28, 277, 1, 0, 0, 0, 30, 286, 1, 0, 0, 0, 32, 290, 1, 0, 0, 0, 34, 304, 1, 0, 0, 0, 36, 308, 1, 0, 0, 0, 38, 323, 1, 0, 0, 0, 40, 326, 1, 0, 0, 0, 42, 375, 1, 0, 0, 0, 44, 378, 1, 0, 0, 0, 46, 384, 1, 0, 0, 0, 48, 388, 1, 0, 0, 0, 50, 394, 1, 0, 0, 0, 52, 412, 1, 0, 0, 0, 54, 415, 1, 0, 0, 0, 56, 418, 1, 0, 0, 0, 58, 428, 1, 0, 0, 0, 60, 431, 1, 0, 0, 0, 62, 435, 1, 0, 0, 0, 64, 468, 1, 0, 0, 0, 66, 470, 1, 0, 0, 0, 68, 473, 1, 0, 0, 0, 70, 488, 1, 0, 0, 0, 72, 550, 1, 0, 0, 0, 74, 555, 1, 0, 0, 0, 76, 566, 1, 0, 0, 0, 78, 568, 1, 0, 0, 0, 80, 574, 1, 0, 0, 0, 82, 582, 1, 0, 0, 0, 84, 600, 1, 0, 0, 0, 86, 602, 1, 0, 0, 0, 88, 610, 1, 0, 0, 0, 90, 615, 1, 0, 0, 0, 92, 623, 1, 0, 0, 0, 94, 627, 1, 0, 0, 0, 96, 631, 1, 0, 0, 0, 98, 640, 1, 0, 0, 0, 100, 654, 1, 0, 0, 0, 102, 656, 1, 0, 0, 0, 104, 715, 1, 0, 0, 0, 106, 717, 1, 0, 0, 0, 108, 877, 1, 0, 0, 0, 110, 986, 1, 0, 0, 0, 112, 999, 1, 0, 0, 0, 114, 1026, 1, 0, 0, 0, 116, 1059, 1, 0, 0, 0, 118, 1072, 1, 0, 0, 0, 120, 1074, 1, 0, 0, 0, 122, 1095, 1, 0, 0, 0, 124, 1104, 1, 0, 0, 0, 126, 1106, 1, 0, 0, 0, 128, 1123, 1, 0, 0, 0, 130, 1136, 1, 0, 0, 0, 132, 1146, 1, 0, 0, 0, 134, 1150, 1, 0, 0, 0, 136, 1161, 1, 0, 0, 0, 138, 1171, 1, 0, 0, 0, 140, 1174, 1, 0, 0, 0, 142, 1187, 1, 0, 0, 0, 144, 1189, 1, 0, 0, 0, 146, 1191, 1, 0, 0, 0, 148, 1193, 1, 0, 0, 0, 150, 1197, 1, 0, 0, 0, 152, 1202, 1, 0, 0, 0, 154, 1204, 1, 0, 0, 0, 156, 1208, 1, 0, 0, 0, 158, 1214, 1, 0, 0, 0, 160, 1216, 1, 0, 0, 0, 162, 1230, 1, 0, 0, 0, 164, 1232, 1, 0, 0, 0, 166, 1246, 1, 0, 0, 0, 168, 170, 3, 2, 1, 0, 169, 168, 1, 0, 0, 0, 170, 173, 1, 0, 0, 0, 171, 169, 1, 0, 0, 0, 171, 172, 1, 0, 0, 0, 172, 174, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 174, 175, 5, 0, 0, 1, 175, 1, 1, 0, 0, 0, 176, 179, 3, 6, 3, 0, 177, 179, 3, 10, 5, 0, 178, 176, 1, 0, 0, 0, 178, 177, 1, 0, 0, 0, 179, 3, 1, 0, 0, 0, 180, 181, 3, 108, 54, 0, 181, 5, 1, 0, 0, 0, 182, 183, 5, 50, 0, 0, 183, 187, 3, 152, 76, 0, 184, 185, 5, 111, 0, 0, 185, 186, 5, 118, 0, 0, 186, 188, 3, 4, 2, 0, 187, 184, 1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 7, 1, 0, 0, 0, 189, 194, 3, 152, 76, 0, 190, 191, 5, 112, 0, 0, 191, 193, 3, 152, 76, 0, 192, 190, 1, 0, 0, 0, 193, 196, 1, 0, 0, 0, 194, 192, 1, 0, 0, 0, 194, 195, 1, 0, 0, 0, 195, 198, 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 197, 199, 5, 112, 0, 0, 198, 197, 1, 0, 0, 0, 198, 199, 1, 0, 0, 0, 199, 9, 1, 0, 0, 0, 200, 210, 3, 12, 6, 0, 201, 210, 3, 14, 7, 0, 202, 210, 3, 16, 8, 0, 203, 210, 3, 18, 9, 0, 204, 210, 3, 20, 10, 0, 205, 210, 3, 22, 11, 0, 206, 210, 3, 28, 14, 0, 207, 210, 3, 24, 12, 0, 208, 210, 3, 26, 13, 0, 209, 200, 1, 0, 0, 0, 209, 201, 1, 0, 0, 0, 209, 202, 1, 0, 0, 0, 209, 203, 1, 0, 0, 0, 209, 204, 1, 0, 0, 0, 209, 205, 1, 0, 0, 0, 209, 206, 1, 0, 0, 0, 209, 207, 1, 0, 0, 0, 209, 208, 1, 0, 0, 0, 210, 11, 1, 0, 0, 0, 211, 213, 5, 70, 0, 0, 212, 214, 3, 4, 2, 0, 213, 212, 1, 0, 0, 0, 213, 214, 1, 0, 0, 0, 214, 216, 1, 0, 0, 0, 215, 217, 5, 146, 0, 0, 216, 215, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 13, 1, 0, 0, 0, 218, 219, 5, 38, 0, 0, 219, 220, 5, 126, 0, 0, 220, 221, 3, 4, 2, 0, 221, 222, 5, 145, 0, 0, 222, 225, 3, 10, 5, 0, 223, 224, 5, 24, 0, 0, 224, 226, 3, 10, 5, 0, 225, 223, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 15, 1, 0, 0, 0, 227, 228, 5, 96, 0, 0, 228, 229, 5, 126, 0, 0, 229, 230, 3, 4, 2, 0, 230, 231, 5, 145, 0, 0, 231, 233, 3, 10, 5, 0, 232, 234, 5, 146, 0, 0, 233, 232, 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 17, 1, 0, 0, 0, 235, 236, 5, 31, 0, 0, 236, 240, 5, 126, 0, 0, 237, 241, 3, 6, 3, 0, 238, 241, 3, 22, 11, 0, 239, 241, 3, 4, 2, 0, 240, 237, 1, 0, 0, 0, 240, 238, 1, 0, 0, 0, 240, 239, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 242, 1, 0, 0, 0, 242, 244, 5, 146, 0, 0, 243, 245, 3, 4, 2, 0, 244, 243, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 250, 5, 146, 0, 0, 247, 251, 3, 6, 3, 0, 248, 251, 3, 22, 11, 0, 249, 251, 3, 4, 2, 0, 250, 247, 1, 0, 0, 0, 250, 248, 1, 0, 0, 0, 250, 249, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 253, 5, 145, 0, 0, 253, 255, 3, 10, 5, 0, 254, 256, 5, 146, 0, 0, 255, 254, 1, 0, 0, 0, 255, 256, 1, 0, 0, 0, 256, 19, 1, 0, 0, 0, 257, 258, 5, 29, 0, 0, 258, 259, 3, 152, 76, 0, 259, 261, 5, 126, 0, 0, 260, 262, 3, 8, 4, 0, 261, 260, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 264, 5, 145, 0, 0, 264, 265, 3, 28, 14, 0, 265, 21, 1, 0, 0, 0, 266, 267, 3, 4, 2, 0, 267, 268, 5, 111, 0, 0, 268, 269, 5, 118, 0, 0, 269, 270, 3, 4, 2, 0, 270, 23, 1, 0, 0, 0, 271, 273, 3, 4, 2, 0, 272, 274, 5, 146, 0, 0, 273, 272, 1, 0, 0, 0, 273, 274, 1, 0, 0, 0, 274, 25, 1, 0, 0, 0, 275, 276, 5, 146, 0, 0, 276, 27, 1, 0, 0, 0, 277, 281, 5, 124, 0, 0, 278, 280, 3, 2, 1, 0, 279, 278, 1, 0, 0, 0, 280, 283, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0, 281, 282, 1, 0, 0, 0, 282, 284, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 284, 285, 5, 143, 0, 0, 285, 29, 1, 0, 0, 0, 286, 287, 3, 4, 2, 0, 287, 288, 5, 111, 0, 0, 288, 289, 3, 4, 2, 0, 289, 31, 1, 0, 0, 0, 290, 295, 3, 30, 15, 0, 291, 292, 5, 112, 0, 0, 292, 294, 3, 30, 15, 0, 293, 291, 1, 0, 0, 0, 294, 297, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 299, 1, 0, 0, 0, 297, 295, 1, 0, 0, 0, 298, 300, 5, 112, 0, 0, 299, 298, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 33, 1, 0, 0, 0, 301, 305, 3, 36, 18, 0, 302, 305, 3, 40, 20, 0, 303, 305, 3, 116, 58, 0, 304, 301, 1, 0, 0, 0, 304, 302, 1, 0, 0, 0, 304, 303, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 307, 5, 0, 0, 1, 307, 35, 1, 0, 0, 0, 308, 314, 3, 38, 19, 0, 309, 310, 5, 91, 0, 0, 310, 311, 5, 1, 0, 0, 311, 313, 3, 38, 19, 0, 312, 309, 1, 0, 0, 0, 313, 316, 1, 0, 0, 0, 314, 312, 1, 0, 0, 0, 314, 315, 1, 0, 0, 0, 315, 37, 1, 0, 0, 0, 316, 314, 1, 0, 0, 0, 317, 324, 3, 40, 20, 0, 318, 319, 5, 126, 0, 0, 319, 320, 3, 36, 18, 0, 320, 321, 5, 145, 0, 0, 321, 324, 1, 0, 0, 0, 322, 324, 3, 156, 78, 0, 323, 317, 1, 0, 0, 0, 323, 318, 1, 0, 0, 0, 323, 322, 1, 0, 0, 0, 324, 39, 1, 0, 0, 0, 325, 327, 3, 42, 21, 0, 326, 325, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 1, 0, 0, 0, 328, 330, 5, 77, 0, 0, 329, 331, 5, 23, 0, 0, 330, 329, 1, 0, 0, 0, 330, 331, 1, 0, 0, 0, 331, 333, 1, 0, 0, 0, 332, 334, 3, 44, 22, 0, 333, 332, 1, 0, 0, 0, 333, 334, 1, 0, 0, 0, 334, 335, 1, 0, 0, 0, 335, 337, 3, 106, 53, 0, 336, 338, 3, 46, 23, 0, 337, 336, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 340, 1, 0, 0, 0, 339, 341, 3, 48, 24, 0, 340, 339, 1, 0, 0, 0, 340, 341, 1, 0, 0, 0, 341, 343, 1, 0, 0, 0, 342, 344, 3, 52, 26, 0, 343, 342, 1, 0, 0, 0, 343, 344, 1, 0, 0, 0, 344, 346, 1, 0, 0, 0, 345, 347, 3, 54, 27, 0, 346, 345, 1, 0, 0, 0, 346, 347, 1, 0, 0, 0, 347, 349, 1, 0, 0, 0, 348, 350, 3, 56, 28, 0, 349, 348, 1, 0, 0, 0, 349, 350, 1, 0, 0, 0, 350, 353, 1, 0, 0, 0, 351, 352, 5, 98, 0, 0, 352, 354, 7, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 357, 1, 0, 0, 0, 355, 356, 5, 98, 0, 0, 356, 358, 5, 86, 0, 0, 357, 355, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 360, 1, 0, 0, 0, 359, 361, 3, 58, 29, 0, 360, 359, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 363, 1, 0, 0, 0, 362, 364, 3, 50, 25, 0, 363, 362, 1, 0, 0, 0, 363, 364, 1, 0, 0, 0, 364, 366, 1, 0, 0, 0, 365, 367, 3, 60, 30, 0, 366, 365, 1, 0, 0, 0, 366, 367, 1, 0, 0, 0, 367, 370, 1, 0, 0, 0, 368, 371, 3, 64, 32, 0, 369, 371, 3, 66, 33, 0, 370, 368, 1, 0, 0, 0, 370, 369, 1, 0, 0, 0, 370, 371, 1, 0, 0, 0, 371, 373, 1, 0, 0, 0, 372, 374, 3, 68, 34, 0, 373, 372, 1, 0, 0, 0, 373, 374, 1, 0, 0, 0, 374, 41, 1, 0, 0, 0, 375, 376, 5, 98, 0, 0, 376, 377, 3, 120, 60, 0, 377, 43, 1, 0, 0, 0, 378, 379, 5, 85, 0, 0, 379, 382, 5, 104, 0, 0, 380, 381, 5, 98, 0, 0, 381, 383, 5, 82, 0, 0, 382, 380, 1, 0, 0, 0, 382, 383, 1, 0, 0, 0, 383, 45, 1, 0, 0, 0, 384, 385, 5, 32, 0, 0, 385, 386, 3, 70, 35, 0, 386, 47, 1, 0, 0, 0, 387, 389, 7, 1, 0, 0, 388, 387, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 390, 1, 0, 0, 0, 390, 391, 5, 5, 0, 0, 391, 392, 5, 45, 0, 0, 392, 393, 3, 106, 53, 0, 393, 49, 1, 0, 0, 0, 394, 395, 5, 97, 0, 0, 395, 396, 3, 152, 76, 0, 396, 397, 5, 6, 0, 0, 397, 398, 5, 126, 0, 0, 398, 399, 3, 90, 45, 0, 399, 409, 5, 145, 0, 0, 400, 401, 5, 112, 0, 0, 401, 402, 3, 152, 76, 0, 402, 403, 5, 6, 0, 0, 403, 404, 5, 126, 0, 0, 404, 405, 3, 90, 45, 0, 405, 406, 5, 145, 0, 0, 406, 408, 1, 0, 0, 0, 407, 400, 1, 0, 0, 0, 408, 411, 1, 0, 0, 0, 409, 407, 1, 0, 0, 0, 409, 410, 1, 0, 0, 0, 410, 51, 1, 0, 0, 0, 411, 409, 1, 0, 0, 0, 412, 413, 5, 67, 0, 0, 413, 414, 3, 108, 54, 0, 414, 53, 1, 0, 0, 0, 415, 416, 5, 95, 0, 0, 416, 417, 3, 108, 54, 0, 417, 55, 1, 0, 0, 0, 418, 419, 5, 34, 0, 0, 419, 426, 5, 11, 0, 0, 420, 421, 7, 0, 0, 0, 421, 422, 5, 126, 0, 0, 422, 423, 3, 106, 53, 0, 423, 424, 5, 145, 0, 0, 424, 427, 1, 0, 0, 0, 425, 427, 3, 106, 53, 0, 426, 420, 1, 0, 0, 0, 426, 425, 1, 0, 0, 0, 427, 57, 1, 0, 0, 0, 428, 429, 5, 35, 0, 0, 429, 430, 3, 108, 54, 0, 430, 59, 1, 0, 0, 0, 431, 432, 5, 62, 0, 0, 432, 433, 5, 11, 0, 0, 433, 434, 3, 80, 40, 0, 434, 61, 1, 0, 0, 0, 435, 436, 5, 62, 0, 0, 436, 437, 5, 11, 0, 0, 437, 438, 3, 106, 53, 0, 438, 63, 1, 0, 0, 0, 439, 440, 5, 52, 0, 0, 440, 443, 3, 108, 54, 0, 441, 442, 5, 112, 0, 0, 442, 444, 3, 108, 54, 0, 443, 441, 1, 0, 0, 0, 443, 444, 1, 0, 0, 0, 444, 449, 1, 0, 0, 0, 445, 446, 5, 98, 0, 0, 446, 450, 5, 82, 0, 0, 447, 448, 5, 11, 0, 0, 448, 450, 3, 106, 53, 0, 449, 445, 1, 0, 0, 0, 449, 447, 1, 0, 0, 0, 449, 450, 1, 0, 0, 0, 450, 469, 1, 0, 0, 0, 451, 452, 5, 52, 0, 0, 452, 455, 3, 108, 54, 0, 453, 454, 5, 98, 0, 0, 454, 456, 5, 82, 0, 0, 455, 453, 1, 0, 0, 0, 455, 456, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 458, 5, 59, 0, 0, 458, 459, 3, 108, 54, 0, 459, 469, 1, 0, 0, 0, 460, 461, 5, 52, 0, 0, 461, 462, 3, 108, 54, 0, 462, 463, 5, 59, 0, 0, 463, 466, 3, 108, 54, 0, 464, 465, 5, 11, 0, 0, 465, 467, 3, 106, 53, 0, 466, 464, 1, 0, 0, 0, 466, 467, 1, 0, 0, 0, 467, 469, 1, 0, 0, 0, 468, 439, 1, 0, 0, 0, 468, 451, 1, 0, 0, 0, 468, 460, 1, 0, 0, 0, 469, 65, 1, 0, 0, 0, 470, 471, 5, 59, 0, 0, 471, 472, 3, 108, 54, 0, 472, 67, 1, 0, 0, 0, 473, 474, 5, 79, 0, 0, 474, 475, 3, 86, 43, 0, 475, 69, 1, 0, 0, 0, 476, 477, 6, 35, -1, 0, 477, 479, 3, 128, 64, 0, 478, 480, 5, 27, 0, 0, 479, 478, 1, 0, 0, 0, 479, 480, 1, 0, 0, 0, 480, 482, 1, 0, 0, 0, 481, 483, 3, 78, 39, 0, 482, 481, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 489, 1, 0, 0, 0, 484, 485, 5, 126, 0, 0, 485, 486, 3, 70, 35, 0, 486, 487, 5, 145, 0, 0, 487, 489, 1, 0, 0, 0, 488, 476, 1, 0, 0, 0, 488, 484, 1, 0, 0, 0, 489, 504, 1, 0, 0, 0, 490, 491, 10, 3, 0, 0, 491, 492, 3, 74, 37, 0, 492, 493, 3, 70, 35, 4, 493, 503, 1, 0, 0, 0, 494, 496, 10, 4, 0, 0, 495, 497, 3, 72, 36, 0, 496, 495, 1, 0, 0, 0, 496, 497, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 499, 5, 45, 0, 0, 499, 500, 3, 70, 35, 0, 500, 501, 3, 76, 38, 0, 501, 503, 1, 0, 0, 0, 502, 490, 1, 0, 0, 0, 502, 494, 1, 0, 0, 0, 503, 506, 1, 0, 0, 0, 504, 502, 1, 0, 0, 0, 504, 505, 1, 0, 0, 0, 505, 71, 1, 0, 0, 0, 506, 504, 1, 0, 0, 0, 507, 509, 7, 2, 0, 0, 508, 507, 1, 0, 0, 0, 508, 509, 1, 0, 0, 0, 509, 510, 1, 0, 0, 0, 510, 517, 5, 42, 0, 0, 511, 513, 5, 42, 0, 0, 512, 514, 7, 2, 0, 0, 513, 512, 1, 0, 0, 0, 513, 514, 1, 0, 0, 0, 514, 517, 1, 0, 0, 0, 515, 517, 7, 2, 0, 0, 516, 508, 1, 0, 0, 0, 516, 511, 1, 0, 0, 0, 516, 515, 1, 0, 0, 0, 517, 551, 1, 0, 0, 0, 518, 520, 7, 3, 0, 0, 519, 518, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 523, 7, 4, 0, 0, 522, 524, 5, 63, 0, 0, 523, 522, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 533, 1, 0, 0, 0, 525, 527, 7, 4, 0, 0, 526, 528, 5, 63, 0, 0, 527, 526, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 530, 1, 0, 0, 0, 529, 531, 7, 3, 0, 0, 530, 529, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 533, 1, 0, 0, 0, 532, 519, 1, 0, 0, 0, 532, 525, 1, 0, 0, 0, 533, 551, 1, 0, 0, 0, 534, 536, 7, 5, 0, 0, 535, 534, 1, 0, 0, 0, 535, 536, 1, 0, 0, 0, 536, 537, 1, 0, 0, 0, 537, 539, 5, 33, 0, 0, 538, 540, 5, 63, 0, 0, 539, 538, 1, 0, 0, 0, 539, 540, 1, 0, 0, 0, 540, 549, 1, 0, 0, 0, 541, 543, 5, 33, 0, 0, 542, 544, 5, 63, 0, 0, 543, 542, 1, 0, 0, 0, 543, 544, 1, 0, 0, 0, 544, 546, 1, 0, 0, 0, 545, 547, 7, 5, 0, 0, 546, 545, 1, 0, 0, 0, 546, 547, 1, 0, 0, 0, 547, 549, 1, 0, 0, 0, 548, 535, 1, 0, 0, 0, 548, 541, 1, 0, 0, 0, 549, 551, 1, 0, 0, 0, 550, 516, 1, 0, 0, 0, 550, 532, 1, 0, 0, 0, 550, 548, 1, 0, 0, 0, 551, 73, 1, 0, 0, 0, 552, 553, 5, 16, 0, 0, 553, 556, 5, 45, 0, 0, 554, 556, 5, 112, 0, 0, 555, 552, 1, 0, 0, 0, 555, 554, 1, 0, 0, 0, 556, 75, 1, 0, 0, 0, 557, 558, 5, 60, 0, 0, 558, 567, 3, 106, 53, 0, 559, 560, 5, 92, 0, 0, 560, 561, 5, 126, 0, 0, 561, 562, 3, 106, 53, 0, 562, 563, 5, 145, 0, 0, 563, 567, 1, 0, 0, 0, 564, 565, 5, 92, 0, 0, 565, 567, 3, 106, 53, 0, 566, 557, 1, 0, 0, 0, 566, 559, 1, 0, 0, 0, 566, 564, 1, 0, 0, 0, 567, 77, 1, 0, 0, 0, 568, 569, 5, 75, 0, 0, 569, 572, 3, 84, 42, 0, 570, 571, 5, 59, 0, 0, 571, 573, 3, 84, 42, 0, 572, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 79, 1, 0, 0, 0, 574, 579, 3, 82, 41, 0, 575, 576, 5, 112, 0, 0, 576, 578, 3, 82, 41, 0, 577, 575, 1, 0, 0, 0, 578, 581, 1, 0, 0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 81, 1, 0, 0, 0, 581, 579, 1, 0, 0, 0, 582, 584, 3, 108, 54, 0, 583, 585, 7, 6, 0, 0, 584, 583, 1, 0, 0, 0, 584, 585, 1, 0, 0, 0, 585, 588, 1, 0, 0, 0, 586, 587, 5, 58, 0, 0, 587, 589, 7, 7, 0, 0, 588, 586, 1, 0, 0, 0, 588, 589, 1, 0, 0, 0, 589, 592, 1, 0, 0, 0, 590, 591, 5, 15, 0, 0, 591, 593, 5, 106, 0, 0, 592, 590, 1, 0, 0, 0, 592, 593, 1, 0, 0, 0, 593, 83, 1, 0, 0, 0, 594, 601, 3, 156, 78, 0, 595, 598, 3, 140, 70, 0, 596, 597, 5, 147, 0, 0, 597, 599, 3, 140, 70, 0, 598, 596, 1, 0, 0, 0, 598, 599, 1, 0, 0, 0, 599, 601, 1, 0, 0, 0, 600, 594, 1, 0, 0, 0, 600, 595, 1, 0, 0, 0, 601, 85, 1, 0, 0, 0, 602, 607, 3, 88, 44, 0, 603, 604, 5, 112, 0, 0, 604, 606, 3, 88, 44, 0, 605, 603, 1, 0, 0, 0, 606, 609, 1, 0, 0, 0, 607, 605, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 87, 1, 0, 0, 0, 609, 607, 1, 0, 0, 0, 610, 611, 3, 152, 76, 0, 611, 612, 5, 118, 0, 0, 612, 613, 3, 142, 71, 0, 613, 89, 1, 0, 0, 0, 614, 616, 3, 92, 46, 0, 615, 614, 1, 0, 0, 0, 615, 616, 1, 0, 0, 0, 616, 618, 1, 0, 0, 0, 617, 619, 3, 94, 47, 0, 618, 617, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 621, 1, 0, 0, 0, 620, 622, 3, 96, 48, 0, 621, 620, 1, 0, 0, 0, 621, 622, 1, 0, 0, 0, 622, 91, 1, 0, 0, 0, 623, 624, 5, 65, 0, 0, 624, 625, 5, 11, 0, 0, 625, 626, 3, 106, 53, 0, 626, 93, 1, 0, 0, 0, 627, 628, 5, 62, 0, 0, 628, 629, 5, 11, 0, 0, 629, 630, 3, 80, 40, 0, 630, 95, 1, 0, 0, 0, 631, 632, 7, 8, 0, 0, 632, 633, 3, 98, 49, 0, 633, 97, 1, 0, 0, 0, 634, 641, 3, 100, 50, 0, 635, 636, 5, 9, 0, 0, 636, 637, 3, 100, 50, 0, 637, 638, 5, 2, 0, 0, 638, 639, 3, 100, 50, 0, 639, 641, 1, 0, 0, 0, 640, 634, 1, 0, 0, 0, 640, 635, 1, 0, 0, 0, 641, 99, 1, 0, 0, 0, 642, 643, 5, 18, 0, 0, 643, 655, 5, 73, 0, 0, 644, 645, 5, 90, 0, 0, 645, 655, 5, 66, 0, 0, 646, 647, 5, 90, 0, 0, 647, 655, 5, 30, 0, 0, 648, 649, 3, 140, 70, 0, 649, 650, 5, 66, 0, 0, 650, 655, 1, 0, 0, 0, 651, 652, 3, 140, 70, 0, 652, 653, 5, 30, 0, 0, 653, 655, 1, 0, 0, 0, 654, 642, 1, 0, 0, 0, 654, 644, 1, 0, 0, 0, 654, 646, 1, 0, 0, 0, 654, 648, 1, 0, 0, 0, 654, 651, 1, 0, 0, 0, 655, 101, 1, 0, 0, 0, 656, 657, 3, 108, 54, 0, 657, 658, 5, 0, 0, 1, 658, 103, 1, 0, 0, 0, 659, 716, 3, 152, 76, 0, 660, 661, 3, 152, 76, 0, 661, 662, 5, 126, 0, 0, 662, 663, 3, 152, 76, 0, 663, 670, 3, 104, 52, 0, 664, 665, 5, 112, 0, 0, 665, 666, 3, 152, 76, 0, 666, 667, 3, 104, 52, 0, 667, 669, 1, 0, 0, 0, 668, 664, 1, 0, 0, 0, 669, 672, 1, 0, 0, 0, 670, 668, 1, 0, 0, 0, 670, 671, 1, 0, 0, 0, 671, 674, 1, 0, 0, 0, 672, 670, 1, 0, 0, 0, 673, 675, 5, 112, 0, 0, 674, 673, 1, 0, 0, 0, 674, 675, 1, 0, 0, 0, 675, 676, 1, 0, 0, 0, 676, 677, 5, 145, 0, 0, 677, 716, 1, 0, 0, 0, 678, 679, 3, 152, 76, 0, 679, 680, 5, 126, 0, 0, 680, 685, 3, 154, 77, 0, 681, 682, 5, 112, 0, 0, 682, 684, 3, 154, 77, 0, 683, 681, 1, 0, 0, 0, 684, 687, 1, 0, 0, 0, 685, 683, 1, 0, 0, 0, 685, 686, 1, 0, 0, 0, 686, 689, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 690, 5, 112, 0, 0, 689, 688, 1, 0, 0, 0, 689, 690, 1, 0, 0, 0, 690, 691, 1, 0, 0, 0, 691, 692, 5, 145, 0, 0, 692, 716, 1, 0, 0, 0, 693, 694, 3, 152, 76, 0, 694, 695, 5, 126, 0, 0, 695, 700, 3, 104, 52, 0, 696, 697, 5, 112, 0, 0, 697, 699, 3, 104, 52, 0, 698, 696, 1, 0, 0, 0, 699, 702, 1, 0, 0, 0, 700, 698, 1, 0, 0, 0, 700, 701, 1, 0, 0, 0, 701, 704, 1, 0, 0, 0, 702, 700, 1, 0, 0, 0, 703, 705, 5, 112, 0, 0, 704, 703, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 706, 707, 5, 145, 0, 0, 707, 716, 1, 0, 0, 0, 708, 709, 3, 152, 76, 0, 709, 711, 5, 126, 0, 0, 710, 712, 3, 106, 53, 0, 711, 710, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 713, 1, 0, 0, 0, 713, 714, 5, 145, 0, 0, 714, 716, 1, 0, 0, 0, 715, 659, 1, 0, 0, 0, 715, 660, 1, 0, 0, 0, 715, 678, 1, 0, 0, 0, 715, 693, 1, 0, 0, 0, 715, 708, 1, 0, 0, 0, 716, 105, 1, 0, 0, 0, 717, 722, 3, 108, 54, 0, 718, 719, 5, 112, 0, 0, 719, 721, 3, 108, 54, 0, 720, 718, 1, 0, 0, 0, 721, 724, 1, 0, 0, 0, 722, 720, 1, 0, 0, 0, 722, 723, 1, 0, 0, 0, 723, 726, 1, 0, 0, 0, 724, 722, 1, 0, 0, 0, 725, 727, 5, 112, 0, 0, 726, 725, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 107, 1, 0, 0, 0, 728, 729, 6, 54, -1, 0, 729, 731, 5, 12, 0, 0, 730, 732, 3, 108, 54, 0, 731, 730, 1, 0, 0, 0, 731, 732, 1, 0, 0, 0, 732, 738, 1, 0, 0, 0, 733, 734, 5, 94, 0, 0, 734, 735, 3, 108, 54, 0, 735, 736, 5, 81, 0, 0, 736, 737, 3, 108, 54, 0, 737, 739, 1, 0, 0, 0, 738, 733, 1, 0, 0, 0, 739, 740, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 744, 1, 0, 0, 0, 742, 743, 5, 24, 0, 0, 743, 745, 3, 108, 54, 0, 744, 742, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 746, 1, 0, 0, 0, 746, 747, 5, 25, 0, 0, 747, 878, 1, 0, 0, 0, 748, 749, 5, 13, 0, 0, 749, 750, 5, 126, 0, 0, 750, 751, 3, 108, 54, 0, 751, 752, 5, 6, 0, 0, 752, 753, 3, 104, 52, 0, 753, 754, 5, 145, 0, 0, 754, 878, 1, 0, 0, 0, 755, 756, 5, 19, 0, 0, 756, 878, 5, 106, 0, 0, 757, 758, 5, 43, 0, 0, 758, 759, 3, 108, 54, 0, 759, 760, 3, 144, 72, 0, 760, 878, 1, 0, 0, 0, 761, 762, 5, 80, 0, 0, 762, 763, 5, 126, 0, 0, 763, 764, 3, 108, 54, 0, 764, 765, 5, 32, 0, 0, 765, 768, 3, 108, 54, 0, 766, 767, 5, 31, 0, 0, 767, 769, 3, 108, 54, 0, 768, 766, 1, 0, 0, 0, 768, 769, 1, 0, 0, 0, 769, 770, 1, 0, 0, 0, 770, 771, 5, 145, 0, 0, 771, 878, 1, 0, 0, 0, 772, 773, 5, 83, 0, 0, 773, 878, 5, 106, 0, 0, 774, 775, 5, 88, 0, 0, 775, 776, 5, 126, 0, 0, 776, 777, 7, 9, 0, 0, 777, 778, 3, 158, 79, 0, 778, 779, 5, 32, 0, 0, 779, 780, 3, 108, 54, 0, 780, 781, 5, 145, 0, 0, 781, 878, 1, 0, 0, 0, 782, 783, 3, 152, 76, 0, 783, 785, 5, 126, 0, 0, 784, 786, 3, 106, 53, 0, 785, 784, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, 5, 145, 0, 0, 788, 797, 1, 0, 0, 0, 789, 791, 5, 126, 0, 0, 790, 792, 5, 23, 0, 0, 791, 790, 1, 0, 0, 0, 791, 792, 1, 0, 0, 0, 792, 794, 1, 0, 0, 0, 793, 795, 3, 110, 55, 0, 794, 793, 1, 0, 0, 0, 794, 795, 1, 0, 0, 0, 795, 796, 1, 0, 0, 0, 796, 798, 5, 145, 0, 0, 797, 789, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 799, 1, 0, 0, 0, 799, 800, 5, 64, 0, 0, 800, 801, 5, 126, 0, 0, 801, 802, 3, 90, 45, 0, 802, 803, 5, 145, 0, 0, 803, 878, 1, 0, 0, 0, 804, 805, 3, 152, 76, 0, 805, 807, 5, 126, 0, 0, 806, 808, 3, 106, 53, 0, 807, 806, 1, 0, 0, 0, 807, 808, 1, 0, 0, 0, 808, 809, 1, 0, 0, 0, 809, 810, 5, 145, 0, 0, 810, 819, 1, 0, 0, 0, 811, 813, 5, 126, 0, 0, 812, 814, 5, 23, 0, 0, 813, 812, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 816, 1, 0, 0, 0, 815, 817, 3, 110, 55, 0, 816, 815, 1, 0, 0, 0, 816, 817, 1, 0, 0, 0, 817, 818, 1, 0, 0, 0, 818, 820, 5, 145, 0, 0, 819, 811, 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 821, 1, 0, 0, 0, 821, 822, 5, 64, 0, 0, 822, 823, 3, 152, 76, 0, 823, 878, 1, 0, 0, 0, 824, 830, 3, 152, 76, 0, 825, 827, 5, 126, 0, 0, 826, 828, 3, 106, 53, 0, 827, 826, 1, 0, 0, 0, 827, 828, 1, 0, 0, 0, 828, 829, 1, 0, 0, 0, 829, 831, 5, 145, 0, 0, 830, 825, 1, 0, 0, 0, 830, 831, 1, 0, 0, 0, 831, 832, 1, 0, 0, 0, 832, 834, 5, 126, 0, 0, 833, 835, 5, 23, 0, 0, 834, 833, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 837, 1, 0, 0, 0, 836, 838, 3, 110, 55, 0, 837, 836, 1, 0, 0, 0, 837, 838, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 840, 5, 145, 0, 0, 840, 878, 1, 0, 0, 0, 841, 878, 3, 116, 58, 0, 842, 878, 3, 160, 80, 0, 843, 878, 3, 142, 71, 0, 844, 845, 5, 114, 0, 0, 845, 878, 3, 108, 54, 19, 846, 847, 5, 56, 0, 0, 847, 878, 3, 108, 54, 13, 848, 849, 3, 132, 66, 0, 849, 850, 5, 116, 0, 0, 850, 852, 1, 0, 0, 0, 851, 848, 1, 0, 0, 0, 851, 852, 1, 0, 0, 0, 852, 853, 1, 0, 0, 0, 853, 878, 5, 108, 0, 0, 854, 855, 5, 126, 0, 0, 855, 856, 3, 36, 18, 0, 856, 857, 5, 145, 0, 0, 857, 878, 1, 0, 0, 0, 858, 859, 5, 126, 0, 0, 859, 860, 3, 108, 54, 0, 860, 861, 5, 145, 0, 0, 861, 878, 1, 0, 0, 0, 862, 863, 5, 126, 0, 0, 863, 864, 3, 106, 53, 0, 864, 865, 5, 145, 0, 0, 865, 878, 1, 0, 0, 0, 866, 868, 5, 125, 0, 0, 867, 869, 3, 106, 53, 0, 868, 867, 1, 0, 0, 0, 868, 869, 1, 0, 0, 0, 869, 870, 1, 0, 0, 0, 870, 878, 5, 144, 0, 0, 871, 873, 5, 124, 0, 0, 872, 874, 3, 32, 16, 0, 873, 872, 1, 0, 0, 0, 873, 874, 1, 0, 0, 0, 874, 875, 1, 0, 0, 0, 875, 878, 5, 143, 0, 0, 876, 878, 3, 124, 62, 0, 877, 728, 1, 0, 0, 0, 877, 748, 1, 0, 0, 0, 877, 755, 1, 0, 0, 0, 877, 757, 1, 0, 0, 0, 877, 761, 1, 0, 0, 0, 877, 772, 1, 0, 0, 0, 877, 774, 1, 0, 0, 0, 877, 782, 1, 0, 0, 0, 877, 804, 1, 0, 0, 0, 877, 824, 1, 0, 0, 0, 877, 841, 1, 0, 0, 0, 877, 842, 1, 0, 0, 0, 877, 843, 1, 0, 0, 0, 877, 844, 1, 0, 0, 0, 877, 846, 1, 0, 0, 0, 877, 851, 1, 0, 0, 0, 877, 854, 1, 0, 0, 0, 877, 858, 1, 0, 0, 0, 877, 862, 1, 0, 0, 0, 877, 866, 1, 0, 0, 0, 877, 871, 1, 0, 0, 0, 877, 876, 1, 0, 0, 0, 878, 983, 1, 0, 0, 0, 879, 883, 10, 18, 0, 0, 880, 884, 5, 108, 0, 0, 881, 884, 5, 147, 0, 0, 882, 884, 5, 134, 0, 0, 883, 880, 1, 0, 0, 0, 883, 881, 1, 0, 0, 0, 883, 882, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 982, 3, 108, 54, 19, 886, 890, 10, 17, 0, 0, 887, 891, 5, 135, 0, 0, 888, 891, 5, 114, 0, 0, 889, 891, 5, 113, 0, 0, 890, 887, 1, 0, 0, 0, 890, 888, 1, 0, 0, 0, 890, 889, 1, 0, 0, 0, 891, 892, 1, 0, 0, 0, 892, 982, 3, 108, 54, 18, 893, 918, 10, 16, 0, 0, 894, 919, 5, 117, 0, 0, 895, 919, 5, 118, 0, 0, 896, 919, 5, 129, 0, 0, 897, 919, 5, 127, 0, 0, 898, 919, 5, 128, 0, 0, 899, 919, 5, 119, 0, 0, 900, 919, 5, 120, 0, 0, 901, 903, 5, 56, 0, 0, 902, 901, 1, 0, 0, 0, 902, 903, 1, 0, 0, 0, 903, 904, 1, 0, 0, 0, 904, 906, 5, 40, 0, 0, 905, 907, 5, 14, 0, 0, 906, 905, 1, 0, 0, 0, 906, 907, 1, 0, 0, 0, 907, 919, 1, 0, 0, 0, 908, 910, 5, 56, 0, 0, 909, 908, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 911, 1, 0, 0, 0, 911, 919, 7, 10, 0, 0, 912, 919, 5, 141, 0, 0, 913, 919, 5, 142, 0, 0, 914, 919, 5, 131, 0, 0, 915, 919, 5, 122, 0, 0, 916, 919, 5, 123, 0, 0, 917, 919, 5, 130, 0, 0, 918, 894, 1, 0, 0, 0, 918, 895, 1, 0, 0, 0, 918, 896, 1, 0, 0, 0, 918, 897, 1, 0, 0, 0, 918, 898, 1, 0, 0, 0, 918, 899, 1, 0, 0, 0, 918, 900, 1, 0, 0, 0, 918, 902, 1, 0, 0, 0, 918, 909, 1, 0, 0, 0, 918, 912, 1, 0, 0, 0, 918, 913, 1, 0, 0, 0, 918, 914, 1, 0, 0, 0, 918, 915, 1, 0, 0, 0, 918, 916, 1, 0, 0, 0, 918, 917, 1, 0, 0, 0, 919, 920, 1, 0, 0, 0, 920, 982, 3, 108, 54, 17, 921, 922, 10, 14, 0, 0, 922, 923, 5, 133, 0, 0, 923, 982, 3, 108, 54, 15, 924, 925, 10, 12, 0, 0, 925, 926, 5, 2, 0, 0, 926, 982, 3, 108, 54, 13, 927, 928, 10, 11, 0, 0, 928, 929, 5, 61, 0, 0, 929, 982, 3, 108, 54, 12, 930, 932, 10, 10, 0, 0, 931, 933, 5, 56, 0, 0, 932, 931, 1, 0, 0, 0, 932, 933, 1, 0, 0, 0, 933, 934, 1, 0, 0, 0, 934, 935, 5, 9, 0, 0, 935, 936, 3, 108, 54, 0, 936, 937, 5, 2, 0, 0, 937, 938, 3, 108, 54, 11, 938, 982, 1, 0, 0, 0, 939, 940, 10, 9, 0, 0, 940, 941, 5, 136, 0, 0, 941, 942, 3, 108, 54, 0, 942, 943, 5, 111, 0, 0, 943, 944, 3, 108, 54, 9, 944, 982, 1, 0, 0, 0, 945, 946, 10, 25, 0, 0, 946, 947, 5, 125, 0, 0, 947, 948, 3, 108, 54, 0, 948, 949, 5, 144, 0, 0, 949, 982, 1, 0, 0, 0, 950, 951, 10, 24, 0, 0, 951, 952, 5, 116, 0, 0, 952, 982, 5, 104, 0, 0, 953, 954, 10, 23, 0, 0, 954, 955, 5, 116, 0, 0, 955, 982, 3, 152, 76, 0, 956, 957, 10, 22, 0, 0, 957, 958, 5, 132, 0, 0, 958, 959, 5, 125, 0, 0, 959, 960, 3, 108, 54, 0, 960, 961, 5, 144, 0, 0, 961, 982, 1, 0, 0, 0, 962, 963, 10, 21, 0, 0, 963, 964, 5, 132, 0, 0, 964, 982, 5, 104, 0, 0, 965, 966, 10, 20, 0, 0, 966, 967, 5, 132, 0, 0, 967, 982, 3, 152, 76, 0, 968, 969, 10, 15, 0, 0, 969, 971, 5, 44, 0, 0, 970, 972, 5, 56, 0, 0, 971, 970, 1, 0, 0, 0, 971, 972, 1, 0, 0, 0, 972, 973, 1, 0, 0, 0, 973, 982, 5, 57, 0, 0, 974, 979, 10, 8, 0, 0, 975, 976, 5, 6, 0, 0, 976, 980, 3, 152, 76, 0, 977, 978, 5, 6, 0, 0, 978, 980, 5, 106, 0, 0, 979, 975, 1, 0, 0, 0, 979, 977, 1, 0, 0, 0, 980, 982, 1, 0, 0, 0, 981, 879, 1, 0, 0, 0, 981, 886, 1, 0, 0, 0, 981, 893, 1, 0, 0, 0, 981, 921, 1, 0, 0, 0, 981, 924, 1, 0, 0, 0, 981, 927, 1, 0, 0, 0, 981, 930, 1, 0, 0, 0, 981, 939, 1, 0, 0, 0, 981, 945, 1, 0, 0, 0, 981, 950, 1, 0, 0, 0, 981, 953, 1, 0, 0, 0, 981, 956, 1, 0, 0, 0, 981, 962, 1, 0, 0, 0, 981, 965, 1, 0, 0, 0, 981, 968, 1, 0, 0, 0, 981, 974, 1, 0, 0, 0, 982, 985, 1, 0, 0, 0, 983, 981, 1, 0, 0, 0, 983, 984, 1, 0, 0, 0, 984, 109, 1, 0, 0, 0, 985, 983, 1, 0, 0, 0, 986, 991, 3, 112, 56, 0, 987, 988, 5, 112, 0, 0, 988, 990, 3, 112, 56, 0, 989, 987, 1, 0, 0, 0, 990, 993, 1, 0, 0, 0, 991, 989, 1, 0, 0, 0, 991, 992, 1, 0, 0, 0, 992, 995, 1, 0, 0, 0, 993, 991, 1, 0, 0, 0, 994, 996, 5, 112, 0, 0, 995, 994, 1, 0, 0, 0, 995, 996, 1, 0, 0, 0, 996, 111, 1, 0, 0, 0, 997, 1000, 3, 114, 57, 0, 998, 1000, 3, 108, 54, 0, 999, 997, 1, 0, 0, 0, 999, 998, 1, 0, 0, 0, 1000, 113, 1, 0, 0, 0, 1001, 1002, 5, 126, 0, 0, 1002, 1007, 3, 152, 76, 0, 1003, 1004, 5, 112, 0, 0, 1004, 1006, 3, 152, 76, 0, 1005, 1003, 1, 0, 0, 0, 1006, 1009, 1, 0, 0, 0, 1007, 1005, 1, 0, 0, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1011, 1, 0, 0, 0, 1009, 1007, 1, 0, 0, 0, 1010, 1012, 5, 112, 0, 0, 1011, 1010, 1, 0, 0, 0, 1011, 1012, 1, 0, 0, 0, 1012, 1013, 1, 0, 0, 0, 1013, 1014, 5, 145, 0, 0, 1014, 1027, 1, 0, 0, 0, 1015, 1020, 3, 152, 76, 0, 1016, 1017, 5, 112, 0, 0, 1017, 1019, 3, 152, 76, 0, 1018, 1016, 1, 0, 0, 0, 1019, 1022, 1, 0, 0, 0, 1020, 1018, 1, 0, 0, 0, 1020, 1021, 1, 0, 0, 0, 1021, 1024, 1, 0, 0, 0, 1022, 1020, 1, 0, 0, 0, 1023, 1025, 5, 112, 0, 0, 1024, 1023, 1, 0, 0, 0, 1024, 1025, 1, 0, 0, 0, 1025, 1027, 1, 0, 0, 0, 1026, 1001, 1, 0, 0, 0, 1026, 1015, 1, 0, 0, 0, 1027, 1028, 1, 0, 0, 0, 1028, 1029, 5, 107, 0, 0, 1029, 1030, 3, 108, 54, 0, 1030, 115, 1, 0, 0, 0, 1031, 1032, 5, 128, 0, 0, 1032, 1036, 3, 152, 76, 0, 1033, 1035, 3, 118, 59, 0, 1034, 1033, 1, 0, 0, 0, 1035, 1038, 1, 0, 0, 0, 1036, 1034, 1, 0, 0, 0, 1036, 1037, 1, 0, 0, 0, 1037, 1039, 1, 0, 0, 0, 1038, 1036, 1, 0, 0, 0, 1039, 1040, 5, 147, 0, 0, 1040, 1041, 5, 120, 0, 0, 1041, 1060, 1, 0, 0, 0, 1042, 1043, 5, 128, 0, 0, 1043, 1047, 3, 152, 76, 0, 1044, 1046, 3, 118, 59, 0, 1045, 1044, 1, 0, 0, 0, 1046, 1049, 1, 0, 0, 0, 1047, 1045, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1050, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1050, 1052, 5, 120, 0, 0, 1051, 1053, 3, 116, 58, 0, 1052, 1051, 1, 0, 0, 0, 1052, 1053, 1, 0, 0, 0, 1053, 1054, 1, 0, 0, 0, 1054, 1055, 5, 128, 0, 0, 1055, 1056, 5, 147, 0, 0, 1056, 1057, 3, 152, 76, 0, 1057, 1058, 5, 120, 0, 0, 1058, 1060, 1, 0, 0, 0, 1059, 1031, 1, 0, 0, 0, 1059, 1042, 1, 0, 0, 0, 1060, 117, 1, 0, 0, 0, 1061, 1062, 3, 152, 76, 0, 1062, 1063, 5, 118, 0, 0, 1063, 1064, 3, 158, 79, 0, 1064, 1073, 1, 0, 0, 0, 1065, 1066, 3, 152, 76, 0, 1066, 1067, 5, 118, 0, 0, 1067, 1068, 5, 124, 0, 0, 1068, 1069, 3, 108, 54, 0, 1069, 1070, 5, 143, 0, 0, 1070, 1073, 1, 0, 0, 0, 1071, 1073, 3, 152, 76, 0, 1072, 1061, 1, 0, 0, 0, 1072, 1065, 1, 0, 0, 0, 1072, 1071, 1, 0, 0, 0, 1073, 119, 1, 0, 0, 0, 1074, 1079, 3, 122, 61, 0, 1075, 1076, 5, 112, 0, 0, 1076, 1078, 3, 122, 61, 0, 1077, 1075, 1, 0, 0, 0, 1078, 1081, 1, 0, 0, 0, 1079, 1077, 1, 0, 0, 0, 1079, 1080, 1, 0, 0, 0, 1080, 1083, 1, 0, 0, 0, 1081, 1079, 1, 0, 0, 0, 1082, 1084, 5, 112, 0, 0, 1083, 1082, 1, 0, 0, 0, 1083, 1084, 1, 0, 0, 0, 1084, 121, 1, 0, 0, 0, 1085, 1086, 3, 152, 76, 0, 1086, 1087, 5, 6, 0, 0, 1087, 1088, 5, 126, 0, 0, 1088, 1089, 3, 36, 18, 0, 1089, 1090, 5, 145, 0, 0, 1090, 1096, 1, 0, 0, 0, 1091, 1092, 3, 108, 54, 0, 1092, 1093, 5, 6, 0, 0, 1093, 1094, 3, 152, 76, 0, 1094, 1096, 1, 0, 0, 0, 1095, 1085, 1, 0, 0, 0, 1095, 1091, 1, 0, 0, 0, 1096, 123, 1, 0, 0, 0, 1097, 1105, 3, 156, 78, 0, 1098, 1099, 3, 132, 66, 0, 1099, 1100, 5, 116, 0, 0, 1100, 1102, 1, 0, 0, 0, 1101, 1098, 1, 0, 0, 0, 1101, 1102, 1, 0, 0, 0, 1102, 1103, 1, 0, 0, 0, 1103, 1105, 3, 126, 63, 0, 1104, 1097, 1, 0, 0, 0, 1104, 1101, 1, 0, 0, 0, 1105, 125, 1, 0, 0, 0, 1106, 1111, 3, 152, 76, 0, 1107, 1108, 5, 116, 0, 0, 1108, 1110, 3, 152, 76, 0, 1109, 1107, 1, 0, 0, 0, 1110, 1113, 1, 0, 0, 0, 1111, 1109, 1, 0, 0, 0, 1111, 1112, 1, 0, 0, 0, 1112, 127, 1, 0, 0, 0, 1113, 1111, 1, 0, 0, 0, 1114, 1115, 6, 64, -1, 0, 1115, 1124, 3, 132, 66, 0, 1116, 1124, 3, 130, 65, 0, 1117, 1118, 5, 126, 0, 0, 1118, 1119, 3, 36, 18, 0, 1119, 1120, 5, 145, 0, 0, 1120, 1124, 1, 0, 0, 0, 1121, 1124, 3, 116, 58, 0, 1122, 1124, 3, 156, 78, 0, 1123, 1114, 1, 0, 0, 0, 1123, 1116, 1, 0, 0, 0, 1123, 1117, 1, 0, 0, 0, 1123, 1121, 1, 0, 0, 0, 1123, 1122, 1, 0, 0, 0, 1124, 1133, 1, 0, 0, 0, 1125, 1129, 10, 3, 0, 0, 1126, 1130, 3, 150, 75, 0, 1127, 1128, 5, 6, 0, 0, 1128, 1130, 3, 152, 76, 0, 1129, 1126, 1, 0, 0, 0, 1129, 1127, 1, 0, 0, 0, 1130, 1132, 1, 0, 0, 0, 1131, 1125, 1, 0, 0, 0, 1132, 1135, 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1133, 1134, 1, 0, 0, 0, 1134, 129, 1, 0, 0, 0, 1135, 1133, 1, 0, 0, 0, 1136, 1137, 3, 152, 76, 0, 1137, 1139, 5, 126, 0, 0, 1138, 1140, 3, 134, 67, 0, 1139, 1138, 1, 0, 0, 0, 1139, 1140, 1, 0, 0, 0, 1140, 1141, 1, 0, 0, 0, 1141, 1142, 5, 145, 0, 0, 1142, 131, 1, 0, 0, 0, 1143, 1144, 3, 136, 68, 0, 1144, 1145, 5, 116, 0, 0, 1145, 1147, 1, 0, 0, 0, 1146, 1143, 1, 0, 0, 0, 1146, 1147, 1, 0, 0, 0, 1147, 1148, 1, 0, 0, 0, 1148, 1149, 3, 152, 76, 0, 1149, 133, 1, 0, 0, 0, 1150, 1155, 3, 108, 54, 0, 1151, 1152, 5, 112, 0, 0, 1152, 1154, 3, 108, 54, 0, 1153, 1151, 1, 0, 0, 0, 1154, 1157, 1, 0, 0, 0, 1155, 1153, 1, 0, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1159, 1, 0, 0, 0, 1157, 1155, 1, 0, 0, 0, 1158, 1160, 5, 112, 0, 0, 1159, 1158, 1, 0, 0, 0, 1159, 1160, 1, 0, 0, 0, 1160, 135, 1, 0, 0, 0, 1161, 1162, 3, 152, 76, 0, 1162, 137, 1, 0, 0, 0, 1163, 1172, 5, 102, 0, 0, 1164, 1165, 5, 116, 0, 0, 1165, 1172, 7, 11, 0, 0, 1166, 1167, 5, 104, 0, 0, 1167, 1169, 5, 116, 0, 0, 1168, 1170, 7, 11, 0, 0, 1169, 1168, 1, 0, 0, 0, 1169, 1170, 1, 0, 0, 0, 1170, 1172, 1, 0, 0, 0, 1171, 1163, 1, 0, 0, 0, 1171, 1164, 1, 0, 0, 0, 1171, 1166, 1, 0, 0, 0, 1172, 139, 1, 0, 0, 0, 1173, 1175, 7, 12, 0, 0, 1174, 1173, 1, 0, 0, 0, 1174, 1175, 1, 0, 0, 0, 1175, 1182, 1, 0, 0, 0, 1176, 1183, 3, 138, 69, 0, 1177, 1183, 5, 103, 0, 0, 1178, 1183, 5, 104, 0, 0, 1179, 1183, 5, 105, 0, 0, 1180, 1183, 5, 41, 0, 0, 1181, 1183, 5, 55, 0, 0, 1182, 1176, 1, 0, 0, 0, 1182, 1177, 1, 0, 0, 0, 1182, 1178, 1, 0, 0, 0, 1182, 1179, 1, 0, 0, 0, 1182, 1180, 1, 0, 0, 0, 1182, 1181, 1, 0, 0, 0, 1183, 141, 1, 0, 0, 0, 1184, 1188, 3, 140, 70, 0, 1185, 1188, 5, 106, 0, 0, 1186, 1188, 5, 57, 0, 0, 1187, 1184, 1, 0, 0, 0, 1187, 1185, 1, 0, 0, 0, 1187, 1186, 1, 0, 0, 0, 1188, 143, 1, 0, 0, 0, 1189, 1190, 7, 13, 0, 0, 1190, 145, 1, 0, 0, 0, 1191, 1192, 7, 14, 0, 0, 1192, 147, 1, 0, 0, 0, 1193, 1194, 7, 15, 0, 0, 1194, 149, 1, 0, 0, 0, 1195, 1198, 5, 101, 0, 0, 1196, 1198, 3, 148, 74, 0, 1197, 1195, 1, 0, 0, 0, 1197, 1196, 1, 0, 0, 0, 1198, 151, 1, 0, 0, 0, 1199, 1203, 5, 101, 0, 0, 1200, 1203, 3, 144, 72, 0, 1201, 1203, 3, 146, 73, 0, 1202, 1199, 1, 0, 0, 0, 1202, 1200, 1, 0, 0, 0, 1202, 1201, 1, 0, 0, 0, 1203, 153, 1, 0, 0, 0, 1204, 1205, 3, 158, 79, 0, 1205, 1206, 5, 118, 0, 0, 1206, 1207, 3, 140, 70, 0, 1207, 155, 1, 0, 0, 0, 1208, 1209, 5, 124, 0, 0, 1209, 1210, 3, 152, 76, 0, 1210, 1211, 5, 143, 0, 0, 1211, 157, 1, 0, 0, 0, 1212, 1215, 5, 106, 0, 0, 1213, 1215, 3, 160, 80, 0, 1214, 1212, 1, 0, 0, 0, 1214, 1213, 1, 0, 0, 0, 1215, 159, 1, 0, 0, 0, 1216, 1220, 5, 138, 0, 0, 1217, 1219, 3, 162, 81, 0, 1218, 1217, 1, 0, 0, 0, 1219, 1222, 1, 0, 0, 0, 1220, 1218, 1, 0, 0, 0, 1220, 1221, 1, 0, 0, 0, 1221, 1223, 1, 0, 0, 0, 1222, 1220, 1, 0, 0, 0, 1223, 1224, 5, 140, 0, 0, 1224, 161, 1, 0, 0, 0, 1225, 1226, 5, 153, 0, 0, 1226, 1227, 3, 108, 54, 0, 1227, 1228, 5, 143, 0, 0, 1228, 1231, 1, 0, 0, 0, 1229, 1231, 5, 152, 0, 0, 1230, 1225, 1, 0, 0, 0, 1230, 1229, 1, 0, 0, 0, 1231, 163, 1, 0, 0, 0, 1232, 1236, 5, 139, 0, 0, 1233, 1235, 3, 166, 83, 0, 1234, 1233, 1, 0, 0, 0, 1235, 1238, 1, 0, 0, 0, 1236, 1234, 1, 0, 0, 0, 1236, 1237, 1, 0, 0, 0, 1237, 1239, 1, 0, 0, 0, 1238, 1236, 1, 0, 0, 0, 1239, 1240, 5, 0, 0, 1, 1240, 165, 1, 0, 0, 0, 1241, 1242, 5, 155, 0, 0, 1242, 1243, 3, 108, 54, 0, 1243, 1244, 5, 143, 0, 0, 1244, 1247, 1, 0, 0, 0, 1245, 1247, 5, 154, 0, 0, 1246, 1241, 1, 0, 0, 0, 1246, 1245, 1, 0, 0, 0, 1247, 167, 1, 0, 0, 0, 160, 171, 178, 187, 194, 198, 209, 213, 216, 225, 233, 240, 244, 250, 255, 261, 273, 281, 295, 299, 304, 314, 323, 326, 330, 333, 337, 340, 343, 346, 349, 353, 357, 360, 363, 366, 370, 373, 382, 388, 409, 426, 443, 449, 455, 466, 468, 479, 482, 488, 496, 502, 504, 508, 513, 516, 519, 523, 527, 530, 532, 535, 539, 543, 546, 548, 550, 555, 566, 572, 579, 584, 588, 592, 598, 600, 607, 615, 618, 621, 640, 654, 670, 674, 685, 689, 700, 704, 711, 715, 722, 726, 731, 740, 744, 768, 785, 791, 794, 797, 807, 813, 816, 819, 827, 830, 834, 837, 851, 868, 873, 877, 883, 890, 902, 906, 909, 918, 932, 971, 979, 981, 983, 991, 995, 999, 1007, 1011, 1020, 1024, 1026, 1036, 1047, 1052, 1059, 1072, 1079, 1083, 1095, 1101, 1104, 1111, 1123, 1129, 1133, 1139, 1146, 1155, 1159, 1169, 1171, 1174, 1182, 1187, 1197, 1202, 1214, 1220, 1230, 1236, 1246] \ No newline at end of file +[4, 1, 155, 1267, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 1, 0, 5, 0, 172, 8, 0, 10, 0, 12, 0, 175, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 181, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 190, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 195, 8, 4, 10, 4, 12, 4, 198, 9, 4, 1, 4, 3, 4, 201, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 213, 8, 5, 1, 6, 1, 6, 3, 6, 217, 8, 6, 1, 6, 3, 6, 220, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 229, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 237, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 244, 8, 9, 1, 9, 1, 9, 3, 9, 248, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 254, 8, 9, 1, 9, 1, 9, 1, 9, 3, 9, 259, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 267, 8, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 274, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 280, 8, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 3, 13, 292, 8, 13, 1, 14, 1, 14, 1, 15, 1, 15, 5, 15, 298, 8, 15, 10, 15, 12, 15, 301, 9, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 5, 17, 312, 8, 17, 10, 17, 12, 17, 315, 9, 17, 1, 17, 3, 17, 318, 8, 17, 1, 18, 1, 18, 1, 18, 3, 18, 323, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 331, 8, 19, 10, 19, 12, 19, 334, 9, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 342, 8, 20, 1, 21, 3, 21, 345, 8, 21, 1, 21, 1, 21, 3, 21, 349, 8, 21, 1, 21, 3, 21, 352, 8, 21, 1, 21, 1, 21, 3, 21, 356, 8, 21, 1, 21, 3, 21, 359, 8, 21, 1, 21, 3, 21, 362, 8, 21, 1, 21, 3, 21, 365, 8, 21, 1, 21, 3, 21, 368, 8, 21, 1, 21, 1, 21, 3, 21, 372, 8, 21, 1, 21, 1, 21, 3, 21, 376, 8, 21, 1, 21, 3, 21, 379, 8, 21, 1, 21, 3, 21, 382, 8, 21, 1, 21, 3, 21, 385, 8, 21, 1, 21, 1, 21, 3, 21, 389, 8, 21, 1, 21, 3, 21, 392, 8, 21, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 401, 8, 23, 1, 24, 1, 24, 1, 24, 1, 25, 3, 25, 407, 8, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 5, 26, 426, 8, 26, 10, 26, 12, 26, 429, 9, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 3, 29, 445, 8, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 462, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 468, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 474, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 485, 8, 33, 3, 33, 487, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 3, 36, 498, 8, 36, 1, 36, 3, 36, 501, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 3, 36, 507, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 3, 36, 515, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 5, 36, 521, 8, 36, 10, 36, 12, 36, 524, 9, 36, 1, 37, 3, 37, 527, 8, 37, 1, 37, 1, 37, 1, 37, 3, 37, 532, 8, 37, 1, 37, 3, 37, 535, 8, 37, 1, 37, 3, 37, 538, 8, 37, 1, 37, 1, 37, 3, 37, 542, 8, 37, 1, 37, 1, 37, 3, 37, 546, 8, 37, 1, 37, 3, 37, 549, 8, 37, 3, 37, 551, 8, 37, 1, 37, 3, 37, 554, 8, 37, 1, 37, 1, 37, 3, 37, 558, 8, 37, 1, 37, 1, 37, 3, 37, 562, 8, 37, 1, 37, 3, 37, 565, 8, 37, 3, 37, 567, 8, 37, 3, 37, 569, 8, 37, 1, 38, 1, 38, 1, 38, 3, 38, 574, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 585, 8, 39, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 591, 8, 40, 1, 41, 1, 41, 1, 41, 5, 41, 596, 8, 41, 10, 41, 12, 41, 599, 9, 41, 1, 42, 1, 42, 3, 42, 603, 8, 42, 1, 42, 1, 42, 3, 42, 607, 8, 42, 1, 42, 1, 42, 3, 42, 611, 8, 42, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 617, 8, 43, 3, 43, 619, 8, 43, 1, 44, 1, 44, 1, 44, 5, 44, 624, 8, 44, 10, 44, 12, 44, 627, 9, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 3, 46, 634, 8, 46, 1, 46, 3, 46, 637, 8, 46, 1, 46, 3, 46, 640, 8, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 659, 8, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 3, 51, 673, 8, 51, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 5, 53, 687, 8, 53, 10, 53, 12, 53, 690, 9, 53, 1, 53, 3, 53, 693, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 5, 53, 702, 8, 53, 10, 53, 12, 53, 705, 9, 53, 1, 53, 3, 53, 708, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 5, 53, 717, 8, 53, 10, 53, 12, 53, 720, 9, 53, 1, 53, 3, 53, 723, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 730, 8, 53, 1, 53, 1, 53, 3, 53, 734, 8, 53, 1, 54, 1, 54, 1, 54, 5, 54, 739, 8, 54, 10, 54, 12, 54, 742, 9, 54, 1, 54, 3, 54, 745, 8, 54, 1, 55, 1, 55, 1, 55, 3, 55, 750, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 4, 55, 757, 8, 55, 11, 55, 12, 55, 758, 1, 55, 1, 55, 3, 55, 763, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 787, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 804, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 810, 8, 55, 1, 55, 3, 55, 813, 8, 55, 1, 55, 3, 55, 816, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 826, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 832, 8, 55, 1, 55, 3, 55, 835, 8, 55, 1, 55, 3, 55, 838, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 846, 8, 55, 1, 55, 3, 55, 849, 8, 55, 1, 55, 1, 55, 3, 55, 853, 8, 55, 1, 55, 3, 55, 856, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 870, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 887, 8, 55, 1, 55, 1, 55, 1, 55, 3, 55, 892, 8, 55, 1, 55, 1, 55, 3, 55, 896, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 902, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 909, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 921, 8, 55, 1, 55, 1, 55, 3, 55, 925, 8, 55, 1, 55, 3, 55, 928, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 937, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 951, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 990, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 998, 8, 55, 5, 55, 1000, 8, 55, 10, 55, 12, 55, 1003, 9, 55, 1, 56, 1, 56, 1, 56, 5, 56, 1008, 8, 56, 10, 56, 12, 56, 1011, 9, 56, 1, 56, 3, 56, 1014, 8, 56, 1, 57, 1, 57, 3, 57, 1018, 8, 57, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 1024, 8, 58, 10, 58, 12, 58, 1027, 9, 58, 1, 58, 3, 58, 1030, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 1037, 8, 58, 10, 58, 12, 58, 1040, 9, 58, 1, 58, 3, 58, 1043, 8, 58, 3, 58, 1045, 8, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 5, 59, 1053, 8, 59, 10, 59, 12, 59, 1056, 9, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 5, 59, 1064, 8, 59, 10, 59, 12, 59, 1067, 9, 59, 1, 59, 1, 59, 3, 59, 1071, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1078, 8, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 3, 60, 1091, 8, 60, 1, 61, 1, 61, 1, 61, 5, 61, 1096, 8, 61, 10, 61, 12, 61, 1099, 9, 61, 1, 61, 3, 61, 1102, 8, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1114, 8, 62, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 1120, 8, 63, 1, 63, 3, 63, 1123, 8, 63, 1, 64, 1, 64, 1, 64, 5, 64, 1128, 8, 64, 10, 64, 12, 64, 1131, 9, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1142, 8, 65, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1148, 8, 65, 5, 65, 1150, 8, 65, 10, 65, 12, 65, 1153, 9, 65, 1, 66, 1, 66, 1, 66, 3, 66, 1158, 8, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 3, 67, 1165, 8, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 5, 68, 1172, 8, 68, 10, 68, 12, 68, 1175, 9, 68, 1, 68, 3, 68, 1178, 8, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 3, 70, 1188, 8, 70, 3, 70, 1190, 8, 70, 1, 71, 3, 71, 1193, 8, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 1201, 8, 71, 1, 72, 1, 72, 1, 72, 3, 72, 1206, 8, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 3, 76, 1216, 8, 76, 1, 77, 1, 77, 1, 77, 3, 77, 1221, 8, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 3, 80, 1233, 8, 80, 1, 81, 1, 81, 5, 81, 1237, 8, 81, 10, 81, 12, 81, 1240, 9, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 3, 82, 1249, 8, 82, 1, 83, 1, 83, 5, 83, 1253, 8, 83, 10, 83, 12, 83, 1256, 9, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 3, 84, 1265, 8, 84, 1, 84, 0, 3, 72, 110, 130, 85, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 0, 16, 2, 0, 17, 17, 72, 72, 2, 0, 42, 42, 49, 49, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 78, 78, 2, 0, 49, 49, 71, 71, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 21, 22, 2, 0, 28, 28, 47, 47, 2, 0, 69, 69, 74, 74, 3, 0, 10, 10, 48, 48, 87, 87, 2, 0, 39, 39, 51, 51, 1, 0, 103, 104, 2, 0, 114, 114, 135, 135, 7, 0, 20, 20, 36, 36, 53, 54, 68, 68, 76, 76, 93, 93, 99, 99, 12, 0, 1, 19, 21, 28, 30, 35, 37, 40, 42, 49, 51, 52, 56, 56, 58, 67, 69, 75, 77, 92, 94, 95, 97, 98, 4, 0, 19, 19, 28, 28, 37, 37, 46, 46, 1429, 0, 173, 1, 0, 0, 0, 2, 180, 1, 0, 0, 0, 4, 182, 1, 0, 0, 0, 6, 184, 1, 0, 0, 0, 8, 191, 1, 0, 0, 0, 10, 212, 1, 0, 0, 0, 12, 214, 1, 0, 0, 0, 14, 221, 1, 0, 0, 0, 16, 230, 1, 0, 0, 0, 18, 238, 1, 0, 0, 0, 20, 260, 1, 0, 0, 0, 22, 275, 1, 0, 0, 0, 24, 284, 1, 0, 0, 0, 26, 289, 1, 0, 0, 0, 28, 293, 1, 0, 0, 0, 30, 295, 1, 0, 0, 0, 32, 304, 1, 0, 0, 0, 34, 308, 1, 0, 0, 0, 36, 322, 1, 0, 0, 0, 38, 326, 1, 0, 0, 0, 40, 341, 1, 0, 0, 0, 42, 344, 1, 0, 0, 0, 44, 393, 1, 0, 0, 0, 46, 396, 1, 0, 0, 0, 48, 402, 1, 0, 0, 0, 50, 406, 1, 0, 0, 0, 52, 412, 1, 0, 0, 0, 54, 430, 1, 0, 0, 0, 56, 433, 1, 0, 0, 0, 58, 436, 1, 0, 0, 0, 60, 446, 1, 0, 0, 0, 62, 449, 1, 0, 0, 0, 64, 453, 1, 0, 0, 0, 66, 486, 1, 0, 0, 0, 68, 488, 1, 0, 0, 0, 70, 491, 1, 0, 0, 0, 72, 506, 1, 0, 0, 0, 74, 568, 1, 0, 0, 0, 76, 573, 1, 0, 0, 0, 78, 584, 1, 0, 0, 0, 80, 586, 1, 0, 0, 0, 82, 592, 1, 0, 0, 0, 84, 600, 1, 0, 0, 0, 86, 618, 1, 0, 0, 0, 88, 620, 1, 0, 0, 0, 90, 628, 1, 0, 0, 0, 92, 633, 1, 0, 0, 0, 94, 641, 1, 0, 0, 0, 96, 645, 1, 0, 0, 0, 98, 649, 1, 0, 0, 0, 100, 658, 1, 0, 0, 0, 102, 672, 1, 0, 0, 0, 104, 674, 1, 0, 0, 0, 106, 733, 1, 0, 0, 0, 108, 735, 1, 0, 0, 0, 110, 895, 1, 0, 0, 0, 112, 1004, 1, 0, 0, 0, 114, 1017, 1, 0, 0, 0, 116, 1044, 1, 0, 0, 0, 118, 1077, 1, 0, 0, 0, 120, 1090, 1, 0, 0, 0, 122, 1092, 1, 0, 0, 0, 124, 1113, 1, 0, 0, 0, 126, 1122, 1, 0, 0, 0, 128, 1124, 1, 0, 0, 0, 130, 1141, 1, 0, 0, 0, 132, 1154, 1, 0, 0, 0, 134, 1164, 1, 0, 0, 0, 136, 1168, 1, 0, 0, 0, 138, 1179, 1, 0, 0, 0, 140, 1189, 1, 0, 0, 0, 142, 1192, 1, 0, 0, 0, 144, 1205, 1, 0, 0, 0, 146, 1207, 1, 0, 0, 0, 148, 1209, 1, 0, 0, 0, 150, 1211, 1, 0, 0, 0, 152, 1215, 1, 0, 0, 0, 154, 1220, 1, 0, 0, 0, 156, 1222, 1, 0, 0, 0, 158, 1226, 1, 0, 0, 0, 160, 1232, 1, 0, 0, 0, 162, 1234, 1, 0, 0, 0, 164, 1248, 1, 0, 0, 0, 166, 1250, 1, 0, 0, 0, 168, 1264, 1, 0, 0, 0, 170, 172, 3, 2, 1, 0, 171, 170, 1, 0, 0, 0, 172, 175, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 173, 174, 1, 0, 0, 0, 174, 176, 1, 0, 0, 0, 175, 173, 1, 0, 0, 0, 176, 177, 5, 0, 0, 1, 177, 1, 1, 0, 0, 0, 178, 181, 3, 6, 3, 0, 179, 181, 3, 10, 5, 0, 180, 178, 1, 0, 0, 0, 180, 179, 1, 0, 0, 0, 181, 3, 1, 0, 0, 0, 182, 183, 3, 110, 55, 0, 183, 5, 1, 0, 0, 0, 184, 185, 5, 50, 0, 0, 185, 189, 3, 154, 77, 0, 186, 187, 5, 111, 0, 0, 187, 188, 5, 118, 0, 0, 188, 190, 3, 4, 2, 0, 189, 186, 1, 0, 0, 0, 189, 190, 1, 0, 0, 0, 190, 7, 1, 0, 0, 0, 191, 196, 3, 154, 77, 0, 192, 193, 5, 112, 0, 0, 193, 195, 3, 154, 77, 0, 194, 192, 1, 0, 0, 0, 195, 198, 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 200, 1, 0, 0, 0, 198, 196, 1, 0, 0, 0, 199, 201, 5, 112, 0, 0, 200, 199, 1, 0, 0, 0, 200, 201, 1, 0, 0, 0, 201, 9, 1, 0, 0, 0, 202, 213, 3, 12, 6, 0, 203, 213, 3, 14, 7, 0, 204, 213, 3, 16, 8, 0, 205, 213, 3, 20, 10, 0, 206, 213, 3, 18, 9, 0, 207, 213, 3, 22, 11, 0, 208, 213, 3, 24, 12, 0, 209, 213, 3, 30, 15, 0, 210, 213, 3, 26, 13, 0, 211, 213, 3, 28, 14, 0, 212, 202, 1, 0, 0, 0, 212, 203, 1, 0, 0, 0, 212, 204, 1, 0, 0, 0, 212, 205, 1, 0, 0, 0, 212, 206, 1, 0, 0, 0, 212, 207, 1, 0, 0, 0, 212, 208, 1, 0, 0, 0, 212, 209, 1, 0, 0, 0, 212, 210, 1, 0, 0, 0, 212, 211, 1, 0, 0, 0, 213, 11, 1, 0, 0, 0, 214, 216, 5, 70, 0, 0, 215, 217, 3, 4, 2, 0, 216, 215, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 219, 1, 0, 0, 0, 218, 220, 5, 146, 0, 0, 219, 218, 1, 0, 0, 0, 219, 220, 1, 0, 0, 0, 220, 13, 1, 0, 0, 0, 221, 222, 5, 38, 0, 0, 222, 223, 5, 126, 0, 0, 223, 224, 3, 4, 2, 0, 224, 225, 5, 145, 0, 0, 225, 228, 3, 10, 5, 0, 226, 227, 5, 24, 0, 0, 227, 229, 3, 10, 5, 0, 228, 226, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 15, 1, 0, 0, 0, 230, 231, 5, 96, 0, 0, 231, 232, 5, 126, 0, 0, 232, 233, 3, 4, 2, 0, 233, 234, 5, 145, 0, 0, 234, 236, 3, 10, 5, 0, 235, 237, 5, 146, 0, 0, 236, 235, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 17, 1, 0, 0, 0, 238, 239, 5, 31, 0, 0, 239, 243, 5, 126, 0, 0, 240, 244, 3, 6, 3, 0, 241, 244, 3, 24, 12, 0, 242, 244, 3, 4, 2, 0, 243, 240, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 243, 242, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 247, 5, 146, 0, 0, 246, 248, 3, 4, 2, 0, 247, 246, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 253, 5, 146, 0, 0, 250, 254, 3, 6, 3, 0, 251, 254, 3, 24, 12, 0, 252, 254, 3, 4, 2, 0, 253, 250, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 252, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 256, 5, 145, 0, 0, 256, 258, 3, 10, 5, 0, 257, 259, 5, 146, 0, 0, 258, 257, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 19, 1, 0, 0, 0, 260, 261, 5, 31, 0, 0, 261, 262, 5, 126, 0, 0, 262, 263, 5, 50, 0, 0, 263, 266, 3, 154, 77, 0, 264, 265, 5, 112, 0, 0, 265, 267, 3, 154, 77, 0, 266, 264, 1, 0, 0, 0, 266, 267, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 269, 5, 40, 0, 0, 269, 270, 3, 4, 2, 0, 270, 271, 5, 145, 0, 0, 271, 273, 3, 10, 5, 0, 272, 274, 5, 146, 0, 0, 273, 272, 1, 0, 0, 0, 273, 274, 1, 0, 0, 0, 274, 21, 1, 0, 0, 0, 275, 276, 5, 29, 0, 0, 276, 277, 3, 154, 77, 0, 277, 279, 5, 126, 0, 0, 278, 280, 3, 8, 4, 0, 279, 278, 1, 0, 0, 0, 279, 280, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 282, 5, 145, 0, 0, 282, 283, 3, 30, 15, 0, 283, 23, 1, 0, 0, 0, 284, 285, 3, 4, 2, 0, 285, 286, 5, 111, 0, 0, 286, 287, 5, 118, 0, 0, 287, 288, 3, 4, 2, 0, 288, 25, 1, 0, 0, 0, 289, 291, 3, 4, 2, 0, 290, 292, 5, 146, 0, 0, 291, 290, 1, 0, 0, 0, 291, 292, 1, 0, 0, 0, 292, 27, 1, 0, 0, 0, 293, 294, 5, 146, 0, 0, 294, 29, 1, 0, 0, 0, 295, 299, 5, 124, 0, 0, 296, 298, 3, 2, 1, 0, 297, 296, 1, 0, 0, 0, 298, 301, 1, 0, 0, 0, 299, 297, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 302, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 302, 303, 5, 143, 0, 0, 303, 31, 1, 0, 0, 0, 304, 305, 3, 4, 2, 0, 305, 306, 5, 111, 0, 0, 306, 307, 3, 4, 2, 0, 307, 33, 1, 0, 0, 0, 308, 313, 3, 32, 16, 0, 309, 310, 5, 112, 0, 0, 310, 312, 3, 32, 16, 0, 311, 309, 1, 0, 0, 0, 312, 315, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 317, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 316, 318, 5, 112, 0, 0, 317, 316, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 35, 1, 0, 0, 0, 319, 323, 3, 38, 19, 0, 320, 323, 3, 42, 21, 0, 321, 323, 3, 118, 59, 0, 322, 319, 1, 0, 0, 0, 322, 320, 1, 0, 0, 0, 322, 321, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 325, 5, 0, 0, 1, 325, 37, 1, 0, 0, 0, 326, 332, 3, 40, 20, 0, 327, 328, 5, 91, 0, 0, 328, 329, 5, 1, 0, 0, 329, 331, 3, 40, 20, 0, 330, 327, 1, 0, 0, 0, 331, 334, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 39, 1, 0, 0, 0, 334, 332, 1, 0, 0, 0, 335, 342, 3, 42, 21, 0, 336, 337, 5, 126, 0, 0, 337, 338, 3, 38, 19, 0, 338, 339, 5, 145, 0, 0, 339, 342, 1, 0, 0, 0, 340, 342, 3, 158, 79, 0, 341, 335, 1, 0, 0, 0, 341, 336, 1, 0, 0, 0, 341, 340, 1, 0, 0, 0, 342, 41, 1, 0, 0, 0, 343, 345, 3, 44, 22, 0, 344, 343, 1, 0, 0, 0, 344, 345, 1, 0, 0, 0, 345, 346, 1, 0, 0, 0, 346, 348, 5, 77, 0, 0, 347, 349, 5, 23, 0, 0, 348, 347, 1, 0, 0, 0, 348, 349, 1, 0, 0, 0, 349, 351, 1, 0, 0, 0, 350, 352, 3, 46, 23, 0, 351, 350, 1, 0, 0, 0, 351, 352, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 355, 3, 108, 54, 0, 354, 356, 3, 48, 24, 0, 355, 354, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 358, 1, 0, 0, 0, 357, 359, 3, 50, 25, 0, 358, 357, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 361, 1, 0, 0, 0, 360, 362, 3, 54, 27, 0, 361, 360, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 364, 1, 0, 0, 0, 363, 365, 3, 56, 28, 0, 364, 363, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 367, 1, 0, 0, 0, 366, 368, 3, 58, 29, 0, 367, 366, 1, 0, 0, 0, 367, 368, 1, 0, 0, 0, 368, 371, 1, 0, 0, 0, 369, 370, 5, 98, 0, 0, 370, 372, 7, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 375, 1, 0, 0, 0, 373, 374, 5, 98, 0, 0, 374, 376, 5, 86, 0, 0, 375, 373, 1, 0, 0, 0, 375, 376, 1, 0, 0, 0, 376, 378, 1, 0, 0, 0, 377, 379, 3, 60, 30, 0, 378, 377, 1, 0, 0, 0, 378, 379, 1, 0, 0, 0, 379, 381, 1, 0, 0, 0, 380, 382, 3, 52, 26, 0, 381, 380, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 384, 1, 0, 0, 0, 383, 385, 3, 62, 31, 0, 384, 383, 1, 0, 0, 0, 384, 385, 1, 0, 0, 0, 385, 388, 1, 0, 0, 0, 386, 389, 3, 66, 33, 0, 387, 389, 3, 68, 34, 0, 388, 386, 1, 0, 0, 0, 388, 387, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 391, 1, 0, 0, 0, 390, 392, 3, 70, 35, 0, 391, 390, 1, 0, 0, 0, 391, 392, 1, 0, 0, 0, 392, 43, 1, 0, 0, 0, 393, 394, 5, 98, 0, 0, 394, 395, 3, 122, 61, 0, 395, 45, 1, 0, 0, 0, 396, 397, 5, 85, 0, 0, 397, 400, 5, 104, 0, 0, 398, 399, 5, 98, 0, 0, 399, 401, 5, 82, 0, 0, 400, 398, 1, 0, 0, 0, 400, 401, 1, 0, 0, 0, 401, 47, 1, 0, 0, 0, 402, 403, 5, 32, 0, 0, 403, 404, 3, 72, 36, 0, 404, 49, 1, 0, 0, 0, 405, 407, 7, 1, 0, 0, 406, 405, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 408, 1, 0, 0, 0, 408, 409, 5, 5, 0, 0, 409, 410, 5, 45, 0, 0, 410, 411, 3, 108, 54, 0, 411, 51, 1, 0, 0, 0, 412, 413, 5, 97, 0, 0, 413, 414, 3, 154, 77, 0, 414, 415, 5, 6, 0, 0, 415, 416, 5, 126, 0, 0, 416, 417, 3, 92, 46, 0, 417, 427, 5, 145, 0, 0, 418, 419, 5, 112, 0, 0, 419, 420, 3, 154, 77, 0, 420, 421, 5, 6, 0, 0, 421, 422, 5, 126, 0, 0, 422, 423, 3, 92, 46, 0, 423, 424, 5, 145, 0, 0, 424, 426, 1, 0, 0, 0, 425, 418, 1, 0, 0, 0, 426, 429, 1, 0, 0, 0, 427, 425, 1, 0, 0, 0, 427, 428, 1, 0, 0, 0, 428, 53, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 430, 431, 5, 67, 0, 0, 431, 432, 3, 110, 55, 0, 432, 55, 1, 0, 0, 0, 433, 434, 5, 95, 0, 0, 434, 435, 3, 110, 55, 0, 435, 57, 1, 0, 0, 0, 436, 437, 5, 34, 0, 0, 437, 444, 5, 11, 0, 0, 438, 439, 7, 0, 0, 0, 439, 440, 5, 126, 0, 0, 440, 441, 3, 108, 54, 0, 441, 442, 5, 145, 0, 0, 442, 445, 1, 0, 0, 0, 443, 445, 3, 108, 54, 0, 444, 438, 1, 0, 0, 0, 444, 443, 1, 0, 0, 0, 445, 59, 1, 0, 0, 0, 446, 447, 5, 35, 0, 0, 447, 448, 3, 110, 55, 0, 448, 61, 1, 0, 0, 0, 449, 450, 5, 62, 0, 0, 450, 451, 5, 11, 0, 0, 451, 452, 3, 82, 41, 0, 452, 63, 1, 0, 0, 0, 453, 454, 5, 62, 0, 0, 454, 455, 5, 11, 0, 0, 455, 456, 3, 108, 54, 0, 456, 65, 1, 0, 0, 0, 457, 458, 5, 52, 0, 0, 458, 461, 3, 110, 55, 0, 459, 460, 5, 112, 0, 0, 460, 462, 3, 110, 55, 0, 461, 459, 1, 0, 0, 0, 461, 462, 1, 0, 0, 0, 462, 467, 1, 0, 0, 0, 463, 464, 5, 98, 0, 0, 464, 468, 5, 82, 0, 0, 465, 466, 5, 11, 0, 0, 466, 468, 3, 108, 54, 0, 467, 463, 1, 0, 0, 0, 467, 465, 1, 0, 0, 0, 467, 468, 1, 0, 0, 0, 468, 487, 1, 0, 0, 0, 469, 470, 5, 52, 0, 0, 470, 473, 3, 110, 55, 0, 471, 472, 5, 98, 0, 0, 472, 474, 5, 82, 0, 0, 473, 471, 1, 0, 0, 0, 473, 474, 1, 0, 0, 0, 474, 475, 1, 0, 0, 0, 475, 476, 5, 59, 0, 0, 476, 477, 3, 110, 55, 0, 477, 487, 1, 0, 0, 0, 478, 479, 5, 52, 0, 0, 479, 480, 3, 110, 55, 0, 480, 481, 5, 59, 0, 0, 481, 484, 3, 110, 55, 0, 482, 483, 5, 11, 0, 0, 483, 485, 3, 108, 54, 0, 484, 482, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 487, 1, 0, 0, 0, 486, 457, 1, 0, 0, 0, 486, 469, 1, 0, 0, 0, 486, 478, 1, 0, 0, 0, 487, 67, 1, 0, 0, 0, 488, 489, 5, 59, 0, 0, 489, 490, 3, 110, 55, 0, 490, 69, 1, 0, 0, 0, 491, 492, 5, 79, 0, 0, 492, 493, 3, 88, 44, 0, 493, 71, 1, 0, 0, 0, 494, 495, 6, 36, -1, 0, 495, 497, 3, 130, 65, 0, 496, 498, 5, 27, 0, 0, 497, 496, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 500, 1, 0, 0, 0, 499, 501, 3, 80, 40, 0, 500, 499, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 507, 1, 0, 0, 0, 502, 503, 5, 126, 0, 0, 503, 504, 3, 72, 36, 0, 504, 505, 5, 145, 0, 0, 505, 507, 1, 0, 0, 0, 506, 494, 1, 0, 0, 0, 506, 502, 1, 0, 0, 0, 507, 522, 1, 0, 0, 0, 508, 509, 10, 3, 0, 0, 509, 510, 3, 76, 38, 0, 510, 511, 3, 72, 36, 4, 511, 521, 1, 0, 0, 0, 512, 514, 10, 4, 0, 0, 513, 515, 3, 74, 37, 0, 514, 513, 1, 0, 0, 0, 514, 515, 1, 0, 0, 0, 515, 516, 1, 0, 0, 0, 516, 517, 5, 45, 0, 0, 517, 518, 3, 72, 36, 0, 518, 519, 3, 78, 39, 0, 519, 521, 1, 0, 0, 0, 520, 508, 1, 0, 0, 0, 520, 512, 1, 0, 0, 0, 521, 524, 1, 0, 0, 0, 522, 520, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 73, 1, 0, 0, 0, 524, 522, 1, 0, 0, 0, 525, 527, 7, 2, 0, 0, 526, 525, 1, 0, 0, 0, 526, 527, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 535, 5, 42, 0, 0, 529, 531, 5, 42, 0, 0, 530, 532, 7, 2, 0, 0, 531, 530, 1, 0, 0, 0, 531, 532, 1, 0, 0, 0, 532, 535, 1, 0, 0, 0, 533, 535, 7, 2, 0, 0, 534, 526, 1, 0, 0, 0, 534, 529, 1, 0, 0, 0, 534, 533, 1, 0, 0, 0, 535, 569, 1, 0, 0, 0, 536, 538, 7, 3, 0, 0, 537, 536, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 539, 1, 0, 0, 0, 539, 541, 7, 4, 0, 0, 540, 542, 5, 63, 0, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 551, 1, 0, 0, 0, 543, 545, 7, 4, 0, 0, 544, 546, 5, 63, 0, 0, 545, 544, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 548, 1, 0, 0, 0, 547, 549, 7, 3, 0, 0, 548, 547, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 551, 1, 0, 0, 0, 550, 537, 1, 0, 0, 0, 550, 543, 1, 0, 0, 0, 551, 569, 1, 0, 0, 0, 552, 554, 7, 5, 0, 0, 553, 552, 1, 0, 0, 0, 553, 554, 1, 0, 0, 0, 554, 555, 1, 0, 0, 0, 555, 557, 5, 33, 0, 0, 556, 558, 5, 63, 0, 0, 557, 556, 1, 0, 0, 0, 557, 558, 1, 0, 0, 0, 558, 567, 1, 0, 0, 0, 559, 561, 5, 33, 0, 0, 560, 562, 5, 63, 0, 0, 561, 560, 1, 0, 0, 0, 561, 562, 1, 0, 0, 0, 562, 564, 1, 0, 0, 0, 563, 565, 7, 5, 0, 0, 564, 563, 1, 0, 0, 0, 564, 565, 1, 0, 0, 0, 565, 567, 1, 0, 0, 0, 566, 553, 1, 0, 0, 0, 566, 559, 1, 0, 0, 0, 567, 569, 1, 0, 0, 0, 568, 534, 1, 0, 0, 0, 568, 550, 1, 0, 0, 0, 568, 566, 1, 0, 0, 0, 569, 75, 1, 0, 0, 0, 570, 571, 5, 16, 0, 0, 571, 574, 5, 45, 0, 0, 572, 574, 5, 112, 0, 0, 573, 570, 1, 0, 0, 0, 573, 572, 1, 0, 0, 0, 574, 77, 1, 0, 0, 0, 575, 576, 5, 60, 0, 0, 576, 585, 3, 108, 54, 0, 577, 578, 5, 92, 0, 0, 578, 579, 5, 126, 0, 0, 579, 580, 3, 108, 54, 0, 580, 581, 5, 145, 0, 0, 581, 585, 1, 0, 0, 0, 582, 583, 5, 92, 0, 0, 583, 585, 3, 108, 54, 0, 584, 575, 1, 0, 0, 0, 584, 577, 1, 0, 0, 0, 584, 582, 1, 0, 0, 0, 585, 79, 1, 0, 0, 0, 586, 587, 5, 75, 0, 0, 587, 590, 3, 86, 43, 0, 588, 589, 5, 59, 0, 0, 589, 591, 3, 86, 43, 0, 590, 588, 1, 0, 0, 0, 590, 591, 1, 0, 0, 0, 591, 81, 1, 0, 0, 0, 592, 597, 3, 84, 42, 0, 593, 594, 5, 112, 0, 0, 594, 596, 3, 84, 42, 0, 595, 593, 1, 0, 0, 0, 596, 599, 1, 0, 0, 0, 597, 595, 1, 0, 0, 0, 597, 598, 1, 0, 0, 0, 598, 83, 1, 0, 0, 0, 599, 597, 1, 0, 0, 0, 600, 602, 3, 110, 55, 0, 601, 603, 7, 6, 0, 0, 602, 601, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 606, 1, 0, 0, 0, 604, 605, 5, 58, 0, 0, 605, 607, 7, 7, 0, 0, 606, 604, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 610, 1, 0, 0, 0, 608, 609, 5, 15, 0, 0, 609, 611, 5, 106, 0, 0, 610, 608, 1, 0, 0, 0, 610, 611, 1, 0, 0, 0, 611, 85, 1, 0, 0, 0, 612, 619, 3, 158, 79, 0, 613, 616, 3, 142, 71, 0, 614, 615, 5, 147, 0, 0, 615, 617, 3, 142, 71, 0, 616, 614, 1, 0, 0, 0, 616, 617, 1, 0, 0, 0, 617, 619, 1, 0, 0, 0, 618, 612, 1, 0, 0, 0, 618, 613, 1, 0, 0, 0, 619, 87, 1, 0, 0, 0, 620, 625, 3, 90, 45, 0, 621, 622, 5, 112, 0, 0, 622, 624, 3, 90, 45, 0, 623, 621, 1, 0, 0, 0, 624, 627, 1, 0, 0, 0, 625, 623, 1, 0, 0, 0, 625, 626, 1, 0, 0, 0, 626, 89, 1, 0, 0, 0, 627, 625, 1, 0, 0, 0, 628, 629, 3, 154, 77, 0, 629, 630, 5, 118, 0, 0, 630, 631, 3, 144, 72, 0, 631, 91, 1, 0, 0, 0, 632, 634, 3, 94, 47, 0, 633, 632, 1, 0, 0, 0, 633, 634, 1, 0, 0, 0, 634, 636, 1, 0, 0, 0, 635, 637, 3, 96, 48, 0, 636, 635, 1, 0, 0, 0, 636, 637, 1, 0, 0, 0, 637, 639, 1, 0, 0, 0, 638, 640, 3, 98, 49, 0, 639, 638, 1, 0, 0, 0, 639, 640, 1, 0, 0, 0, 640, 93, 1, 0, 0, 0, 641, 642, 5, 65, 0, 0, 642, 643, 5, 11, 0, 0, 643, 644, 3, 108, 54, 0, 644, 95, 1, 0, 0, 0, 645, 646, 5, 62, 0, 0, 646, 647, 5, 11, 0, 0, 647, 648, 3, 82, 41, 0, 648, 97, 1, 0, 0, 0, 649, 650, 7, 8, 0, 0, 650, 651, 3, 100, 50, 0, 651, 99, 1, 0, 0, 0, 652, 659, 3, 102, 51, 0, 653, 654, 5, 9, 0, 0, 654, 655, 3, 102, 51, 0, 655, 656, 5, 2, 0, 0, 656, 657, 3, 102, 51, 0, 657, 659, 1, 0, 0, 0, 658, 652, 1, 0, 0, 0, 658, 653, 1, 0, 0, 0, 659, 101, 1, 0, 0, 0, 660, 661, 5, 18, 0, 0, 661, 673, 5, 73, 0, 0, 662, 663, 5, 90, 0, 0, 663, 673, 5, 66, 0, 0, 664, 665, 5, 90, 0, 0, 665, 673, 5, 30, 0, 0, 666, 667, 3, 142, 71, 0, 667, 668, 5, 66, 0, 0, 668, 673, 1, 0, 0, 0, 669, 670, 3, 142, 71, 0, 670, 671, 5, 30, 0, 0, 671, 673, 1, 0, 0, 0, 672, 660, 1, 0, 0, 0, 672, 662, 1, 0, 0, 0, 672, 664, 1, 0, 0, 0, 672, 666, 1, 0, 0, 0, 672, 669, 1, 0, 0, 0, 673, 103, 1, 0, 0, 0, 674, 675, 3, 110, 55, 0, 675, 676, 5, 0, 0, 1, 676, 105, 1, 0, 0, 0, 677, 734, 3, 154, 77, 0, 678, 679, 3, 154, 77, 0, 679, 680, 5, 126, 0, 0, 680, 681, 3, 154, 77, 0, 681, 688, 3, 106, 53, 0, 682, 683, 5, 112, 0, 0, 683, 684, 3, 154, 77, 0, 684, 685, 3, 106, 53, 0, 685, 687, 1, 0, 0, 0, 686, 682, 1, 0, 0, 0, 687, 690, 1, 0, 0, 0, 688, 686, 1, 0, 0, 0, 688, 689, 1, 0, 0, 0, 689, 692, 1, 0, 0, 0, 690, 688, 1, 0, 0, 0, 691, 693, 5, 112, 0, 0, 692, 691, 1, 0, 0, 0, 692, 693, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 694, 695, 5, 145, 0, 0, 695, 734, 1, 0, 0, 0, 696, 697, 3, 154, 77, 0, 697, 698, 5, 126, 0, 0, 698, 703, 3, 156, 78, 0, 699, 700, 5, 112, 0, 0, 700, 702, 3, 156, 78, 0, 701, 699, 1, 0, 0, 0, 702, 705, 1, 0, 0, 0, 703, 701, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 704, 707, 1, 0, 0, 0, 705, 703, 1, 0, 0, 0, 706, 708, 5, 112, 0, 0, 707, 706, 1, 0, 0, 0, 707, 708, 1, 0, 0, 0, 708, 709, 1, 0, 0, 0, 709, 710, 5, 145, 0, 0, 710, 734, 1, 0, 0, 0, 711, 712, 3, 154, 77, 0, 712, 713, 5, 126, 0, 0, 713, 718, 3, 106, 53, 0, 714, 715, 5, 112, 0, 0, 715, 717, 3, 106, 53, 0, 716, 714, 1, 0, 0, 0, 717, 720, 1, 0, 0, 0, 718, 716, 1, 0, 0, 0, 718, 719, 1, 0, 0, 0, 719, 722, 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 721, 723, 5, 112, 0, 0, 722, 721, 1, 0, 0, 0, 722, 723, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 725, 5, 145, 0, 0, 725, 734, 1, 0, 0, 0, 726, 727, 3, 154, 77, 0, 727, 729, 5, 126, 0, 0, 728, 730, 3, 108, 54, 0, 729, 728, 1, 0, 0, 0, 729, 730, 1, 0, 0, 0, 730, 731, 1, 0, 0, 0, 731, 732, 5, 145, 0, 0, 732, 734, 1, 0, 0, 0, 733, 677, 1, 0, 0, 0, 733, 678, 1, 0, 0, 0, 733, 696, 1, 0, 0, 0, 733, 711, 1, 0, 0, 0, 733, 726, 1, 0, 0, 0, 734, 107, 1, 0, 0, 0, 735, 740, 3, 110, 55, 0, 736, 737, 5, 112, 0, 0, 737, 739, 3, 110, 55, 0, 738, 736, 1, 0, 0, 0, 739, 742, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 744, 1, 0, 0, 0, 742, 740, 1, 0, 0, 0, 743, 745, 5, 112, 0, 0, 744, 743, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 109, 1, 0, 0, 0, 746, 747, 6, 55, -1, 0, 747, 749, 5, 12, 0, 0, 748, 750, 3, 110, 55, 0, 749, 748, 1, 0, 0, 0, 749, 750, 1, 0, 0, 0, 750, 756, 1, 0, 0, 0, 751, 752, 5, 94, 0, 0, 752, 753, 3, 110, 55, 0, 753, 754, 5, 81, 0, 0, 754, 755, 3, 110, 55, 0, 755, 757, 1, 0, 0, 0, 756, 751, 1, 0, 0, 0, 757, 758, 1, 0, 0, 0, 758, 756, 1, 0, 0, 0, 758, 759, 1, 0, 0, 0, 759, 762, 1, 0, 0, 0, 760, 761, 5, 24, 0, 0, 761, 763, 3, 110, 55, 0, 762, 760, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 764, 1, 0, 0, 0, 764, 765, 5, 25, 0, 0, 765, 896, 1, 0, 0, 0, 766, 767, 5, 13, 0, 0, 767, 768, 5, 126, 0, 0, 768, 769, 3, 110, 55, 0, 769, 770, 5, 6, 0, 0, 770, 771, 3, 106, 53, 0, 771, 772, 5, 145, 0, 0, 772, 896, 1, 0, 0, 0, 773, 774, 5, 19, 0, 0, 774, 896, 5, 106, 0, 0, 775, 776, 5, 43, 0, 0, 776, 777, 3, 110, 55, 0, 777, 778, 3, 146, 73, 0, 778, 896, 1, 0, 0, 0, 779, 780, 5, 80, 0, 0, 780, 781, 5, 126, 0, 0, 781, 782, 3, 110, 55, 0, 782, 783, 5, 32, 0, 0, 783, 786, 3, 110, 55, 0, 784, 785, 5, 31, 0, 0, 785, 787, 3, 110, 55, 0, 786, 784, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, 1, 0, 0, 0, 788, 789, 5, 145, 0, 0, 789, 896, 1, 0, 0, 0, 790, 791, 5, 83, 0, 0, 791, 896, 5, 106, 0, 0, 792, 793, 5, 88, 0, 0, 793, 794, 5, 126, 0, 0, 794, 795, 7, 9, 0, 0, 795, 796, 3, 160, 80, 0, 796, 797, 5, 32, 0, 0, 797, 798, 3, 110, 55, 0, 798, 799, 5, 145, 0, 0, 799, 896, 1, 0, 0, 0, 800, 801, 3, 154, 77, 0, 801, 803, 5, 126, 0, 0, 802, 804, 3, 108, 54, 0, 803, 802, 1, 0, 0, 0, 803, 804, 1, 0, 0, 0, 804, 805, 1, 0, 0, 0, 805, 806, 5, 145, 0, 0, 806, 815, 1, 0, 0, 0, 807, 809, 5, 126, 0, 0, 808, 810, 5, 23, 0, 0, 809, 808, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 812, 1, 0, 0, 0, 811, 813, 3, 112, 56, 0, 812, 811, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 816, 5, 145, 0, 0, 815, 807, 1, 0, 0, 0, 815, 816, 1, 0, 0, 0, 816, 817, 1, 0, 0, 0, 817, 818, 5, 64, 0, 0, 818, 819, 5, 126, 0, 0, 819, 820, 3, 92, 46, 0, 820, 821, 5, 145, 0, 0, 821, 896, 1, 0, 0, 0, 822, 823, 3, 154, 77, 0, 823, 825, 5, 126, 0, 0, 824, 826, 3, 108, 54, 0, 825, 824, 1, 0, 0, 0, 825, 826, 1, 0, 0, 0, 826, 827, 1, 0, 0, 0, 827, 828, 5, 145, 0, 0, 828, 837, 1, 0, 0, 0, 829, 831, 5, 126, 0, 0, 830, 832, 5, 23, 0, 0, 831, 830, 1, 0, 0, 0, 831, 832, 1, 0, 0, 0, 832, 834, 1, 0, 0, 0, 833, 835, 3, 112, 56, 0, 834, 833, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 836, 1, 0, 0, 0, 836, 838, 5, 145, 0, 0, 837, 829, 1, 0, 0, 0, 837, 838, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 840, 5, 64, 0, 0, 840, 841, 3, 154, 77, 0, 841, 896, 1, 0, 0, 0, 842, 848, 3, 154, 77, 0, 843, 845, 5, 126, 0, 0, 844, 846, 3, 108, 54, 0, 845, 844, 1, 0, 0, 0, 845, 846, 1, 0, 0, 0, 846, 847, 1, 0, 0, 0, 847, 849, 5, 145, 0, 0, 848, 843, 1, 0, 0, 0, 848, 849, 1, 0, 0, 0, 849, 850, 1, 0, 0, 0, 850, 852, 5, 126, 0, 0, 851, 853, 5, 23, 0, 0, 852, 851, 1, 0, 0, 0, 852, 853, 1, 0, 0, 0, 853, 855, 1, 0, 0, 0, 854, 856, 3, 112, 56, 0, 855, 854, 1, 0, 0, 0, 855, 856, 1, 0, 0, 0, 856, 857, 1, 0, 0, 0, 857, 858, 5, 145, 0, 0, 858, 896, 1, 0, 0, 0, 859, 896, 3, 118, 59, 0, 860, 896, 3, 162, 81, 0, 861, 896, 3, 144, 72, 0, 862, 863, 5, 114, 0, 0, 863, 896, 3, 110, 55, 19, 864, 865, 5, 56, 0, 0, 865, 896, 3, 110, 55, 13, 866, 867, 3, 134, 67, 0, 867, 868, 5, 116, 0, 0, 868, 870, 1, 0, 0, 0, 869, 866, 1, 0, 0, 0, 869, 870, 1, 0, 0, 0, 870, 871, 1, 0, 0, 0, 871, 896, 5, 108, 0, 0, 872, 873, 5, 126, 0, 0, 873, 874, 3, 38, 19, 0, 874, 875, 5, 145, 0, 0, 875, 896, 1, 0, 0, 0, 876, 877, 5, 126, 0, 0, 877, 878, 3, 110, 55, 0, 878, 879, 5, 145, 0, 0, 879, 896, 1, 0, 0, 0, 880, 881, 5, 126, 0, 0, 881, 882, 3, 108, 54, 0, 882, 883, 5, 145, 0, 0, 883, 896, 1, 0, 0, 0, 884, 886, 5, 125, 0, 0, 885, 887, 3, 108, 54, 0, 886, 885, 1, 0, 0, 0, 886, 887, 1, 0, 0, 0, 887, 888, 1, 0, 0, 0, 888, 896, 5, 144, 0, 0, 889, 891, 5, 124, 0, 0, 890, 892, 3, 34, 17, 0, 891, 890, 1, 0, 0, 0, 891, 892, 1, 0, 0, 0, 892, 893, 1, 0, 0, 0, 893, 896, 5, 143, 0, 0, 894, 896, 3, 126, 63, 0, 895, 746, 1, 0, 0, 0, 895, 766, 1, 0, 0, 0, 895, 773, 1, 0, 0, 0, 895, 775, 1, 0, 0, 0, 895, 779, 1, 0, 0, 0, 895, 790, 1, 0, 0, 0, 895, 792, 1, 0, 0, 0, 895, 800, 1, 0, 0, 0, 895, 822, 1, 0, 0, 0, 895, 842, 1, 0, 0, 0, 895, 859, 1, 0, 0, 0, 895, 860, 1, 0, 0, 0, 895, 861, 1, 0, 0, 0, 895, 862, 1, 0, 0, 0, 895, 864, 1, 0, 0, 0, 895, 869, 1, 0, 0, 0, 895, 872, 1, 0, 0, 0, 895, 876, 1, 0, 0, 0, 895, 880, 1, 0, 0, 0, 895, 884, 1, 0, 0, 0, 895, 889, 1, 0, 0, 0, 895, 894, 1, 0, 0, 0, 896, 1001, 1, 0, 0, 0, 897, 901, 10, 18, 0, 0, 898, 902, 5, 108, 0, 0, 899, 902, 5, 147, 0, 0, 900, 902, 5, 134, 0, 0, 901, 898, 1, 0, 0, 0, 901, 899, 1, 0, 0, 0, 901, 900, 1, 0, 0, 0, 902, 903, 1, 0, 0, 0, 903, 1000, 3, 110, 55, 19, 904, 908, 10, 17, 0, 0, 905, 909, 5, 135, 0, 0, 906, 909, 5, 114, 0, 0, 907, 909, 5, 113, 0, 0, 908, 905, 1, 0, 0, 0, 908, 906, 1, 0, 0, 0, 908, 907, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 1000, 3, 110, 55, 18, 911, 936, 10, 16, 0, 0, 912, 937, 5, 117, 0, 0, 913, 937, 5, 118, 0, 0, 914, 937, 5, 129, 0, 0, 915, 937, 5, 127, 0, 0, 916, 937, 5, 128, 0, 0, 917, 937, 5, 119, 0, 0, 918, 937, 5, 120, 0, 0, 919, 921, 5, 56, 0, 0, 920, 919, 1, 0, 0, 0, 920, 921, 1, 0, 0, 0, 921, 922, 1, 0, 0, 0, 922, 924, 5, 40, 0, 0, 923, 925, 5, 14, 0, 0, 924, 923, 1, 0, 0, 0, 924, 925, 1, 0, 0, 0, 925, 937, 1, 0, 0, 0, 926, 928, 5, 56, 0, 0, 927, 926, 1, 0, 0, 0, 927, 928, 1, 0, 0, 0, 928, 929, 1, 0, 0, 0, 929, 937, 7, 10, 0, 0, 930, 937, 5, 141, 0, 0, 931, 937, 5, 142, 0, 0, 932, 937, 5, 131, 0, 0, 933, 937, 5, 122, 0, 0, 934, 937, 5, 123, 0, 0, 935, 937, 5, 130, 0, 0, 936, 912, 1, 0, 0, 0, 936, 913, 1, 0, 0, 0, 936, 914, 1, 0, 0, 0, 936, 915, 1, 0, 0, 0, 936, 916, 1, 0, 0, 0, 936, 917, 1, 0, 0, 0, 936, 918, 1, 0, 0, 0, 936, 920, 1, 0, 0, 0, 936, 927, 1, 0, 0, 0, 936, 930, 1, 0, 0, 0, 936, 931, 1, 0, 0, 0, 936, 932, 1, 0, 0, 0, 936, 933, 1, 0, 0, 0, 936, 934, 1, 0, 0, 0, 936, 935, 1, 0, 0, 0, 937, 938, 1, 0, 0, 0, 938, 1000, 3, 110, 55, 17, 939, 940, 10, 14, 0, 0, 940, 941, 5, 133, 0, 0, 941, 1000, 3, 110, 55, 15, 942, 943, 10, 12, 0, 0, 943, 944, 5, 2, 0, 0, 944, 1000, 3, 110, 55, 13, 945, 946, 10, 11, 0, 0, 946, 947, 5, 61, 0, 0, 947, 1000, 3, 110, 55, 12, 948, 950, 10, 10, 0, 0, 949, 951, 5, 56, 0, 0, 950, 949, 1, 0, 0, 0, 950, 951, 1, 0, 0, 0, 951, 952, 1, 0, 0, 0, 952, 953, 5, 9, 0, 0, 953, 954, 3, 110, 55, 0, 954, 955, 5, 2, 0, 0, 955, 956, 3, 110, 55, 11, 956, 1000, 1, 0, 0, 0, 957, 958, 10, 9, 0, 0, 958, 959, 5, 136, 0, 0, 959, 960, 3, 110, 55, 0, 960, 961, 5, 111, 0, 0, 961, 962, 3, 110, 55, 9, 962, 1000, 1, 0, 0, 0, 963, 964, 10, 25, 0, 0, 964, 965, 5, 125, 0, 0, 965, 966, 3, 110, 55, 0, 966, 967, 5, 144, 0, 0, 967, 1000, 1, 0, 0, 0, 968, 969, 10, 24, 0, 0, 969, 970, 5, 116, 0, 0, 970, 1000, 5, 104, 0, 0, 971, 972, 10, 23, 0, 0, 972, 973, 5, 116, 0, 0, 973, 1000, 3, 154, 77, 0, 974, 975, 10, 22, 0, 0, 975, 976, 5, 132, 0, 0, 976, 977, 5, 125, 0, 0, 977, 978, 3, 110, 55, 0, 978, 979, 5, 144, 0, 0, 979, 1000, 1, 0, 0, 0, 980, 981, 10, 21, 0, 0, 981, 982, 5, 132, 0, 0, 982, 1000, 5, 104, 0, 0, 983, 984, 10, 20, 0, 0, 984, 985, 5, 132, 0, 0, 985, 1000, 3, 154, 77, 0, 986, 987, 10, 15, 0, 0, 987, 989, 5, 44, 0, 0, 988, 990, 5, 56, 0, 0, 989, 988, 1, 0, 0, 0, 989, 990, 1, 0, 0, 0, 990, 991, 1, 0, 0, 0, 991, 1000, 5, 57, 0, 0, 992, 997, 10, 8, 0, 0, 993, 994, 5, 6, 0, 0, 994, 998, 3, 154, 77, 0, 995, 996, 5, 6, 0, 0, 996, 998, 5, 106, 0, 0, 997, 993, 1, 0, 0, 0, 997, 995, 1, 0, 0, 0, 998, 1000, 1, 0, 0, 0, 999, 897, 1, 0, 0, 0, 999, 904, 1, 0, 0, 0, 999, 911, 1, 0, 0, 0, 999, 939, 1, 0, 0, 0, 999, 942, 1, 0, 0, 0, 999, 945, 1, 0, 0, 0, 999, 948, 1, 0, 0, 0, 999, 957, 1, 0, 0, 0, 999, 963, 1, 0, 0, 0, 999, 968, 1, 0, 0, 0, 999, 971, 1, 0, 0, 0, 999, 974, 1, 0, 0, 0, 999, 980, 1, 0, 0, 0, 999, 983, 1, 0, 0, 0, 999, 986, 1, 0, 0, 0, 999, 992, 1, 0, 0, 0, 1000, 1003, 1, 0, 0, 0, 1001, 999, 1, 0, 0, 0, 1001, 1002, 1, 0, 0, 0, 1002, 111, 1, 0, 0, 0, 1003, 1001, 1, 0, 0, 0, 1004, 1009, 3, 114, 57, 0, 1005, 1006, 5, 112, 0, 0, 1006, 1008, 3, 114, 57, 0, 1007, 1005, 1, 0, 0, 0, 1008, 1011, 1, 0, 0, 0, 1009, 1007, 1, 0, 0, 0, 1009, 1010, 1, 0, 0, 0, 1010, 1013, 1, 0, 0, 0, 1011, 1009, 1, 0, 0, 0, 1012, 1014, 5, 112, 0, 0, 1013, 1012, 1, 0, 0, 0, 1013, 1014, 1, 0, 0, 0, 1014, 113, 1, 0, 0, 0, 1015, 1018, 3, 116, 58, 0, 1016, 1018, 3, 110, 55, 0, 1017, 1015, 1, 0, 0, 0, 1017, 1016, 1, 0, 0, 0, 1018, 115, 1, 0, 0, 0, 1019, 1020, 5, 126, 0, 0, 1020, 1025, 3, 154, 77, 0, 1021, 1022, 5, 112, 0, 0, 1022, 1024, 3, 154, 77, 0, 1023, 1021, 1, 0, 0, 0, 1024, 1027, 1, 0, 0, 0, 1025, 1023, 1, 0, 0, 0, 1025, 1026, 1, 0, 0, 0, 1026, 1029, 1, 0, 0, 0, 1027, 1025, 1, 0, 0, 0, 1028, 1030, 5, 112, 0, 0, 1029, 1028, 1, 0, 0, 0, 1029, 1030, 1, 0, 0, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1032, 5, 145, 0, 0, 1032, 1045, 1, 0, 0, 0, 1033, 1038, 3, 154, 77, 0, 1034, 1035, 5, 112, 0, 0, 1035, 1037, 3, 154, 77, 0, 1036, 1034, 1, 0, 0, 0, 1037, 1040, 1, 0, 0, 0, 1038, 1036, 1, 0, 0, 0, 1038, 1039, 1, 0, 0, 0, 1039, 1042, 1, 0, 0, 0, 1040, 1038, 1, 0, 0, 0, 1041, 1043, 5, 112, 0, 0, 1042, 1041, 1, 0, 0, 0, 1042, 1043, 1, 0, 0, 0, 1043, 1045, 1, 0, 0, 0, 1044, 1019, 1, 0, 0, 0, 1044, 1033, 1, 0, 0, 0, 1045, 1046, 1, 0, 0, 0, 1046, 1047, 5, 107, 0, 0, 1047, 1048, 3, 110, 55, 0, 1048, 117, 1, 0, 0, 0, 1049, 1050, 5, 128, 0, 0, 1050, 1054, 3, 154, 77, 0, 1051, 1053, 3, 120, 60, 0, 1052, 1051, 1, 0, 0, 0, 1053, 1056, 1, 0, 0, 0, 1054, 1052, 1, 0, 0, 0, 1054, 1055, 1, 0, 0, 0, 1055, 1057, 1, 0, 0, 0, 1056, 1054, 1, 0, 0, 0, 1057, 1058, 5, 147, 0, 0, 1058, 1059, 5, 120, 0, 0, 1059, 1078, 1, 0, 0, 0, 1060, 1061, 5, 128, 0, 0, 1061, 1065, 3, 154, 77, 0, 1062, 1064, 3, 120, 60, 0, 1063, 1062, 1, 0, 0, 0, 1064, 1067, 1, 0, 0, 0, 1065, 1063, 1, 0, 0, 0, 1065, 1066, 1, 0, 0, 0, 1066, 1068, 1, 0, 0, 0, 1067, 1065, 1, 0, 0, 0, 1068, 1070, 5, 120, 0, 0, 1069, 1071, 3, 118, 59, 0, 1070, 1069, 1, 0, 0, 0, 1070, 1071, 1, 0, 0, 0, 1071, 1072, 1, 0, 0, 0, 1072, 1073, 5, 128, 0, 0, 1073, 1074, 5, 147, 0, 0, 1074, 1075, 3, 154, 77, 0, 1075, 1076, 5, 120, 0, 0, 1076, 1078, 1, 0, 0, 0, 1077, 1049, 1, 0, 0, 0, 1077, 1060, 1, 0, 0, 0, 1078, 119, 1, 0, 0, 0, 1079, 1080, 3, 154, 77, 0, 1080, 1081, 5, 118, 0, 0, 1081, 1082, 3, 160, 80, 0, 1082, 1091, 1, 0, 0, 0, 1083, 1084, 3, 154, 77, 0, 1084, 1085, 5, 118, 0, 0, 1085, 1086, 5, 124, 0, 0, 1086, 1087, 3, 110, 55, 0, 1087, 1088, 5, 143, 0, 0, 1088, 1091, 1, 0, 0, 0, 1089, 1091, 3, 154, 77, 0, 1090, 1079, 1, 0, 0, 0, 1090, 1083, 1, 0, 0, 0, 1090, 1089, 1, 0, 0, 0, 1091, 121, 1, 0, 0, 0, 1092, 1097, 3, 124, 62, 0, 1093, 1094, 5, 112, 0, 0, 1094, 1096, 3, 124, 62, 0, 1095, 1093, 1, 0, 0, 0, 1096, 1099, 1, 0, 0, 0, 1097, 1095, 1, 0, 0, 0, 1097, 1098, 1, 0, 0, 0, 1098, 1101, 1, 0, 0, 0, 1099, 1097, 1, 0, 0, 0, 1100, 1102, 5, 112, 0, 0, 1101, 1100, 1, 0, 0, 0, 1101, 1102, 1, 0, 0, 0, 1102, 123, 1, 0, 0, 0, 1103, 1104, 3, 154, 77, 0, 1104, 1105, 5, 6, 0, 0, 1105, 1106, 5, 126, 0, 0, 1106, 1107, 3, 38, 19, 0, 1107, 1108, 5, 145, 0, 0, 1108, 1114, 1, 0, 0, 0, 1109, 1110, 3, 110, 55, 0, 1110, 1111, 5, 6, 0, 0, 1111, 1112, 3, 154, 77, 0, 1112, 1114, 1, 0, 0, 0, 1113, 1103, 1, 0, 0, 0, 1113, 1109, 1, 0, 0, 0, 1114, 125, 1, 0, 0, 0, 1115, 1123, 3, 158, 79, 0, 1116, 1117, 3, 134, 67, 0, 1117, 1118, 5, 116, 0, 0, 1118, 1120, 1, 0, 0, 0, 1119, 1116, 1, 0, 0, 0, 1119, 1120, 1, 0, 0, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1123, 3, 128, 64, 0, 1122, 1115, 1, 0, 0, 0, 1122, 1119, 1, 0, 0, 0, 1123, 127, 1, 0, 0, 0, 1124, 1129, 3, 154, 77, 0, 1125, 1126, 5, 116, 0, 0, 1126, 1128, 3, 154, 77, 0, 1127, 1125, 1, 0, 0, 0, 1128, 1131, 1, 0, 0, 0, 1129, 1127, 1, 0, 0, 0, 1129, 1130, 1, 0, 0, 0, 1130, 129, 1, 0, 0, 0, 1131, 1129, 1, 0, 0, 0, 1132, 1133, 6, 65, -1, 0, 1133, 1142, 3, 134, 67, 0, 1134, 1142, 3, 132, 66, 0, 1135, 1136, 5, 126, 0, 0, 1136, 1137, 3, 38, 19, 0, 1137, 1138, 5, 145, 0, 0, 1138, 1142, 1, 0, 0, 0, 1139, 1142, 3, 118, 59, 0, 1140, 1142, 3, 158, 79, 0, 1141, 1132, 1, 0, 0, 0, 1141, 1134, 1, 0, 0, 0, 1141, 1135, 1, 0, 0, 0, 1141, 1139, 1, 0, 0, 0, 1141, 1140, 1, 0, 0, 0, 1142, 1151, 1, 0, 0, 0, 1143, 1147, 10, 3, 0, 0, 1144, 1148, 3, 152, 76, 0, 1145, 1146, 5, 6, 0, 0, 1146, 1148, 3, 154, 77, 0, 1147, 1144, 1, 0, 0, 0, 1147, 1145, 1, 0, 0, 0, 1148, 1150, 1, 0, 0, 0, 1149, 1143, 1, 0, 0, 0, 1150, 1153, 1, 0, 0, 0, 1151, 1149, 1, 0, 0, 0, 1151, 1152, 1, 0, 0, 0, 1152, 131, 1, 0, 0, 0, 1153, 1151, 1, 0, 0, 0, 1154, 1155, 3, 154, 77, 0, 1155, 1157, 5, 126, 0, 0, 1156, 1158, 3, 136, 68, 0, 1157, 1156, 1, 0, 0, 0, 1157, 1158, 1, 0, 0, 0, 1158, 1159, 1, 0, 0, 0, 1159, 1160, 5, 145, 0, 0, 1160, 133, 1, 0, 0, 0, 1161, 1162, 3, 138, 69, 0, 1162, 1163, 5, 116, 0, 0, 1163, 1165, 1, 0, 0, 0, 1164, 1161, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 1166, 1, 0, 0, 0, 1166, 1167, 3, 154, 77, 0, 1167, 135, 1, 0, 0, 0, 1168, 1173, 3, 110, 55, 0, 1169, 1170, 5, 112, 0, 0, 1170, 1172, 3, 110, 55, 0, 1171, 1169, 1, 0, 0, 0, 1172, 1175, 1, 0, 0, 0, 1173, 1171, 1, 0, 0, 0, 1173, 1174, 1, 0, 0, 0, 1174, 1177, 1, 0, 0, 0, 1175, 1173, 1, 0, 0, 0, 1176, 1178, 5, 112, 0, 0, 1177, 1176, 1, 0, 0, 0, 1177, 1178, 1, 0, 0, 0, 1178, 137, 1, 0, 0, 0, 1179, 1180, 3, 154, 77, 0, 1180, 139, 1, 0, 0, 0, 1181, 1190, 5, 102, 0, 0, 1182, 1183, 5, 116, 0, 0, 1183, 1190, 7, 11, 0, 0, 1184, 1185, 5, 104, 0, 0, 1185, 1187, 5, 116, 0, 0, 1186, 1188, 7, 11, 0, 0, 1187, 1186, 1, 0, 0, 0, 1187, 1188, 1, 0, 0, 0, 1188, 1190, 1, 0, 0, 0, 1189, 1181, 1, 0, 0, 0, 1189, 1182, 1, 0, 0, 0, 1189, 1184, 1, 0, 0, 0, 1190, 141, 1, 0, 0, 0, 1191, 1193, 7, 12, 0, 0, 1192, 1191, 1, 0, 0, 0, 1192, 1193, 1, 0, 0, 0, 1193, 1200, 1, 0, 0, 0, 1194, 1201, 3, 140, 70, 0, 1195, 1201, 5, 103, 0, 0, 1196, 1201, 5, 104, 0, 0, 1197, 1201, 5, 105, 0, 0, 1198, 1201, 5, 41, 0, 0, 1199, 1201, 5, 55, 0, 0, 1200, 1194, 1, 0, 0, 0, 1200, 1195, 1, 0, 0, 0, 1200, 1196, 1, 0, 0, 0, 1200, 1197, 1, 0, 0, 0, 1200, 1198, 1, 0, 0, 0, 1200, 1199, 1, 0, 0, 0, 1201, 143, 1, 0, 0, 0, 1202, 1206, 3, 142, 71, 0, 1203, 1206, 5, 106, 0, 0, 1204, 1206, 5, 57, 0, 0, 1205, 1202, 1, 0, 0, 0, 1205, 1203, 1, 0, 0, 0, 1205, 1204, 1, 0, 0, 0, 1206, 145, 1, 0, 0, 0, 1207, 1208, 7, 13, 0, 0, 1208, 147, 1, 0, 0, 0, 1209, 1210, 7, 14, 0, 0, 1210, 149, 1, 0, 0, 0, 1211, 1212, 7, 15, 0, 0, 1212, 151, 1, 0, 0, 0, 1213, 1216, 5, 101, 0, 0, 1214, 1216, 3, 150, 75, 0, 1215, 1213, 1, 0, 0, 0, 1215, 1214, 1, 0, 0, 0, 1216, 153, 1, 0, 0, 0, 1217, 1221, 5, 101, 0, 0, 1218, 1221, 3, 146, 73, 0, 1219, 1221, 3, 148, 74, 0, 1220, 1217, 1, 0, 0, 0, 1220, 1218, 1, 0, 0, 0, 1220, 1219, 1, 0, 0, 0, 1221, 155, 1, 0, 0, 0, 1222, 1223, 3, 160, 80, 0, 1223, 1224, 5, 118, 0, 0, 1224, 1225, 3, 142, 71, 0, 1225, 157, 1, 0, 0, 0, 1226, 1227, 5, 124, 0, 0, 1227, 1228, 3, 154, 77, 0, 1228, 1229, 5, 143, 0, 0, 1229, 159, 1, 0, 0, 0, 1230, 1233, 5, 106, 0, 0, 1231, 1233, 3, 162, 81, 0, 1232, 1230, 1, 0, 0, 0, 1232, 1231, 1, 0, 0, 0, 1233, 161, 1, 0, 0, 0, 1234, 1238, 5, 138, 0, 0, 1235, 1237, 3, 164, 82, 0, 1236, 1235, 1, 0, 0, 0, 1237, 1240, 1, 0, 0, 0, 1238, 1236, 1, 0, 0, 0, 1238, 1239, 1, 0, 0, 0, 1239, 1241, 1, 0, 0, 0, 1240, 1238, 1, 0, 0, 0, 1241, 1242, 5, 140, 0, 0, 1242, 163, 1, 0, 0, 0, 1243, 1244, 5, 153, 0, 0, 1244, 1245, 3, 110, 55, 0, 1245, 1246, 5, 143, 0, 0, 1246, 1249, 1, 0, 0, 0, 1247, 1249, 5, 152, 0, 0, 1248, 1243, 1, 0, 0, 0, 1248, 1247, 1, 0, 0, 0, 1249, 165, 1, 0, 0, 0, 1250, 1254, 5, 139, 0, 0, 1251, 1253, 3, 168, 84, 0, 1252, 1251, 1, 0, 0, 0, 1253, 1256, 1, 0, 0, 0, 1254, 1252, 1, 0, 0, 0, 1254, 1255, 1, 0, 0, 0, 1255, 1257, 1, 0, 0, 0, 1256, 1254, 1, 0, 0, 0, 1257, 1258, 5, 0, 0, 1, 1258, 167, 1, 0, 0, 0, 1259, 1260, 5, 155, 0, 0, 1260, 1261, 3, 110, 55, 0, 1261, 1262, 5, 143, 0, 0, 1262, 1265, 1, 0, 0, 0, 1263, 1265, 5, 154, 0, 0, 1264, 1259, 1, 0, 0, 0, 1264, 1263, 1, 0, 0, 0, 1265, 169, 1, 0, 0, 0, 162, 173, 180, 189, 196, 200, 212, 216, 219, 228, 236, 243, 247, 253, 258, 266, 273, 279, 291, 299, 313, 317, 322, 332, 341, 344, 348, 351, 355, 358, 361, 364, 367, 371, 375, 378, 381, 384, 388, 391, 400, 406, 427, 444, 461, 467, 473, 484, 486, 497, 500, 506, 514, 520, 522, 526, 531, 534, 537, 541, 545, 548, 550, 553, 557, 561, 564, 566, 568, 573, 584, 590, 597, 602, 606, 610, 616, 618, 625, 633, 636, 639, 658, 672, 688, 692, 703, 707, 718, 722, 729, 733, 740, 744, 749, 758, 762, 786, 803, 809, 812, 815, 825, 831, 834, 837, 845, 848, 852, 855, 869, 886, 891, 895, 901, 908, 920, 924, 927, 936, 950, 989, 997, 999, 1001, 1009, 1013, 1017, 1025, 1029, 1038, 1042, 1044, 1054, 1065, 1070, 1077, 1090, 1097, 1101, 1113, 1119, 1122, 1129, 1141, 1147, 1151, 1157, 1164, 1173, 1177, 1187, 1189, 1192, 1200, 1205, 1215, 1220, 1232, 1238, 1248, 1254, 1264] \ No newline at end of file diff --git a/hogql_parser/HogQLParserBaseVisitor.h b/hogql_parser/HogQLParserBaseVisitor.h index 5db41f7dc515f..6209109ea76c3 100644 --- a/hogql_parser/HogQLParserBaseVisitor.h +++ b/hogql_parser/HogQLParserBaseVisitor.h @@ -55,6 +55,10 @@ class HogQLParserBaseVisitor : public HogQLParserVisitor { return visitChildren(ctx); } + virtual std::any visitForInStmt(HogQLParser::ForInStmtContext *ctx) override { + return visitChildren(ctx); + } + virtual std::any visitFuncStmt(HogQLParser::FuncStmtContext *ctx) override { return visitChildren(ctx); } diff --git a/hogql_parser/HogQLParserVisitor.h b/hogql_parser/HogQLParserVisitor.h index e85c6871511bc..cbac4ba6d71b5 100644 --- a/hogql_parser/HogQLParserVisitor.h +++ b/hogql_parser/HogQLParserVisitor.h @@ -39,6 +39,8 @@ class HogQLParserVisitor : public antlr4::tree::AbstractParseTreeVisitor { virtual std::any visitForStmt(HogQLParser::ForStmtContext *context) = 0; + virtual std::any visitForInStmt(HogQLParser::ForInStmtContext *context) = 0; + virtual std::any visitFuncStmt(HogQLParser::FuncStmtContext *context) = 0; virtual std::any visitVarAssignment(HogQLParser::VarAssignmentContext *context) = 0; diff --git a/hogql_parser/parser.cpp b/hogql_parser/parser.cpp index 54a11ce5d3ca0..4b2b9684909e1 100644 --- a/hogql_parser/parser.cpp +++ b/hogql_parser/parser.cpp @@ -388,6 +388,11 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor { return visit(for_stmt_ctx); } + auto for_in_stmt_ctx = ctx->forInStmt(); + if (for_in_stmt_ctx) { + return visit(for_in_stmt_ctx); + } + auto func_stmt_ctx = ctx->funcStmt(); if (func_stmt_ctx) { return visit(func_stmt_ctx); @@ -413,8 +418,8 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor { return visit(empty_stmt_ctx); } - throw ParsingError("Statement must be one of returnStmt, ifStmt, whileStmt, forStmt, funcStmt, varAssignment, " - "block, exprStmt, or emptyStmt"); + throw ParsingError("Statement must be one of returnStmt, ifStmt, whileStmt, forStmt, forInStmt, funcStmt, " + "varAssignment, block, exprStmt, or emptyStmt"); } VISIT(ExprStmt) { @@ -580,6 +585,43 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor { return ret; } + VISIT(ForInStmt) { + string first_identifier = visitAsString(ctx->identifier(0)); + string second_identifier; + if (ctx->identifier(1)) { + second_identifier = visitAsString(ctx->identifier(1)); + } + PyObject* expr = visitAsPyObject(ctx->expression()); + PyObject* body; + try { + body = visitAsPyObject(ctx->statement()); + } catch (...) { + Py_DECREF(expr); + throw; + } + PyObject* ret = second_identifier.empty() + ? build_ast_node( + "ForInStatement", "{s:O,s:s#,s:N,s:N}", + "keyVar", Py_None, + "valueVar", first_identifier.data(), first_identifier.size(), + "expr", expr, + "body", body + ) + : build_ast_node( + "ForInStatement", "{s:s#,s:s#,s:N,s:N}", + "keyVar", first_identifier.data(), first_identifier.size(), + "valueVar", second_identifier.data(), second_identifier.size(), + "expr", expr, + "body", body + ); + if (!ret) { + Py_DECREF(expr); + Py_DECREF(body); + throw PyInternalError(); + } + return ret; + } + VISIT(FuncStmt) { PyObject* params; string name = visitAsString(ctx->identifier()); diff --git a/hogql_parser/setup.py b/hogql_parser/setup.py index 4f33b7f420a5d..b7b81c6cf6fc6 100644 --- a/hogql_parser/setup.py +++ b/hogql_parser/setup.py @@ -32,7 +32,7 @@ setup( name="hogql_parser", - version="1.0.27", + version="1.0.28", url="https://github.com/PostHog/posthog/tree/master/hogql_parser", author="PostHog Inc.", author_email="hey@posthog.com", diff --git a/hogvm/__tests__/__snapshots__/keysValues.hoge b/hogvm/__tests__/__snapshots__/keysValues.hoge new file mode 100644 index 0000000000000..594b0869a792b --- /dev/null +++ b/hogvm/__tests__/__snapshots__/keysValues.hoge @@ -0,0 +1,4 @@ +["_h", 33, 3, 33, 4, 33, 5, 43, 3, 33, 3, 33, 4, 33, 5, 44, 3, 32, "key", 32, "value", 32, "other", 32, "val", 42, 2, +32, ">> A", 2, "print", 1, 35, 36, 0, 2, "keys", 1, 2, "print", 1, 35, 36, 0, 2, "values", 1, 2, "print", 1, 35, 32, +">> B", 2, "print", 1, 35, 36, 1, 2, "keys", 1, 2, "print", 1, 35, 36, 1, 2, "values", 1, 2, "print", 1, 35, 32, ">> C", +2, "print", 1, 35, 36, 2, 2, "keys", 1, 2, "print", 1, 35, 36, 2, 2, "values", 1, 2, "print", 1, 35, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/keysValues.stdout b/hogvm/__tests__/__snapshots__/keysValues.stdout new file mode 100644 index 0000000000000..0620367d7d30b --- /dev/null +++ b/hogvm/__tests__/__snapshots__/keysValues.stdout @@ -0,0 +1,9 @@ +>> A +[0, 1, 2] +[3, 4, 5] +>> B +[0, 1, 2] +[3, 4, 5] +>> C +['key', 'other'] +['value', 'val'] diff --git a/hogvm/__tests__/__snapshots__/loops.hoge b/hogvm/__tests__/__snapshots__/loops.hoge index 4709d2037a3e6..01d7b73666780 100644 --- a/hogvm/__tests__/__snapshots__/loops.hoge +++ b/hogvm/__tests__/__snapshots__/loops.hoge @@ -2,4 +2,24 @@ 2, "print", 1, 35, 39, -22, 36, 0, 2, "print", 1, 35, 35, 32, "-- test for loop --", 2, "print", 1, 35, 33, 0, 33, 3, 36, 0, 15, 40, 15, 36, 0, 2, "print", 1, 35, 33, 1, 36, 0, 6, 37, 0, 39, -22, 35, 32, "i", 1, 1, 2, "print", 1, 35, 32, "-- test emptier for loop --", 2, "print", 1, 35, 33, 0, 33, 3, 36, 0, 15, 40, 15, 32, "woo", 2, "print", 1, 35, 33, 1, -36, 0, 6, 37, 0, 39, -22, 32, "hoo", 2, "print", 1, 35, 35] +36, 0, 6, 37, 0, 39, -22, 32, "hoo", 2, "print", 1, 35, 35, 32, "-- for in loop with arrays --", 2, "print", 1, 35, 33, +1, 33, 2, 33, 3, 43, 3, 31, 31, 31, 31, 31, 31, 36, 0, 37, 1, 36, 1, 2, "values", 1, 37, 3, 33, 0, 37, 4, 36, 3, 2, +"length", 1, 37, 5, 36, 5, 36, 4, 15, 40, 22, 36, 3, 36, 4, 45, 37, 6, 36, 6, 2, "print", 1, 35, 36, 4, 33, 1, 6, 37, 4, +39, -29, 35, 35, 35, 35, 35, 35, 35, 32, "-- for in loop with arrays and keys --", 2, "print", 1, 35, 33, 1, 33, 2, 33, +3, 43, 3, 31, 31, 31, 31, 31, 31, 31, 36, 0, 37, 1, 36, 1, 2, "keys", 1, 37, 2, 36, 1, 2, "values", 1, 37, 3, 33, 0, 37, +4, 36, 3, 2, "length", 1, 37, 5, 36, 5, 36, 4, 15, 40, 31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, 4, 45, 37, 7, 36, 7, 36, +6, 2, "print", 2, 35, 36, 4, 33, 1, 6, 37, 4, 39, -38, 35, 35, 35, 35, 35, 35, 35, 35, 32, +"-- for in loop with tuples --", 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 31, 31, 31, 31, 31, 31, 36, 0, 37, 1, +36, 1, 2, "values", 1, 37, 3, 33, 0, 37, 4, 36, 3, 2, "length", 1, 37, 5, 36, 5, 36, 4, 15, 40, 22, 36, 3, 36, 4, 45, +37, 6, 36, 6, 2, "print", 1, 35, 36, 4, 33, 1, 6, 37, 4, 39, -29, 35, 35, 35, 35, 35, 35, 35, 32, +"-- for in loop with tuples and keys --", 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 31, 31, 31, 31, 31, 31, 31, 36, +0, 37, 1, 36, 1, 2, "keys", 1, 37, 2, 36, 1, 2, "values", 1, 37, 3, 33, 0, 37, 4, 36, 3, 2, "length", 1, 37, 5, 36, 5, +36, 4, 15, 40, 31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, 4, 45, 37, 7, 36, 7, 36, 6, 2, "print", 2, 35, 36, 4, 33, 1, 6, +37, 4, 39, -38, 35, 35, 35, 35, 35, 35, 35, 35, 32, "-- for in loop with dicts --", 2, "print", 1, 35, 32, "first", 32, +"v1", 32, "second", 32, "v2", 32, "third", 32, "v3", 42, 3, 31, 31, 31, 31, 31, 31, 36, 0, 37, 1, 36, 1, 2, "values", 1, +37, 3, 33, 0, 37, 4, 36, 3, 2, "length", 1, 37, 5, 36, 5, 36, 4, 15, 40, 22, 36, 3, 36, 4, 45, 37, 6, 36, 6, 2, "print", +1, 35, 36, 4, 33, 1, 6, 37, 4, 39, -29, 35, 35, 35, 35, 35, 35, 35, 32, "-- for in loop with dicts and keys --", 2, +"print", 1, 35, 32, "first", 32, "v1", 32, "second", 32, "v2", 32, "third", 32, "v3", 42, 3, 31, 31, 31, 31, 31, 31, 31, +36, 0, 37, 1, 36, 1, 2, "keys", 1, 37, 2, 36, 1, 2, "values", 1, 37, 3, 33, 0, 37, 4, 36, 3, 2, "length", 1, 37, 5, 36, +5, 36, 4, 15, 40, 31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, 4, 45, 37, 7, 36, 7, 36, 6, 2, "print", 2, 35, 36, 4, 33, 1, +6, 37, 4, 39, -38, 35, 35, 35, 35, 35, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/loops.stdout b/hogvm/__tests__/__snapshots__/loops.stdout index a66e37d944731..270cb30b7ea47 100644 --- a/hogvm/__tests__/__snapshots__/loops.stdout +++ b/hogvm/__tests__/__snapshots__/loops.stdout @@ -13,3 +13,27 @@ woo woo woo hoo +-- for in loop with arrays -- +1 +2 +3 +-- for in loop with arrays and keys -- +0 1 +1 2 +2 3 +-- for in loop with tuples -- +1 +2 +3 +-- for in loop with tuples and keys -- +0 1 +1 2 +2 3 +-- for in loop with dicts -- +v1 +v2 +v3 +-- for in loop with dicts and keys -- +first v1 +second v2 +third v3 diff --git a/hogvm/__tests__/keysValues.hog b/hogvm/__tests__/keysValues.hog new file mode 100644 index 0000000000000..e28ed8e567a2d --- /dev/null +++ b/hogvm/__tests__/keysValues.hog @@ -0,0 +1,15 @@ +let a := [3,4,5] +let b := (3,4,5) +let c := {'key': 'value', 'other': 'val'} + +print('>> A') +print(keys(a)) +print(values(a)) + +print('>> B') +print(keys(b)) +print(values(b)) + +print('>> C') +print(keys(c)) +print(values(c)) diff --git a/hogvm/__tests__/loops.hog b/hogvm/__tests__/loops.hog index 815b44fdf3635..369baf58a2339 100644 --- a/hogvm/__tests__/loops.hog +++ b/hogvm/__tests__/loops.hog @@ -25,3 +25,53 @@ print('-- test emptier for loop --') } print('hoo') } + +print('-- for in loop with arrays --') +{ + let arr := [1, 2, 3] + for (let i in arr) { + print(i) + } +} + +print('-- for in loop with arrays and keys --') +{ + let arr := [1, 2, 3] + for (let k, v in arr) { + print(k, v) + } +} + +print('-- for in loop with tuples --') +{ + let tup := (1, 2, 3) + for (let i in tup) { + print(i) + } +} + +print('-- for in loop with tuples and keys --') +{ + let tup := (1, 2, 3) + for (let k, v in tup) { + print(k, v) + } +} + + +print('-- for in loop with dicts --') +{ + let obj := {'first': 'v1', 'second': 'v2', 'third': 'v3'} + for (let i in obj) { + print(i) + } +} + +print('-- for in loop with dicts and keys --') +{ + let obj := {'first': 'v1', 'second': 'v2', 'third': 'v3'} + for (let k, v in obj) { + print(k, v) + } +} + diff --git a/hogvm/python/execute.py b/hogvm/python/execute.py index e3273ced36e41..ac1a34cb29aad 100644 --- a/hogvm/python/execute.py +++ b/hogvm/python/execute.py @@ -46,6 +46,8 @@ def execute_bytecode( ops = 0 stdout: list[str] = [] colored_bytecode = color_bytecode(bytecode) if debug else [] + if isinstance(timeout, int): + timeout = timedelta(seconds=timeout) def next_token(): nonlocal ip diff --git a/hogvm/python/stl/__init__.py b/hogvm/python/stl/__init__.py index 8734eea8cc756..6fbc3946de71a 100644 --- a/hogvm/python/stl/__init__.py +++ b/hogvm/python/stl/__init__.py @@ -175,6 +175,24 @@ def generateUUIDv4( return str(uuid.uuid4()) +def keys(name: str, args: list[Any], team: Optional["Team"], stdout: Optional[list[str]], timeout: int) -> list: + obj = args[0] + if isinstance(obj, dict): + return list(obj.keys()) + if isinstance(obj, list) or isinstance(obj, tuple): + return list(range(len(obj))) + return [] + + +def values(name: str, args: list[Any], team: Optional["Team"], stdout: Optional[list[str]], timeout: int) -> list: + obj = args[0] + if isinstance(obj, dict): + return list(obj.values()) + if isinstance(obj, list) or isinstance(obj, tuple): + return list(obj) + return [] + + STL: dict[str, Callable[[str, list[Any], Optional["Team"], list[str] | None, int], Any]] = { "concat": concat, "match": match, @@ -202,4 +220,6 @@ def generateUUIDv4( "replaceOne": replaceOne, "replaceAll": replaceAll, "generateUUIDv4": generateUUIDv4, + "keys": keys, + "values": values, } diff --git a/hogvm/typescript/package.json b/hogvm/typescript/package.json index fe48cdcf4ae49..9740e6e83a505 100644 --- a/hogvm/typescript/package.json +++ b/hogvm/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@posthog/hogvm", - "version": "1.0.21", + "version": "1.0.22", "description": "PostHog Hog Virtual Machine", "types": "dist/index.d.ts", "main": "dist/index.js", diff --git a/hogvm/typescript/src/stl/stl.ts b/hogvm/typescript/src/stl/stl.ts index 3e58deb0f7aff..76e8a5cb069d4 100644 --- a/hogvm/typescript/src/stl/stl.ts +++ b/hogvm/typescript/src/stl/stl.ts @@ -149,6 +149,32 @@ export const STL: Record return v.toString(16) }) }, + keys(args) { + const obj = args[0] + if (typeof obj === 'object') { + if (Array.isArray(obj)) { + return Array.from(obj.keys()) + } else if (obj instanceof Map) { + return Array.from(obj.keys()) + } else { + return Object.keys(obj) + } + } + return [] + }, + values(args) { + const obj = args[0] + if (typeof obj === 'object') { + if (Array.isArray(obj)) { + return [...obj] + } else if (obj instanceof Map) { + return Array.from(obj.values()) + } else { + return Object.values(obj) + } + } + return [] + }, } export const ASYNC_STL: Record Promise> = { diff --git a/latest_migrations.manifest b/latest_migrations.manifest index 3e86dbba9fafa..cb99297c807a7 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name ee: 0016_rolemembership_organization_member otp_static: 0002_throttling otp_totp: 0002_auto_20190420_0723 -posthog: 0434_add_web_vitals_opt_in +posthog: 0437_externaldataschema_sync_frequency sessions: 0001_initial social_django: 0010_uid_db_index two_factor: 0007_auto_20201201_1019 diff --git a/mypy-baseline.txt b/mypy-baseline.txt index 8ddefdefc0922..26f6ae76341bc 100644 --- a/mypy-baseline.txt +++ b/mypy-baseline.txt @@ -250,7 +250,6 @@ posthog/test/base.py:0: error: Incompatible types in assignment (expression has posthog/test/base.py:0: error: Incompatible types in assignment (expression has type "None", variable has type "OrganizationMembership") [assignment] posthog/test/base.py:0: error: "MemoryLeakTestMixin" has no attribute "assertLessEqual" [attr-defined] posthog/test/base.py:0: error: "MemoryLeakTestMixin" has no attribute "assertLessEqual" [attr-defined] -posthog/test/base.py:0: error: Argument "table_column" to "materialize" has incompatible type "str"; expected "Literal['properties', 'group_properties', 'person_properties', 'group0_properties', 'group1_properties', 'group2_properties', 'group3_properties', 'group4_properties']" [arg-type] posthog/test/base.py:0: error: Item "None" of "FrameType | None" has no attribute "f_back" [union-attr] posthog/test/base.py:0: error: Item "None" of "FrameType | Any | None" has no attribute "f_locals" [union-attr] posthog/test/base.py:0: error: Item "None" of "AppConfig | None" has no attribute "name" [union-attr] @@ -607,10 +606,6 @@ posthog/api/test/test_decide.py:0: error: Item "None" of "Any | None" has no att posthog/api/test/test_decide.py:0: error: Item "None" of "Any | None" has no attribute "save" [union-attr] posthog/api/plugin.py:0: error: Item "None" of "IO[Any] | None" has no attribute "read" [union-attr] posthog/api/plugin.py:0: error: Item "None" of "IO[Any] | None" has no attribute "read" [union-attr] -posthog/warehouse/external_data_source/source.py:0: error: Incompatible types in assignment (expression has type "int", target has type "str") [assignment] -posthog/warehouse/external_data_source/source.py:0: error: Incompatible types in assignment (expression has type "int", target has type "str") [assignment] -posthog/warehouse/external_data_source/source.py:0: error: Incompatible types in assignment (expression has type "dict[str, Collection[str]]", variable has type "StripeSourcePayload") [assignment] -posthog/warehouse/external_data_source/source.py:0: error: Argument 1 to "_create_source" has incompatible type "StripeSourcePayload"; expected "dict[Any, Any]" [arg-type] posthog/temporal/tests/batch_exports/test_snowflake_batch_export_workflow.py:0: error: Need type annotation for "_execute_calls" (hint: "_execute_calls: list[] = ...") [var-annotated] posthog/temporal/tests/batch_exports/test_snowflake_batch_export_workflow.py:0: error: Need type annotation for "_execute_async_calls" (hint: "_execute_async_calls: list[] = ...") [var-annotated] posthog/temporal/tests/batch_exports/test_snowflake_batch_export_workflow.py:0: error: Need type annotation for "_cursors" (hint: "_cursors: list[] = ...") [var-annotated] @@ -624,6 +619,10 @@ posthog/api/plugin_log_entry.py:0: error: Module "django.utils.timezone" does no posthog/api/plugin_log_entry.py:0: error: Name "timezone.datetime" is not defined [name-defined] posthog/api/plugin_log_entry.py:0: error: Module "django.utils.timezone" does not explicitly export attribute "datetime" [attr-defined] posthog/api/action.py:0: error: Argument 1 to has incompatible type "*tuple[str, ...]"; expected "type[BaseRenderer]" [arg-type] +posthog/warehouse/external_data_source/source.py:0: error: Incompatible types in assignment (expression has type "int", target has type "str") [assignment] +posthog/warehouse/external_data_source/source.py:0: error: Incompatible types in assignment (expression has type "int", target has type "str") [assignment] +posthog/warehouse/external_data_source/source.py:0: error: Incompatible types in assignment (expression has type "dict[str, Collection[str]]", variable has type "StripeSourcePayload") [assignment] +posthog/warehouse/external_data_source/source.py:0: error: Argument 1 to "_create_source" has incompatible type "StripeSourcePayload"; expected "dict[Any, Any]" [arg-type] posthog/temporal/tests/batch_exports/test_redshift_batch_export_workflow.py:0: error: Incompatible types in assignment (expression has type "str | int", variable has type "int") [assignment] posthog/api/test/batch_exports/conftest.py:0: error: Argument "activities" to "ThreadedWorker" has incompatible type "list[function]"; expected "Sequence[Callable[..., Any]]" [arg-type] posthog/temporal/tests/data_imports/test_end_to_end.py:0: error: Unused "type: ignore" comment [unused-ignore] @@ -679,5 +678,3 @@ posthog/api/test/batch_exports/test_update.py:0: error: Value of type "BatchExpo posthog/api/test/batch_exports/test_update.py:0: error: Value of type "BatchExport" is not indexable [index] posthog/api/test/batch_exports/test_update.py:0: error: Value of type "BatchExport" is not indexable [index] posthog/api/test/batch_exports/test_pause.py:0: error: "batch_export_delete_schedule" does not return a value (it only ever returns None) [func-returns-value] -posthog/api/test/batch_exports/test_log_entry.py:0: error: Need type annotation for "results" (hint: "results: list[] = ...") [var-annotated] -posthog/api/test/batch_exports/test_log_entry.py:0: error: Need type annotation for "results" (hint: "results: list[] = ...") [var-annotated] diff --git a/plugin-server/functional_tests/analytics-ingestion/happy-path.test.ts b/plugin-server/functional_tests/analytics-ingestion/happy-path.test.ts index 98806e6aebafd..c1e45d476eb09 100644 --- a/plugin-server/functional_tests/analytics-ingestion/happy-path.test.ts +++ b/plugin-server/functional_tests/analytics-ingestion/happy-path.test.ts @@ -618,9 +618,7 @@ test.concurrent(`properties still $set even if merge fails`, async () => { }) }) -const testIfPoEEmbraceJoinEnabled = - process.env.POE_EMBRACE_JOIN_FOR_TEAMS === '*' ? test.concurrent : test.concurrent.skip -testIfPoEEmbraceJoinEnabled(`single merge results in all events resolving to the same person id`, async () => { +test.concurrent(`single merge results in all events resolving to the same person id`, async () => { const teamId = await createTeam(organizationId) const initialDistinctId = new UUIDT().toString() const secondDistinctId = new UUIDT().toString() @@ -680,7 +678,7 @@ testIfPoEEmbraceJoinEnabled(`single merge results in all events resolving to the }, 10000) }) -testIfPoEEmbraceJoinEnabled(`chained merge results in all events resolving to the same person id`, async () => { +test.concurrent(`chained merge results in all events resolving to the same person id`, async () => { const teamId = await createTeam(organizationId) const initialDistinctId = new UUIDT().toString() const secondDistinctId = new UUIDT().toString() @@ -735,76 +733,73 @@ testIfPoEEmbraceJoinEnabled(`chained merge results in all events resolving to th }, 20000) }) -testIfPoEEmbraceJoinEnabled( - `complex chained merge adds results in all events resolving to the same person id`, - async () => { - // let's assume we have 4 persons 1234, we'll first merge 1-2 & 3-4, then we'll merge 2-3 - // this should still result in all events having the same person_id or override[person_id] +test.concurrent(`complex chained merge adds results in all events resolving to the same person id`, async () => { + // let's assume we have 4 persons 1234, we'll first merge 1-2 & 3-4, then we'll merge 2-3 + // this should still result in all events having the same person_id or override[person_id] - const teamId = await createTeam(organizationId) - const initialDistinctId = new UUIDT().toString() - const secondDistinctId = new UUIDT().toString() - const thirdDistinctId = new UUIDT().toString() - const forthDistinctId = new UUIDT().toString() - - // First we emit anoymous events and wait for the persons to be created. - await capture({ teamId, distinctId: initialDistinctId, uuid: new UUIDT().toString(), event: 'custom event' }) - await capture({ teamId, distinctId: secondDistinctId, uuid: new UUIDT().toString(), event: 'custom event 2' }) - await capture({ teamId, distinctId: thirdDistinctId, uuid: new UUIDT().toString(), event: 'custom event 3' }) - await capture({ teamId, distinctId: forthDistinctId, uuid: new UUIDT().toString(), event: 'custom event 3' }) - await waitForExpect(async () => { - const persons = await fetchPersons(teamId) - expect(persons.length).toBe(4) - }, 10000) + const teamId = await createTeam(organizationId) + const initialDistinctId = new UUIDT().toString() + const secondDistinctId = new UUIDT().toString() + const thirdDistinctId = new UUIDT().toString() + const forthDistinctId = new UUIDT().toString() - // Then we identify 1-2 and 3-4 - await capture({ - teamId, - distinctId: initialDistinctId, - uuid: new UUIDT().toString(), - event: '$identify', - properties: { - distinct_id: initialDistinctId, - $anon_distinct_id: secondDistinctId, - }, - }) - await capture({ - teamId, - distinctId: thirdDistinctId, - uuid: new UUIDT().toString(), - event: '$identify', - properties: { - distinct_id: thirdDistinctId, - $anon_distinct_id: forthDistinctId, - }, - }) + // First we emit anoymous events and wait for the persons to be created. + await capture({ teamId, distinctId: initialDistinctId, uuid: new UUIDT().toString(), event: 'custom event' }) + await capture({ teamId, distinctId: secondDistinctId, uuid: new UUIDT().toString(), event: 'custom event 2' }) + await capture({ teamId, distinctId: thirdDistinctId, uuid: new UUIDT().toString(), event: 'custom event 3' }) + await capture({ teamId, distinctId: forthDistinctId, uuid: new UUIDT().toString(), event: 'custom event 3' }) + await waitForExpect(async () => { + const persons = await fetchPersons(teamId) + expect(persons.length).toBe(4) + }, 10000) - await waitForExpect(async () => { - const events = await fetchEvents(teamId) - expect(events.length).toBe(6) - }, 10000) + // Then we identify 1-2 and 3-4 + await capture({ + teamId, + distinctId: initialDistinctId, + uuid: new UUIDT().toString(), + event: '$identify', + properties: { + distinct_id: initialDistinctId, + $anon_distinct_id: secondDistinctId, + }, + }) + await capture({ + teamId, + distinctId: thirdDistinctId, + uuid: new UUIDT().toString(), + event: '$identify', + properties: { + distinct_id: thirdDistinctId, + $anon_distinct_id: forthDistinctId, + }, + }) - // Then we merge 2-3 - await capture({ - teamId, - distinctId: initialDistinctId, - uuid: new UUIDT().toString(), - event: '$merge_dangerously', - properties: { - distinct_id: secondDistinctId, - alias: thirdDistinctId, - }, - }) + await waitForExpect(async () => { + const events = await fetchEvents(teamId) + expect(events.length).toBe(6) + }, 10000) - await waitForExpect(async () => { - const events = await fetchEvents(teamId) - expect(events.length).toBe(7) - expect(events[0].person_id).toBeDefined() - expect(events[0].person_id).not.toBe('00000000-0000-0000-0000-000000000000') - expect(new Set(events.map((event) => event.person_id)).size).toBe(1) - }, 20000) - } -) + // Then we merge 2-3 + await capture({ + teamId, + distinctId: initialDistinctId, + uuid: new UUIDT().toString(), + event: '$merge_dangerously', + properties: { + distinct_id: secondDistinctId, + alias: thirdDistinctId, + }, + }) + + await waitForExpect(async () => { + const events = await fetchEvents(teamId) + expect(events.length).toBe(7) + expect(events[0].person_id).toBeDefined() + expect(events[0].person_id).not.toBe('00000000-0000-0000-0000-000000000000') + expect(new Set(events.map((event) => event.person_id)).size).toBe(1) + }, 20000) +}) // TODO: adjust this test to poEEmbraceJoin test.skip(`person properties don't see properties from descendents`, async () => { diff --git a/plugin-server/functional_tests/api.ts b/plugin-server/functional_tests/api.ts index 4e6c01c37385f..35e728843dbb2 100644 --- a/plugin-server/functional_tests/api.ts +++ b/plugin-server/functional_tests/api.ts @@ -271,12 +271,14 @@ export const fetchEvents = async (teamId: number, uuid?: string) => { SELECT *, if(notEmpty(overrides.person_id), overrides.person_id, e.person_id) as person_id FROM events e - LEFT OUTER JOIN - (SELECT argMax(override_person_id, version) as person_id, - old_person_id - FROM person_overrides + LEFT OUTER JOIN ( + SELECT + distinct_id, + argMax(person_id, version) as person_id + FROM person_distinct_id_overrides WHERE team_id = ${teamId} - GROUP BY old_person_id) AS overrides ON e.person_id = overrides.old_person_id + GROUP BY distinct_id + ) AS overrides USING distinct_id WHERE team_id = ${teamId} ${uuid ? `AND uuid = '${uuid}'` : ``} ORDER BY timestamp ASC `)) as unknown as ClickHouse.ObjectQueryResult diff --git a/plugin-server/package.json b/plugin-server/package.json index 81a2e9603bb18..216334dd5e0b0 100644 --- a/plugin-server/package.json +++ b/plugin-server/package.json @@ -50,7 +50,7 @@ "@google-cloud/storage": "^5.8.5", "@maxmind/geoip2-node": "^3.4.0", "@posthog/clickhouse": "^1.7.0", - "@posthog/hogvm": "^1.0.21", + "@posthog/hogvm": "^1.0.22", "@posthog/plugin-scaffold": "1.4.4", "@sentry/node": "^7.49.0", "@sentry/profiling-node": "^0.3.0", diff --git a/plugin-server/pnpm-lock.yaml b/plugin-server/pnpm-lock.yaml index 68484afdf5b52..c1b93c5becf57 100644 --- a/plugin-server/pnpm-lock.yaml +++ b/plugin-server/pnpm-lock.yaml @@ -44,8 +44,8 @@ dependencies: specifier: ^1.7.0 version: 1.7.0 '@posthog/hogvm': - specifier: ^1.0.21 - version: 1.0.21(re2@1.20.3) + specifier: ^1.0.22 + version: 1.0.22(re2@1.20.3) '@posthog/plugin-scaffold': specifier: 1.4.4 version: 1.4.4 @@ -3110,8 +3110,8 @@ packages: engines: {node: '>=12'} dev: false - /@posthog/hogvm@1.0.21(re2@1.20.3): - resolution: {integrity: sha512-bCXsjUJfB4kekX3mb6CkzuxfBRjFy/t2sKK8hznf8pdUTl6Hc8fahPUY/Zd+8fAPbQZhtkzHJATqUqHSYcyjgA==} + /@posthog/hogvm@1.0.22(re2@1.20.3): + resolution: {integrity: sha512-nTNz7RXmOMAbsTPSpejE7eAsjoLbn8RJv/db//r/k+FwwnYwKcKkw4ix/8vhsiT7V8dXdGm7I8U2kMRd74dbeQ==} peerDependencies: re2: ^1.21.3 dependencies: diff --git a/plugin-server/src/capabilities.ts b/plugin-server/src/capabilities.ts index 4d971e4709cf7..8f45d91d9cc75 100644 --- a/plugin-server/src/capabilities.ts +++ b/plugin-server/src/capabilities.ts @@ -20,7 +20,6 @@ export function getPluginServerCapabilities(config: PluginsServerConfig): Plugin processAsyncWebhooksHandlers: true, sessionRecordingBlobIngestion: true, sessionRecordingBlobOverflowIngestion: config.SESSION_RECORDING_OVERFLOW_ENABLED, - personOverrides: true, appManagementSingleton: true, preflightSchedules: true, cdpProcessedEvents: true, @@ -85,12 +84,6 @@ export function getPluginServerCapabilities(config: PluginsServerConfig): Plugin appManagementSingleton: true, ...sharedCapabilities, } - case PluginServerMode.person_overrides: - return { - personOverrides: true, - ...sharedCapabilities, - } - case PluginServerMode.cdp_processed_events: return { cdpProcessedEvents: true, diff --git a/plugin-server/src/config/config.ts b/plugin-server/src/config/config.ts index f3d1a47eaac29..17feeaeb1b2c9 100644 --- a/plugin-server/src/config/config.ts +++ b/plugin-server/src/config/config.ts @@ -128,9 +128,6 @@ export function getDefaultConfig(): PluginsServerConfig { EXTERNAL_REQUEST_TIMEOUT_MS: 10 * 1000, // 10 seconds DROP_EVENTS_BY_TOKEN_DISTINCT_ID: '', DROP_EVENTS_BY_TOKEN: '', - POE_EMBRACE_JOIN_FOR_TEAMS: '', - POE_WRITES_ENABLED_MAX_TEAM_ID: 0, - POE_WRITES_EXCLUDE_TEAMS: '', PIPELINE_STEP_STALLED_LOG_TIMEOUT: 30, RELOAD_PLUGIN_JITTER_MAX_MS: 60000, RUSTY_HOOK_FOR_TEAMS: '', @@ -165,7 +162,7 @@ export function getDefaultConfig(): PluginsServerConfig { POSTHOG_SESSION_RECORDING_REDIS_PORT: undefined, SESSION_RECORDING_CONSOLE_LOGS_INGESTION_ENABLED: true, SESSION_RECORDING_REPLAY_EVENTS_INGESTION_ENABLED: true, - SESSION_RECORDING_DEBUG_PARTITION: undefined, + SESSION_RECORDING_DEBUG_PARTITION: '', SESSION_RECORDING_KAFKA_DEBUG: undefined, SESSION_RECORDING_MAX_PARALLEL_FLUSHES: 10, SESSION_RECORDING_OVERFLOW_ENABLED: false, diff --git a/plugin-server/src/config/kafka-topics.ts b/plugin-server/src/config/kafka-topics.ts index 4865693e81112..83ca0f07ea424 100644 --- a/plugin-server/src/config/kafka-topics.ts +++ b/plugin-server/src/config/kafka-topics.ts @@ -25,7 +25,6 @@ export const KAFKA_JOBS_DLQ = `${prefix}jobs_dlq${suffix}` export const KAFKA_SCHEDULED_TASKS = `${prefix}scheduled_tasks${suffix}` export const KAFKA_SCHEDULED_TASKS_DLQ = `${prefix}scheduled_tasks_dlq${suffix}` export const KAFKA_METRICS_TIME_TO_SEE_DATA = `${prefix}clickhouse_metrics_time_to_see_data${suffix}` -export const KAFKA_PERSON_OVERRIDE = `${prefix}clickhouse_person_override${suffix}` // read session recording snapshot items export const KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_EVENTS = `${prefix}session_recording_snapshot_item_events${suffix}` diff --git a/plugin-server/src/kafka/batch-consumer.ts b/plugin-server/src/kafka/batch-consumer.ts index a286c50b5ac94..4f86bd9cc77e9 100644 --- a/plugin-server/src/kafka/batch-consumer.ts +++ b/plugin-server/src/kafka/batch-consumer.ts @@ -184,7 +184,7 @@ export const startBatchConsumer = async ({ } if (debug) { - // NOTE: If the key exists with value undefined the consumer will throw which is annoying so we define it here instead + // NOTE: If the key exists with value undefined the consumer will throw which is annoying, so we define it here instead consumerConfig.debug = debug } diff --git a/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts b/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts index c249f0027d738..1e3b9ec315996 100644 --- a/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts +++ b/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts @@ -5,7 +5,7 @@ import { mkdirSync, rmSync } from 'node:fs' import { CODES, features, KafkaConsumer, librdkafkaVersion, Message, TopicPartition } from 'node-rdkafka' import { Counter, Gauge, Histogram, Summary } from 'prom-client' -import { sessionRecordingConsumerConfig } from '../../../config/config' +import { buildIntegerMatcher, sessionRecordingConsumerConfig } from '../../../config/config' import { KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_EVENTS, KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_OVERFLOW, @@ -13,7 +13,7 @@ import { import { BatchConsumer, startBatchConsumer } from '../../../kafka/batch-consumer' import { createRdConnectionConfigFromEnvVars, createRdProducerConfigFromEnvVars } from '../../../kafka/config' import { createKafkaProducer } from '../../../kafka/producer' -import { PluginsServerConfig, RedisPool, TeamId } from '../../../types' +import { PluginsServerConfig, RedisPool, TeamId, ValueMatcher } from '../../../types' import { BackgroundRefresher } from '../../../utils/background-refresher' import { KafkaProducerWrapper } from '../../../utils/db/kafka-producer-wrapper' import { PostgresRouter } from '../../../utils/db/postgres' @@ -155,6 +155,7 @@ export class SessionRecordingIngester { private debugPartition: number | undefined = undefined private sharedClusterProducerWrapper: KafkaProducerWrapper | undefined = undefined + private isDebugLoggingEnabled: ValueMatcher constructor( private globalServerConfig: PluginsServerConfig, @@ -163,9 +164,8 @@ export class SessionRecordingIngester { private consumeOverflow: boolean, captureRedis: Redis | undefined ) { - this.debugPartition = globalServerConfig.SESSION_RECORDING_DEBUG_PARTITION - ? parseInt(globalServerConfig.SESSION_RECORDING_DEBUG_PARTITION) - : undefined + this.isDebugLoggingEnabled = buildIntegerMatcher(globalServerConfig.SESSION_RECORDING_DEBUG_PARTITION, true) + this.topic = consumeOverflow ? KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_OVERFLOW : KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_EVENTS @@ -270,11 +270,8 @@ export class SessionRecordingIngester { const { team_id, session_id } = event const key = `${team_id}-${session_id}` - const { - // partition, - highOffset, - } = event.metadata - const isDebug = true // this.debugPartition === partition + const { partition, highOffset } = event.metadata + const isDebug = this.isDebugLoggingEnabled(partition) if (isDebug) { status.info('🔁', '[blob_ingester_consumer] - [PARTITION DEBUG] - consuming event', { ...event.metadata, @@ -323,7 +320,7 @@ export class SessionRecordingIngester { session_id, partition, topic, - this.debugPartition === partition + this.isDebugLoggingEnabled(partition) ) } @@ -810,7 +807,7 @@ export class SessionRecordingIngester { : metrics.lastMessageOffset // Or the last message we have seen as it is no longer blocked if (!highestOffsetToCommit) { - const partitionDebug = true //this.debugPartition === partition + const partitionDebug = this.isDebugLoggingEnabled(partition) const logMethod = partitionDebug ? status.info : status.debug logMethod( '🤔', diff --git a/plugin-server/src/main/pluginsServer.ts b/plugin-server/src/main/pluginsServer.ts index 8921aa4b2e094..54ddfaff521ba 100644 --- a/plugin-server/src/main/pluginsServer.ts +++ b/plugin-server/src/main/pluginsServer.ts @@ -17,7 +17,6 @@ import { Hub, PluginServerCapabilities, PluginsServerConfig } from '../types' import { createHub, createKafkaClient, createKafkaProducerWrapper } from '../utils/db/hub' import { PostgresRouter } from '../utils/db/postgres' import { cancelAllScheduledJobs } from '../utils/node-schedule' -import { PeriodicTask } from '../utils/periodic-task' import { PubSub } from '../utils/pubsub' import { status } from '../utils/status' import { createRedisClient, delay } from '../utils/utils' @@ -26,7 +25,6 @@ import { ActionMatcher } from '../worker/ingestion/action-matcher' import { AppMetrics } from '../worker/ingestion/app-metrics' import { GroupTypeManager } from '../worker/ingestion/group-type-manager' import { OrganizationManager } from '../worker/ingestion/organization-manager' -import { DeferredPersonOverrideWorker, FlatPersonOverrideWriter } from '../worker/ingestion/person-state' import { TeamManager } from '../worker/ingestion/team-manager' import Piscina, { makePiscina as defaultMakePiscina } from '../worker/piscina' import { RustyHook } from '../worker/rusty-hook' @@ -119,8 +117,6 @@ export async function startPluginsServer( let jobsConsumer: Consumer | undefined let schedulerTasksConsumer: Consumer | undefined - let personOverridesPeriodicTask: PeriodicTask | undefined - let httpServer: Server | undefined // server let graphileWorker: GraphileWorker | undefined @@ -160,7 +156,6 @@ export async function startPluginsServer( stopSessionRecordingBlobConsumer?.(), stopSessionRecordingBlobOverflowConsumer?.(), schedulerTasksConsumer?.disconnect(), - personOverridesPeriodicTask?.stop(), ...shutdownCallbacks.map((cb) => cb()), ]) @@ -529,22 +524,6 @@ export async function startPluginsServer( healthChecks['cdp-overflow'] = () => consumer.isHealthy() ?? false } - if (capabilities.personOverrides) { - const postgres = hub?.postgres ?? new PostgresRouter(serverConfig) - const kafkaProducer = hub?.kafkaProducer ?? (await createKafkaProducerWrapper(serverConfig)) - - personOverridesPeriodicTask = new DeferredPersonOverrideWorker( - postgres, - kafkaProducer, - new FlatPersonOverrideWriter(postgres) - ).runTask(5000) - personOverridesPeriodicTask.promise.catch(async () => { - status.error('⚠️', 'Person override worker task crashed! Requesting shutdown...') - await closeJobs() - process.exit(1) - }) - } - if (capabilities.http) { const app = setupCommonRoutes(healthChecks, analyticsEventsIngestionConsumer) diff --git a/plugin-server/src/types.ts b/plugin-server/src/types.ts index 6eb7b4bb102e5..29672a19db6fc 100644 --- a/plugin-server/src/types.ts +++ b/plugin-server/src/types.ts @@ -81,7 +81,6 @@ export enum PluginServerMode { analytics_ingestion = 'analytics-ingestion', recordings_blob_ingestion = 'recordings-blob-ingestion', recordings_blob_ingestion_overflow = 'recordings-blob-ingestion-overflow', - person_overrides = 'person-overrides', cdp_processed_events = 'cdp-processed-events', cdp_function_callbacks = 'cdp-function-callbacks', cdp_function_overflow = 'cdp-function-overflow', @@ -216,9 +215,6 @@ export interface PluginsServerConfig extends CdpConfig { EXTERNAL_REQUEST_TIMEOUT_MS: number DROP_EVENTS_BY_TOKEN_DISTINCT_ID: string DROP_EVENTS_BY_TOKEN: string - POE_EMBRACE_JOIN_FOR_TEAMS: string - POE_WRITES_ENABLED_MAX_TEAM_ID: number - POE_WRITES_EXCLUDE_TEAMS: string RELOAD_PLUGIN_JITTER_MAX_MS: number RUSTY_HOOK_FOR_TEAMS: string RUSTY_HOOK_ROLLOUT_PERCENTAGE: number @@ -248,6 +244,7 @@ export interface PluginsServerConfig extends CdpConfig { SESSION_RECORDING_REPLAY_EVENTS_INGESTION_ENABLED: boolean // a single partition which will output many more log messages to the console // useful when that partition is lagging unexpectedly + // allows comma separated list of partition numbers or '*' for all SESSION_RECORDING_DEBUG_PARTITION: string | undefined // overflow detection, updating Redis for capture to move the traffic away SESSION_RECORDING_OVERFLOW_ENABLED: boolean @@ -316,8 +313,6 @@ export interface Hub extends PluginsServerConfig { enqueuePluginJob: (job: EnqueuedPluginJob) => Promise // ValueMatchers used for various opt-in/out features pluginConfigsToSkipElementsParsing: ValueMatcher - poeEmbraceJoinForTeams: ValueMatcher - poeWritesExcludeTeams: ValueMatcher // lookups eventsToDropByToken: Map } @@ -337,7 +332,6 @@ export interface PluginServerCapabilities { cdpProcessedEvents?: boolean cdpFunctionCallbacks?: boolean cdpFunctionOverflow?: boolean - personOverrides?: boolean appManagementSingleton?: boolean preflightSchedules?: boolean // Used for instance health checks on hobby deploy, not useful on cloud http?: boolean diff --git a/plugin-server/src/utils/db/hub.ts b/plugin-server/src/utils/db/hub.ts index d0084c7087cd7..69ad05296abf0 100644 --- a/plugin-server/src/utils/db/hub.ts +++ b/plugin-server/src/utils/db/hub.ts @@ -205,8 +205,6 @@ export async function createHub( actionManager, conversionBufferEnabledTeams, pluginConfigsToSkipElementsParsing: buildIntegerMatcher(process.env.SKIP_ELEMENTS_PARSING_PLUGINS, true), - poeEmbraceJoinForTeams: buildIntegerMatcher(process.env.POE_EMBRACE_JOIN_FOR_TEAMS, true), - poeWritesExcludeTeams: buildIntegerMatcher(process.env.POE_WRITES_EXCLUDE_TEAMS, false), eventsToDropByToken: createEventsToDropByToken(process.env.DROP_EVENTS_BY_TOKEN_DISTINCT_ID), } diff --git a/plugin-server/src/worker/ingestion/event-pipeline/enrichExceptionEventStep.ts b/plugin-server/src/worker/ingestion/event-pipeline/enrichExceptionEventStep.ts new file mode 100644 index 0000000000000..37c6404d23df9 --- /dev/null +++ b/plugin-server/src/worker/ingestion/event-pipeline/enrichExceptionEventStep.ts @@ -0,0 +1,75 @@ +import { captureException } from '@sentry/node' +import { Counter } from 'prom-client' + +import { PreIngestionEvent } from '../../../types' +import { EventPipelineRunner } from './runner' + +const EXTERNAL_FINGERPRINT_COUNTER = new Counter({ + name: 'enrich_exception_events_external_fingerprint', + help: 'Counter for exceptions that already have a fingerprint', +}) + +const COULD_NOT_PARSE_STACK_TRACE_COUNTER = new Counter({ + name: 'enrich_exception_events_could_not_parse_stack_trace', + help: 'Counter for exceptions where the stack trace could not be parsed', +}) + +const COULD_NOT_PREPARE_FOR_FINGERPRINTING_COUNTER = new Counter({ + name: 'enrich_exception_events_could_not_prepare_for_fingerprinting', + help: 'Counter for exceptions where the event could not be prepared for fingerprinting', +}) + +const EXCEPTIONS_ENRICHED_COUNTER = new Counter({ + name: 'enrich_exception_events_enriched', + help: 'Counter for exceptions that have been enriched', +}) + +export function enrichExceptionEventStep( + _runner: EventPipelineRunner, + event: PreIngestionEvent +): Promise { + if (event.event !== '$exception') { + return Promise.resolve(event) + } + + let type: string | null = null + let message: string | null = null + let firstFunction: string | null = null + let exceptionStack: string | null = null + + try { + exceptionStack = event.properties['$exception_stack_trace_raw'] + const fingerPrint = event.properties['$exception_fingerprint'] + type = event.properties['$exception_type'] + message = event.properties['$exception_message'] + + if (fingerPrint) { + EXTERNAL_FINGERPRINT_COUNTER.inc() + return Promise.resolve(event) + } + } catch (e) { + captureException(e) + COULD_NOT_PREPARE_FOR_FINGERPRINTING_COUNTER.inc() + } + + try { + if (exceptionStack) { + const parsedStack = JSON.parse(exceptionStack) + if (parsedStack.length > 0) { + firstFunction = parsedStack[0].function + } + } + } catch (e) { + captureException(e) + COULD_NOT_PARSE_STACK_TRACE_COUNTER.inc() + } + + const fingerprint = [type, message, firstFunction].filter(Boolean) + event.properties['$exception_fingerprint'] = fingerprint.length ? fingerprint : undefined + + if (event.properties['$exception_fingerprint']) { + EXCEPTIONS_ENRICHED_COUNTER.inc() + } + + return Promise.resolve(event) +} diff --git a/plugin-server/src/worker/ingestion/event-pipeline/processPersonsStep.ts b/plugin-server/src/worker/ingestion/event-pipeline/processPersonsStep.ts index 377981fe64b09..28f6961e36940 100644 --- a/plugin-server/src/worker/ingestion/event-pipeline/processPersonsStep.ts +++ b/plugin-server/src/worker/ingestion/event-pipeline/processPersonsStep.ts @@ -2,7 +2,7 @@ import { PluginEvent } from '@posthog/plugin-scaffold' import { DateTime } from 'luxon' import { Person } from 'types' -import { DeferredPersonOverrideWriter, PersonState } from '../person-state' +import { PersonState } from '../person-state' import { EventPipelineRunner } from './runner' export async function processPersonsStep( @@ -11,19 +11,13 @@ export async function processPersonsStep( timestamp: DateTime, processPerson: boolean ): Promise<[PluginEvent, Person, Promise]> { - let overridesWriter: DeferredPersonOverrideWriter | undefined = undefined - if (runner.poEEmbraceJoin) { - overridesWriter = new DeferredPersonOverrideWriter(runner.hub.db.postgres) - } - const [person, kafkaAck] = await new PersonState( event, event.team_id, String(event.distinct_id), timestamp, processPerson, - runner.hub.db, - overridesWriter + runner.hub.db ).update() return [event, person, kafkaAck] diff --git a/plugin-server/src/worker/ingestion/event-pipeline/runner.ts b/plugin-server/src/worker/ingestion/event-pipeline/runner.ts index c1d38976a1c2a..0518902516410 100644 --- a/plugin-server/src/worker/ingestion/event-pipeline/runner.ts +++ b/plugin-server/src/worker/ingestion/event-pipeline/runner.ts @@ -10,6 +10,7 @@ import { normalizeProcessPerson } from '../../../utils/event' import { status } from '../../../utils/status' import { captureIngestionWarning, generateEventDeadLetterQueueMessage } from '../utils' import { createEventStep } from './createEventStep' +import { enrichExceptionEventStep } from './enrichExceptionEventStep' import { extractHeatmapDataStep } from './extractHeatmapDataStep' import { eventProcessedAndIngestedCounter, @@ -50,12 +51,8 @@ export class EventPipelineRunner { hub: Hub originalEvent: PipelineEvent - // See https://docs.google.com/document/d/12Q1KcJ41TicIwySCfNJV5ZPKXWVtxT7pzpB3r9ivz_0 - poEEmbraceJoin: boolean - - constructor(hub: Hub, event: PipelineEvent, poEEmbraceJoin = false) { + constructor(hub: Hub, event: PipelineEvent) { this.hub = hub - this.poEEmbraceJoin = poEEmbraceJoin this.originalEvent = event } @@ -139,19 +136,6 @@ export class EventPipelineRunner { } async runEventPipelineSteps(event: PluginEvent): Promise { - if ( - this.hub.poeEmbraceJoinForTeams?.(event.team_id) || - (event.team_id <= this.hub.POE_WRITES_ENABLED_MAX_TEAM_ID && !this.hub.poeWritesExcludeTeams(event.team_id)) - ) { - // https://docs.google.com/document/d/12Q1KcJ41TicIwySCfNJV5ZPKXWVtxT7pzpB3r9ivz_0 - // We're not using the buffer anymore - // instead we'll (if within timeframe) merge into the newer personId - - // TODO: remove this step and runner env once we're confident that the new - // ingestion pipeline is working well for all teams. - this.poEEmbraceJoin = true - } - const kafkaAcks: Promise[] = [] let processPerson = true // The default. @@ -264,9 +248,15 @@ export class EventPipelineRunner { kafkaAcks.push(...heatmapKafkaAcks) } + const enrichedIfErrorEvent = await this.runStep( + enrichExceptionEventStep, + [this, preparedEventWithoutHeatmaps], + event.team_id + ) + const [rawClickhouseEvent, eventAck] = await this.runStep( createEventStep, - [this, preparedEventWithoutHeatmaps, person, processPerson], + [this, enrichedIfErrorEvent, person, processPerson], event.team_id ) diff --git a/plugin-server/src/worker/ingestion/person-state.ts b/plugin-server/src/worker/ingestion/person-state.ts index 59f7c9699378c..2a4aa85592547 100644 --- a/plugin-server/src/worker/ingestion/person-state.ts +++ b/plugin-server/src/worker/ingestion/person-state.ts @@ -4,18 +4,14 @@ import { ProducerRecord } from 'kafkajs' import LRU from 'lru-cache' import { DateTime } from 'luxon' import { Counter } from 'prom-client' -import { KafkaProducerWrapper } from 'utils/db/kafka-producer-wrapper' import { ONE_HOUR } from '../../config/constants' -import { KAFKA_PERSON_OVERRIDE } from '../../config/kafka-topics' -import { InternalPerson, Person, PropertyUpdateOperation, TimestampFormat } from '../../types' +import { InternalPerson, Person, PropertyUpdateOperation } from '../../types' import { DB } from '../../utils/db/db' -import { PostgresRouter, PostgresUse, TransactionClient } from '../../utils/db/postgres' +import { PostgresUse, TransactionClient } from '../../utils/db/postgres' import { eventToPersonProperties, initialEventToPersonProperties, timeoutGuard } from '../../utils/db/utils' -import { PeriodicTask } from '../../utils/periodic-task' import { promiseRetry } from '../../utils/retries' import { status } from '../../utils/status' -import { castTimestampOrNow } from '../../utils/utils' import { uuidFromDistinctId } from './person-uuid' import { captureIngestionWarning } from './utils' @@ -27,13 +23,13 @@ export const mergeFinalFailuresCounter = new Counter({ export const mergeTxnAttemptCounter = new Counter({ name: 'person_merge_txn_attempt_total', help: 'Number of person merge attempts.', - labelNames: ['call', 'oldPersonIdentified', 'newPersonIdentified', 'poEEmbraceJoin'], + labelNames: ['call', 'oldPersonIdentified', 'newPersonIdentified'], }) export const mergeTxnSuccessCounter = new Counter({ name: 'person_merge_txn_success_total', help: 'Number of person merges that succeeded.', - labelNames: ['call', 'oldPersonIdentified', 'newPersonIdentified', 'poEEmbraceJoin'], + labelNames: ['call', 'oldPersonIdentified', 'newPersonIdentified'], }) export const personPropertyKeyUpdateCounter = new Counter({ @@ -113,8 +109,7 @@ export class PersonState { private distinctId: string, private timestamp: DateTime, private processPerson: boolean, // $process_person_profile flag from the event - private db: DB, - private personOverrideWriter?: DeferredPersonOverrideWriter + private db: DB ) { this.eventProperties = event.properties! @@ -714,7 +709,6 @@ export class PersonState { call: this.event.event, // $identify, $create_alias or $merge_dangerously oldPersonIdentified: String(otherPerson.is_identified), newPersonIdentified: String(mergeInto.is_identified), - poEEmbraceJoin: String(!!this.personOverrideWriter), }) .inc() @@ -762,13 +756,6 @@ export class PersonState { const deletePersonMessages = await this.db.deletePerson(otherPerson, tx) - if (this.personOverrideWriter) { - await this.personOverrideWriter.addPersonOverride( - tx, - getPersonOverrideDetails(this.teamId, otherPerson, mergeInto) - ) - } - return [person, [...updatePersonMessages, ...distinctIdMessages, ...deletePersonMessages]] } ) @@ -778,7 +765,6 @@ export class PersonState { call: this.event.event, // $identify, $create_alias or $merge_dangerously oldPersonIdentified: String(otherPerson.is_identified), newPersonIdentified: String(mergeInto.is_identified), - poEEmbraceJoin: String(!!this.personOverrideWriter), }) .inc() @@ -787,283 +773,3 @@ export class PersonState { return [mergedPerson, kafkaAck] } } - -/** - * A record of a merge operation occurring. - * - * These property names need to be kept in sync with the ``PersonOverride`` - * Django model (and ``posthog_personoverride`` table schema) as defined in - * ``posthog/models/person/person.py``. - */ -type PersonOverrideDetails = { - team_id: number - old_person_id: string - override_person_id: string - oldest_event: DateTime -} - -function getPersonOverrideDetails( - teamId: number, - oldPerson: InternalPerson, - overridePerson: InternalPerson -): PersonOverrideDetails { - if (teamId != oldPerson.team_id || teamId != overridePerson.team_id) { - throw new Error('cannot merge persons across different teams') - } - return { - team_id: teamId, - old_person_id: oldPerson.uuid, - override_person_id: overridePerson.uuid, - oldest_event: overridePerson.created_at, - } -} - -export class FlatPersonOverrideWriter { - constructor(private postgres: PostgresRouter) {} - - public async addPersonOverride( - tx: TransactionClient, - overrideDetails: PersonOverrideDetails - ): Promise { - const mergedAt = DateTime.now() - - await this.postgres.query( - tx, - SQL` - INSERT INTO posthog_flatpersonoverride ( - team_id, - old_person_id, - override_person_id, - oldest_event, - version - ) VALUES ( - ${overrideDetails.team_id}, - ${overrideDetails.old_person_id}, - ${overrideDetails.override_person_id}, - ${overrideDetails.oldest_event}, - 0 - ) - `, - undefined, - 'personOverride' - ) - - const { rows: transitiveUpdates } = await this.postgres.query( - tx, - SQL` - UPDATE - posthog_flatpersonoverride - SET - override_person_id = ${overrideDetails.override_person_id}, - version = COALESCE(version, 0)::numeric + 1 - WHERE - team_id = ${overrideDetails.team_id} AND override_person_id = ${overrideDetails.old_person_id} - RETURNING - old_person_id, - version, - oldest_event - `, - undefined, - 'transitivePersonOverrides' - ) - - status.debug('🔁', 'person_overrides_updated', { transitiveUpdates }) - - const personOverrideMessages: ProducerRecord[] = [ - { - topic: KAFKA_PERSON_OVERRIDE, - messages: [ - { - value: JSON.stringify({ - team_id: overrideDetails.team_id, - old_person_id: overrideDetails.old_person_id, - override_person_id: overrideDetails.override_person_id, - oldest_event: castTimestampOrNow(overrideDetails.oldest_event, TimestampFormat.ClickHouse), - merged_at: castTimestampOrNow(mergedAt, TimestampFormat.ClickHouse), - version: 0, - }), - }, - ...transitiveUpdates.map(({ old_person_id, version, oldest_event }) => ({ - value: JSON.stringify({ - team_id: overrideDetails.team_id, - old_person_id: old_person_id, - override_person_id: overrideDetails.override_person_id, - oldest_event: castTimestampOrNow(oldest_event, TimestampFormat.ClickHouse), - merged_at: castTimestampOrNow(mergedAt, TimestampFormat.ClickHouse), - version: version, - }), - })), - ], - }, - ] - - return personOverrideMessages - } - - public async getPersonOverrides(teamId: number): Promise { - const { rows } = await this.postgres.query( - PostgresUse.COMMON_WRITE, - SQL` - SELECT - team_id, - old_person_id, - override_person_id, - oldest_event - FROM posthog_flatpersonoverride - WHERE team_id = ${teamId} - `, - undefined, - 'getPersonOverrides' - ) - return rows.map((row) => ({ - ...row, - team_id: parseInt(row.team_id), // XXX: pg returns bigint as str (reasonably so) - oldest_event: DateTime.fromISO(row.oldest_event), - })) - } -} - -const deferredPersonOverridesWrittenCounter = new Counter({ - name: 'deferred_person_overrides_written', - help: 'Number of person overrides that have been written as pending', -}) -export class DeferredPersonOverrideWriter { - constructor(private postgres: PostgresRouter) {} - - /** - * Enqueue an override for deferred processing. - */ - public async addPersonOverride(tx: TransactionClient, overrideDetails: PersonOverrideDetails): Promise { - await this.postgres.query( - tx, - SQL` - INSERT INTO posthog_pendingpersonoverride ( - team_id, - old_person_id, - override_person_id, - oldest_event - ) VALUES ( - ${overrideDetails.team_id}, - ${overrideDetails.old_person_id}, - ${overrideDetails.override_person_id}, - ${overrideDetails.oldest_event} - )`, - undefined, - 'pendingPersonOverride' - ) - deferredPersonOverridesWrittenCounter.inc() - } -} - -const deferredPersonOverridesProcessedCounter = new Counter({ - name: 'deferred_person_overrides_processed', - help: 'Number of pending person overrides that have been successfully processed', -}) - -export class DeferredPersonOverrideWorker { - // This lock ID is used as an advisory lock identifier/key for a lock that - // ensures only one worker is able to update the overrides table at a time. - // (We do this to make it simpler to ensure that we maintain the consistency - // of transitive updates.) There isn't any special significance to this - // particular value (other than Postgres requires it to be a numeric one), - // it just needs to be consistent across all processes. - public readonly lockId = 567 - - constructor( - private postgres: PostgresRouter, - private kafkaProducer: KafkaProducerWrapper, - private writer: FlatPersonOverrideWriter - ) {} - - /** - * Process all (or up to the given limit) pending overrides. - * - * An advisory lock is acquired prior to processing to ensure that this - * function has exclusive access to the pending overrides during the update - * process. - * - * @returns the number of overrides processed - */ - public async processPendingOverrides(limit?: number): Promise { - const overridesCount = await this.postgres.transaction( - PostgresUse.COMMON_WRITE, - 'processPendingOverrides', - async (tx) => { - const { - rows: [{ acquired }], - } = await this.postgres.query( - tx, - SQL`SELECT pg_try_advisory_xact_lock(${this.lockId}) as acquired`, - undefined, - 'processPendingOverrides' - ) - if (!acquired) { - throw new Error('could not acquire lock') - } - - // n.b.: Ordering by id ensures we are processing in (roughly) FIFO order - const { rows } = await this.postgres.query( - tx, - `SELECT * FROM posthog_pendingpersonoverride ORDER BY id` + - (limit !== undefined ? ` LIMIT ${limit}` : ''), - undefined, - 'processPendingOverrides' - ) - - const messages: ProducerRecord[] = [] - for (const { id, ...mergeOperation } of rows) { - messages.push(...(await this.writer.addPersonOverride(tx, mergeOperation))) - await this.postgres.query( - tx, - SQL`DELETE FROM posthog_pendingpersonoverride WHERE id = ${id}`, - undefined, - 'processPendingOverrides' - ) - } - - // n.b.: We publish the messages here (and wait for acks) to ensure - // that all of our override updates are sent to Kafka prior to - // committing the transaction. If we're unable to publish, we should - // discard updates and try again later when it's available -- not - // doing so would cause the copy of this data in ClickHouse to - // slowly drift out of sync with the copy in Postgres. This write is - // safe to retry if we write to Kafka but then fail to commit to - // Postgres for some reason -- the same row state should be - // generated each call, and the receiving ReplacingMergeTree will - // ensure we keep only the latest version after all writes settle.) - await this.kafkaProducer.queueMessages({ kafkaMessages: messages, waitForAck: true }) - - return rows.length - } - ) - - deferredPersonOverridesProcessedCounter.inc(overridesCount) - - return overridesCount - } - - public runTask(intervalMs: number): PeriodicTask { - return new PeriodicTask( - 'processPendingOverrides', - async () => { - status.debug('👥', 'Processing pending overrides...') - const overridesCount = await this.processPendingOverrides(5000) - ;(overridesCount > 0 ? status.info : status.debug)( - '👥', - `Processed ${overridesCount} pending overrides.` - ) - }, - intervalMs - ) - } -} - -function SQL(sqlParts: TemplateStringsArray, ...args: any[]): { text: string; values: any[] } { - // Generates a node-pq compatible query object given a tagged - // template literal. The intention is to remove the need to match up - // the positional arguments with the $1, $2, etc. placeholders in - // the query string. - const text = sqlParts.reduce((acc, part, i) => acc + '$' + i + part) - const values = args - return { text, values } -} diff --git a/plugin-server/tests/main/ingestion-queues/session-recording/session-recordings-consumer.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording/session-recordings-consumer.test.ts index a171dccff78ef..76d23d9daa742 100644 --- a/plugin-server/tests/main/ingestion-queues/session-recording/session-recordings-consumer.test.ts +++ b/plugin-server/tests/main/ingestion-queues/session-recording/session-recordings-consumer.test.ts @@ -159,9 +159,17 @@ describe.each([[true], [false]])('ingester with consumeOverflow=%p', (consumeOve afterEach(async () => { await ingester.stop() }) - it('can parse debug partition config', () => { + + it.each([ + ['103', 103, true], + ['103', 102, false], + ['*', 101, true], + ['', 99, false], + ['102, 103, 104', 102, true], + ['102, 103, 104', 101, false], + ])('can parse debug partition config', (partition_config, partition, expected) => { const config = { - SESSION_RECORDING_DEBUG_PARTITION: '103', + SESSION_RECORDING_DEBUG_PARTITION: partition_config, KAFKA_HOSTS: 'localhost:9092', } satisfies Partial as PluginsServerConfig @@ -172,7 +180,7 @@ describe.each([[true], [false]])('ingester with consumeOverflow=%p', (consumeOve consumeOverflow, undefined ) - expect(ingester['debugPartition']).toEqual(103) + expect(ingester['isDebugLoggingEnabled'](partition)).toEqual(expected) }) it('can parse absence of debug partition config', () => { diff --git a/plugin-server/tests/worker/ingestion/event-pipeline/__snapshots__/runner.test.ts.snap b/plugin-server/tests/worker/ingestion/event-pipeline/__snapshots__/runner.test.ts.snap index fd5ccf04db02d..f84f2ccc21250 100644 --- a/plugin-server/tests/worker/ingestion/event-pipeline/__snapshots__/runner.test.ts.snap +++ b/plugin-server/tests/worker/ingestion/event-pipeline/__snapshots__/runner.test.ts.snap @@ -95,6 +95,21 @@ Array [ }, ], ], + Array [ + "enrichExceptionEventStep", + Array [ + Object { + "distinctId": "my_id", + "elementsList": Array [], + "event": "$pageview", + "eventUuid": "uuid1", + "ip": "127.0.0.1", + "properties": Object {}, + "teamId": 2, + "timestamp": "2020-02-23T02:15:00.000Z", + }, + ], + ], Array [ "createEventStep", Array [ diff --git a/plugin-server/tests/worker/ingestion/event-pipeline/enrichExceptionEventStep.test.ts b/plugin-server/tests/worker/ingestion/event-pipeline/enrichExceptionEventStep.test.ts new file mode 100644 index 0000000000000..e59f42fb71001 --- /dev/null +++ b/plugin-server/tests/worker/ingestion/event-pipeline/enrichExceptionEventStep.test.ts @@ -0,0 +1,109 @@ +import { ISOTimestamp, PreIngestionEvent } from '../../../../src/types' +import { cloneObject } from '../../../../src/utils/utils' +import { enrichExceptionEventStep } from '../../../../src/worker/ingestion/event-pipeline/enrichExceptionEventStep' + +jest.mock('../../../../src/worker/plugins/run') + +const aStackTrace = + '[{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"dependenciesChecker","in_app":true,"lineno":721,"colno":42},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"?","in_app":true,"lineno":2474,"colno":40},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"Object.memoized [as tiles]","in_app":true,"lineno":632,"colno":24},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"dependenciesChecker","in_app":true,"lineno":721,"colno":42},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"memoized","in_app":true,"lineno":632,"colno":24},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"dependenciesChecker","in_app":true,"lineno":721,"colno":42},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"logic.selector","in_app":true,"lineno":2517,"colno":18},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"pathSelector","in_app":true,"lineno":2622,"colno":37},{"filename":"","function":"Array.reduce","in_app":true},{"filename":"http://localhost:8234/static/chunk-VDD5ZZ2W.js","function":"?","in_app":true,"lineno":2626,"colno":15}]' + +const preIngestionEvent: PreIngestionEvent = { + eventUuid: '018eebf3-cb48-750b-bfad-36409ea6f2b2', + event: '$exception', + distinctId: '018eebf3-79b1-7082-a7c6-eeb56a36002f', + properties: { + $current_url: 'http://localhost:3000/', + $host: 'localhost:3000', + $pathname: '/', + $viewport_height: 1328, + $viewport_width: 1071, + $device_id: '018eebf3-79b1-7082-a7c6-eeb56a36002f', + $session_id: '018eebf3-79cd-70da-895f-b6cf352bd688', + $window_id: '018eebf3-79cd-70da-895f-b6d09add936a', + }, + timestamp: '2024-04-17T12:06:46.861Z' as ISOTimestamp, + teamId: 1, +} + +describe('enrichExceptionEvent()', () => { + let runner: any + let event: PreIngestionEvent + + beforeEach(() => { + event = cloneObject(preIngestionEvent) + runner = { + hub: { + kafkaProducer: { + produce: jest.fn((e) => Promise.resolve(e)), + }, + }, + nextStep: (...args: any[]) => args, + } + }) + + it('ignores non-exception events - even if they have a stack trace', async () => { + event.event = 'not_exception' + event.properties['$exception_stack_trace_raw'] = '[{"some": "data"}]' + expect(event.properties['$exception_fingerprint']).toBeUndefined() + + const response = await enrichExceptionEventStep(runner, event) + expect(response).toBe(event) + }) + + it('use a fingerprint if it is present', async () => { + event.event = '$exception' + event.properties['$exception_stack_trace_raw'] = '[{"some": "data"}]' + event.properties['$exception_fingerprint'] = 'some-fingerprint' + + const response = await enrichExceptionEventStep(runner, event) + + expect(response.properties['$exception_fingerprint']).toBe('some-fingerprint') + }) + + it('uses the message and stack trace as the simplest grouping', async () => { + event.event = '$exception' + event.properties['$exception_message'] = 'some-message' + event.properties['$exception_stack_trace_raw'] = aStackTrace + + const response = await enrichExceptionEventStep(runner, event) + + expect(response.properties['$exception_fingerprint']).toStrictEqual(['some-message', 'dependenciesChecker']) + }) + + it('includes type in stack grouping when present', async () => { + event.event = '$exception' + event.properties['$exception_message'] = 'some-message' + event.properties['$exception_stack_trace_raw'] = aStackTrace + event.properties['$exception_type'] = 'UnhandledRejection' + + const response = await enrichExceptionEventStep(runner, event) + + expect(response.properties['$exception_fingerprint']).toStrictEqual([ + 'UnhandledRejection', + 'some-message', + 'dependenciesChecker', + ]) + }) + + it('falls back to message and type when no stack trace', async () => { + event.event = '$exception' + event.properties['$exception_message'] = 'some-message' + event.properties['$exception_stack_trace_raw'] = null + event.properties['$exception_type'] = 'UnhandledRejection' + + const response = await enrichExceptionEventStep(runner, event) + + expect(response.properties['$exception_fingerprint']).toStrictEqual(['UnhandledRejection', 'some-message']) + }) + + it('adds no fingerprint if no qualifying properties', async () => { + event.event = '$exception' + event.properties['$exception_message'] = null + event.properties['$exception_stack_trace_raw'] = null + event.properties['$exception_type'] = null + + const response = await enrichExceptionEventStep(runner, event) + + expect(response.properties['$exception_fingerprint']).toBeUndefined() + }) +}) diff --git a/plugin-server/tests/worker/ingestion/event-pipeline/processPersonsStep.test.ts b/plugin-server/tests/worker/ingestion/event-pipeline/processPersonsStep.test.ts index 771a6900edc05..ea3a592f79ed4 100644 --- a/plugin-server/tests/worker/ingestion/event-pipeline/processPersonsStep.test.ts +++ b/plugin-server/tests/worker/ingestion/event-pipeline/processPersonsStep.test.ts @@ -8,7 +8,7 @@ import { normalizeEventStep } from '../../../../src/worker/ingestion/event-pipel import { processPersonsStep } from '../../../../src/worker/ingestion/event-pipeline/processPersonsStep' import { createOrganization, createTeam, fetchPostgresPersons, resetTestDatabase } from '../../../helpers/sql' -describe.each([[true], [false]])('processPersonsStep()', (poEEmbraceJoin) => { +describe('processPersonsStep()', () => { let runner: any let hub: Hub let closeHub: () => Promise @@ -24,7 +24,6 @@ describe.each([[true], [false]])('processPersonsStep()', (poEEmbraceJoin) => { runner = { nextStep: (...args: any[]) => args, hub: hub, - poEEmbraceJoin: poEEmbraceJoin, } const organizationId = await createOrganization(runner.hub.db.postgres) teamId = await createTeam(runner.hub.db.postgres, organizationId) diff --git a/plugin-server/tests/worker/ingestion/event-pipeline/runner.test.ts b/plugin-server/tests/worker/ingestion/event-pipeline/runner.test.ts index ec741625041b9..e70d721828b96 100644 --- a/plugin-server/tests/worker/ingestion/event-pipeline/runner.test.ts +++ b/plugin-server/tests/worker/ingestion/event-pipeline/runner.test.ts @@ -125,6 +125,7 @@ describe('EventPipelineRunner', () => { 'processPersonsStep', 'prepareEventStep', 'extractHeatmapDataStep', + 'enrichExceptionEventStep', 'createEventStep', ]) expect(runner.stepsWithArgs).toMatchSnapshot() @@ -153,6 +154,7 @@ describe('EventPipelineRunner', () => { 'processPersonsStep', 'prepareEventStep', 'extractHeatmapDataStep', + 'enrichExceptionEventStep', 'createEventStep', ]) }) @@ -175,7 +177,7 @@ describe('EventPipelineRunner', () => { const result = await runner.runEventPipeline(pipelineEvent) expect(result.error).toBeUndefined() - expect(pipelineStepMsSummarySpy).toHaveBeenCalledTimes(7) + expect(pipelineStepMsSummarySpy).toHaveBeenCalledTimes(8) expect(pipelineLastStepCounterSpy).toHaveBeenCalledTimes(1) expect(eventProcessedAndIngestedCounterSpy).toHaveBeenCalledTimes(1) expect(pipelineStepMsSummarySpy).toHaveBeenCalledWith('createEventStep') diff --git a/plugin-server/tests/worker/ingestion/person-state.test.ts b/plugin-server/tests/worker/ingestion/person-state.test.ts index ee46483405c50..7405f6799dab4 100644 --- a/plugin-server/tests/worker/ingestion/person-state.test.ts +++ b/plugin-server/tests/worker/ingestion/person-state.test.ts @@ -1,22 +1,15 @@ import { PluginEvent } from '@posthog/plugin-scaffold' import { DateTime } from 'luxon' -import { waitForExpect } from '../../../functional_tests/expectations' import { Database, Hub, InternalPerson } from '../../../src/types' import { DependencyUnavailableError } from '../../../src/utils/db/error' import { createHub } from '../../../src/utils/db/hub' import { PostgresUse } from '../../../src/utils/db/postgres' import { defaultRetryConfig } from '../../../src/utils/retries' import { UUIDT } from '../../../src/utils/utils' -import { - DeferredPersonOverrideWorker, - DeferredPersonOverrideWriter, - FlatPersonOverrideWriter, - PersonState, -} from '../../../src/worker/ingestion/person-state' +import { PersonState } from '../../../src/worker/ingestion/person-state' import { uuidFromDistinctId } from '../../../src/worker/ingestion/person-uuid' import { delayUntilEventIngested } from '../../helpers/clickhouse' -import { WaitEvent } from '../../helpers/promises' import { createOrganization, createTeam, fetchPostgresPersons, insertRow } from '../../helpers/sql' jest.setTimeout(5000) // 5 sec timeout @@ -25,43 +18,11 @@ const timestamp = DateTime.fromISO('2020-01-01T12:00:05.200Z').toUTC() const timestamp2 = DateTime.fromISO('2020-02-02T12:00:05.200Z').toUTC() const timestampch = '2020-01-01 12:00:05.000' -interface PersonOverridesMode { - supportsSyncTransaction: boolean - getWriter(hub: Hub): DeferredPersonOverrideWriter - fetchPostgresPersonIdOverrides( - hub: Hub, - teamId: number - ): Promise> -} - -const PersonOverridesModes: Record = { - disabled: undefined, - 'deferred, without mappings (flat)': { - supportsSyncTransaction: false, - getWriter: (hub) => new DeferredPersonOverrideWriter(hub.db.postgres), - fetchPostgresPersonIdOverrides: async (hub, teamId) => { - const syncWriter = new FlatPersonOverrideWriter(hub.db.postgres) - await new DeferredPersonOverrideWorker( - hub.db.postgres, - hub.db.kafkaProducer, - syncWriter - ).processPendingOverrides() - return new Set( - (await syncWriter.getPersonOverrides(teamId)).map(({ old_person_id, override_person_id }) => ({ - old_person_id, - override_person_id, - })) - ) - }, - }, -} - describe('PersonState.update()', () => { let hub: Hub let closeHub: () => Promise let teamId: number - let overridesMode: PersonOverridesMode | undefined let organizationId: string // Common Distinct IDs (and their deterministic UUIDs) used in tests below. @@ -82,8 +43,6 @@ describe('PersonState.update()', () => { }) beforeEach(async () => { - overridesMode = undefined - teamId = await createTeam(hub.db.postgres, organizationId) newUserUuid = uuidFromDistinctId(teamId, newUserDistinctId) @@ -125,8 +84,7 @@ describe('PersonState.update()', () => { event.distinct_id!, timestampParam, processPerson, - customHub ? customHub.db : hub.db, - overridesMode?.getWriter(customHub ?? hub) + customHub ? customHub.db : hub.db ) } @@ -952,535 +910,529 @@ describe('PersonState.update()', () => { }) }) - describe.each(Object.keys(PersonOverridesModes))('on $identify event', (useOverridesMode) => { - beforeEach(() => { - overridesMode = PersonOverridesModes[useOverridesMode] // n.b. mutating outer scope here -- be careful + describe('on $identify event', () => { + it(`no-op when $anon_distinct_id not passed`, async () => { + const [person, kafkaAcks] = await personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $set: { foo: 'bar' }, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks + + expect(person).toEqual(undefined) + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(0) }) - describe(`overrides: ${useOverridesMode}`, () => { - it(`no-op when $anon_distinct_id not passed`, async () => { - const [person, kafkaAcks] = await personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $set: { foo: 'bar' }, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + it(`creates person with both distinct_ids and marks user as is_identified when $anon_distinct_id passed`, async () => { + const [person, kafkaAcks] = await personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $set: { foo: 'bar' }, + $anon_distinct_id: oldUserDistinctId, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - expect(person).toEqual(undefined) - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(0) - }) + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: newUserUuid, + properties: { foo: 'bar' }, + created_at: timestamp, + version: 0, + is_identified: true, + }) + ) - it(`creates person with both distinct_ids and marks user as is_identified when $anon_distinct_id passed`, async () => { - const [person, kafkaAcks] = await personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $set: { foo: 'bar' }, - $anon_distinct_id: oldUserDistinctId, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + expect(hub.db.updatePersonDeprecated).not.toHaveBeenCalled() - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: newUserUuid, - properties: { foo: 'bar' }, - created_at: timestamp, - version: 0, - is_identified: true, - }) - ) + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) - expect(hub.db.updatePersonDeprecated).not.toHaveBeenCalled() + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + }) - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) + it(`marks is_identified to be updated when no changes to distinct_ids but $anon_distinct_id passe`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, newUserUuid, [ + { distinctId: newUserDistinctId }, + { distinctId: oldUserDistinctId }, + ]) - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + const personS = personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $anon_distinct_id: oldUserDistinctId, + }, }) + const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - it(`marks is_identified to be updated when no changes to distinct_ids but $anon_distinct_id passe`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, newUserUuid, [ - { distinctId: newUserDistinctId }, - { distinctId: oldUserDistinctId }, - ]) - - const personS = personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $anon_distinct_id: oldUserDistinctId, - }, + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: newUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: false, }) - const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + ) + expect(personS.updateIsIdentified).toBeTruthy() - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: newUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }) - ) - expect(personS.updateIsIdentified).toBeTruthy() + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) + }) - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - }) + it(`add distinct id and marks user is_identified when passed $anon_distinct_id person does not exists and distinct_id does`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, newUserUuid, [ + { distinctId: newUserDistinctId }, + ]) - it(`add distinct id and marks user is_identified when passed $anon_distinct_id person does not exists and distinct_id does`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, newUserUuid, [ - { distinctId: newUserDistinctId }, - ]) + const personS = personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $anon_distinct_id: oldUserDistinctId, + }, + }) + const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - const personS = personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $anon_distinct_id: oldUserDistinctId, - }, + const persons = await fetchPostgresPersonsH() + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: newUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: false, }) - const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + ) + expect(personS.updateIsIdentified).toBeTruthy() - const persons = await fetchPostgresPersonsH() - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: newUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }) - ) - expect(personS.updateIsIdentified).toBeTruthy() + // verify Postgres persons + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) + + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + }) - // verify Postgres persons - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) + it(`add distinct id and marks user as is_identified when passed $anon_distinct_id person exists and distinct_id does not`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + const personS = personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $anon_distinct_id: oldUserDistinctId, + }, }) + const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - it(`add distinct id and marks user as is_identified when passed $anon_distinct_id person exists and distinct_id does not`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) + const persons = await fetchPostgresPersonsH() - const personS = personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $anon_distinct_id: oldUserDistinctId, - }, + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: oldUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: false, }) - const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks - - const persons = await fetchPostgresPersonsH() - - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: oldUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }) - ) - expect(personS.updateIsIdentified).toBeTruthy() - - // verify Postgres persons - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - }) + ) + expect(personS.updateIsIdentified).toBeTruthy() - it(`merge into distinct_id person and marks user as is_identified when both persons have is_identified false`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) - await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, false, newUserUuid, [ - { distinctId: newUserDistinctId }, - ]) + // verify Postgres persons + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) - const [person, kafkaAcks] = await personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $anon_distinct_id: oldUserDistinctId, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + }) - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: expect.any(String), - properties: {}, - created_at: timestamp, - version: 1, - is_identified: true, - }) - ) + it(`merge into distinct_id person and marks user as is_identified when both persons have is_identified false`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) + await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, false, newUserUuid, [ + { distinctId: newUserDistinctId }, + ]) - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - - // verify ClickHouse persons - await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed - const clickhousePersons = await fetchPersonsRows() // but verify full state - expect(clickhousePersons.length).toEqual(2) - expect(clickhousePersons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - properties: '{}', - created_at: timestampch, - version: 1, - is_identified: 1, - }), - expect.objectContaining({ - id: expect.any(String), - is_deleted: 1, - version: 100, - }), - ]) - ) - expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) + const [person, kafkaAcks] = await personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $anon_distinct_id: oldUserDistinctId, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - // verify ClickHouse distinct_ids - await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) - const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) - expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - }) + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: expect.any(String), + properties: {}, + created_at: timestamp, + version: 1, + is_identified: true, + }) + ) - it(`merge into distinct_id person and marks user as is_identified when distinct_id user is identified and $anon_distinct_id user is not`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) - await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, true, newUserUuid, [ - { distinctId: newUserDistinctId }, - ]) + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) + expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) - const [person, kafkaAcks] = await personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $anon_distinct_id: oldUserDistinctId, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - expect(person).toEqual( + // verify ClickHouse persons + await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed + const clickhousePersons = await fetchPersonsRows() // but verify full state + expect(clickhousePersons.length).toEqual(2) + expect(clickhousePersons).toEqual( + expect.arrayContaining([ expect.objectContaining({ - id: expect.any(Number), - uuid: expect.any(String), - properties: {}, - created_at: timestamp, + id: expect.any(String), + properties: '{}', + created_at: timestampch, version: 1, - is_identified: true, - }) - ) + is_identified: 1, + }), + expect.objectContaining({ + id: expect.any(String), + is_deleted: 1, + version: 100, + }), + ]) + ) + expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - - // verify ClickHouse persons - await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed - const clickhousePersons = await fetchPersonsRows() // but verify full state - expect(clickhousePersons.length).toEqual(2) - expect(clickhousePersons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - properties: '{}', - created_at: timestampch, - version: 1, - is_identified: 1, - }), - expect.objectContaining({ - id: expect.any(String), - is_deleted: 1, - version: 100, - }), - ]) - ) - expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) + // verify ClickHouse distinct_ids + await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) + const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) + expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + }) - // verify ClickHouse distinct_ids - await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) - const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) - expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - }) + it(`merge into distinct_id person and marks user as is_identified when distinct_id user is identified and $anon_distinct_id user is not`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) + await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, true, newUserUuid, [ + { distinctId: newUserDistinctId }, + ]) - it(`does not merge people when distinct_id user is not identified and $anon_distinct_id user is`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) - await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, false, newUserUuid, [ - { distinctId: newUserDistinctId }, - ]) + const [person, kafkaAcks] = await personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $anon_distinct_id: oldUserDistinctId, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - const personS = personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $anon_distinct_id: oldUserDistinctId, - }, + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: expect.any(String), + properties: {}, + created_at: timestamp, + version: 1, + is_identified: true, }) - const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + ) - expect(personS.updateIsIdentified).toBeTruthy() - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: newUserUuid, - properties: {}, - created_at: timestamp2, - version: 0, - is_identified: false, - }) - ) + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) + expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) + + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - // verify Postgres persons - const persons = (await fetchPostgresPersonsH()).sort((a, b) => a.id - b.id) - expect(persons.length).toEqual(2) - expect(persons[0]).toEqual( + // verify ClickHouse persons + await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed + const clickhousePersons = await fetchPersonsRows() // but verify full state + expect(clickhousePersons.length).toEqual(2) + expect(clickhousePersons).toEqual( + expect.arrayContaining([ expect.objectContaining({ - id: expect.any(Number), - uuid: oldUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: true, - }) - ) - expect(persons[1]).toEqual(person) + id: expect.any(String), + properties: '{}', + created_at: timestampch, + version: 1, + is_identified: 1, + }), + expect.objectContaining({ + id: expect.any(String), + is_deleted: 1, + version: 100, + }), + ]) + ) + expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId])) - const distinctIds2 = await hub.db.fetchDistinctIdValues(persons[1]) - expect(distinctIds2).toEqual(expect.arrayContaining([newUserDistinctId])) - }) + // verify ClickHouse distinct_ids + await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) + const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) + expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + }) - it(`does not merge people when both users are identified`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) - await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, true, newUserUuid, [ - { distinctId: newUserDistinctId }, - ]) + it(`does not merge people when distinct_id user is not identified and $anon_distinct_id user is`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) + await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, false, newUserUuid, [ + { distinctId: newUserDistinctId }, + ]) - const [person, kafkaAcks] = await personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $anon_distinct_id: oldUserDistinctId, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + const personS = personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $anon_distinct_id: oldUserDistinctId, + }, + }) + const [person, kafkaAcks] = await personS.handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: newUserUuid, - properties: {}, - created_at: timestamp2, - version: 0, - is_identified: true, - }) - ) + expect(personS.updateIsIdentified).toBeTruthy() + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: newUserUuid, + properties: {}, + created_at: timestamp2, + version: 0, + is_identified: false, + }) + ) - // verify Postgres persons - const persons = (await fetchPostgresPersonsH()).sort((a, b) => a.id - b.id) - expect(persons.length).toEqual(2) - expect(persons[0]).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: oldUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: true, - }) - ) - expect(persons[1]).toEqual(person) + // verify Postgres persons + const persons = (await fetchPostgresPersonsH()).sort((a, b) => a.id - b.id) + expect(persons.length).toEqual(2) + expect(persons[0]).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: oldUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: true, + }) + ) + expect(persons[1]).toEqual(person) - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId])) - const distinctIds2 = await hub.db.fetchDistinctIdValues(persons[1]) - expect(distinctIds2).toEqual(expect.arrayContaining([newUserDistinctId])) - }) + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId])) + const distinctIds2 = await hub.db.fetchDistinctIdValues(persons[1]) + expect(distinctIds2).toEqual(expect.arrayContaining([newUserDistinctId])) + }) - it(`merge into distinct_id person and updates properties with $set/$set_once`, async () => { - await hub.db.createPerson(timestamp, { a: 1, b: 2 }, {}, {}, teamId, null, false, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) - await hub.db.createPerson(timestamp2, { b: 3, c: 4, d: 5 }, {}, {}, teamId, null, false, newUserUuid, [ - { distinctId: newUserDistinctId }, - ]) + it(`does not merge people when both users are identified`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) + await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, true, newUserUuid, [ + { distinctId: newUserDistinctId }, + ]) - const [person, kafkaAcks] = await personState({ - event: '$identify', - distinct_id: newUserDistinctId, - properties: { - $set: { d: 6, e: 7 }, - $set_once: { a: 8, f: 9 }, - $anon_distinct_id: oldUserDistinctId, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks + const [person, kafkaAcks] = await personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $anon_distinct_id: oldUserDistinctId, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: expect.any(String), - properties: { a: 1, b: 3, c: 4, d: 6, e: 7, f: 9 }, - created_at: timestamp, - version: 1, - is_identified: true, - }) - ) + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: newUserUuid, + properties: {}, + created_at: timestamp2, + version: 0, + is_identified: true, + }) + ) - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - - // verify ClickHouse persons - await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed - const clickhousePersons = await fetchPersonsRows() // but verify full state - expect(clickhousePersons.length).toEqual(2) - expect(clickhousePersons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - properties: JSON.stringify({ a: 1, b: 3, c: 4, d: 6, e: 7, f: 9 }), - created_at: timestampch, - version: 1, - is_identified: 1, - }), - expect.objectContaining({ - id: expect.any(String), - is_deleted: 1, - version: 100, - }), - ]) - ) - expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) + // verify Postgres persons + const persons = (await fetchPostgresPersonsH()).sort((a, b) => a.id - b.id) + expect(persons.length).toEqual(2) + expect(persons[0]).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: oldUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: true, + }) + ) + expect(persons[1]).toEqual(person) - // verify ClickHouse distinct_ids - await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) - const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) - expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - }) + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId])) + const distinctIds2 = await hub.db.fetchDistinctIdValues(persons[1]) + expect(distinctIds2).toEqual(expect.arrayContaining([newUserDistinctId])) + }) - it(`handles race condition when other thread creates the user`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) + it(`merge into distinct_id person and updates properties with $set/$set_once`, async () => { + await hub.db.createPerson(timestamp, { a: 1, b: 2 }, {}, {}, teamId, null, false, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) + await hub.db.createPerson(timestamp2, { b: 3, c: 4, d: 5 }, {}, {}, teamId, null, false, newUserUuid, [ + { distinctId: newUserDistinctId }, + ]) - // Fake the race by assuming createPerson was called before the addDistinctId creation above - jest.spyOn(hub.db, 'addDistinctId').mockImplementation(async (person, distinctId) => { - await hub.db.createPerson( - timestamp, - {}, - {}, - {}, - teamId, - null, - false, - uuidFromDistinctId(teamId, distinctId), - [{ distinctId }] - ) - await hub.db.addDistinctId(person, distinctId, 0) // this throws + const [person, kafkaAcks] = await personState({ + event: '$identify', + distinct_id: newUserDistinctId, + properties: { + $set: { d: 6, e: 7 }, + $set_once: { a: 8, f: 9 }, + $anon_distinct_id: oldUserDistinctId, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks + + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: expect.any(String), + properties: { a: 1, b: 3, c: 4, d: 6, e: 7, f: 9 }, + created_at: timestamp, + version: 1, + is_identified: true, }) + ) - const [person, kafkaAcks] = await personState({ - event: '$identify', - distinct_id: oldUserDistinctId, - properties: { - $anon_distinct_id: newUserDistinctId, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks - jest.spyOn(hub.db, 'addDistinctId').mockRestore() // Necessary for other tests not to fail + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) + expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) - // if creation fails we should return the person that another thread already created - expect(person).toEqual( + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + + // verify ClickHouse persons + await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed + const clickhousePersons = await fetchPersonsRows() // but verify full state + expect(clickhousePersons.length).toEqual(2) + expect(clickhousePersons).toEqual( + expect.arrayContaining([ expect.objectContaining({ - id: expect.any(Number), - uuid: oldUserUuid, - properties: {}, - created_at: timestamp, + id: expect.any(String), + properties: JSON.stringify({ a: 1, b: 3, c: 4, d: 6, e: 7, f: 9 }), + created_at: timestampch, version: 1, - is_identified: true, - }) + is_identified: 1, + }), + expect.objectContaining({ + id: expect.any(String), + is_deleted: 1, + version: 100, + }), + ]) + ) + expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) + + // verify ClickHouse distinct_ids + await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) + const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) + expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + }) + + it(`handles race condition when other thread creates the user`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) + + // Fake the race by assuming createPerson was called before the addDistinctId creation above + jest.spyOn(hub.db, 'addDistinctId').mockImplementation(async (person, distinctId) => { + await hub.db.createPerson( + timestamp, + {}, + {}, + {}, + teamId, + null, + false, + uuidFromDistinctId(teamId, distinctId), + [{ distinctId }] ) - // expect(hub.db.updatePersonDeprecated).not.toHaveBeenCalled() - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([newUserDistinctId])) + await hub.db.addDistinctId(person, distinctId, 0) // this throws }) + + const [person, kafkaAcks] = await personState({ + event: '$identify', + distinct_id: oldUserDistinctId, + properties: { + $anon_distinct_id: newUserDistinctId, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks + jest.spyOn(hub.db, 'addDistinctId').mockRestore() // Necessary for other tests not to fail + + // if creation fails we should return the person that another thread already created + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: oldUserUuid, + properties: {}, + created_at: timestamp, + version: 1, + is_identified: true, + }) + ) + // expect(hub.db.updatePersonDeprecated).not.toHaveBeenCalled() + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) + + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([newUserDistinctId])) }) }) @@ -1540,90 +1492,84 @@ describe('PersonState.update()', () => { }) }) - describe.each(Object.keys(PersonOverridesModes))('on $merge_dangerously events', (useOverridesMode) => { - beforeEach(() => { - overridesMode = PersonOverridesModes[useOverridesMode] // n.b. mutating outer scope here -- be careful - }) - - describe(`overrides: ${useOverridesMode}`, () => { - // only difference between $merge_dangerously and $identify - it(`merge_dangerously can merge people when alias id user is identified`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, oldUserUuid, [ - { distinctId: oldUserDistinctId }, - ]) - await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, true, newUserUuid, [ - { distinctId: newUserDistinctId }, - ]) - - const [person, kafkaAcks] = await personState({ - event: '$merge_dangerously', - distinct_id: newUserDistinctId, - properties: { - alias: oldUserDistinctId, - }, - }).handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - await kafkaAcks - - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: expect.any(String), - properties: {}, - created_at: timestamp, - version: 1, - is_identified: true, - }) - ) - - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - - // verify ClickHouse persons - await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed - const clickhousePersons = await fetchPersonsRows() // but verify full state - expect(clickhousePersons.length).toEqual(2) - expect(clickhousePersons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - properties: '{}', - created_at: timestampch, - version: 1, - is_identified: 1, - }), - expect.objectContaining({ - id: expect.any(String), - is_deleted: 1, - version: 100, - }), - ]) - ) - expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) - - // verify ClickHouse distinct_ids - await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) - const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) - expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) - }) - }) - }) + describe('on $merge_dangerously events', () => { + // only difference between $merge_dangerously and $identify + it(`merge_dangerously can merge people when alias id user is identified`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, oldUserUuid, [ + { distinctId: oldUserDistinctId }, + ]) + await hub.db.createPerson(timestamp2, {}, {}, {}, teamId, null, true, newUserUuid, [ + { distinctId: newUserDistinctId }, + ]) - describe('illegal aliasing', () => { - const illegalIds = ['', ' ', 'null', 'undefined', '"undefined"', '[object Object]', '"[object Object]"'] - it.each(illegalIds)('stops $identify if current distinct_id is illegal: `%s`', async (illegalId: string) => { - const [person] = await personState({ - event: '$identify', - distinct_id: illegalId, + const [person, kafkaAcks] = await personState({ + event: '$merge_dangerously', + distinct_id: newUserDistinctId, properties: { - $anon_distinct_id: 'anonymous_id', + alias: oldUserDistinctId, + }, + }).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() + await kafkaAcks + + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: expect.any(String), + properties: {}, + created_at: timestamp, + version: 1, + is_identified: true, + }) + ) + + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) + expect([newUserUuid, oldUserUuid]).toContain(persons[0].uuid) + + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) + expect(distinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + + // verify ClickHouse persons + await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed + const clickhousePersons = await fetchPersonsRows() // but verify full state + expect(clickhousePersons.length).toEqual(2) + expect(clickhousePersons).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + properties: '{}', + created_at: timestampch, + version: 1, + is_identified: 1, + }), + expect.objectContaining({ + id: expect.any(String), + is_deleted: 1, + version: 100, + }), + ]) + ) + expect(new Set(clickhousePersons.map((p) => p.id))).toEqual(new Set([newUserUuid, oldUserUuid])) + + // verify ClickHouse distinct_ids + await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) + const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(persons[0]) + expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([oldUserDistinctId, newUserDistinctId])) + }) + }) + + describe('illegal aliasing', () => { + const illegalIds = ['', ' ', 'null', 'undefined', '"undefined"', '[object Object]', '"[object Object]"'] + it.each(illegalIds)('stops $identify if current distinct_id is illegal: `%s`', async (illegalId: string) => { + const [person] = await personState({ + event: '$identify', + distinct_id: illegalId, + properties: { + $anon_distinct_id: 'anonymous_id', }, }).handleIdentifyOrAlias() @@ -1911,7 +1857,7 @@ describe('PersonState.update()', () => { ) }) }) - describe.each(Object.keys(PersonOverridesModes))('on persons merges', (useOverridesMode) => { + describe('on persons merges', () => { // For some reason these tests failed if I ran them with a hub shared // with other tests, so I'm creating a new hub for each test. let hub: Hub @@ -1919,7 +1865,6 @@ describe('PersonState.update()', () => { beforeEach(async () => { ;[hub, closeHub] = await createHub({}) - overridesMode = PersonOverridesModes[useOverridesMode] // n.b. mutating outer scope here -- be careful jest.spyOn(hub.db, 'fetchPerson') jest.spyOn(hub.db, 'updatePersonDeprecated') @@ -1928,929 +1873,282 @@ describe('PersonState.update()', () => { afterEach(async () => { await closeHub() }) - describe(`overrides: ${useOverridesMode}`, () => { - it(`no-op if persons already merged`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, firstUserUuid, [ - { distinctId: firstUserDistinctId }, - { distinctId: secondUserDistinctId }, - ]) - const state: PersonState = personState({}, hub) - jest.spyOn(hub.db.kafkaProducer, 'queueMessages') - const [person, kafkaAcks] = await state.merge( - secondUserDistinctId, - firstUserDistinctId, - teamId, - timestamp - ) - await hub.db.kafkaProducer.flush() - await kafkaAcks - - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: firstUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: true, - }) - ) - expect(hub.db.updatePersonDeprecated).not.toHaveBeenCalled() - expect(hub.db.kafkaProducer.queueMessages).not.toHaveBeenCalled() - }) - it(`postgres and clickhouse get updated`, async () => { - const first: InternalPerson = await hub.db.createPerson( - timestamp, - {}, - {}, - {}, - teamId, - null, - false, - firstUserUuid, - [{ distinctId: firstUserDistinctId }] - ) - const second: InternalPerson = await hub.db.createPerson( - timestamp, - {}, - {}, - {}, - teamId, - null, - false, - secondUserUuid, - [{ distinctId: secondUserDistinctId }] - ) + it(`no-op if persons already merged`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, true, firstUserUuid, [ + { distinctId: firstUserDistinctId }, + { distinctId: secondUserDistinctId }, + ]) + const state: PersonState = personState({}, hub) + jest.spyOn(hub.db.kafkaProducer, 'queueMessages') + const [person, kafkaAcks] = await state.merge(secondUserDistinctId, firstUserDistinctId, teamId, timestamp) + await hub.db.kafkaProducer.flush() + await kafkaAcks - const state: PersonState = personState({}, hub) - jest.spyOn(hub.db.kafkaProducer, 'queueMessages') - const [person, kafkaAcks] = await state.mergePeople({ - mergeInto: first, - mergeIntoDistinctId: firstUserDistinctId, - otherPerson: second, - otherPersonDistinctId: secondUserDistinctId, + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: firstUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: true, }) - await hub.db.kafkaProducer.flush() - await kafkaAcks - - expect(person).toEqual( - expect.objectContaining({ - id: expect.any(Number), - uuid: firstUserUuid, - properties: {}, - created_at: timestamp, - version: 1, - is_identified: true, - }) - ) - - expect(hub.db.updatePersonDeprecated).toHaveBeenCalledTimes(1) - expect(hub.db.kafkaProducer.queueMessages).toHaveBeenCalledTimes(1) - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(person) - expect(distinctIds).toEqual(expect.arrayContaining([firstUserDistinctId, secondUserDistinctId])) - - // verify ClickHouse persons - await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed - const clickhousePersons = await fetchPersonsRows() // but verify full state - expect(clickhousePersons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: firstUserUuid, - properties: '{}', - created_at: timestampch, - version: 1, - is_identified: 1, - }), - expect.objectContaining({ - id: secondUserUuid, - is_deleted: 1, - version: 100, - }), - ]) - ) + ) + expect(hub.db.updatePersonDeprecated).not.toHaveBeenCalled() + expect(hub.db.kafkaProducer.queueMessages).not.toHaveBeenCalled() + }) - // verify ClickHouse distinct_ids - await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) - const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(person) - expect(clickHouseDistinctIds).toEqual( - expect.arrayContaining([firstUserDistinctId, secondUserDistinctId]) - ) + it(`postgres and clickhouse get updated`, async () => { + const first: InternalPerson = await hub.db.createPerson( + timestamp, + {}, + {}, + {}, + teamId, + null, + false, + firstUserUuid, + [{ distinctId: firstUserDistinctId }] + ) + const second: InternalPerson = await hub.db.createPerson( + timestamp, + {}, + {}, + {}, + teamId, + null, + false, + secondUserUuid, + [{ distinctId: secondUserDistinctId }] + ) - // verify Postgres person_id overrides, if applicable - if (overridesMode) { - const overrides = await overridesMode.fetchPostgresPersonIdOverrides(hub, teamId) - expect(overrides).toEqual(new Set([{ old_person_id: second.uuid, override_person_id: first.uuid }])) - // & CH person overrides - // TODO - } + const state: PersonState = personState({}, hub) + jest.spyOn(hub.db.kafkaProducer, 'queueMessages') + const [person, kafkaAcks] = await state.mergePeople({ + mergeInto: first, + mergeIntoDistinctId: firstUserDistinctId, + otherPerson: second, + otherPersonDistinctId: secondUserDistinctId, }) + await hub.db.kafkaProducer.flush() + await kafkaAcks - it(`throws if postgres unavailable`, async () => { - const first: InternalPerson = await hub.db.createPerson( - timestamp, - {}, - {}, - {}, - teamId, - null, - false, - firstUserUuid, - [{ distinctId: firstUserDistinctId }] - ) - const second: InternalPerson = await hub.db.createPerson( - timestamp, - {}, - {}, - {}, - teamId, - null, - false, - secondUserUuid, - [{ distinctId: secondUserDistinctId }] - ) - - const state: PersonState = personState({}, hub) - // break postgres - const error = new DependencyUnavailableError('testing', 'Postgres', new Error('test')) - jest.spyOn(hub.db.postgres, 'transaction').mockImplementation(() => { - throw error + expect(person).toEqual( + expect.objectContaining({ + id: expect.any(Number), + uuid: firstUserUuid, + properties: {}, + created_at: timestamp, + version: 1, + is_identified: true, }) - jest.spyOn(hub.db.kafkaProducer, 'queueMessages') - await expect( - state.mergePeople({ - mergeInto: first, - mergeIntoDistinctId: firstUserDistinctId, - otherPerson: second, - otherPersonDistinctId: secondUserDistinctId, - }) - ).rejects.toThrow(error) - await hub.db.kafkaProducer.flush() - - expect(hub.db.postgres.transaction).toHaveBeenCalledTimes(1) - jest.spyOn(hub.db.postgres, 'transaction').mockRestore() - expect(hub.db.kafkaProducer.queueMessages).not.toBeCalled() - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(Number), - uuid: firstUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - expect.objectContaining({ - id: expect.any(Number), - uuid: secondUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - ]) - ) - }) - - it(`retries merges up to retry limit if postgres down`, async () => { - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, firstUserUuid, [ - { distinctId: firstUserDistinctId }, - ]) - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, secondUserUuid, [ - { distinctId: secondUserDistinctId }, - ]) + ) - const state: PersonState = personState({}, hub) - // break postgres - const error = new DependencyUnavailableError('testing', 'Postgres', new Error('test')) - jest.spyOn(state, 'mergePeople').mockImplementation(() => { - throw error - }) - jest.spyOn(hub.db.kafkaProducer, 'queueMessages') - await expect(state.merge(secondUserDistinctId, firstUserDistinctId, teamId, timestamp)).rejects.toThrow( - error - ) + expect(hub.db.updatePersonDeprecated).toHaveBeenCalledTimes(1) + expect(hub.db.kafkaProducer.queueMessages).toHaveBeenCalledTimes(1) + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons.length).toEqual(1) + expect(persons[0]).toEqual(person) - await hub.db.kafkaProducer.flush() - - expect(state.mergePeople).toHaveBeenCalledTimes(3) - jest.spyOn(state, 'mergePeople').mockRestore() - expect(hub.db.kafkaProducer.queueMessages).not.toBeCalled() - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(Number), - uuid: firstUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - expect.objectContaining({ - id: expect.any(Number), - uuid: secondUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - ]) - ) - }) + // verify Postgres distinct_ids + const distinctIds = await hub.db.fetchDistinctIdValues(person) + expect(distinctIds).toEqual(expect.arrayContaining([firstUserDistinctId, secondUserDistinctId])) - it(`handleIdentifyOrAlias does not throw on merge failure`, async () => { - // TODO: This the current state, we should probably change it - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, firstUserUuid, [ - { distinctId: firstUserDistinctId }, - ]) - await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, secondUserUuid, [ - { distinctId: secondUserDistinctId }, + // verify ClickHouse persons + await delayUntilEventIngested(() => fetchPersonsRowsWithVersionHigerEqualThan(), 2) // wait until merge and delete processed + const clickhousePersons = await fetchPersonsRows() // but verify full state + expect(clickhousePersons).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: firstUserUuid, + properties: '{}', + created_at: timestampch, + version: 1, + is_identified: 1, + }), + expect.objectContaining({ + id: secondUserUuid, + is_deleted: 1, + version: 100, + }), ]) + ) - const state: PersonState = personState( - { - event: '$merge_dangerously', - distinct_id: firstUserDistinctId, - properties: { alias: secondUserDistinctId }, - }, - hub - ) - // break postgres - const error = new DependencyUnavailableError('testing', 'Postgres', new Error('test')) - jest.spyOn(state, 'mergePeople').mockImplementation(() => { - throw error - }) - jest.spyOn(hub.db.kafkaProducer, 'queueMessages') - await state.handleIdentifyOrAlias() - await hub.db.kafkaProducer.flush() - - expect(state.mergePeople).toHaveBeenCalledTimes(3) - jest.spyOn(state, 'mergePeople').mockRestore() - expect(hub.db.kafkaProducer.queueMessages).not.toBeCalled() - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(Number), - uuid: firstUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - expect.objectContaining({ - id: expect.any(Number), - uuid: secondUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - ]) - ) - }) - - it(`does not commit partial transactions on override conflicts`, async () => { - if (!overridesMode?.supportsSyncTransaction) { - return - } - const first: InternalPerson = await hub.db.createPerson( - timestamp, - {}, - {}, - {}, - teamId, - null, - false, - firstUserUuid, - [{ distinctId: firstUserDistinctId }] - ) - const second: InternalPerson = await hub.db.createPerson( - timestamp, - {}, - {}, - {}, - teamId, - null, - false, - secondUserUuid, - [{ distinctId: secondUserDistinctId }] - ) - - const state: PersonState = personState({}, hub) - const originalPostgresQuery = hub.db.postgres.query.bind(hub.db.postgres) - const error = new Error('Conflict') - const mockPostgresQuery = jest - .spyOn(hub.db.postgres, 'query') - .mockImplementation( - async ( - use: PostgresUse, - query: any, - values: any[] | undefined, - tag: string, - ...args: any[] - ) => { - if (tag === 'transitivePersonOverrides') { - throw error - } - return await originalPostgresQuery(use, query, values, tag, ...args) - } - ) - - jest.spyOn(hub.db.kafkaProducer, 'queueMessages') - await expect( - state.mergePeople({ - mergeInto: first, - mergeIntoDistinctId: firstUserDistinctId, - otherPerson: second, - otherPersonDistinctId: secondUserDistinctId, - }) - ).rejects.toThrow(error) - await hub.db.kafkaProducer.flush() - - // verify Postgres persons - const personsAfterFailure = await fetchPostgresPersonsH() - expect(personsAfterFailure).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(Number), - uuid: firstUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - expect.objectContaining({ - id: expect.any(Number), - uuid: secondUserUuid, - properties: {}, - created_at: timestamp, - version: 0, - is_identified: false, - }), - ]) - ) - - // verify Postgres distinct_ids - const distinctIdsAfterFailure = [ - await hub.db.fetchDistinctIdValues(personsAfterFailure[0]), - await hub.db.fetchDistinctIdValues(personsAfterFailure[1]), - ] - expect(distinctIdsAfterFailure).toEqual( - expect.arrayContaining([[firstUserDistinctId], [secondUserDistinctId]]) - ) + // verify ClickHouse distinct_ids + await delayUntilEventIngested(() => fetchDistinctIdsClickhouseVersion1()) + const clickHouseDistinctIds = await fetchDistinctIdsClickhouse(person) + expect(clickHouseDistinctIds).toEqual(expect.arrayContaining([firstUserDistinctId, secondUserDistinctId])) + }) - // verify Postgres person_id overrides - const overridesAfterFailure = await overridesMode!.fetchPostgresPersonIdOverrides(hub, teamId) - expect(overridesAfterFailure).toEqual(new Set()) + it(`throws if postgres unavailable`, async () => { + const first: InternalPerson = await hub.db.createPerson( + timestamp, + {}, + {}, + {}, + teamId, + null, + false, + firstUserUuid, + [{ distinctId: firstUserDistinctId }] + ) + const second: InternalPerson = await hub.db.createPerson( + timestamp, + {}, + {}, + {}, + teamId, + null, + false, + secondUserUuid, + [{ distinctId: secondUserDistinctId }] + ) - // Now verify we successfully get to our target state if we do not have - // any db errors. - mockPostgresQuery.mockRestore() - const [person, kafkaAcks] = await state.mergePeople({ + const state: PersonState = personState({}, hub) + // break postgres + const error = new DependencyUnavailableError('testing', 'Postgres', new Error('test')) + jest.spyOn(hub.db.postgres, 'transaction').mockImplementation(() => { + throw error + }) + jest.spyOn(hub.db.kafkaProducer, 'queueMessages') + await expect( + state.mergePeople({ mergeInto: first, mergeIntoDistinctId: firstUserDistinctId, otherPerson: second, otherPersonDistinctId: secondUserDistinctId, }) - await hub.db.kafkaProducer.flush() - await kafkaAcks + ).rejects.toThrow(error) + await hub.db.kafkaProducer.flush() - expect(person).toEqual( + expect(hub.db.postgres.transaction).toHaveBeenCalledTimes(1) + jest.spyOn(hub.db.postgres, 'transaction').mockRestore() + expect(hub.db.kafkaProducer.queueMessages).not.toBeCalled() + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons).toEqual( + expect.arrayContaining([ expect.objectContaining({ id: expect.any(Number), uuid: firstUserUuid, properties: {}, created_at: timestamp, - version: 1, - is_identified: true, - }) - ) - - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual(person) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(person) - expect(distinctIds).toEqual(expect.arrayContaining([firstUserDistinctId, secondUserDistinctId])) - - // verify Postgres person_id overrides - const overrides = await overridesMode!.fetchPostgresPersonIdOverrides(hub, teamId) - expect(overrides).toEqual(new Set([{ old_person_id: second.uuid, override_person_id: first.uuid }])) - }) - - it(`handles a chain of overrides being applied concurrently`, async () => { - const first: InternalPerson = await hub.db.createPerson( - timestamp, - { first: true }, - {}, - {}, - teamId, - null, - false, - firstUserUuid, - [{ distinctId: firstUserDistinctId }] - ) - const second: InternalPerson = await hub.db.createPerson( - timestamp.plus({ minutes: 2 }), - { second: true }, - {}, - {}, - teamId, - null, - false, - secondUserUuid, - [{ distinctId: secondUserDistinctId }] - ) - const third: InternalPerson = await hub.db.createPerson( - timestamp.plus({ minutes: 5 }), - { third: true }, - {}, - {}, - teamId, - null, - false, - new UUIDT().toString(), - [{ distinctId: 'third' }] - ) - - // We want to simulate a concurrent update to person_overrides. We do - // this by first mocking the implementation to block at a certain point - // in the transaction, then running the update function twice. - // We then wait for them to block before letting them resume. - let resumeExecution: (value: unknown) => void - - const postgresTransaction = hub.db.postgres.transaction.bind(hub.db.postgres) - jest.spyOn(hub.db.postgres, 'transaction').mockImplementation( - async (use: PostgresUse, tag: string, transaction: any) => { - if (tag === 'mergePeople') { - return await postgresTransaction(use, tag, async (client) => { - if (resumeExecution) { - resumeExecution(undefined) - } else { - await new Promise((resolve) => { - resumeExecution = resolve - }) - } - - return await transaction(client) - }) - } else { - return await postgresTransaction(use, tag, transaction) - } - } - ) - - await Promise.all([ - personState( - { - event: '$merge_dangerously', - distinct_id: firstUserDistinctId, - properties: { - alias: secondUserDistinctId, - }, - }, - hub - ).handleIdentifyOrAlias(), - personState( - { - event: '$merge_dangerously', - distinct_id: secondUserDistinctId, - properties: { - alias: 'third', - }, - }, - hub - ).handleIdentifyOrAlias(), - ]) - - // Note: we can't verify anything here because the concurrency might have enabled both merges to already happen. - - await Promise.all([ - personState( - { - event: '$merge_dangerously', - distinct_id: firstUserDistinctId, - properties: { - alias: secondUserDistinctId, - }, - }, - hub - ).handleIdentifyOrAlias(), - personState( - { - event: '$merge_dangerously', - distinct_id: secondUserDistinctId, - properties: { - alias: 'third', - }, - }, - hub - ).handleIdentifyOrAlias(), - ]) - - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual( + version: 0, + is_identified: false, + }), expect.objectContaining({ id: expect.any(Number), - uuid: firstUserUuid, // guaranteed to be merged into this based on timestamps - // There's a race condition in our code where - // if different distinctIDs are used same time, - // then pros can be dropped, see https://docs.google.com/presentation/d/1Osz7r8bKkDD5yFzw0cCtsGVf1LTEifXS-dzuwaS8JGY - // properties: { first: true, second: true, third: true }, + uuid: secondUserUuid, + properties: {}, created_at: timestamp, - // This is 2 because they all start with version 0, and then: x - // third -> second = max(third(0), second(0)) + 1 == version 1 - // second -> first = max(second(1), first(0)) + 1 == version 2 - version: 2, - is_identified: true, - }) - ) + version: 0, + is_identified: false, + }), + ]) + ) + }) - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual( - expect.arrayContaining([firstUserDistinctId, secondUserDistinctId, 'third']) - ) + it(`retries merges up to retry limit if postgres down`, async () => { + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, firstUserUuid, [ + { distinctId: firstUserDistinctId }, + ]) + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, secondUserUuid, [ + { distinctId: secondUserDistinctId }, + ]) - // verify Postgres person_id overrides, if applicable - if (overridesMode) { - const overrides = await overridesMode.fetchPostgresPersonIdOverrides(hub, teamId) - expect(overrides).toEqual( - new Set([ - { old_person_id: second.uuid, override_person_id: first.uuid }, - { old_person_id: third.uuid, override_person_id: first.uuid }, - ]) - ) - } + const state: PersonState = personState({}, hub) + // break postgres + const error = new DependencyUnavailableError('testing', 'Postgres', new Error('test')) + jest.spyOn(state, 'mergePeople').mockImplementation(() => { + throw error }) + jest.spyOn(hub.db.kafkaProducer, 'queueMessages') + await expect(state.merge(secondUserDistinctId, firstUserDistinctId, teamId, timestamp)).rejects.toThrow( + error + ) - it(`handles a chain of overrides being applied out of order`, async () => { - const first: InternalPerson = await hub.db.createPerson( - timestamp, - { first: true }, - {}, - {}, - teamId, - null, - false, - firstUserUuid, - [{ distinctId: firstUserDistinctId }] - ) - const second: InternalPerson = await hub.db.createPerson( - timestamp.plus({ minutes: 2 }), - { second: true }, - {}, - {}, - teamId, - null, - false, - secondUserUuid, - [{ distinctId: secondUserDistinctId }] - ) - const third: InternalPerson = await hub.db.createPerson( - timestamp.plus({ minutes: 5 }), - { third: true }, - {}, - {}, - teamId, - null, - false, - new UUIDT().toString(), - [{ distinctId: 'third' }] - ) - - await personState( - { - event: '$merge_dangerously', - distinct_id: secondUserDistinctId, - properties: { - alias: 'third', - }, - }, - hub - ).handleIdentifyOrAlias() - - await personState( - { - event: '$merge_dangerously', - distinct_id: firstUserDistinctId, - properties: { - alias: secondUserDistinctId, - }, - }, - hub - ).handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() - // verify Postgres persons - const persons = await fetchPostgresPersonsH() - expect(persons.length).toEqual(1) - expect(persons[0]).toEqual( + expect(state.mergePeople).toHaveBeenCalledTimes(3) + jest.spyOn(state, 'mergePeople').mockRestore() + expect(hub.db.kafkaProducer.queueMessages).not.toBeCalled() + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons).toEqual( + expect.arrayContaining([ expect.objectContaining({ id: expect.any(Number), - uuid: firstUserUuid, // guaranteed to be merged into this based on timestamps - properties: { first: true, second: true, third: true }, + uuid: firstUserUuid, + properties: {}, created_at: timestamp, - // This is 2 because they all start with version 0, and then: - // third -> second = max(third(0), second(0)) + 1 == version 1 - // second -> first = max(second(1), first(0)) + 1 == version 2 - version: 2, - is_identified: true, - }) - ) - - // verify Postgres distinct_ids - const distinctIds = await hub.db.fetchDistinctIdValues(persons[0]) - expect(distinctIds).toEqual( - expect.arrayContaining([firstUserDistinctId, secondUserDistinctId, 'third']) - ) - - // verify Postgres person_id overrides, if applicable - if (overridesMode) { - const overrides = await overridesMode.fetchPostgresPersonIdOverrides(hub, teamId) - expect(overrides).toEqual( - new Set([ - { old_person_id: second.uuid, override_person_id: first.uuid }, - { old_person_id: third.uuid, override_person_id: first.uuid }, - ]) - ) - } - }) - }) - }) -}) - -describe('flat person overrides writer', () => { - let hub: Hub - let closeHub: () => Promise - - let organizationId: string - let teamId: number - let writer: FlatPersonOverrideWriter - - beforeAll(async () => { - ;[hub, closeHub] = await createHub({}) - organizationId = await createOrganization(hub.db.postgres) - writer = new FlatPersonOverrideWriter(hub.db.postgres) - }) - - beforeEach(async () => { - teamId = await createTeam(hub.db.postgres, organizationId) - }) - - afterAll(async () => { - await closeHub() - }) - - it('handles direct overrides', async () => { - const { postgres } = hub.db - - const defaults = { - team_id: teamId, - oldest_event: DateTime.fromMillis(0), - } - - const override = { - old_person_id: new UUIDT().toString(), - override_person_id: new UUIDT().toString(), - } - - await postgres.transaction(PostgresUse.COMMON_WRITE, '', async (tx) => { - await writer.addPersonOverride(tx, { ...defaults, ...override }) - }) - - expect(await writer.getPersonOverrides(teamId)).toEqual([{ ...defaults, ...override }]) - }) - - it('handles transitive overrides', async () => { - const { postgres } = hub.db - - const defaults = { - team_id: teamId, - oldest_event: DateTime.fromMillis(0), - } - - const overrides = [ - { - old_person_id: new UUIDT().toString(), - override_person_id: new UUIDT().toString(), - }, - ] - - overrides.push({ - old_person_id: overrides[0].override_person_id, - override_person_id: new UUIDT().toString(), - }) - - await postgres.transaction(PostgresUse.COMMON_WRITE, '', async (tx) => { - for (const override of overrides) { - await writer.addPersonOverride(tx, { ...defaults, ...override }) - } - }) - - expect(new Set(await writer.getPersonOverrides(teamId))).toEqual( - new Set( - overrides.map(({ old_person_id }) => ({ - old_person_id, - override_person_id: overrides.at(-1)!.override_person_id, - ...defaults, - })) + version: 0, + is_identified: false, + }), + expect.objectContaining({ + id: expect.any(Number), + uuid: secondUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: false, + }), + ]) ) - ) - }) -}) - -describe('deferred person overrides', () => { - let hub: Hub - let closeHub: () => Promise - - // not always used, but used more often then not - let organizationId: string - let teamId: number - - let writer: DeferredPersonOverrideWriter - let syncWriter: FlatPersonOverrideWriter - let worker: DeferredPersonOverrideWorker - - beforeAll(async () => { - ;[hub, closeHub] = await createHub({}) - organizationId = await createOrganization(hub.db.postgres) - writer = new DeferredPersonOverrideWriter(hub.db.postgres) - syncWriter = new FlatPersonOverrideWriter(hub.db.postgres) - worker = new DeferredPersonOverrideWorker(hub.db.postgres, hub.db.kafkaProducer, syncWriter) - }) - - beforeEach(async () => { - teamId = await createTeam(hub.db.postgres, organizationId) - await hub.db.postgres.query( - PostgresUse.COMMON_WRITE, - 'TRUNCATE TABLE posthog_pendingpersonoverride', - undefined, - '' - ) - }) - - afterEach(() => { - jest.restoreAllMocks() - }) - - afterAll(async () => { - await closeHub() - }) - - const getPendingPersonOverrides = async () => { - const { rows } = await hub.db.postgres.query( - PostgresUse.COMMON_WRITE, - `SELECT old_person_id, override_person_id - FROM posthog_pendingpersonoverride - WHERE team_id = ${teamId}`, - undefined, - '' - ) - return rows - } - - it('moves overrides from the pending table to the overrides table', async () => { - const { postgres } = hub.db - - const override = { - old_person_id: new UUIDT().toString(), - override_person_id: new UUIDT().toString(), - } - - await postgres.transaction(PostgresUse.COMMON_WRITE, '', async (tx) => { - await writer.addPersonOverride(tx, { team_id: teamId, ...override, oldest_event: DateTime.fromMillis(0) }) }) - expect(await getPendingPersonOverrides()).toEqual([override]) - - expect(await worker.processPendingOverrides()).toEqual(1) - - expect(await getPendingPersonOverrides()).toMatchObject([]) - - expect( - (await syncWriter.getPersonOverrides(teamId)).map(({ old_person_id, override_person_id }) => [ - old_person_id, - override_person_id, + it(`handleIdentifyOrAlias does not throw on merge failure`, async () => { + // TODO: This the current state, we should probably change it + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, firstUserUuid, [ + { distinctId: firstUserDistinctId }, + ]) + await hub.db.createPerson(timestamp, {}, {}, {}, teamId, null, false, secondUserUuid, [ + { distinctId: secondUserDistinctId }, ]) - ).toEqual([[override.old_person_id, override.override_person_id]]) - - const clickhouseOverrides = await waitForExpect(async () => { - const { data } = await hub.db.clickhouse.querying( - ` - SELECT old_person_id, override_person_id - FROM person_overrides - WHERE team_id = ${teamId} - `, - { dataObjects: true } - ) - expect(data).toHaveLength(1) - return data - }) - expect(clickhouseOverrides).toEqual([override]) - }) - - it('rolls back on kafka producer error', async () => { - const { postgres } = hub.db - - const override = { - old_person_id: new UUIDT().toString(), - override_person_id: new UUIDT().toString(), - } - - await postgres.transaction(PostgresUse.COMMON_WRITE, '', async (tx) => { - await writer.addPersonOverride(tx, { team_id: teamId, ...override, oldest_event: DateTime.fromMillis(0) }) - }) - - expect(await getPendingPersonOverrides()).toEqual([override]) - - jest.spyOn(hub.db.kafkaProducer, 'queueMessages').mockImplementation(() => { - throw new Error('something bad happened') - }) - - await expect(worker.processPendingOverrides()).rejects.toThrow() - - expect(await getPendingPersonOverrides()).toEqual([override]) - }) - - it('ensures advisory lock is held before processing', async () => { - const { postgres } = hub.db - - let acquiredLock: boolean - const tryLockComplete = new WaitEvent() - const readyToReleaseLock = new WaitEvent() - const transactionHolder = postgres - .transaction(PostgresUse.COMMON_WRITE, '', async (tx) => { - const { rows } = await postgres.query( - tx, - `SELECT pg_try_advisory_lock(${worker.lockId}) as acquired, pg_backend_pid()`, - undefined, - '' - ) - ;[{ acquired: acquiredLock }] = rows - tryLockComplete.set() - await readyToReleaseLock.wait() - }) - .then(() => { - acquiredLock = false + const state: PersonState = personState( + { + event: '$merge_dangerously', + distinct_id: firstUserDistinctId, + properties: { alias: secondUserDistinctId }, + }, + hub + ) + // break postgres + const error = new DependencyUnavailableError('testing', 'Postgres', new Error('test')) + jest.spyOn(state, 'mergePeople').mockImplementation(() => { + throw error }) + jest.spyOn(hub.db.kafkaProducer, 'queueMessages') + await state.handleIdentifyOrAlias() + await hub.db.kafkaProducer.flush() - try { - await tryLockComplete.wait() - expect(acquiredLock!).toBe(true) - await expect(worker.processPendingOverrides()).rejects.toThrow(Error('could not acquire lock')) - } finally { - readyToReleaseLock.set() - await transactionHolder - } - - expect(acquiredLock!).toBe(false) - await expect(worker.processPendingOverrides()).resolves.toEqual(0) - }) - - it('respects limit if provided', async () => { - const { postgres } = hub.db - - const overrides = [...Array(3)].map(() => ({ - old_person_id: new UUIDT().toString(), - override_person_id: new UUIDT().toString(), - })) - - await postgres.transaction(PostgresUse.COMMON_WRITE, '', async (tx) => { - await Promise.all( - overrides.map( - async (override) => - await writer.addPersonOverride(tx, { - team_id: teamId, - ...override, - oldest_event: DateTime.fromMillis(0), - }) - ) + expect(state.mergePeople).toHaveBeenCalledTimes(3) + jest.spyOn(state, 'mergePeople').mockRestore() + expect(hub.db.kafkaProducer.queueMessages).not.toBeCalled() + // verify Postgres persons + const persons = await fetchPostgresPersonsH() + expect(persons).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + uuid: firstUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: false, + }), + expect.objectContaining({ + id: expect.any(Number), + uuid: secondUserUuid, + properties: {}, + created_at: timestamp, + version: 0, + is_identified: false, + }), + ]) ) }) - - expect(await getPendingPersonOverrides()).toEqual(overrides) - - expect(await worker.processPendingOverrides(2)).toEqual(2) - expect(await getPendingPersonOverrides()).toMatchObject(overrides.slice(-1)) - - expect(await worker.processPendingOverrides(2)).toEqual(1) - expect(await getPendingPersonOverrides()).toEqual([]) }) }) diff --git a/posthog/api/__init__.py b/posthog/api/__init__.py index 3dc12ac96ed8e..c9722bd06d272 100644 --- a/posthog/api/__init__.py +++ b/posthog/api/__init__.py @@ -3,7 +3,13 @@ from posthog.api.routing import DefaultRouterPlusPlus from posthog.batch_exports import http as batch_exports from posthog.settings import EE_AVAILABLE -from posthog.warehouse.api import external_data_source, saved_query, table, view_link, external_data_schema +from posthog.warehouse.api import ( + external_data_source, + saved_query, + table, + view_link, + external_data_schema, +) from ..heatmaps.heatmaps_api import LegacyHeatmapViewSet, HeatmapViewSet from .session import SessionViewSet from ..session_recordings.session_recording_api import SessionRecordingViewSet @@ -174,22 +180,9 @@ def api_not_found(request): batch_exports_router = projects_router.register( r"batch_exports", batch_exports.BatchExportViewSet, "environment_batch_exports", ["team_id"] ) -batch_exports_router.register( - r"logs", - batch_exports.BatchExportLogViewSet, - "environment_batch_export_logs", - ["team_id", "batch_export_id"], -) - batch_export_runs_router = batch_exports_router.register( r"runs", batch_exports.BatchExportRunViewSet, "environment_batch_export_runs", ["team_id", "batch_export_id"] ) -batch_export_runs_router.register( - r"logs", - batch_exports.BatchExportLogViewSet, - "environment_batch_export_run_logs", - ["team_id", "batch_export_id", "run_id"], -) projects_router.register(r"warehouse_tables", table.TableViewSet, "environment_warehouse_tables", ["team_id"]) projects_router.register( diff --git a/posthog/api/log_entries.py b/posthog/api/log_entries.py index fda13747bf266..2dd9a1a92d5b2 100644 --- a/posthog/api/log_entries.py +++ b/posthog/api/log_entries.py @@ -29,7 +29,7 @@ class LogEntryRequestSerializer(serializers.Serializer): limit = serializers.IntegerField(required=False, default=50, max_value=500, min_value=1) after = serializers.DateTimeField(required=False) before = serializers.DateTimeField(required=False) - level = serializers.ListField(child=serializers.CharField(), required=False) + level = serializers.CharField(required=False) search = serializers.CharField(required=False) instance_id = serializers.CharField(required=False) @@ -72,7 +72,7 @@ def fetch_log_entries( clickhouse_kwargs["search"] = f"%{search}%" if len(level) > 0: clickhouse_where_parts.append("upper(level) in %(levels)s") - clickhouse_kwargs["levels"] = level + clickhouse_kwargs["levels"] = [lev.upper() for lev in level] clickhouse_query = f""" SELECT log_source_id, instance_id, timestamp, upper(level) as level, message FROM log_entries @@ -85,6 +85,13 @@ def fetch_log_entries( class LogEntryMixin(viewsets.GenericViewSet): log_source: str # Should be set by the inheriting class + def get_log_entry_instance_id(self) -> Optional[str]: + """ + Can be used overridden to help with getting the instance_id for the log entry. + Otherwise it defaults to null or the query param if given + """ + raise NotImplementedError() + @action(detail=True, methods=["GET"]) def logs(self, request: Request, *args, **kwargs): obj = self.get_object() @@ -99,17 +106,22 @@ def logs(self, request: Request, *args, **kwargs): params = param_serializer.validated_data + try: + instance_id = self.get_log_entry_instance_id() + except NotImplementedError: + instance_id = params.get("instance_id") + data = fetch_log_entries( team_id=self.team_id, # type: ignore log_source=self.log_source, log_source_id=str(obj.id), limit=params["limit"], # From request params - instance_id=params.get("instance_id"), + instance_id=instance_id, after=params.get("after"), before=params.get("before"), search=params.get("search"), - level=params.get("level"), + level=params["level"].split(",") if params.get("level") else None, ) page = self.paginate_queryset(data) diff --git a/posthog/api/proxy_record.py b/posthog/api/proxy_record.py index c9e3083c50d73..42e664f4989ee 100644 --- a/posthog/api/proxy_record.py +++ b/posthog/api/proxy_record.py @@ -81,7 +81,11 @@ def create(self, request, *args, **kwargs): def destroy(self, request, *args, pk=None, **kwargs): record = self.organization.proxy_records.get(id=pk) - if record and record.status in (ProxyRecord.Status.WAITING, ProxyRecord.Status.ERRORING): + if record and record.status in ( + ProxyRecord.Status.WAITING, + ProxyRecord.Status.ERRORING, + ProxyRecord.Status.TIMED_OUT, + ): record.delete() elif record: temporal = sync_connect() diff --git a/posthog/api/test/__snapshots__/test_api_docs.ambr b/posthog/api/test/__snapshots__/test_api_docs.ambr index 65096569352c0..0f05b096f8976 100644 --- a/posthog/api/test/__snapshots__/test_api_docs.ambr +++ b/posthog/api/test/__snapshots__/test_api_docs.ambr @@ -21,10 +21,7 @@ '/home/runner/work/posthog/posthog/posthog/api/app_metrics.py: Warning [HistoricalExportsAppMetricsViewSet]: could not derive type of path parameter "plugin_config_id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. ) or annotating the parameter type with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/app_metrics.py: Warning [HistoricalExportsAppMetricsViewSet]: could not derive type of path parameter "id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. ) or annotating the parameter type with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportViewSet]: could not derive type of path parameter "project_id" because model "posthog.batch_exports.models.BatchExport" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', - '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportLogViewSet]: could not derive type of path parameter "project_id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. ) or annotating the parameter type with @extend_schema. Defaulting to "string".', - '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportLogViewSet]: could not derive type of path parameter "batch_export_id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. ) or annotating the parameter type with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportRunViewSet]: could not derive type of path parameter "project_id" because model "posthog.batch_exports.models.BatchExportRun" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', - '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportLogViewSet]: could not derive type of path parameter "run_id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. ) or annotating the parameter type with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/cohort.py: Warning [CohortViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.cohort.cohort.Cohort" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/dashboards/dashboard_templates.py: Warning [DashboardTemplateViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.dashboard_templates.DashboardTemplate" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/dashboards/dashboard.py: Warning [DashboardsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.dashboard.Dashboard" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', @@ -108,6 +105,7 @@ 'Warning: operationId "batch_exports_partial_update" has collisions [(\'/api/organizations/{organization_id}/batch_exports/{id}/\', \'patch\'), (\'/api/projects/{project_id}/batch_exports/{id}/\', \'patch\')]. resolving with numeral suffixes.', 'Warning: operationId "batch_exports_destroy" has collisions [(\'/api/organizations/{organization_id}/batch_exports/{id}/\', \'delete\'), (\'/api/projects/{project_id}/batch_exports/{id}/\', \'delete\')]. resolving with numeral suffixes.', 'Warning: operationId "batch_exports_backfill_create" has collisions [(\'/api/organizations/{organization_id}/batch_exports/{id}/backfill/\', \'post\'), (\'/api/projects/{project_id}/batch_exports/{id}/backfill/\', \'post\')]. resolving with numeral suffixes.', + 'Warning: operationId "batch_exports_logs_retrieve" has collisions [(\'/api/organizations/{organization_id}/batch_exports/{id}/logs/\', \'get\'), (\'/api/projects/{project_id}/batch_exports/{id}/logs/\', \'get\')]. resolving with numeral suffixes.', 'Warning: operationId "batch_exports_pause_create" has collisions [(\'/api/organizations/{organization_id}/batch_exports/{id}/pause/\', \'post\'), (\'/api/projects/{project_id}/batch_exports/{id}/pause/\', \'post\')]. resolving with numeral suffixes.', 'Warning: operationId "batch_exports_unpause_create" has collisions [(\'/api/organizations/{organization_id}/batch_exports/{id}/unpause/\', \'post\'), (\'/api/projects/{project_id}/batch_exports/{id}/unpause/\', \'post\')]. resolving with numeral suffixes.', 'Warning: operationId "app_metrics_historical_exports_retrieve" has collisions [(\'/api/projects/{project_id}/app_metrics/{plugin_config_id}/historical_exports/\', \'get\'), (\'/api/projects/{project_id}/app_metrics/{plugin_config_id}/historical_exports/{id}/\', \'get\')]. resolving with numeral suffixes.', diff --git a/posthog/api/test/batch_exports/test_log_entry.py b/posthog/api/test/batch_exports/test_log_entry.py deleted file mode 100644 index 06dcb6ce4a8d2..0000000000000 --- a/posthog/api/test/batch_exports/test_log_entry.py +++ /dev/null @@ -1,338 +0,0 @@ -import datetime as dt -import uuid - -import pytest -from freezegun import freeze_time - -from posthog.api.test.batch_exports.conftest import start_test_worker -from posthog.api.test.batch_exports.operations import ( - create_batch_export_ok, - get_batch_export_log_entries, - get_batch_export_run_log_entries, -) -from posthog.api.test.test_organization import create_organization -from posthog.api.test.test_team import create_team -from posthog.api.test.test_user import create_user -from posthog.batch_exports.models import ( - BatchExportLogEntryLevel, - fetch_batch_export_log_entries, -) -from posthog.client import sync_execute -from posthog.temporal.common.client import sync_connect - - -def create_batch_export_log_entry( - *, - team_id: int, - batch_export_id: str, - run_id: str | None, - message: str, - level: BatchExportLogEntryLevel, -): - from posthog.clickhouse.log_entries import INSERT_LOG_ENTRY_SQL - - sync_execute( - INSERT_LOG_ENTRY_SQL, - { - "team_id": team_id, - "log_source": "batch_exports", - "log_source_id": batch_export_id, - "instance_id": run_id, - "timestamp": dt.datetime.now(dt.UTC).strftime("%Y-%m-%d %H:%M:%S.%f"), - "level": level, - "message": message, - }, - ) - - -@pytest.fixture -def organization(): - organization = create_organization("Test Org") - - yield organization - - organization.delete() - - -@pytest.fixture -def team(organization): - team = create_team(organization) - - yield team - - team.delete() - - -@pytest.fixture -def batch_export(client, organization, team): - user = create_user("test@user.com", "Test User", organization) - client.force_login(user) - - temporal = sync_connect() - - destination_data = { - "type": "S3", - "config": { - "bucket_name": "my-production-s3-bucket", - "region": "us-east-1", - "prefix": "posthog-events/", - "aws_access_key_id": "abc123", - "aws_secret_access_key": "secret", - }, - } - - batch_export_data = { - "name": "my-production-s3-bucket-destination", - "destination": destination_data, - "interval": "hour", - "start_at": "2023-07-19 00:00:00", - "end_at": "2023-07-20 00:00:00", - } - with start_test_worker(temporal): - batch_export = create_batch_export_ok( - client, - team.pk, - batch_export_data, - ) - - yield batch_export - - -@pytest.mark.django_db -def test_simple_log_is_fetched(batch_export, team): - """Test the simple case of fetching a batch export log entry.""" - with freeze_time("2023-09-22 01:00:00"): - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=None, - message="Test log. Much INFO.", - level=BatchExportLogEntryLevel.INFO, - ) - - results = fetch_batch_export_log_entries( - team_id=team.pk, - batch_export_id=batch_export["id"], - after=dt.datetime(2023, 9, 22, 0, 59, 59), - before=dt.datetime(2023, 9, 22, 1, 0, 1), - ) - - assert len(results) == 1 - assert results[0].message == "Test log. Much INFO." - assert results[0].level == BatchExportLogEntryLevel.INFO - assert results[0].batch_export_id == str(batch_export["id"]) - - -@pytest.mark.django_db -@pytest.mark.parametrize( - "level", - [ - BatchExportLogEntryLevel.INFO, - BatchExportLogEntryLevel.WARNING, - BatchExportLogEntryLevel.ERROR, - BatchExportLogEntryLevel.DEBUG, - ], -) -def test_log_level_filter(batch_export, team, level): - """Test fetching a batch export log entries of a particular level.""" - with freeze_time("2023-09-22 01:00:00"): - for message in ("Test log 1", "Test log 2"): - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=None, - message=message, - level=level, - ) - - results = [] - timeout = 10 - start = dt.datetime.now(dt.UTC) - - while not results: - results = fetch_batch_export_log_entries( - team_id=team.pk, - batch_export_id=batch_export["id"], - level_filter=[level], - after=dt.datetime(2023, 9, 22, 0, 59, 59), - before=dt.datetime(2023, 9, 22, 1, 0, 1), - ) - if (dt.datetime.now(dt.UTC) - start) > dt.timedelta(seconds=timeout): - break - - results.sort(key=lambda record: record.message) - - assert len(results) == 2 - assert results[0].message == "Test log 1" - assert results[0].level == level - assert results[0].batch_export_id == str(batch_export["id"]) - assert results[1].message == "Test log 2" - assert results[1].level == level - assert results[1].batch_export_id == str(batch_export["id"]) - - -@pytest.mark.django_db -@pytest.mark.parametrize( - "level", - [ - BatchExportLogEntryLevel.INFO, - BatchExportLogEntryLevel.WARNING, - BatchExportLogEntryLevel.ERROR, - BatchExportLogEntryLevel.DEBUG, - ], -) -def test_log_level_filter_with_lowercase(batch_export, team, level): - """Test fetching a batch export log entries of a particular level.""" - with freeze_time("2023-09-22 01:00:00"): - for message in ("Test log 1", "Test log 2"): - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=None, - message=message, - level=level.lower(), - ) - - results = [] - timeout = 10 - start = dt.datetime.now(dt.UTC) - - while not results: - results = fetch_batch_export_log_entries( - team_id=team.pk, - batch_export_id=batch_export["id"], - level_filter=[level], - after=dt.datetime(2023, 9, 22, 0, 59, 59), - before=dt.datetime(2023, 9, 22, 1, 0, 1), - ) - if (dt.datetime.now(dt.UTC) - start) > dt.timedelta(seconds=timeout): - break - - results.sort(key=lambda record: record.message) - - assert len(results) == 2 - assert results[0].message == "Test log 1" - assert results[0].level == level - assert results[0].batch_export_id == str(batch_export["id"]) - assert results[1].message == "Test log 2" - assert results[1].level == level - assert results[1].batch_export_id == str(batch_export["id"]) - - -@pytest.mark.django_db -def test_batch_export_log_api(client, batch_export, team): - """Test fetching batch export log entries using the API.""" - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=str(uuid.uuid4()), - message="Test log. Much INFO.", - level=BatchExportLogEntryLevel.INFO, - ) - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=str(uuid.uuid4()), - message="Test log. Much ERROR.", - level=BatchExportLogEntryLevel.ERROR, - ) - - response = get_batch_export_log_entries( - client, - team_id=team.pk, - batch_export_id=batch_export["id"], - ) - - json_response = response.json() - results = json_response["results"] - - assert response.status_code == 200 - assert json_response["count"] == 2 - assert len(results) == 2 - # Logs are ordered by timestamp DESC, so ERROR log comes first. - assert results[0]["message"] == "Test log. Much ERROR." - assert results[0]["level"] == BatchExportLogEntryLevel.ERROR - assert results[0]["batch_export_id"] == str(batch_export["id"]) - assert results[1]["message"] == "Test log. Much INFO." - assert results[1]["level"] == BatchExportLogEntryLevel.INFO - assert results[1]["batch_export_id"] == str(batch_export["id"]) - - -@pytest.mark.django_db -def test_batch_export_run_log_api(client, batch_export, team): - """Test fetching batch export run log entries using the API.""" - run_id = str(uuid.uuid4()) - - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=run_id, - message="Test log. Much INFO.", - level=BatchExportLogEntryLevel.INFO, - ) - - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - # Logs from a different run shouldn't be in results. - run_id=str(uuid.uuid4()), - message="Test log. Much INFO.", - level=BatchExportLogEntryLevel.INFO, - ) - - response = get_batch_export_run_log_entries( - client, - team_id=team.pk, - batch_export_id=batch_export["id"], - run_id=run_id, - ) - - json_response = response.json() - results = json_response["results"] - - assert response.status_code == 200 - assert json_response["count"] == 1 - assert len(results) == 1 - assert results[0]["message"] == "Test log. Much INFO." - assert results[0]["level"] == BatchExportLogEntryLevel.INFO - assert results[0]["batch_export_id"] == str(batch_export["id"]) - - -@pytest.mark.django_db -def test_batch_export_run_log_api_with_level_filter(client, batch_export, team): - """Test fetching batch export run log entries using the API.""" - run_id = str(uuid.uuid4()) - - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=run_id, - message="Test log. Much INFO.", - level=BatchExportLogEntryLevel.INFO, - ) - - create_batch_export_log_entry( - team_id=team.pk, - batch_export_id=str(batch_export["id"]), - run_id=run_id, - message="Test log. Much DEBUG.", - level=BatchExportLogEntryLevel.DEBUG, - ) - - response = get_batch_export_run_log_entries( - client, - team_id=team.pk, - batch_export_id=batch_export["id"], - run_id=run_id, - level_filter="info", - ) - - json_response = response.json() - results = json_response["results"] - - assert response.status_code == 200 - assert json_response["count"] == 1 - assert len(results) == 1 - assert results[0]["message"] == "Test log. Much INFO." - assert results[0]["level"] == BatchExportLogEntryLevel.INFO - assert results[0]["batch_export_id"] == str(batch_export["id"]) diff --git a/posthog/api/test/test_log_entries.py b/posthog/api/test/test_log_entries.py new file mode 100644 index 0000000000000..fdcdd48deee97 --- /dev/null +++ b/posthog/api/test/test_log_entries.py @@ -0,0 +1,115 @@ +from datetime import UTC, datetime + +from rest_framework import status + +from posthog.clickhouse.client.execute import sync_execute +from posthog.models.hog_functions.hog_function import HogFunction +from posthog.test.base import APIBaseTest, ClickhouseTestMixin, QueryMatchingTest + + +def create_log_entry( + team_id: int, + log_source: str, + log_source_id: str, + message: str, + level: str, + instance_id: str | None = None, + timestamp: str | None = None, +): + from posthog.clickhouse.log_entries import INSERT_LOG_ENTRY_SQL + + sync_execute( + INSERT_LOG_ENTRY_SQL, + { + "team_id": team_id, + "log_source": log_source, + "log_source_id": log_source_id, + "instance_id": instance_id, + "timestamp": timestamp or datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S.%f"), + "level": level, + "message": message, + }, + ) + + +class TestLogEntries(ClickhouseTestMixin, APIBaseTest, QueryMatchingTest): + hog_function: HogFunction + + def setUp(self): + super().setUp() + # Create a base hog function to use as the reference for log entries + self.hog_function = HogFunction.objects.create( + team=self.team, + name="Fetch URL", + description="Test description", + hog="fetch(inputs.url);", + ) + + def get_log_entries(self, params=None): + return self.client.get(f"/api/projects/{self.team.id}/hog_functions/{self.hog_function.id}/logs/", params) + + def create_log_for_function(self, level: str, instance_id="instance-id-1", message=None, timestamp=None): + create_log_entry( + team_id=self.team.pk, + log_source="hog_function", + log_source_id=str(self.hog_function.pk), + instance_id=instance_id, + message=message or f"Test log. Much {level}.", + level=level, + timestamp=timestamp, + ) + + def test_returns_default(self): + res = self.get_log_entries() + assert res.status_code == status.HTTP_200_OK + assert res.json() == { + "count": 0, + "next": None, + "previous": None, + "results": [], + } + + def test_returns_log_entries(self): + """Test the simple case of fetching a log entry.""" + self.create_log_for_function(level="info", timestamp="2023-09-22 01:00:00") + + results = self.get_log_entries().json()["results"] + + assert results == [ + { + "log_source_id": str(self.hog_function.pk), + "instance_id": "instance-id-1", + "timestamp": "2023-09-22T01:00:00Z", + "level": "INFO", + "message": "Test log. Much info.", + } + ] + + def test_filters_log_entries_by_level(self): + """Test the simple case of fetching a log entry.""" + self.create_log_for_function(level="info") + self.create_log_for_function(level="error") + self.create_log_for_function(level="info") + self.create_log_for_function(level="warn") + + results = self.get_log_entries({"level": "info,WARN"}).json()["results"] + + assert len(results) == 3 + assert results[0]["level"] == "WARN" + assert results[1]["level"] == "INFO" + assert results[2]["level"] == "INFO" + + def test_filters_log_entries_by_instance_id(self): + """Test the simple case of fetching a log entry.""" + self.create_log_for_function(level="info", instance_id="instance-id-1") + self.create_log_for_function(level="error", instance_id="instance-id-2") + self.create_log_for_function(level="error", instance_id="instance-id-1") + self.create_log_for_function(level="error", instance_id="instance-id-2") + + results = self.get_log_entries({"instance_id": "instance-id-1"}).json()["results"] + + assert len(results) == 2 + assert results[0]["instance_id"] == "instance-id-1" + assert results[1]["instance_id"] == "instance-id-1" + assert results[0]["level"] == "ERROR" + assert results[1]["level"] == "INFO" diff --git a/posthog/api/test/test_properties_timeline.py b/posthog/api/test/test_properties_timeline.py index b4eb9a080f17d..b32e976dd5528 100644 --- a/posthog/api/test/test_properties_timeline.py +++ b/posthog/api/test/test_properties_timeline.py @@ -315,7 +315,9 @@ def test_timeline_for_existing_actor_with_three_events_and_return_to_previous_va ) @snapshot_clickhouse_queries - @also_test_with_materialized_columns(person_properties=["bar"], materialize_only_with_person_on_events=True) + @also_test_with_materialized_columns( + person_properties=["bar"], + ) def test_timeline_for_existing_person_with_three_events_and_return_to_previous_value_at_single_day_point(self): self._create_person(properties={"foo": "abc", "bar": 123}) self._create_event( @@ -380,7 +382,9 @@ def test_timeline_for_existing_person_with_three_events_and_return_to_previous_v ) @snapshot_clickhouse_queries - @also_test_with_materialized_columns(person_properties=["bar"], materialize_only_with_person_on_events=True) + @also_test_with_materialized_columns( + person_properties=["bar"], + ) def test_timeline_for_existing_person_with_three_events_and_return_to_previous_value_at_single_hour_point(self): self._create_person(properties={"foo": "abc", "bar": 123}) self._create_event( @@ -443,7 +447,9 @@ def test_timeline_for_existing_person_with_three_events_and_return_to_previous_v ) @snapshot_clickhouse_queries - @also_test_with_materialized_columns(person_properties=["bar"], materialize_only_with_person_on_events=True) + @also_test_with_materialized_columns( + person_properties=["bar"], + ) def test_timeline_for_existing_person_with_three_events_and_return_to_previous_value_at_single_month_point( self, ): @@ -508,7 +514,9 @@ def test_timeline_for_existing_person_with_three_events_and_return_to_previous_v ) @snapshot_clickhouse_queries - @also_test_with_materialized_columns(person_properties=["bar"], materialize_only_with_person_on_events=True) + @also_test_with_materialized_columns( + person_properties=["bar"], + ) def test_timeline_for_existing_person_with_three_events_and_return_to_previous_value_using_relative_date_from( self, ): diff --git a/posthog/batch_exports/http.py b/posthog/batch_exports/http.py index 8c8b12b5a000f..1a2b45fd25617 100644 --- a/posthog/batch_exports/http.py +++ b/posthog/batch_exports/http.py @@ -14,15 +14,10 @@ ValidationError, ) from rest_framework.pagination import CursorPagination -from rest_framework_dataclasses.serializers import DataclassSerializer +from posthog.api.log_entries import LogEntryMixin from posthog.api.routing import TeamAndOrgViewSetMixin -from posthog.batch_exports.models import ( - BATCH_EXPORT_INTERVALS, - BatchExportLogEntry, - BatchExportLogEntryLevel, - fetch_batch_export_log_entries, -) +from posthog.batch_exports.models import BATCH_EXPORT_INTERVALS from posthog.batch_exports.service import ( BatchExportIdError, BatchExportSchema, @@ -99,7 +94,7 @@ class RunsCursorPagination(CursorPagination): page_size = 100 -class BatchExportRunViewSet(TeamAndOrgViewSetMixin, viewsets.ReadOnlyModelViewSet): +class BatchExportRunViewSet(TeamAndOrgViewSetMixin, LogEntryMixin, viewsets.ReadOnlyModelViewSet): scope_object = "batch_export" queryset = BatchExportRun.objects.all() serializer_class = BatchExportRunSerializer @@ -108,6 +103,10 @@ class BatchExportRunViewSet(TeamAndOrgViewSetMixin, viewsets.ReadOnlyModelViewSe filter_backends = [filters.OrderingFilter] ordering_fields = ["created_at", "data_interval_start"] ordering = "-created_at" + log_source = "batch_exports" + + def get_log_entry_instance_id(self) -> str: + return self.parents_query_dict.get("run_id", None) def safely_get_queryset(self, queryset): after = self.request.GET.get("after", None) @@ -350,14 +349,13 @@ def update(self, batch_export: BatchExport, validated_data: dict) -> BatchExport return batch_export -class BatchExportViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet): +class BatchExportViewSet(TeamAndOrgViewSetMixin, LogEntryMixin, viewsets.ModelViewSet): scope_object = "batch_export" queryset = BatchExport.objects.exclude(deleted=True).order_by("-created_at").prefetch_related("destination").all() serializer_class = BatchExportSerializer - scope_object_read_actions: list[str] = ["retrieve", "list"] - scope_object_write_actions: list[str] = ["destroy", "create", "backfill", "pause", "unpause"] + log_source = "batch_exports" - @action(methods=["POST"], detail=True) + @action(methods=["POST"], detail=True, required_scopes=["batch_export:write"]) def backfill(self, request: request.Request, *args, **kwargs) -> response.Response: """Trigger a backfill for a BatchExport.""" if not isinstance(request.user, User) or request.user.current_team is None: @@ -386,7 +384,7 @@ def backfill(self, request: request.Request, *args, **kwargs) -> response.Respon return response.Response({"backfill_id": backfill_id}) - @action(methods=["POST"], detail=True) + @action(methods=["POST"], detail=True, required_scopes=["batch_export:write"]) def pause(self, request: request.Request, *args, **kwargs) -> response.Response: """Pause a BatchExport.""" if not isinstance(request.user, User): @@ -410,7 +408,7 @@ def pause(self, request: request.Request, *args, **kwargs) -> response.Response: return response.Response({"paused": True}) - @action(methods=["POST"], detail=True) + @action(methods=["POST"], detail=True, required_scopes=["batch_export:write"]) def unpause(self, request: request.Request, *args, **kwargs) -> response.Response: """Unpause a BatchExport.""" if not isinstance(request.user, User) or request.user.current_team is None: @@ -448,54 +446,3 @@ def perform_destroy(self, instance: BatchExport): class BatchExportOrganizationViewSet(BatchExportViewSet): filter_rewrite_rules = {"organization_id": "team__organization_id"} - - -class BatchExportLogEntrySerializer(DataclassSerializer): - class Meta: - dataclass = BatchExportLogEntry - - -class BatchExportLogViewSet(TeamAndOrgViewSetMixin, viewsets.GenericViewSet): - scope_object = "batch_export" - serializer_class = BatchExportLogEntrySerializer - - def list(self, request: request.Request, *args, **kwargs): - limit_raw = request.GET.get("limit") - limit: int | None - if limit_raw: - try: - limit = int(limit_raw) - except ValueError: - raise ValidationError("Query param limit must be omitted or an integer!") - else: - limit = None - - after_raw: str | None = request.GET.get("after") - after: dt.datetime | None = None - if after_raw is not None: - after = dt.datetime.fromisoformat(after_raw.replace("Z", "+00:00")) - - before_raw: str | None = request.GET.get("before") - before: dt.datetime | None = None - if before_raw is not None: - before = dt.datetime.fromisoformat(before_raw.replace("Z", "+00:00")) - - level_filter = [BatchExportLogEntryLevel[t.upper()] for t in (request.GET.getlist("level_filter", []))] - data = fetch_batch_export_log_entries( - team_id=self.team_id, - batch_export_id=self.parents_query_dict["batch_export_id"], - run_id=self.parents_query_dict.get("run_id", None), - after=after, - before=before, - search=request.GET.get("search"), - limit=limit, - level_filter=level_filter, - ) - - page = self.paginate_queryset(data) - if page is not None: - serializer = self.get_serializer(page, many=True) - return self.get_paginated_response(serializer.data) - - serializer = self.get_serializer(data, many=True) - return response.Response(serializer.data) diff --git a/posthog/batch_exports/models.py b/posthog/batch_exports/models.py index 7c1b3b7b0a4a3..2315575cf4ae1 100644 --- a/posthog/batch_exports/models.py +++ b/posthog/batch_exports/models.py @@ -1,8 +1,5 @@ import collections.abc -import dataclasses import datetime as dt -import enum -import typing from datetime import timedelta from django.db import models @@ -254,76 +251,6 @@ def interval_time_delta(self) -> timedelta: raise ValueError(f"Invalid interval: '{self.interval}'") -class BatchExportLogEntryLevel(enum.StrEnum): - """Enumeration of batch export log levels.""" - - DEBUG = "DEBUG" - LOG = "LOG" - INFO = "INFO" - WARNING = "WARNING" - ERROR = "ERROR" - - -@dataclasses.dataclass(frozen=True) -class BatchExportLogEntry: - """Represents a single batch export log entry.""" - - team_id: int - batch_export_id: str - run_id: str - timestamp: dt.datetime - level: BatchExportLogEntryLevel - message: str - - -def fetch_batch_export_log_entries( - *, - batch_export_id: str, - team_id: int, - run_id: str | None = None, - after: dt.datetime | None = None, - before: dt.datetime | None = None, - search: str | None = None, - limit: int | None = None, - level_filter: typing.Optional[list[BatchExportLogEntryLevel]] = None, -) -> list[BatchExportLogEntry]: - """Fetch a list of batch export log entries from ClickHouse.""" - if level_filter is None: - level_filter = [] - clickhouse_where_parts: list[str] = [] - clickhouse_kwargs: dict[str, typing.Any] = {} - - clickhouse_where_parts.append("log_source_id = %(log_source_id)s") - clickhouse_kwargs["log_source_id"] = batch_export_id - clickhouse_where_parts.append("team_id = %(team_id)s") - clickhouse_kwargs["team_id"] = team_id - - if run_id is not None: - clickhouse_where_parts.append("instance_id = %(instance_id)s") - clickhouse_kwargs["instance_id"] = run_id - if after is not None: - clickhouse_where_parts.append("timestamp > toDateTime64(%(after)s, 6)") - clickhouse_kwargs["after"] = after.isoformat().replace("+00:00", "") - if before is not None: - clickhouse_where_parts.append("timestamp < toDateTime64(%(before)s, 6)") - clickhouse_kwargs["before"] = before.isoformat().replace("+00:00", "") - if search: - clickhouse_where_parts.append("message ILIKE %(search)s") - clickhouse_kwargs["search"] = f"%{search}%" - if len(level_filter) > 0: - clickhouse_where_parts.append("upper(level) in %(levels)s") - clickhouse_kwargs["levels"] = level_filter - - clickhouse_query = f""" - SELECT team_id, log_source_id AS batch_export_id, instance_id AS run_id, timestamp, upper(level) as level, message FROM log_entries - WHERE {' AND '.join(clickhouse_where_parts)} ORDER BY timestamp DESC {f'LIMIT {limit}' if limit else ''} - """ - - return [ - BatchExportLogEntry(*result) for result in typing.cast(list, sync_execute(clickhouse_query, clickhouse_kwargs)) - ] - - class BatchExportBackfill(UUIDModel): class Status(models.TextChoices): """Possible states of the BatchExportRun.""" diff --git a/posthog/cdp/templates/webhook/template_webhook.py b/posthog/cdp/templates/webhook/template_webhook.py index fd7b44108d5d3..9b5e8d6449f3f 100644 --- a/posthog/cdp/templates/webhook/template_webhook.py +++ b/posthog/cdp/templates/webhook/template_webhook.py @@ -36,6 +36,10 @@ "label": "PUT", "value": "PUT", }, + { + "label": "PATCH", + "value": "PATCH", + }, { "label": "GET", "value": "GET", diff --git a/posthog/hogql/ast.py b/posthog/hogql/ast.py index 3322d9b190021..746db312e67a5 100644 --- a/posthog/hogql/ast.py +++ b/posthog/hogql/ast.py @@ -76,6 +76,14 @@ class ForStatement(Statement): body: Statement +@dataclass(kw_only=True) +class ForInStatement(Statement): + keyVar: Optional[str] + valueVar: str + expr: Expr + body: Statement + + @dataclass(kw_only=True) class Function(Statement): name: str diff --git a/posthog/hogql/autocomplete.py b/posthog/hogql/autocomplete.py index e67e75314116c..487ecab81fe42 100644 --- a/posthog/hogql/autocomplete.py +++ b/posthog/hogql/autocomplete.py @@ -47,28 +47,41 @@ class GetNodeAtPositionTraverser(TraversingVisitor): start: int end: int - selects: list[ast.SelectQuery] = [] + selects: list[ast.SelectQuery] node: Optional[AST] = None parent_node: Optional[AST] = None - last_node: Optional[AST] = None nearest_select_query: Optional[ast.SelectQuery] = None + stack: list[AST] def __init__(self, expr: ast.AST, start: int, end: int): super().__init__() + self.selects = [] + self.stack = [] self.start = start self.end = end self.visit(expr) def visit(self, node: AST | None): if node is not None and node.start is not None and node.end is not None: + parent_node = self.stack[-1] if len(self.stack) > 0 else None if self.start >= node.start and self.end <= node.end: self.node = node - self.parent_node = self.last_node + self.parent_node = parent_node if len(self.selects) > 0: self.nearest_select_query = self.selects[-1] - - self.last_node = node - super().visit(node) + elif isinstance(parent_node, ast.Program) or isinstance(parent_node, ast.Block): + if ( + self.node is None or isinstance(self.node, ast.Program) or isinstance(self.node, ast.Block) + ) and node.start >= self.start: + self.node = node + self.parent_node = parent_node + + if node is not None: + self.stack.append(node) + super().visit(node) + self.stack.pop() + else: + super().visit(node) def visit_select_query(self, node): self.selects.append(node) @@ -405,39 +418,27 @@ def get_hogql_autocomplete( node = find_node.node parent_node = find_node.parent_node - if isinstance(query.globals, dict) and isinstance(node, ast.Field): - for index, key in enumerate(node.chain): - if MATCH_ANY_CHARACTER in str(key): - break - if query.globals is not None and str(key) in query.globals: - query.globals = query.globals[str(key)] - elif index == len(node.chain) - 1: - break - else: - query.globals = None - break - if isinstance(query.globals, dict): - values: list[str | None] = [] - for value in list(query.globals.values()): - if isinstance(value, dict): - values.append("Object") - elif isinstance(value, list): - values.append("Array") - elif isinstance(value, tuple): - values.append("Tuple") + if isinstance(query.globals, dict): + if isinstance(node, ast.Field): + loop_globals: dict | None = query.globals + for index, key in enumerate(node.chain): + if MATCH_ANY_CHARACTER in str(key): + break + if loop_globals is not None and str(key) in loop_globals: + loop_globals = loop_globals[str(key)] + elif index == len(node.chain) - 1: + break else: - value = json.dumps(value) - if len(value) > 20: - value = value[:20] + "..." - values.append(value) - extend_responses( - keys=list(query.globals.keys()), - suggestions=response.suggestions, - kind=Kind.FOLDER, - details=values, - ) - - if query.language == HogLanguage.HOG: + loop_globals = None + break + if loop_globals is not None: + add_globals_to_suggestions(loop_globals, response) + # looking at a nested global object, no need for other suggestions + if loop_globals != query.globals: + break + + if query.language in (HogLanguage.HOG, HogLanguage.HOG_TEMPLATE): + # For Hog, first add all local variables in scope hog_vars = gather_hog_variables_in_scope(root_node, node) extend_responses( keys=hog_vars, @@ -450,6 +451,15 @@ def get_hogql_autocomplete( Kind.FUNCTION, insert_text=lambda key: f"{key}()", ) + + if isinstance(query.globals, dict): + # Override globals if a local variable has the same name + existing_values = {item.label for item in response.suggestions} + filtered_globals = {key: value for key, value in query.globals.items() if key not in existing_values} + add_globals_to_suggestions(filtered_globals, response) + + if query.language in (HogLanguage.HOG, HogLanguage.HOG_TEMPLATE) and query.sourceQuery is None: + # For Hog, break after the remaining globals are added break if query.filters: @@ -602,3 +612,29 @@ def get_hogql_autocomplete( response.timings = timings.to_list() return response + + +def add_globals_to_suggestions(globalVars: dict, response: HogQLAutocompleteResponse): + if isinstance(globalVars, dict): + existing_values = {item.label for item in response.suggestions} + values: list[str | None] = [] + for key, value in globalVars.items(): + if key in existing_values: + continue + if isinstance(value, dict): + values.append("Object") + elif isinstance(value, list): + values.append("Array") + elif isinstance(value, tuple): + values.append("Tuple") + else: + value = json.dumps(value) + if len(value) > 20: + value = value[:20] + "..." + values.append(value) + extend_responses( + keys=list(globalVars.keys()), + suggestions=response.suggestions, + kind=Kind.VARIABLE, + details=values, + ) diff --git a/posthog/hogql/bytecode.py b/posthog/hogql/bytecode.py index e2390d1150de6..33f209b6b9462 100644 --- a/posthog/hogql/bytecode.py +++ b/posthog/hogql/bytecode.py @@ -105,7 +105,7 @@ def _end_scope(self) -> list[Any]: response.append(Operation.POP) return response - def _declare_local(self, name: str): + def _declare_local(self, name: str) -> int: for local in reversed(self.locals): if local.depth < self.scope_depth: break @@ -113,6 +113,7 @@ def _declare_local(self, name: str): raise QueryError(f"Variable `{name}` already declared in this scope") self.locals.append(Local(name, self.scope_depth)) + return len(self.locals) - 1 def visit_and(self, node: ast.And): response = [] @@ -336,6 +337,90 @@ def visit_for_statement(self, node: ast.ForStatement): response.extend(self._end_scope()) return response + def visit_for_in_statement(self, node: ast.ForInStatement): + response: list = [] + self._start_scope() + + key_var = node.keyVar + value_var = node.valueVar + + # set up a bunch of temporary variables + expr_local = self._declare_local("__H_expr_H__") # the obj/array itself + expr_keys_local = self._declare_local("__H_keys_H__") # keys + expr_values_local = self._declare_local("__H_values_H__") # values + loop_index_local = self._declare_local("__H_index_H__") # 0 + loop_limit_local = self._declare_local("__H_limit_H__") # length of keys + key_var_local = self._declare_local(key_var) if key_var is not None else -1 # loop key + value_var_local = self._declare_local(value_var) # loop value + response.extend([Operation.NULL] * (7 if key_var is not None else 6)) + response.extend([*self.visit(node.expr), Operation.SET_LOCAL, expr_local]) + + # populate keys, value, loop index and max loop index + if key_var is not None: + response.extend( + [Operation.GET_LOCAL, expr_local, Operation.CALL, "keys", 1, Operation.SET_LOCAL, expr_keys_local] + ) + response.extend( + [Operation.GET_LOCAL, expr_local, Operation.CALL, "values", 1, Operation.SET_LOCAL, expr_values_local] + ) + response.extend([Operation.INTEGER, 0, Operation.SET_LOCAL, loop_index_local]) + response.extend( + [Operation.GET_LOCAL, expr_values_local, Operation.CALL, "length", 1, Operation.SET_LOCAL, loop_limit_local] + ) + + # check if loop_index < loop_limit + condition = [Operation.GET_LOCAL, loop_limit_local, Operation.GET_LOCAL, loop_index_local, Operation.LT] + + # set key_var and value_var + body: list = [] + if key_var is not None: + body.extend( + [ + Operation.GET_LOCAL, + expr_keys_local, + Operation.GET_LOCAL, + loop_index_local, + Operation.GET_PROPERTY, + Operation.SET_LOCAL, + key_var_local, + ] + ) + body.extend( + [ + Operation.GET_LOCAL, + expr_values_local, + Operation.GET_LOCAL, + loop_index_local, + Operation.GET_PROPERTY, + Operation.SET_LOCAL, + value_var_local, + ] + ) + + # the actual body + body.extend(self.visit(node.body)) + + # i += 1 at the end + increment = [ + Operation.GET_LOCAL, + loop_index_local, + Operation.INTEGER, + 1, + Operation.PLUS, + Operation.SET_LOCAL, + loop_index_local, + ] + + # add to response + response.extend(condition) + response.extend([Operation.JUMP_IF_FALSE, len(body) + len(increment) + 2]) + response.extend(body) + response.extend(increment) + response.extend([Operation.JUMP, -len(increment) - len(body) - 2 - len(condition) - 2]) + + response.extend(self._end_scope()) + return response + def visit_variable_declaration(self, node: ast.VariableDeclaration): self._declare_local(node.name) if node.expr: diff --git a/posthog/hogql/grammar/HogQLParser.g4 b/posthog/hogql/grammar/HogQLParser.g4 index ce4469667b004..2c0d7f206ebb7 100644 --- a/posthog/hogql/grammar/HogQLParser.g4 +++ b/posthog/hogql/grammar/HogQLParser.g4 @@ -17,6 +17,7 @@ identifierList: identifier (COMMA identifier)* COMMA?; statement : returnStmt | ifStmt | whileStmt + | forInStmt | forStmt | funcStmt | varAssignment @@ -33,6 +34,7 @@ forStmt : FOR LPAREN condition=expression? SEMICOLON (incrementVarDeclr=varDecl | incrementVarAssignment=varAssignment | incrementExpression=expression)? RPAREN statement SEMICOLON?; +forInStmt : FOR LPAREN LET identifier (COMMA identifier)? IN expression RPAREN statement SEMICOLON?; funcStmt : FN identifier LPAREN identifierList? RPAREN block; varAssignment : expression COLON EQ_SINGLE expression ; exprStmt : expression SEMICOLON?; diff --git a/posthog/hogql/grammar/HogQLParser.interp b/posthog/hogql/grammar/HogQLParser.interp index 42ddfb5536fe8..22fc706e26505 100644 --- a/posthog/hogql/grammar/HogQLParser.interp +++ b/posthog/hogql/grammar/HogQLParser.interp @@ -325,6 +325,7 @@ returnStmt ifStmt whileStmt forStmt +forInStmt funcStmt varAssignment exprStmt @@ -402,4 +403,4 @@ stringContentsFull atn: -[4, 1, 155, 1249, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 1, 0, 5, 0, 170, 8, 0, 10, 0, 12, 0, 173, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 179, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 188, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 193, 8, 4, 10, 4, 12, 4, 196, 9, 4, 1, 4, 3, 4, 199, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 210, 8, 5, 1, 6, 1, 6, 3, 6, 214, 8, 6, 1, 6, 3, 6, 217, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 226, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 234, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 241, 8, 9, 1, 9, 1, 9, 3, 9, 245, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 251, 8, 9, 1, 9, 1, 9, 1, 9, 3, 9, 256, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 262, 8, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 3, 12, 274, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 5, 14, 280, 8, 14, 10, 14, 12, 14, 283, 9, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 5, 16, 294, 8, 16, 10, 16, 12, 16, 297, 9, 16, 1, 16, 3, 16, 300, 8, 16, 1, 17, 1, 17, 1, 17, 3, 17, 305, 8, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 5, 18, 313, 8, 18, 10, 18, 12, 18, 316, 9, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 324, 8, 19, 1, 20, 3, 20, 327, 8, 20, 1, 20, 1, 20, 3, 20, 331, 8, 20, 1, 20, 3, 20, 334, 8, 20, 1, 20, 1, 20, 3, 20, 338, 8, 20, 1, 20, 3, 20, 341, 8, 20, 1, 20, 3, 20, 344, 8, 20, 1, 20, 3, 20, 347, 8, 20, 1, 20, 3, 20, 350, 8, 20, 1, 20, 1, 20, 3, 20, 354, 8, 20, 1, 20, 1, 20, 3, 20, 358, 8, 20, 1, 20, 3, 20, 361, 8, 20, 1, 20, 3, 20, 364, 8, 20, 1, 20, 3, 20, 367, 8, 20, 1, 20, 1, 20, 3, 20, 371, 8, 20, 1, 20, 3, 20, 374, 8, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 383, 8, 22, 1, 23, 1, 23, 1, 23, 1, 24, 3, 24, 389, 8, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 5, 25, 408, 8, 25, 10, 25, 12, 25, 411, 9, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 427, 8, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 444, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 450, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 456, 8, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 467, 8, 32, 3, 32, 469, 8, 32, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 3, 35, 480, 8, 35, 1, 35, 3, 35, 483, 8, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 489, 8, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 497, 8, 35, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 503, 8, 35, 10, 35, 12, 35, 506, 9, 35, 1, 36, 3, 36, 509, 8, 36, 1, 36, 1, 36, 1, 36, 3, 36, 514, 8, 36, 1, 36, 3, 36, 517, 8, 36, 1, 36, 3, 36, 520, 8, 36, 1, 36, 1, 36, 3, 36, 524, 8, 36, 1, 36, 1, 36, 3, 36, 528, 8, 36, 1, 36, 3, 36, 531, 8, 36, 3, 36, 533, 8, 36, 1, 36, 3, 36, 536, 8, 36, 1, 36, 1, 36, 3, 36, 540, 8, 36, 1, 36, 1, 36, 3, 36, 544, 8, 36, 1, 36, 3, 36, 547, 8, 36, 3, 36, 549, 8, 36, 3, 36, 551, 8, 36, 1, 37, 1, 37, 1, 37, 3, 37, 556, 8, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 567, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 573, 8, 39, 1, 40, 1, 40, 1, 40, 5, 40, 578, 8, 40, 10, 40, 12, 40, 581, 9, 40, 1, 41, 1, 41, 3, 41, 585, 8, 41, 1, 41, 1, 41, 3, 41, 589, 8, 41, 1, 41, 1, 41, 3, 41, 593, 8, 41, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 599, 8, 42, 3, 42, 601, 8, 42, 1, 43, 1, 43, 1, 43, 5, 43, 606, 8, 43, 10, 43, 12, 43, 609, 9, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 3, 45, 616, 8, 45, 1, 45, 3, 45, 619, 8, 45, 1, 45, 3, 45, 622, 8, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 641, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 655, 8, 50, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 5, 52, 669, 8, 52, 10, 52, 12, 52, 672, 9, 52, 1, 52, 3, 52, 675, 8, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 5, 52, 684, 8, 52, 10, 52, 12, 52, 687, 9, 52, 1, 52, 3, 52, 690, 8, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 5, 52, 699, 8, 52, 10, 52, 12, 52, 702, 9, 52, 1, 52, 3, 52, 705, 8, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 3, 52, 712, 8, 52, 1, 52, 1, 52, 3, 52, 716, 8, 52, 1, 53, 1, 53, 1, 53, 5, 53, 721, 8, 53, 10, 53, 12, 53, 724, 9, 53, 1, 53, 3, 53, 727, 8, 53, 1, 54, 1, 54, 1, 54, 3, 54, 732, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 4, 54, 739, 8, 54, 11, 54, 12, 54, 740, 1, 54, 1, 54, 3, 54, 745, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 769, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 786, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 792, 8, 54, 1, 54, 3, 54, 795, 8, 54, 1, 54, 3, 54, 798, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 808, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 814, 8, 54, 1, 54, 3, 54, 817, 8, 54, 1, 54, 3, 54, 820, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 828, 8, 54, 1, 54, 3, 54, 831, 8, 54, 1, 54, 1, 54, 3, 54, 835, 8, 54, 1, 54, 3, 54, 838, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 852, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 869, 8, 54, 1, 54, 1, 54, 1, 54, 3, 54, 874, 8, 54, 1, 54, 1, 54, 3, 54, 878, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 884, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 891, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 903, 8, 54, 1, 54, 1, 54, 3, 54, 907, 8, 54, 1, 54, 3, 54, 910, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 919, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 933, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 972, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 980, 8, 54, 5, 54, 982, 8, 54, 10, 54, 12, 54, 985, 9, 54, 1, 55, 1, 55, 1, 55, 5, 55, 990, 8, 55, 10, 55, 12, 55, 993, 9, 55, 1, 55, 3, 55, 996, 8, 55, 1, 56, 1, 56, 3, 56, 1000, 8, 56, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 1006, 8, 57, 10, 57, 12, 57, 1009, 9, 57, 1, 57, 3, 57, 1012, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 1019, 8, 57, 10, 57, 12, 57, 1022, 9, 57, 1, 57, 3, 57, 1025, 8, 57, 3, 57, 1027, 8, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 5, 58, 1035, 8, 58, 10, 58, 12, 58, 1038, 9, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 1046, 8, 58, 10, 58, 12, 58, 1049, 9, 58, 1, 58, 1, 58, 3, 58, 1053, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 1060, 8, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1073, 8, 59, 1, 60, 1, 60, 1, 60, 5, 60, 1078, 8, 60, 10, 60, 12, 60, 1081, 9, 60, 1, 60, 3, 60, 1084, 8, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1096, 8, 61, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1102, 8, 62, 1, 62, 3, 62, 1105, 8, 62, 1, 63, 1, 63, 1, 63, 5, 63, 1110, 8, 63, 10, 63, 12, 63, 1113, 9, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 3, 64, 1124, 8, 64, 1, 64, 1, 64, 1, 64, 1, 64, 3, 64, 1130, 8, 64, 5, 64, 1132, 8, 64, 10, 64, 12, 64, 1135, 9, 64, 1, 65, 1, 65, 1, 65, 3, 65, 1140, 8, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 3, 66, 1147, 8, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 5, 67, 1154, 8, 67, 10, 67, 12, 67, 1157, 9, 67, 1, 67, 3, 67, 1160, 8, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 1170, 8, 69, 3, 69, 1172, 8, 69, 1, 70, 3, 70, 1175, 8, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 3, 70, 1183, 8, 70, 1, 71, 1, 71, 1, 71, 3, 71, 1188, 8, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 3, 75, 1198, 8, 75, 1, 76, 1, 76, 1, 76, 3, 76, 1203, 8, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 3, 79, 1215, 8, 79, 1, 80, 1, 80, 5, 80, 1219, 8, 80, 10, 80, 12, 80, 1222, 9, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 3, 81, 1231, 8, 81, 1, 82, 1, 82, 5, 82, 1235, 8, 82, 10, 82, 12, 82, 1238, 9, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 3, 83, 1247, 8, 83, 1, 83, 0, 3, 70, 108, 128, 84, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 0, 16, 2, 0, 17, 17, 72, 72, 2, 0, 42, 42, 49, 49, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 78, 78, 2, 0, 49, 49, 71, 71, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 21, 22, 2, 0, 28, 28, 47, 47, 2, 0, 69, 69, 74, 74, 3, 0, 10, 10, 48, 48, 87, 87, 2, 0, 39, 39, 51, 51, 1, 0, 103, 104, 2, 0, 114, 114, 135, 135, 7, 0, 20, 20, 36, 36, 53, 54, 68, 68, 76, 76, 93, 93, 99, 99, 12, 0, 1, 19, 21, 28, 30, 35, 37, 40, 42, 49, 51, 52, 56, 56, 58, 67, 69, 75, 77, 92, 94, 95, 97, 98, 4, 0, 19, 19, 28, 28, 37, 37, 46, 46, 1409, 0, 171, 1, 0, 0, 0, 2, 178, 1, 0, 0, 0, 4, 180, 1, 0, 0, 0, 6, 182, 1, 0, 0, 0, 8, 189, 1, 0, 0, 0, 10, 209, 1, 0, 0, 0, 12, 211, 1, 0, 0, 0, 14, 218, 1, 0, 0, 0, 16, 227, 1, 0, 0, 0, 18, 235, 1, 0, 0, 0, 20, 257, 1, 0, 0, 0, 22, 266, 1, 0, 0, 0, 24, 271, 1, 0, 0, 0, 26, 275, 1, 0, 0, 0, 28, 277, 1, 0, 0, 0, 30, 286, 1, 0, 0, 0, 32, 290, 1, 0, 0, 0, 34, 304, 1, 0, 0, 0, 36, 308, 1, 0, 0, 0, 38, 323, 1, 0, 0, 0, 40, 326, 1, 0, 0, 0, 42, 375, 1, 0, 0, 0, 44, 378, 1, 0, 0, 0, 46, 384, 1, 0, 0, 0, 48, 388, 1, 0, 0, 0, 50, 394, 1, 0, 0, 0, 52, 412, 1, 0, 0, 0, 54, 415, 1, 0, 0, 0, 56, 418, 1, 0, 0, 0, 58, 428, 1, 0, 0, 0, 60, 431, 1, 0, 0, 0, 62, 435, 1, 0, 0, 0, 64, 468, 1, 0, 0, 0, 66, 470, 1, 0, 0, 0, 68, 473, 1, 0, 0, 0, 70, 488, 1, 0, 0, 0, 72, 550, 1, 0, 0, 0, 74, 555, 1, 0, 0, 0, 76, 566, 1, 0, 0, 0, 78, 568, 1, 0, 0, 0, 80, 574, 1, 0, 0, 0, 82, 582, 1, 0, 0, 0, 84, 600, 1, 0, 0, 0, 86, 602, 1, 0, 0, 0, 88, 610, 1, 0, 0, 0, 90, 615, 1, 0, 0, 0, 92, 623, 1, 0, 0, 0, 94, 627, 1, 0, 0, 0, 96, 631, 1, 0, 0, 0, 98, 640, 1, 0, 0, 0, 100, 654, 1, 0, 0, 0, 102, 656, 1, 0, 0, 0, 104, 715, 1, 0, 0, 0, 106, 717, 1, 0, 0, 0, 108, 877, 1, 0, 0, 0, 110, 986, 1, 0, 0, 0, 112, 999, 1, 0, 0, 0, 114, 1026, 1, 0, 0, 0, 116, 1059, 1, 0, 0, 0, 118, 1072, 1, 0, 0, 0, 120, 1074, 1, 0, 0, 0, 122, 1095, 1, 0, 0, 0, 124, 1104, 1, 0, 0, 0, 126, 1106, 1, 0, 0, 0, 128, 1123, 1, 0, 0, 0, 130, 1136, 1, 0, 0, 0, 132, 1146, 1, 0, 0, 0, 134, 1150, 1, 0, 0, 0, 136, 1161, 1, 0, 0, 0, 138, 1171, 1, 0, 0, 0, 140, 1174, 1, 0, 0, 0, 142, 1187, 1, 0, 0, 0, 144, 1189, 1, 0, 0, 0, 146, 1191, 1, 0, 0, 0, 148, 1193, 1, 0, 0, 0, 150, 1197, 1, 0, 0, 0, 152, 1202, 1, 0, 0, 0, 154, 1204, 1, 0, 0, 0, 156, 1208, 1, 0, 0, 0, 158, 1214, 1, 0, 0, 0, 160, 1216, 1, 0, 0, 0, 162, 1230, 1, 0, 0, 0, 164, 1232, 1, 0, 0, 0, 166, 1246, 1, 0, 0, 0, 168, 170, 3, 2, 1, 0, 169, 168, 1, 0, 0, 0, 170, 173, 1, 0, 0, 0, 171, 169, 1, 0, 0, 0, 171, 172, 1, 0, 0, 0, 172, 174, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 174, 175, 5, 0, 0, 1, 175, 1, 1, 0, 0, 0, 176, 179, 3, 6, 3, 0, 177, 179, 3, 10, 5, 0, 178, 176, 1, 0, 0, 0, 178, 177, 1, 0, 0, 0, 179, 3, 1, 0, 0, 0, 180, 181, 3, 108, 54, 0, 181, 5, 1, 0, 0, 0, 182, 183, 5, 50, 0, 0, 183, 187, 3, 152, 76, 0, 184, 185, 5, 111, 0, 0, 185, 186, 5, 118, 0, 0, 186, 188, 3, 4, 2, 0, 187, 184, 1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 7, 1, 0, 0, 0, 189, 194, 3, 152, 76, 0, 190, 191, 5, 112, 0, 0, 191, 193, 3, 152, 76, 0, 192, 190, 1, 0, 0, 0, 193, 196, 1, 0, 0, 0, 194, 192, 1, 0, 0, 0, 194, 195, 1, 0, 0, 0, 195, 198, 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 197, 199, 5, 112, 0, 0, 198, 197, 1, 0, 0, 0, 198, 199, 1, 0, 0, 0, 199, 9, 1, 0, 0, 0, 200, 210, 3, 12, 6, 0, 201, 210, 3, 14, 7, 0, 202, 210, 3, 16, 8, 0, 203, 210, 3, 18, 9, 0, 204, 210, 3, 20, 10, 0, 205, 210, 3, 22, 11, 0, 206, 210, 3, 28, 14, 0, 207, 210, 3, 24, 12, 0, 208, 210, 3, 26, 13, 0, 209, 200, 1, 0, 0, 0, 209, 201, 1, 0, 0, 0, 209, 202, 1, 0, 0, 0, 209, 203, 1, 0, 0, 0, 209, 204, 1, 0, 0, 0, 209, 205, 1, 0, 0, 0, 209, 206, 1, 0, 0, 0, 209, 207, 1, 0, 0, 0, 209, 208, 1, 0, 0, 0, 210, 11, 1, 0, 0, 0, 211, 213, 5, 70, 0, 0, 212, 214, 3, 4, 2, 0, 213, 212, 1, 0, 0, 0, 213, 214, 1, 0, 0, 0, 214, 216, 1, 0, 0, 0, 215, 217, 5, 146, 0, 0, 216, 215, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 13, 1, 0, 0, 0, 218, 219, 5, 38, 0, 0, 219, 220, 5, 126, 0, 0, 220, 221, 3, 4, 2, 0, 221, 222, 5, 145, 0, 0, 222, 225, 3, 10, 5, 0, 223, 224, 5, 24, 0, 0, 224, 226, 3, 10, 5, 0, 225, 223, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 15, 1, 0, 0, 0, 227, 228, 5, 96, 0, 0, 228, 229, 5, 126, 0, 0, 229, 230, 3, 4, 2, 0, 230, 231, 5, 145, 0, 0, 231, 233, 3, 10, 5, 0, 232, 234, 5, 146, 0, 0, 233, 232, 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 17, 1, 0, 0, 0, 235, 236, 5, 31, 0, 0, 236, 240, 5, 126, 0, 0, 237, 241, 3, 6, 3, 0, 238, 241, 3, 22, 11, 0, 239, 241, 3, 4, 2, 0, 240, 237, 1, 0, 0, 0, 240, 238, 1, 0, 0, 0, 240, 239, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 242, 1, 0, 0, 0, 242, 244, 5, 146, 0, 0, 243, 245, 3, 4, 2, 0, 244, 243, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 250, 5, 146, 0, 0, 247, 251, 3, 6, 3, 0, 248, 251, 3, 22, 11, 0, 249, 251, 3, 4, 2, 0, 250, 247, 1, 0, 0, 0, 250, 248, 1, 0, 0, 0, 250, 249, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 253, 5, 145, 0, 0, 253, 255, 3, 10, 5, 0, 254, 256, 5, 146, 0, 0, 255, 254, 1, 0, 0, 0, 255, 256, 1, 0, 0, 0, 256, 19, 1, 0, 0, 0, 257, 258, 5, 29, 0, 0, 258, 259, 3, 152, 76, 0, 259, 261, 5, 126, 0, 0, 260, 262, 3, 8, 4, 0, 261, 260, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 264, 5, 145, 0, 0, 264, 265, 3, 28, 14, 0, 265, 21, 1, 0, 0, 0, 266, 267, 3, 4, 2, 0, 267, 268, 5, 111, 0, 0, 268, 269, 5, 118, 0, 0, 269, 270, 3, 4, 2, 0, 270, 23, 1, 0, 0, 0, 271, 273, 3, 4, 2, 0, 272, 274, 5, 146, 0, 0, 273, 272, 1, 0, 0, 0, 273, 274, 1, 0, 0, 0, 274, 25, 1, 0, 0, 0, 275, 276, 5, 146, 0, 0, 276, 27, 1, 0, 0, 0, 277, 281, 5, 124, 0, 0, 278, 280, 3, 2, 1, 0, 279, 278, 1, 0, 0, 0, 280, 283, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0, 281, 282, 1, 0, 0, 0, 282, 284, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 284, 285, 5, 143, 0, 0, 285, 29, 1, 0, 0, 0, 286, 287, 3, 4, 2, 0, 287, 288, 5, 111, 0, 0, 288, 289, 3, 4, 2, 0, 289, 31, 1, 0, 0, 0, 290, 295, 3, 30, 15, 0, 291, 292, 5, 112, 0, 0, 292, 294, 3, 30, 15, 0, 293, 291, 1, 0, 0, 0, 294, 297, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 299, 1, 0, 0, 0, 297, 295, 1, 0, 0, 0, 298, 300, 5, 112, 0, 0, 299, 298, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 33, 1, 0, 0, 0, 301, 305, 3, 36, 18, 0, 302, 305, 3, 40, 20, 0, 303, 305, 3, 116, 58, 0, 304, 301, 1, 0, 0, 0, 304, 302, 1, 0, 0, 0, 304, 303, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 307, 5, 0, 0, 1, 307, 35, 1, 0, 0, 0, 308, 314, 3, 38, 19, 0, 309, 310, 5, 91, 0, 0, 310, 311, 5, 1, 0, 0, 311, 313, 3, 38, 19, 0, 312, 309, 1, 0, 0, 0, 313, 316, 1, 0, 0, 0, 314, 312, 1, 0, 0, 0, 314, 315, 1, 0, 0, 0, 315, 37, 1, 0, 0, 0, 316, 314, 1, 0, 0, 0, 317, 324, 3, 40, 20, 0, 318, 319, 5, 126, 0, 0, 319, 320, 3, 36, 18, 0, 320, 321, 5, 145, 0, 0, 321, 324, 1, 0, 0, 0, 322, 324, 3, 156, 78, 0, 323, 317, 1, 0, 0, 0, 323, 318, 1, 0, 0, 0, 323, 322, 1, 0, 0, 0, 324, 39, 1, 0, 0, 0, 325, 327, 3, 42, 21, 0, 326, 325, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 1, 0, 0, 0, 328, 330, 5, 77, 0, 0, 329, 331, 5, 23, 0, 0, 330, 329, 1, 0, 0, 0, 330, 331, 1, 0, 0, 0, 331, 333, 1, 0, 0, 0, 332, 334, 3, 44, 22, 0, 333, 332, 1, 0, 0, 0, 333, 334, 1, 0, 0, 0, 334, 335, 1, 0, 0, 0, 335, 337, 3, 106, 53, 0, 336, 338, 3, 46, 23, 0, 337, 336, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 340, 1, 0, 0, 0, 339, 341, 3, 48, 24, 0, 340, 339, 1, 0, 0, 0, 340, 341, 1, 0, 0, 0, 341, 343, 1, 0, 0, 0, 342, 344, 3, 52, 26, 0, 343, 342, 1, 0, 0, 0, 343, 344, 1, 0, 0, 0, 344, 346, 1, 0, 0, 0, 345, 347, 3, 54, 27, 0, 346, 345, 1, 0, 0, 0, 346, 347, 1, 0, 0, 0, 347, 349, 1, 0, 0, 0, 348, 350, 3, 56, 28, 0, 349, 348, 1, 0, 0, 0, 349, 350, 1, 0, 0, 0, 350, 353, 1, 0, 0, 0, 351, 352, 5, 98, 0, 0, 352, 354, 7, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 357, 1, 0, 0, 0, 355, 356, 5, 98, 0, 0, 356, 358, 5, 86, 0, 0, 357, 355, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 360, 1, 0, 0, 0, 359, 361, 3, 58, 29, 0, 360, 359, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 363, 1, 0, 0, 0, 362, 364, 3, 50, 25, 0, 363, 362, 1, 0, 0, 0, 363, 364, 1, 0, 0, 0, 364, 366, 1, 0, 0, 0, 365, 367, 3, 60, 30, 0, 366, 365, 1, 0, 0, 0, 366, 367, 1, 0, 0, 0, 367, 370, 1, 0, 0, 0, 368, 371, 3, 64, 32, 0, 369, 371, 3, 66, 33, 0, 370, 368, 1, 0, 0, 0, 370, 369, 1, 0, 0, 0, 370, 371, 1, 0, 0, 0, 371, 373, 1, 0, 0, 0, 372, 374, 3, 68, 34, 0, 373, 372, 1, 0, 0, 0, 373, 374, 1, 0, 0, 0, 374, 41, 1, 0, 0, 0, 375, 376, 5, 98, 0, 0, 376, 377, 3, 120, 60, 0, 377, 43, 1, 0, 0, 0, 378, 379, 5, 85, 0, 0, 379, 382, 5, 104, 0, 0, 380, 381, 5, 98, 0, 0, 381, 383, 5, 82, 0, 0, 382, 380, 1, 0, 0, 0, 382, 383, 1, 0, 0, 0, 383, 45, 1, 0, 0, 0, 384, 385, 5, 32, 0, 0, 385, 386, 3, 70, 35, 0, 386, 47, 1, 0, 0, 0, 387, 389, 7, 1, 0, 0, 388, 387, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 390, 1, 0, 0, 0, 390, 391, 5, 5, 0, 0, 391, 392, 5, 45, 0, 0, 392, 393, 3, 106, 53, 0, 393, 49, 1, 0, 0, 0, 394, 395, 5, 97, 0, 0, 395, 396, 3, 152, 76, 0, 396, 397, 5, 6, 0, 0, 397, 398, 5, 126, 0, 0, 398, 399, 3, 90, 45, 0, 399, 409, 5, 145, 0, 0, 400, 401, 5, 112, 0, 0, 401, 402, 3, 152, 76, 0, 402, 403, 5, 6, 0, 0, 403, 404, 5, 126, 0, 0, 404, 405, 3, 90, 45, 0, 405, 406, 5, 145, 0, 0, 406, 408, 1, 0, 0, 0, 407, 400, 1, 0, 0, 0, 408, 411, 1, 0, 0, 0, 409, 407, 1, 0, 0, 0, 409, 410, 1, 0, 0, 0, 410, 51, 1, 0, 0, 0, 411, 409, 1, 0, 0, 0, 412, 413, 5, 67, 0, 0, 413, 414, 3, 108, 54, 0, 414, 53, 1, 0, 0, 0, 415, 416, 5, 95, 0, 0, 416, 417, 3, 108, 54, 0, 417, 55, 1, 0, 0, 0, 418, 419, 5, 34, 0, 0, 419, 426, 5, 11, 0, 0, 420, 421, 7, 0, 0, 0, 421, 422, 5, 126, 0, 0, 422, 423, 3, 106, 53, 0, 423, 424, 5, 145, 0, 0, 424, 427, 1, 0, 0, 0, 425, 427, 3, 106, 53, 0, 426, 420, 1, 0, 0, 0, 426, 425, 1, 0, 0, 0, 427, 57, 1, 0, 0, 0, 428, 429, 5, 35, 0, 0, 429, 430, 3, 108, 54, 0, 430, 59, 1, 0, 0, 0, 431, 432, 5, 62, 0, 0, 432, 433, 5, 11, 0, 0, 433, 434, 3, 80, 40, 0, 434, 61, 1, 0, 0, 0, 435, 436, 5, 62, 0, 0, 436, 437, 5, 11, 0, 0, 437, 438, 3, 106, 53, 0, 438, 63, 1, 0, 0, 0, 439, 440, 5, 52, 0, 0, 440, 443, 3, 108, 54, 0, 441, 442, 5, 112, 0, 0, 442, 444, 3, 108, 54, 0, 443, 441, 1, 0, 0, 0, 443, 444, 1, 0, 0, 0, 444, 449, 1, 0, 0, 0, 445, 446, 5, 98, 0, 0, 446, 450, 5, 82, 0, 0, 447, 448, 5, 11, 0, 0, 448, 450, 3, 106, 53, 0, 449, 445, 1, 0, 0, 0, 449, 447, 1, 0, 0, 0, 449, 450, 1, 0, 0, 0, 450, 469, 1, 0, 0, 0, 451, 452, 5, 52, 0, 0, 452, 455, 3, 108, 54, 0, 453, 454, 5, 98, 0, 0, 454, 456, 5, 82, 0, 0, 455, 453, 1, 0, 0, 0, 455, 456, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 458, 5, 59, 0, 0, 458, 459, 3, 108, 54, 0, 459, 469, 1, 0, 0, 0, 460, 461, 5, 52, 0, 0, 461, 462, 3, 108, 54, 0, 462, 463, 5, 59, 0, 0, 463, 466, 3, 108, 54, 0, 464, 465, 5, 11, 0, 0, 465, 467, 3, 106, 53, 0, 466, 464, 1, 0, 0, 0, 466, 467, 1, 0, 0, 0, 467, 469, 1, 0, 0, 0, 468, 439, 1, 0, 0, 0, 468, 451, 1, 0, 0, 0, 468, 460, 1, 0, 0, 0, 469, 65, 1, 0, 0, 0, 470, 471, 5, 59, 0, 0, 471, 472, 3, 108, 54, 0, 472, 67, 1, 0, 0, 0, 473, 474, 5, 79, 0, 0, 474, 475, 3, 86, 43, 0, 475, 69, 1, 0, 0, 0, 476, 477, 6, 35, -1, 0, 477, 479, 3, 128, 64, 0, 478, 480, 5, 27, 0, 0, 479, 478, 1, 0, 0, 0, 479, 480, 1, 0, 0, 0, 480, 482, 1, 0, 0, 0, 481, 483, 3, 78, 39, 0, 482, 481, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 489, 1, 0, 0, 0, 484, 485, 5, 126, 0, 0, 485, 486, 3, 70, 35, 0, 486, 487, 5, 145, 0, 0, 487, 489, 1, 0, 0, 0, 488, 476, 1, 0, 0, 0, 488, 484, 1, 0, 0, 0, 489, 504, 1, 0, 0, 0, 490, 491, 10, 3, 0, 0, 491, 492, 3, 74, 37, 0, 492, 493, 3, 70, 35, 4, 493, 503, 1, 0, 0, 0, 494, 496, 10, 4, 0, 0, 495, 497, 3, 72, 36, 0, 496, 495, 1, 0, 0, 0, 496, 497, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 499, 5, 45, 0, 0, 499, 500, 3, 70, 35, 0, 500, 501, 3, 76, 38, 0, 501, 503, 1, 0, 0, 0, 502, 490, 1, 0, 0, 0, 502, 494, 1, 0, 0, 0, 503, 506, 1, 0, 0, 0, 504, 502, 1, 0, 0, 0, 504, 505, 1, 0, 0, 0, 505, 71, 1, 0, 0, 0, 506, 504, 1, 0, 0, 0, 507, 509, 7, 2, 0, 0, 508, 507, 1, 0, 0, 0, 508, 509, 1, 0, 0, 0, 509, 510, 1, 0, 0, 0, 510, 517, 5, 42, 0, 0, 511, 513, 5, 42, 0, 0, 512, 514, 7, 2, 0, 0, 513, 512, 1, 0, 0, 0, 513, 514, 1, 0, 0, 0, 514, 517, 1, 0, 0, 0, 515, 517, 7, 2, 0, 0, 516, 508, 1, 0, 0, 0, 516, 511, 1, 0, 0, 0, 516, 515, 1, 0, 0, 0, 517, 551, 1, 0, 0, 0, 518, 520, 7, 3, 0, 0, 519, 518, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 523, 7, 4, 0, 0, 522, 524, 5, 63, 0, 0, 523, 522, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 533, 1, 0, 0, 0, 525, 527, 7, 4, 0, 0, 526, 528, 5, 63, 0, 0, 527, 526, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 530, 1, 0, 0, 0, 529, 531, 7, 3, 0, 0, 530, 529, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 533, 1, 0, 0, 0, 532, 519, 1, 0, 0, 0, 532, 525, 1, 0, 0, 0, 533, 551, 1, 0, 0, 0, 534, 536, 7, 5, 0, 0, 535, 534, 1, 0, 0, 0, 535, 536, 1, 0, 0, 0, 536, 537, 1, 0, 0, 0, 537, 539, 5, 33, 0, 0, 538, 540, 5, 63, 0, 0, 539, 538, 1, 0, 0, 0, 539, 540, 1, 0, 0, 0, 540, 549, 1, 0, 0, 0, 541, 543, 5, 33, 0, 0, 542, 544, 5, 63, 0, 0, 543, 542, 1, 0, 0, 0, 543, 544, 1, 0, 0, 0, 544, 546, 1, 0, 0, 0, 545, 547, 7, 5, 0, 0, 546, 545, 1, 0, 0, 0, 546, 547, 1, 0, 0, 0, 547, 549, 1, 0, 0, 0, 548, 535, 1, 0, 0, 0, 548, 541, 1, 0, 0, 0, 549, 551, 1, 0, 0, 0, 550, 516, 1, 0, 0, 0, 550, 532, 1, 0, 0, 0, 550, 548, 1, 0, 0, 0, 551, 73, 1, 0, 0, 0, 552, 553, 5, 16, 0, 0, 553, 556, 5, 45, 0, 0, 554, 556, 5, 112, 0, 0, 555, 552, 1, 0, 0, 0, 555, 554, 1, 0, 0, 0, 556, 75, 1, 0, 0, 0, 557, 558, 5, 60, 0, 0, 558, 567, 3, 106, 53, 0, 559, 560, 5, 92, 0, 0, 560, 561, 5, 126, 0, 0, 561, 562, 3, 106, 53, 0, 562, 563, 5, 145, 0, 0, 563, 567, 1, 0, 0, 0, 564, 565, 5, 92, 0, 0, 565, 567, 3, 106, 53, 0, 566, 557, 1, 0, 0, 0, 566, 559, 1, 0, 0, 0, 566, 564, 1, 0, 0, 0, 567, 77, 1, 0, 0, 0, 568, 569, 5, 75, 0, 0, 569, 572, 3, 84, 42, 0, 570, 571, 5, 59, 0, 0, 571, 573, 3, 84, 42, 0, 572, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 79, 1, 0, 0, 0, 574, 579, 3, 82, 41, 0, 575, 576, 5, 112, 0, 0, 576, 578, 3, 82, 41, 0, 577, 575, 1, 0, 0, 0, 578, 581, 1, 0, 0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 81, 1, 0, 0, 0, 581, 579, 1, 0, 0, 0, 582, 584, 3, 108, 54, 0, 583, 585, 7, 6, 0, 0, 584, 583, 1, 0, 0, 0, 584, 585, 1, 0, 0, 0, 585, 588, 1, 0, 0, 0, 586, 587, 5, 58, 0, 0, 587, 589, 7, 7, 0, 0, 588, 586, 1, 0, 0, 0, 588, 589, 1, 0, 0, 0, 589, 592, 1, 0, 0, 0, 590, 591, 5, 15, 0, 0, 591, 593, 5, 106, 0, 0, 592, 590, 1, 0, 0, 0, 592, 593, 1, 0, 0, 0, 593, 83, 1, 0, 0, 0, 594, 601, 3, 156, 78, 0, 595, 598, 3, 140, 70, 0, 596, 597, 5, 147, 0, 0, 597, 599, 3, 140, 70, 0, 598, 596, 1, 0, 0, 0, 598, 599, 1, 0, 0, 0, 599, 601, 1, 0, 0, 0, 600, 594, 1, 0, 0, 0, 600, 595, 1, 0, 0, 0, 601, 85, 1, 0, 0, 0, 602, 607, 3, 88, 44, 0, 603, 604, 5, 112, 0, 0, 604, 606, 3, 88, 44, 0, 605, 603, 1, 0, 0, 0, 606, 609, 1, 0, 0, 0, 607, 605, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 87, 1, 0, 0, 0, 609, 607, 1, 0, 0, 0, 610, 611, 3, 152, 76, 0, 611, 612, 5, 118, 0, 0, 612, 613, 3, 142, 71, 0, 613, 89, 1, 0, 0, 0, 614, 616, 3, 92, 46, 0, 615, 614, 1, 0, 0, 0, 615, 616, 1, 0, 0, 0, 616, 618, 1, 0, 0, 0, 617, 619, 3, 94, 47, 0, 618, 617, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 621, 1, 0, 0, 0, 620, 622, 3, 96, 48, 0, 621, 620, 1, 0, 0, 0, 621, 622, 1, 0, 0, 0, 622, 91, 1, 0, 0, 0, 623, 624, 5, 65, 0, 0, 624, 625, 5, 11, 0, 0, 625, 626, 3, 106, 53, 0, 626, 93, 1, 0, 0, 0, 627, 628, 5, 62, 0, 0, 628, 629, 5, 11, 0, 0, 629, 630, 3, 80, 40, 0, 630, 95, 1, 0, 0, 0, 631, 632, 7, 8, 0, 0, 632, 633, 3, 98, 49, 0, 633, 97, 1, 0, 0, 0, 634, 641, 3, 100, 50, 0, 635, 636, 5, 9, 0, 0, 636, 637, 3, 100, 50, 0, 637, 638, 5, 2, 0, 0, 638, 639, 3, 100, 50, 0, 639, 641, 1, 0, 0, 0, 640, 634, 1, 0, 0, 0, 640, 635, 1, 0, 0, 0, 641, 99, 1, 0, 0, 0, 642, 643, 5, 18, 0, 0, 643, 655, 5, 73, 0, 0, 644, 645, 5, 90, 0, 0, 645, 655, 5, 66, 0, 0, 646, 647, 5, 90, 0, 0, 647, 655, 5, 30, 0, 0, 648, 649, 3, 140, 70, 0, 649, 650, 5, 66, 0, 0, 650, 655, 1, 0, 0, 0, 651, 652, 3, 140, 70, 0, 652, 653, 5, 30, 0, 0, 653, 655, 1, 0, 0, 0, 654, 642, 1, 0, 0, 0, 654, 644, 1, 0, 0, 0, 654, 646, 1, 0, 0, 0, 654, 648, 1, 0, 0, 0, 654, 651, 1, 0, 0, 0, 655, 101, 1, 0, 0, 0, 656, 657, 3, 108, 54, 0, 657, 658, 5, 0, 0, 1, 658, 103, 1, 0, 0, 0, 659, 716, 3, 152, 76, 0, 660, 661, 3, 152, 76, 0, 661, 662, 5, 126, 0, 0, 662, 663, 3, 152, 76, 0, 663, 670, 3, 104, 52, 0, 664, 665, 5, 112, 0, 0, 665, 666, 3, 152, 76, 0, 666, 667, 3, 104, 52, 0, 667, 669, 1, 0, 0, 0, 668, 664, 1, 0, 0, 0, 669, 672, 1, 0, 0, 0, 670, 668, 1, 0, 0, 0, 670, 671, 1, 0, 0, 0, 671, 674, 1, 0, 0, 0, 672, 670, 1, 0, 0, 0, 673, 675, 5, 112, 0, 0, 674, 673, 1, 0, 0, 0, 674, 675, 1, 0, 0, 0, 675, 676, 1, 0, 0, 0, 676, 677, 5, 145, 0, 0, 677, 716, 1, 0, 0, 0, 678, 679, 3, 152, 76, 0, 679, 680, 5, 126, 0, 0, 680, 685, 3, 154, 77, 0, 681, 682, 5, 112, 0, 0, 682, 684, 3, 154, 77, 0, 683, 681, 1, 0, 0, 0, 684, 687, 1, 0, 0, 0, 685, 683, 1, 0, 0, 0, 685, 686, 1, 0, 0, 0, 686, 689, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 690, 5, 112, 0, 0, 689, 688, 1, 0, 0, 0, 689, 690, 1, 0, 0, 0, 690, 691, 1, 0, 0, 0, 691, 692, 5, 145, 0, 0, 692, 716, 1, 0, 0, 0, 693, 694, 3, 152, 76, 0, 694, 695, 5, 126, 0, 0, 695, 700, 3, 104, 52, 0, 696, 697, 5, 112, 0, 0, 697, 699, 3, 104, 52, 0, 698, 696, 1, 0, 0, 0, 699, 702, 1, 0, 0, 0, 700, 698, 1, 0, 0, 0, 700, 701, 1, 0, 0, 0, 701, 704, 1, 0, 0, 0, 702, 700, 1, 0, 0, 0, 703, 705, 5, 112, 0, 0, 704, 703, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 706, 707, 5, 145, 0, 0, 707, 716, 1, 0, 0, 0, 708, 709, 3, 152, 76, 0, 709, 711, 5, 126, 0, 0, 710, 712, 3, 106, 53, 0, 711, 710, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 713, 1, 0, 0, 0, 713, 714, 5, 145, 0, 0, 714, 716, 1, 0, 0, 0, 715, 659, 1, 0, 0, 0, 715, 660, 1, 0, 0, 0, 715, 678, 1, 0, 0, 0, 715, 693, 1, 0, 0, 0, 715, 708, 1, 0, 0, 0, 716, 105, 1, 0, 0, 0, 717, 722, 3, 108, 54, 0, 718, 719, 5, 112, 0, 0, 719, 721, 3, 108, 54, 0, 720, 718, 1, 0, 0, 0, 721, 724, 1, 0, 0, 0, 722, 720, 1, 0, 0, 0, 722, 723, 1, 0, 0, 0, 723, 726, 1, 0, 0, 0, 724, 722, 1, 0, 0, 0, 725, 727, 5, 112, 0, 0, 726, 725, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 107, 1, 0, 0, 0, 728, 729, 6, 54, -1, 0, 729, 731, 5, 12, 0, 0, 730, 732, 3, 108, 54, 0, 731, 730, 1, 0, 0, 0, 731, 732, 1, 0, 0, 0, 732, 738, 1, 0, 0, 0, 733, 734, 5, 94, 0, 0, 734, 735, 3, 108, 54, 0, 735, 736, 5, 81, 0, 0, 736, 737, 3, 108, 54, 0, 737, 739, 1, 0, 0, 0, 738, 733, 1, 0, 0, 0, 739, 740, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 744, 1, 0, 0, 0, 742, 743, 5, 24, 0, 0, 743, 745, 3, 108, 54, 0, 744, 742, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 746, 1, 0, 0, 0, 746, 747, 5, 25, 0, 0, 747, 878, 1, 0, 0, 0, 748, 749, 5, 13, 0, 0, 749, 750, 5, 126, 0, 0, 750, 751, 3, 108, 54, 0, 751, 752, 5, 6, 0, 0, 752, 753, 3, 104, 52, 0, 753, 754, 5, 145, 0, 0, 754, 878, 1, 0, 0, 0, 755, 756, 5, 19, 0, 0, 756, 878, 5, 106, 0, 0, 757, 758, 5, 43, 0, 0, 758, 759, 3, 108, 54, 0, 759, 760, 3, 144, 72, 0, 760, 878, 1, 0, 0, 0, 761, 762, 5, 80, 0, 0, 762, 763, 5, 126, 0, 0, 763, 764, 3, 108, 54, 0, 764, 765, 5, 32, 0, 0, 765, 768, 3, 108, 54, 0, 766, 767, 5, 31, 0, 0, 767, 769, 3, 108, 54, 0, 768, 766, 1, 0, 0, 0, 768, 769, 1, 0, 0, 0, 769, 770, 1, 0, 0, 0, 770, 771, 5, 145, 0, 0, 771, 878, 1, 0, 0, 0, 772, 773, 5, 83, 0, 0, 773, 878, 5, 106, 0, 0, 774, 775, 5, 88, 0, 0, 775, 776, 5, 126, 0, 0, 776, 777, 7, 9, 0, 0, 777, 778, 3, 158, 79, 0, 778, 779, 5, 32, 0, 0, 779, 780, 3, 108, 54, 0, 780, 781, 5, 145, 0, 0, 781, 878, 1, 0, 0, 0, 782, 783, 3, 152, 76, 0, 783, 785, 5, 126, 0, 0, 784, 786, 3, 106, 53, 0, 785, 784, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, 5, 145, 0, 0, 788, 797, 1, 0, 0, 0, 789, 791, 5, 126, 0, 0, 790, 792, 5, 23, 0, 0, 791, 790, 1, 0, 0, 0, 791, 792, 1, 0, 0, 0, 792, 794, 1, 0, 0, 0, 793, 795, 3, 110, 55, 0, 794, 793, 1, 0, 0, 0, 794, 795, 1, 0, 0, 0, 795, 796, 1, 0, 0, 0, 796, 798, 5, 145, 0, 0, 797, 789, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 799, 1, 0, 0, 0, 799, 800, 5, 64, 0, 0, 800, 801, 5, 126, 0, 0, 801, 802, 3, 90, 45, 0, 802, 803, 5, 145, 0, 0, 803, 878, 1, 0, 0, 0, 804, 805, 3, 152, 76, 0, 805, 807, 5, 126, 0, 0, 806, 808, 3, 106, 53, 0, 807, 806, 1, 0, 0, 0, 807, 808, 1, 0, 0, 0, 808, 809, 1, 0, 0, 0, 809, 810, 5, 145, 0, 0, 810, 819, 1, 0, 0, 0, 811, 813, 5, 126, 0, 0, 812, 814, 5, 23, 0, 0, 813, 812, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 816, 1, 0, 0, 0, 815, 817, 3, 110, 55, 0, 816, 815, 1, 0, 0, 0, 816, 817, 1, 0, 0, 0, 817, 818, 1, 0, 0, 0, 818, 820, 5, 145, 0, 0, 819, 811, 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 821, 1, 0, 0, 0, 821, 822, 5, 64, 0, 0, 822, 823, 3, 152, 76, 0, 823, 878, 1, 0, 0, 0, 824, 830, 3, 152, 76, 0, 825, 827, 5, 126, 0, 0, 826, 828, 3, 106, 53, 0, 827, 826, 1, 0, 0, 0, 827, 828, 1, 0, 0, 0, 828, 829, 1, 0, 0, 0, 829, 831, 5, 145, 0, 0, 830, 825, 1, 0, 0, 0, 830, 831, 1, 0, 0, 0, 831, 832, 1, 0, 0, 0, 832, 834, 5, 126, 0, 0, 833, 835, 5, 23, 0, 0, 834, 833, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 837, 1, 0, 0, 0, 836, 838, 3, 110, 55, 0, 837, 836, 1, 0, 0, 0, 837, 838, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 840, 5, 145, 0, 0, 840, 878, 1, 0, 0, 0, 841, 878, 3, 116, 58, 0, 842, 878, 3, 160, 80, 0, 843, 878, 3, 142, 71, 0, 844, 845, 5, 114, 0, 0, 845, 878, 3, 108, 54, 19, 846, 847, 5, 56, 0, 0, 847, 878, 3, 108, 54, 13, 848, 849, 3, 132, 66, 0, 849, 850, 5, 116, 0, 0, 850, 852, 1, 0, 0, 0, 851, 848, 1, 0, 0, 0, 851, 852, 1, 0, 0, 0, 852, 853, 1, 0, 0, 0, 853, 878, 5, 108, 0, 0, 854, 855, 5, 126, 0, 0, 855, 856, 3, 36, 18, 0, 856, 857, 5, 145, 0, 0, 857, 878, 1, 0, 0, 0, 858, 859, 5, 126, 0, 0, 859, 860, 3, 108, 54, 0, 860, 861, 5, 145, 0, 0, 861, 878, 1, 0, 0, 0, 862, 863, 5, 126, 0, 0, 863, 864, 3, 106, 53, 0, 864, 865, 5, 145, 0, 0, 865, 878, 1, 0, 0, 0, 866, 868, 5, 125, 0, 0, 867, 869, 3, 106, 53, 0, 868, 867, 1, 0, 0, 0, 868, 869, 1, 0, 0, 0, 869, 870, 1, 0, 0, 0, 870, 878, 5, 144, 0, 0, 871, 873, 5, 124, 0, 0, 872, 874, 3, 32, 16, 0, 873, 872, 1, 0, 0, 0, 873, 874, 1, 0, 0, 0, 874, 875, 1, 0, 0, 0, 875, 878, 5, 143, 0, 0, 876, 878, 3, 124, 62, 0, 877, 728, 1, 0, 0, 0, 877, 748, 1, 0, 0, 0, 877, 755, 1, 0, 0, 0, 877, 757, 1, 0, 0, 0, 877, 761, 1, 0, 0, 0, 877, 772, 1, 0, 0, 0, 877, 774, 1, 0, 0, 0, 877, 782, 1, 0, 0, 0, 877, 804, 1, 0, 0, 0, 877, 824, 1, 0, 0, 0, 877, 841, 1, 0, 0, 0, 877, 842, 1, 0, 0, 0, 877, 843, 1, 0, 0, 0, 877, 844, 1, 0, 0, 0, 877, 846, 1, 0, 0, 0, 877, 851, 1, 0, 0, 0, 877, 854, 1, 0, 0, 0, 877, 858, 1, 0, 0, 0, 877, 862, 1, 0, 0, 0, 877, 866, 1, 0, 0, 0, 877, 871, 1, 0, 0, 0, 877, 876, 1, 0, 0, 0, 878, 983, 1, 0, 0, 0, 879, 883, 10, 18, 0, 0, 880, 884, 5, 108, 0, 0, 881, 884, 5, 147, 0, 0, 882, 884, 5, 134, 0, 0, 883, 880, 1, 0, 0, 0, 883, 881, 1, 0, 0, 0, 883, 882, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 982, 3, 108, 54, 19, 886, 890, 10, 17, 0, 0, 887, 891, 5, 135, 0, 0, 888, 891, 5, 114, 0, 0, 889, 891, 5, 113, 0, 0, 890, 887, 1, 0, 0, 0, 890, 888, 1, 0, 0, 0, 890, 889, 1, 0, 0, 0, 891, 892, 1, 0, 0, 0, 892, 982, 3, 108, 54, 18, 893, 918, 10, 16, 0, 0, 894, 919, 5, 117, 0, 0, 895, 919, 5, 118, 0, 0, 896, 919, 5, 129, 0, 0, 897, 919, 5, 127, 0, 0, 898, 919, 5, 128, 0, 0, 899, 919, 5, 119, 0, 0, 900, 919, 5, 120, 0, 0, 901, 903, 5, 56, 0, 0, 902, 901, 1, 0, 0, 0, 902, 903, 1, 0, 0, 0, 903, 904, 1, 0, 0, 0, 904, 906, 5, 40, 0, 0, 905, 907, 5, 14, 0, 0, 906, 905, 1, 0, 0, 0, 906, 907, 1, 0, 0, 0, 907, 919, 1, 0, 0, 0, 908, 910, 5, 56, 0, 0, 909, 908, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 911, 1, 0, 0, 0, 911, 919, 7, 10, 0, 0, 912, 919, 5, 141, 0, 0, 913, 919, 5, 142, 0, 0, 914, 919, 5, 131, 0, 0, 915, 919, 5, 122, 0, 0, 916, 919, 5, 123, 0, 0, 917, 919, 5, 130, 0, 0, 918, 894, 1, 0, 0, 0, 918, 895, 1, 0, 0, 0, 918, 896, 1, 0, 0, 0, 918, 897, 1, 0, 0, 0, 918, 898, 1, 0, 0, 0, 918, 899, 1, 0, 0, 0, 918, 900, 1, 0, 0, 0, 918, 902, 1, 0, 0, 0, 918, 909, 1, 0, 0, 0, 918, 912, 1, 0, 0, 0, 918, 913, 1, 0, 0, 0, 918, 914, 1, 0, 0, 0, 918, 915, 1, 0, 0, 0, 918, 916, 1, 0, 0, 0, 918, 917, 1, 0, 0, 0, 919, 920, 1, 0, 0, 0, 920, 982, 3, 108, 54, 17, 921, 922, 10, 14, 0, 0, 922, 923, 5, 133, 0, 0, 923, 982, 3, 108, 54, 15, 924, 925, 10, 12, 0, 0, 925, 926, 5, 2, 0, 0, 926, 982, 3, 108, 54, 13, 927, 928, 10, 11, 0, 0, 928, 929, 5, 61, 0, 0, 929, 982, 3, 108, 54, 12, 930, 932, 10, 10, 0, 0, 931, 933, 5, 56, 0, 0, 932, 931, 1, 0, 0, 0, 932, 933, 1, 0, 0, 0, 933, 934, 1, 0, 0, 0, 934, 935, 5, 9, 0, 0, 935, 936, 3, 108, 54, 0, 936, 937, 5, 2, 0, 0, 937, 938, 3, 108, 54, 11, 938, 982, 1, 0, 0, 0, 939, 940, 10, 9, 0, 0, 940, 941, 5, 136, 0, 0, 941, 942, 3, 108, 54, 0, 942, 943, 5, 111, 0, 0, 943, 944, 3, 108, 54, 9, 944, 982, 1, 0, 0, 0, 945, 946, 10, 25, 0, 0, 946, 947, 5, 125, 0, 0, 947, 948, 3, 108, 54, 0, 948, 949, 5, 144, 0, 0, 949, 982, 1, 0, 0, 0, 950, 951, 10, 24, 0, 0, 951, 952, 5, 116, 0, 0, 952, 982, 5, 104, 0, 0, 953, 954, 10, 23, 0, 0, 954, 955, 5, 116, 0, 0, 955, 982, 3, 152, 76, 0, 956, 957, 10, 22, 0, 0, 957, 958, 5, 132, 0, 0, 958, 959, 5, 125, 0, 0, 959, 960, 3, 108, 54, 0, 960, 961, 5, 144, 0, 0, 961, 982, 1, 0, 0, 0, 962, 963, 10, 21, 0, 0, 963, 964, 5, 132, 0, 0, 964, 982, 5, 104, 0, 0, 965, 966, 10, 20, 0, 0, 966, 967, 5, 132, 0, 0, 967, 982, 3, 152, 76, 0, 968, 969, 10, 15, 0, 0, 969, 971, 5, 44, 0, 0, 970, 972, 5, 56, 0, 0, 971, 970, 1, 0, 0, 0, 971, 972, 1, 0, 0, 0, 972, 973, 1, 0, 0, 0, 973, 982, 5, 57, 0, 0, 974, 979, 10, 8, 0, 0, 975, 976, 5, 6, 0, 0, 976, 980, 3, 152, 76, 0, 977, 978, 5, 6, 0, 0, 978, 980, 5, 106, 0, 0, 979, 975, 1, 0, 0, 0, 979, 977, 1, 0, 0, 0, 980, 982, 1, 0, 0, 0, 981, 879, 1, 0, 0, 0, 981, 886, 1, 0, 0, 0, 981, 893, 1, 0, 0, 0, 981, 921, 1, 0, 0, 0, 981, 924, 1, 0, 0, 0, 981, 927, 1, 0, 0, 0, 981, 930, 1, 0, 0, 0, 981, 939, 1, 0, 0, 0, 981, 945, 1, 0, 0, 0, 981, 950, 1, 0, 0, 0, 981, 953, 1, 0, 0, 0, 981, 956, 1, 0, 0, 0, 981, 962, 1, 0, 0, 0, 981, 965, 1, 0, 0, 0, 981, 968, 1, 0, 0, 0, 981, 974, 1, 0, 0, 0, 982, 985, 1, 0, 0, 0, 983, 981, 1, 0, 0, 0, 983, 984, 1, 0, 0, 0, 984, 109, 1, 0, 0, 0, 985, 983, 1, 0, 0, 0, 986, 991, 3, 112, 56, 0, 987, 988, 5, 112, 0, 0, 988, 990, 3, 112, 56, 0, 989, 987, 1, 0, 0, 0, 990, 993, 1, 0, 0, 0, 991, 989, 1, 0, 0, 0, 991, 992, 1, 0, 0, 0, 992, 995, 1, 0, 0, 0, 993, 991, 1, 0, 0, 0, 994, 996, 5, 112, 0, 0, 995, 994, 1, 0, 0, 0, 995, 996, 1, 0, 0, 0, 996, 111, 1, 0, 0, 0, 997, 1000, 3, 114, 57, 0, 998, 1000, 3, 108, 54, 0, 999, 997, 1, 0, 0, 0, 999, 998, 1, 0, 0, 0, 1000, 113, 1, 0, 0, 0, 1001, 1002, 5, 126, 0, 0, 1002, 1007, 3, 152, 76, 0, 1003, 1004, 5, 112, 0, 0, 1004, 1006, 3, 152, 76, 0, 1005, 1003, 1, 0, 0, 0, 1006, 1009, 1, 0, 0, 0, 1007, 1005, 1, 0, 0, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1011, 1, 0, 0, 0, 1009, 1007, 1, 0, 0, 0, 1010, 1012, 5, 112, 0, 0, 1011, 1010, 1, 0, 0, 0, 1011, 1012, 1, 0, 0, 0, 1012, 1013, 1, 0, 0, 0, 1013, 1014, 5, 145, 0, 0, 1014, 1027, 1, 0, 0, 0, 1015, 1020, 3, 152, 76, 0, 1016, 1017, 5, 112, 0, 0, 1017, 1019, 3, 152, 76, 0, 1018, 1016, 1, 0, 0, 0, 1019, 1022, 1, 0, 0, 0, 1020, 1018, 1, 0, 0, 0, 1020, 1021, 1, 0, 0, 0, 1021, 1024, 1, 0, 0, 0, 1022, 1020, 1, 0, 0, 0, 1023, 1025, 5, 112, 0, 0, 1024, 1023, 1, 0, 0, 0, 1024, 1025, 1, 0, 0, 0, 1025, 1027, 1, 0, 0, 0, 1026, 1001, 1, 0, 0, 0, 1026, 1015, 1, 0, 0, 0, 1027, 1028, 1, 0, 0, 0, 1028, 1029, 5, 107, 0, 0, 1029, 1030, 3, 108, 54, 0, 1030, 115, 1, 0, 0, 0, 1031, 1032, 5, 128, 0, 0, 1032, 1036, 3, 152, 76, 0, 1033, 1035, 3, 118, 59, 0, 1034, 1033, 1, 0, 0, 0, 1035, 1038, 1, 0, 0, 0, 1036, 1034, 1, 0, 0, 0, 1036, 1037, 1, 0, 0, 0, 1037, 1039, 1, 0, 0, 0, 1038, 1036, 1, 0, 0, 0, 1039, 1040, 5, 147, 0, 0, 1040, 1041, 5, 120, 0, 0, 1041, 1060, 1, 0, 0, 0, 1042, 1043, 5, 128, 0, 0, 1043, 1047, 3, 152, 76, 0, 1044, 1046, 3, 118, 59, 0, 1045, 1044, 1, 0, 0, 0, 1046, 1049, 1, 0, 0, 0, 1047, 1045, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1050, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1050, 1052, 5, 120, 0, 0, 1051, 1053, 3, 116, 58, 0, 1052, 1051, 1, 0, 0, 0, 1052, 1053, 1, 0, 0, 0, 1053, 1054, 1, 0, 0, 0, 1054, 1055, 5, 128, 0, 0, 1055, 1056, 5, 147, 0, 0, 1056, 1057, 3, 152, 76, 0, 1057, 1058, 5, 120, 0, 0, 1058, 1060, 1, 0, 0, 0, 1059, 1031, 1, 0, 0, 0, 1059, 1042, 1, 0, 0, 0, 1060, 117, 1, 0, 0, 0, 1061, 1062, 3, 152, 76, 0, 1062, 1063, 5, 118, 0, 0, 1063, 1064, 3, 158, 79, 0, 1064, 1073, 1, 0, 0, 0, 1065, 1066, 3, 152, 76, 0, 1066, 1067, 5, 118, 0, 0, 1067, 1068, 5, 124, 0, 0, 1068, 1069, 3, 108, 54, 0, 1069, 1070, 5, 143, 0, 0, 1070, 1073, 1, 0, 0, 0, 1071, 1073, 3, 152, 76, 0, 1072, 1061, 1, 0, 0, 0, 1072, 1065, 1, 0, 0, 0, 1072, 1071, 1, 0, 0, 0, 1073, 119, 1, 0, 0, 0, 1074, 1079, 3, 122, 61, 0, 1075, 1076, 5, 112, 0, 0, 1076, 1078, 3, 122, 61, 0, 1077, 1075, 1, 0, 0, 0, 1078, 1081, 1, 0, 0, 0, 1079, 1077, 1, 0, 0, 0, 1079, 1080, 1, 0, 0, 0, 1080, 1083, 1, 0, 0, 0, 1081, 1079, 1, 0, 0, 0, 1082, 1084, 5, 112, 0, 0, 1083, 1082, 1, 0, 0, 0, 1083, 1084, 1, 0, 0, 0, 1084, 121, 1, 0, 0, 0, 1085, 1086, 3, 152, 76, 0, 1086, 1087, 5, 6, 0, 0, 1087, 1088, 5, 126, 0, 0, 1088, 1089, 3, 36, 18, 0, 1089, 1090, 5, 145, 0, 0, 1090, 1096, 1, 0, 0, 0, 1091, 1092, 3, 108, 54, 0, 1092, 1093, 5, 6, 0, 0, 1093, 1094, 3, 152, 76, 0, 1094, 1096, 1, 0, 0, 0, 1095, 1085, 1, 0, 0, 0, 1095, 1091, 1, 0, 0, 0, 1096, 123, 1, 0, 0, 0, 1097, 1105, 3, 156, 78, 0, 1098, 1099, 3, 132, 66, 0, 1099, 1100, 5, 116, 0, 0, 1100, 1102, 1, 0, 0, 0, 1101, 1098, 1, 0, 0, 0, 1101, 1102, 1, 0, 0, 0, 1102, 1103, 1, 0, 0, 0, 1103, 1105, 3, 126, 63, 0, 1104, 1097, 1, 0, 0, 0, 1104, 1101, 1, 0, 0, 0, 1105, 125, 1, 0, 0, 0, 1106, 1111, 3, 152, 76, 0, 1107, 1108, 5, 116, 0, 0, 1108, 1110, 3, 152, 76, 0, 1109, 1107, 1, 0, 0, 0, 1110, 1113, 1, 0, 0, 0, 1111, 1109, 1, 0, 0, 0, 1111, 1112, 1, 0, 0, 0, 1112, 127, 1, 0, 0, 0, 1113, 1111, 1, 0, 0, 0, 1114, 1115, 6, 64, -1, 0, 1115, 1124, 3, 132, 66, 0, 1116, 1124, 3, 130, 65, 0, 1117, 1118, 5, 126, 0, 0, 1118, 1119, 3, 36, 18, 0, 1119, 1120, 5, 145, 0, 0, 1120, 1124, 1, 0, 0, 0, 1121, 1124, 3, 116, 58, 0, 1122, 1124, 3, 156, 78, 0, 1123, 1114, 1, 0, 0, 0, 1123, 1116, 1, 0, 0, 0, 1123, 1117, 1, 0, 0, 0, 1123, 1121, 1, 0, 0, 0, 1123, 1122, 1, 0, 0, 0, 1124, 1133, 1, 0, 0, 0, 1125, 1129, 10, 3, 0, 0, 1126, 1130, 3, 150, 75, 0, 1127, 1128, 5, 6, 0, 0, 1128, 1130, 3, 152, 76, 0, 1129, 1126, 1, 0, 0, 0, 1129, 1127, 1, 0, 0, 0, 1130, 1132, 1, 0, 0, 0, 1131, 1125, 1, 0, 0, 0, 1132, 1135, 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1133, 1134, 1, 0, 0, 0, 1134, 129, 1, 0, 0, 0, 1135, 1133, 1, 0, 0, 0, 1136, 1137, 3, 152, 76, 0, 1137, 1139, 5, 126, 0, 0, 1138, 1140, 3, 134, 67, 0, 1139, 1138, 1, 0, 0, 0, 1139, 1140, 1, 0, 0, 0, 1140, 1141, 1, 0, 0, 0, 1141, 1142, 5, 145, 0, 0, 1142, 131, 1, 0, 0, 0, 1143, 1144, 3, 136, 68, 0, 1144, 1145, 5, 116, 0, 0, 1145, 1147, 1, 0, 0, 0, 1146, 1143, 1, 0, 0, 0, 1146, 1147, 1, 0, 0, 0, 1147, 1148, 1, 0, 0, 0, 1148, 1149, 3, 152, 76, 0, 1149, 133, 1, 0, 0, 0, 1150, 1155, 3, 108, 54, 0, 1151, 1152, 5, 112, 0, 0, 1152, 1154, 3, 108, 54, 0, 1153, 1151, 1, 0, 0, 0, 1154, 1157, 1, 0, 0, 0, 1155, 1153, 1, 0, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1159, 1, 0, 0, 0, 1157, 1155, 1, 0, 0, 0, 1158, 1160, 5, 112, 0, 0, 1159, 1158, 1, 0, 0, 0, 1159, 1160, 1, 0, 0, 0, 1160, 135, 1, 0, 0, 0, 1161, 1162, 3, 152, 76, 0, 1162, 137, 1, 0, 0, 0, 1163, 1172, 5, 102, 0, 0, 1164, 1165, 5, 116, 0, 0, 1165, 1172, 7, 11, 0, 0, 1166, 1167, 5, 104, 0, 0, 1167, 1169, 5, 116, 0, 0, 1168, 1170, 7, 11, 0, 0, 1169, 1168, 1, 0, 0, 0, 1169, 1170, 1, 0, 0, 0, 1170, 1172, 1, 0, 0, 0, 1171, 1163, 1, 0, 0, 0, 1171, 1164, 1, 0, 0, 0, 1171, 1166, 1, 0, 0, 0, 1172, 139, 1, 0, 0, 0, 1173, 1175, 7, 12, 0, 0, 1174, 1173, 1, 0, 0, 0, 1174, 1175, 1, 0, 0, 0, 1175, 1182, 1, 0, 0, 0, 1176, 1183, 3, 138, 69, 0, 1177, 1183, 5, 103, 0, 0, 1178, 1183, 5, 104, 0, 0, 1179, 1183, 5, 105, 0, 0, 1180, 1183, 5, 41, 0, 0, 1181, 1183, 5, 55, 0, 0, 1182, 1176, 1, 0, 0, 0, 1182, 1177, 1, 0, 0, 0, 1182, 1178, 1, 0, 0, 0, 1182, 1179, 1, 0, 0, 0, 1182, 1180, 1, 0, 0, 0, 1182, 1181, 1, 0, 0, 0, 1183, 141, 1, 0, 0, 0, 1184, 1188, 3, 140, 70, 0, 1185, 1188, 5, 106, 0, 0, 1186, 1188, 5, 57, 0, 0, 1187, 1184, 1, 0, 0, 0, 1187, 1185, 1, 0, 0, 0, 1187, 1186, 1, 0, 0, 0, 1188, 143, 1, 0, 0, 0, 1189, 1190, 7, 13, 0, 0, 1190, 145, 1, 0, 0, 0, 1191, 1192, 7, 14, 0, 0, 1192, 147, 1, 0, 0, 0, 1193, 1194, 7, 15, 0, 0, 1194, 149, 1, 0, 0, 0, 1195, 1198, 5, 101, 0, 0, 1196, 1198, 3, 148, 74, 0, 1197, 1195, 1, 0, 0, 0, 1197, 1196, 1, 0, 0, 0, 1198, 151, 1, 0, 0, 0, 1199, 1203, 5, 101, 0, 0, 1200, 1203, 3, 144, 72, 0, 1201, 1203, 3, 146, 73, 0, 1202, 1199, 1, 0, 0, 0, 1202, 1200, 1, 0, 0, 0, 1202, 1201, 1, 0, 0, 0, 1203, 153, 1, 0, 0, 0, 1204, 1205, 3, 158, 79, 0, 1205, 1206, 5, 118, 0, 0, 1206, 1207, 3, 140, 70, 0, 1207, 155, 1, 0, 0, 0, 1208, 1209, 5, 124, 0, 0, 1209, 1210, 3, 152, 76, 0, 1210, 1211, 5, 143, 0, 0, 1211, 157, 1, 0, 0, 0, 1212, 1215, 5, 106, 0, 0, 1213, 1215, 3, 160, 80, 0, 1214, 1212, 1, 0, 0, 0, 1214, 1213, 1, 0, 0, 0, 1215, 159, 1, 0, 0, 0, 1216, 1220, 5, 138, 0, 0, 1217, 1219, 3, 162, 81, 0, 1218, 1217, 1, 0, 0, 0, 1219, 1222, 1, 0, 0, 0, 1220, 1218, 1, 0, 0, 0, 1220, 1221, 1, 0, 0, 0, 1221, 1223, 1, 0, 0, 0, 1222, 1220, 1, 0, 0, 0, 1223, 1224, 5, 140, 0, 0, 1224, 161, 1, 0, 0, 0, 1225, 1226, 5, 153, 0, 0, 1226, 1227, 3, 108, 54, 0, 1227, 1228, 5, 143, 0, 0, 1228, 1231, 1, 0, 0, 0, 1229, 1231, 5, 152, 0, 0, 1230, 1225, 1, 0, 0, 0, 1230, 1229, 1, 0, 0, 0, 1231, 163, 1, 0, 0, 0, 1232, 1236, 5, 139, 0, 0, 1233, 1235, 3, 166, 83, 0, 1234, 1233, 1, 0, 0, 0, 1235, 1238, 1, 0, 0, 0, 1236, 1234, 1, 0, 0, 0, 1236, 1237, 1, 0, 0, 0, 1237, 1239, 1, 0, 0, 0, 1238, 1236, 1, 0, 0, 0, 1239, 1240, 5, 0, 0, 1, 1240, 165, 1, 0, 0, 0, 1241, 1242, 5, 155, 0, 0, 1242, 1243, 3, 108, 54, 0, 1243, 1244, 5, 143, 0, 0, 1244, 1247, 1, 0, 0, 0, 1245, 1247, 5, 154, 0, 0, 1246, 1241, 1, 0, 0, 0, 1246, 1245, 1, 0, 0, 0, 1247, 167, 1, 0, 0, 0, 160, 171, 178, 187, 194, 198, 209, 213, 216, 225, 233, 240, 244, 250, 255, 261, 273, 281, 295, 299, 304, 314, 323, 326, 330, 333, 337, 340, 343, 346, 349, 353, 357, 360, 363, 366, 370, 373, 382, 388, 409, 426, 443, 449, 455, 466, 468, 479, 482, 488, 496, 502, 504, 508, 513, 516, 519, 523, 527, 530, 532, 535, 539, 543, 546, 548, 550, 555, 566, 572, 579, 584, 588, 592, 598, 600, 607, 615, 618, 621, 640, 654, 670, 674, 685, 689, 700, 704, 711, 715, 722, 726, 731, 740, 744, 768, 785, 791, 794, 797, 807, 813, 816, 819, 827, 830, 834, 837, 851, 868, 873, 877, 883, 890, 902, 906, 909, 918, 932, 971, 979, 981, 983, 991, 995, 999, 1007, 1011, 1020, 1024, 1026, 1036, 1047, 1052, 1059, 1072, 1079, 1083, 1095, 1101, 1104, 1111, 1123, 1129, 1133, 1139, 1146, 1155, 1159, 1169, 1171, 1174, 1182, 1187, 1197, 1202, 1214, 1220, 1230, 1236, 1246] \ No newline at end of file +[4, 1, 155, 1267, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 1, 0, 5, 0, 172, 8, 0, 10, 0, 12, 0, 175, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 181, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 190, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 195, 8, 4, 10, 4, 12, 4, 198, 9, 4, 1, 4, 3, 4, 201, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 213, 8, 5, 1, 6, 1, 6, 3, 6, 217, 8, 6, 1, 6, 3, 6, 220, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 229, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 237, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 244, 8, 9, 1, 9, 1, 9, 3, 9, 248, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 254, 8, 9, 1, 9, 1, 9, 1, 9, 3, 9, 259, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 267, 8, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 274, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 280, 8, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 3, 13, 292, 8, 13, 1, 14, 1, 14, 1, 15, 1, 15, 5, 15, 298, 8, 15, 10, 15, 12, 15, 301, 9, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 5, 17, 312, 8, 17, 10, 17, 12, 17, 315, 9, 17, 1, 17, 3, 17, 318, 8, 17, 1, 18, 1, 18, 1, 18, 3, 18, 323, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 331, 8, 19, 10, 19, 12, 19, 334, 9, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 342, 8, 20, 1, 21, 3, 21, 345, 8, 21, 1, 21, 1, 21, 3, 21, 349, 8, 21, 1, 21, 3, 21, 352, 8, 21, 1, 21, 1, 21, 3, 21, 356, 8, 21, 1, 21, 3, 21, 359, 8, 21, 1, 21, 3, 21, 362, 8, 21, 1, 21, 3, 21, 365, 8, 21, 1, 21, 3, 21, 368, 8, 21, 1, 21, 1, 21, 3, 21, 372, 8, 21, 1, 21, 1, 21, 3, 21, 376, 8, 21, 1, 21, 3, 21, 379, 8, 21, 1, 21, 3, 21, 382, 8, 21, 1, 21, 3, 21, 385, 8, 21, 1, 21, 1, 21, 3, 21, 389, 8, 21, 1, 21, 3, 21, 392, 8, 21, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 401, 8, 23, 1, 24, 1, 24, 1, 24, 1, 25, 3, 25, 407, 8, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 5, 26, 426, 8, 26, 10, 26, 12, 26, 429, 9, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 3, 29, 445, 8, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 462, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 468, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 474, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 485, 8, 33, 3, 33, 487, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 3, 36, 498, 8, 36, 1, 36, 3, 36, 501, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 3, 36, 507, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 3, 36, 515, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 5, 36, 521, 8, 36, 10, 36, 12, 36, 524, 9, 36, 1, 37, 3, 37, 527, 8, 37, 1, 37, 1, 37, 1, 37, 3, 37, 532, 8, 37, 1, 37, 3, 37, 535, 8, 37, 1, 37, 3, 37, 538, 8, 37, 1, 37, 1, 37, 3, 37, 542, 8, 37, 1, 37, 1, 37, 3, 37, 546, 8, 37, 1, 37, 3, 37, 549, 8, 37, 3, 37, 551, 8, 37, 1, 37, 3, 37, 554, 8, 37, 1, 37, 1, 37, 3, 37, 558, 8, 37, 1, 37, 1, 37, 3, 37, 562, 8, 37, 1, 37, 3, 37, 565, 8, 37, 3, 37, 567, 8, 37, 3, 37, 569, 8, 37, 1, 38, 1, 38, 1, 38, 3, 38, 574, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 585, 8, 39, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 591, 8, 40, 1, 41, 1, 41, 1, 41, 5, 41, 596, 8, 41, 10, 41, 12, 41, 599, 9, 41, 1, 42, 1, 42, 3, 42, 603, 8, 42, 1, 42, 1, 42, 3, 42, 607, 8, 42, 1, 42, 1, 42, 3, 42, 611, 8, 42, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 617, 8, 43, 3, 43, 619, 8, 43, 1, 44, 1, 44, 1, 44, 5, 44, 624, 8, 44, 10, 44, 12, 44, 627, 9, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 3, 46, 634, 8, 46, 1, 46, 3, 46, 637, 8, 46, 1, 46, 3, 46, 640, 8, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 659, 8, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 3, 51, 673, 8, 51, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 5, 53, 687, 8, 53, 10, 53, 12, 53, 690, 9, 53, 1, 53, 3, 53, 693, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 5, 53, 702, 8, 53, 10, 53, 12, 53, 705, 9, 53, 1, 53, 3, 53, 708, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 5, 53, 717, 8, 53, 10, 53, 12, 53, 720, 9, 53, 1, 53, 3, 53, 723, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 730, 8, 53, 1, 53, 1, 53, 3, 53, 734, 8, 53, 1, 54, 1, 54, 1, 54, 5, 54, 739, 8, 54, 10, 54, 12, 54, 742, 9, 54, 1, 54, 3, 54, 745, 8, 54, 1, 55, 1, 55, 1, 55, 3, 55, 750, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 4, 55, 757, 8, 55, 11, 55, 12, 55, 758, 1, 55, 1, 55, 3, 55, 763, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 787, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 804, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 810, 8, 55, 1, 55, 3, 55, 813, 8, 55, 1, 55, 3, 55, 816, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 826, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 832, 8, 55, 1, 55, 3, 55, 835, 8, 55, 1, 55, 3, 55, 838, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 846, 8, 55, 1, 55, 3, 55, 849, 8, 55, 1, 55, 1, 55, 3, 55, 853, 8, 55, 1, 55, 3, 55, 856, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 870, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 887, 8, 55, 1, 55, 1, 55, 1, 55, 3, 55, 892, 8, 55, 1, 55, 1, 55, 3, 55, 896, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 902, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 909, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 921, 8, 55, 1, 55, 1, 55, 3, 55, 925, 8, 55, 1, 55, 3, 55, 928, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 937, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 951, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 990, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 998, 8, 55, 5, 55, 1000, 8, 55, 10, 55, 12, 55, 1003, 9, 55, 1, 56, 1, 56, 1, 56, 5, 56, 1008, 8, 56, 10, 56, 12, 56, 1011, 9, 56, 1, 56, 3, 56, 1014, 8, 56, 1, 57, 1, 57, 3, 57, 1018, 8, 57, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 1024, 8, 58, 10, 58, 12, 58, 1027, 9, 58, 1, 58, 3, 58, 1030, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 1037, 8, 58, 10, 58, 12, 58, 1040, 9, 58, 1, 58, 3, 58, 1043, 8, 58, 3, 58, 1045, 8, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 5, 59, 1053, 8, 59, 10, 59, 12, 59, 1056, 9, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 5, 59, 1064, 8, 59, 10, 59, 12, 59, 1067, 9, 59, 1, 59, 1, 59, 3, 59, 1071, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1078, 8, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 3, 60, 1091, 8, 60, 1, 61, 1, 61, 1, 61, 5, 61, 1096, 8, 61, 10, 61, 12, 61, 1099, 9, 61, 1, 61, 3, 61, 1102, 8, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1114, 8, 62, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 1120, 8, 63, 1, 63, 3, 63, 1123, 8, 63, 1, 64, 1, 64, 1, 64, 5, 64, 1128, 8, 64, 10, 64, 12, 64, 1131, 9, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1142, 8, 65, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1148, 8, 65, 5, 65, 1150, 8, 65, 10, 65, 12, 65, 1153, 9, 65, 1, 66, 1, 66, 1, 66, 3, 66, 1158, 8, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 3, 67, 1165, 8, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 5, 68, 1172, 8, 68, 10, 68, 12, 68, 1175, 9, 68, 1, 68, 3, 68, 1178, 8, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 3, 70, 1188, 8, 70, 3, 70, 1190, 8, 70, 1, 71, 3, 71, 1193, 8, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 1201, 8, 71, 1, 72, 1, 72, 1, 72, 3, 72, 1206, 8, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 3, 76, 1216, 8, 76, 1, 77, 1, 77, 1, 77, 3, 77, 1221, 8, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 3, 80, 1233, 8, 80, 1, 81, 1, 81, 5, 81, 1237, 8, 81, 10, 81, 12, 81, 1240, 9, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 3, 82, 1249, 8, 82, 1, 83, 1, 83, 5, 83, 1253, 8, 83, 10, 83, 12, 83, 1256, 9, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 3, 84, 1265, 8, 84, 1, 84, 0, 3, 72, 110, 130, 85, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 0, 16, 2, 0, 17, 17, 72, 72, 2, 0, 42, 42, 49, 49, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 78, 78, 2, 0, 49, 49, 71, 71, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 21, 22, 2, 0, 28, 28, 47, 47, 2, 0, 69, 69, 74, 74, 3, 0, 10, 10, 48, 48, 87, 87, 2, 0, 39, 39, 51, 51, 1, 0, 103, 104, 2, 0, 114, 114, 135, 135, 7, 0, 20, 20, 36, 36, 53, 54, 68, 68, 76, 76, 93, 93, 99, 99, 12, 0, 1, 19, 21, 28, 30, 35, 37, 40, 42, 49, 51, 52, 56, 56, 58, 67, 69, 75, 77, 92, 94, 95, 97, 98, 4, 0, 19, 19, 28, 28, 37, 37, 46, 46, 1429, 0, 173, 1, 0, 0, 0, 2, 180, 1, 0, 0, 0, 4, 182, 1, 0, 0, 0, 6, 184, 1, 0, 0, 0, 8, 191, 1, 0, 0, 0, 10, 212, 1, 0, 0, 0, 12, 214, 1, 0, 0, 0, 14, 221, 1, 0, 0, 0, 16, 230, 1, 0, 0, 0, 18, 238, 1, 0, 0, 0, 20, 260, 1, 0, 0, 0, 22, 275, 1, 0, 0, 0, 24, 284, 1, 0, 0, 0, 26, 289, 1, 0, 0, 0, 28, 293, 1, 0, 0, 0, 30, 295, 1, 0, 0, 0, 32, 304, 1, 0, 0, 0, 34, 308, 1, 0, 0, 0, 36, 322, 1, 0, 0, 0, 38, 326, 1, 0, 0, 0, 40, 341, 1, 0, 0, 0, 42, 344, 1, 0, 0, 0, 44, 393, 1, 0, 0, 0, 46, 396, 1, 0, 0, 0, 48, 402, 1, 0, 0, 0, 50, 406, 1, 0, 0, 0, 52, 412, 1, 0, 0, 0, 54, 430, 1, 0, 0, 0, 56, 433, 1, 0, 0, 0, 58, 436, 1, 0, 0, 0, 60, 446, 1, 0, 0, 0, 62, 449, 1, 0, 0, 0, 64, 453, 1, 0, 0, 0, 66, 486, 1, 0, 0, 0, 68, 488, 1, 0, 0, 0, 70, 491, 1, 0, 0, 0, 72, 506, 1, 0, 0, 0, 74, 568, 1, 0, 0, 0, 76, 573, 1, 0, 0, 0, 78, 584, 1, 0, 0, 0, 80, 586, 1, 0, 0, 0, 82, 592, 1, 0, 0, 0, 84, 600, 1, 0, 0, 0, 86, 618, 1, 0, 0, 0, 88, 620, 1, 0, 0, 0, 90, 628, 1, 0, 0, 0, 92, 633, 1, 0, 0, 0, 94, 641, 1, 0, 0, 0, 96, 645, 1, 0, 0, 0, 98, 649, 1, 0, 0, 0, 100, 658, 1, 0, 0, 0, 102, 672, 1, 0, 0, 0, 104, 674, 1, 0, 0, 0, 106, 733, 1, 0, 0, 0, 108, 735, 1, 0, 0, 0, 110, 895, 1, 0, 0, 0, 112, 1004, 1, 0, 0, 0, 114, 1017, 1, 0, 0, 0, 116, 1044, 1, 0, 0, 0, 118, 1077, 1, 0, 0, 0, 120, 1090, 1, 0, 0, 0, 122, 1092, 1, 0, 0, 0, 124, 1113, 1, 0, 0, 0, 126, 1122, 1, 0, 0, 0, 128, 1124, 1, 0, 0, 0, 130, 1141, 1, 0, 0, 0, 132, 1154, 1, 0, 0, 0, 134, 1164, 1, 0, 0, 0, 136, 1168, 1, 0, 0, 0, 138, 1179, 1, 0, 0, 0, 140, 1189, 1, 0, 0, 0, 142, 1192, 1, 0, 0, 0, 144, 1205, 1, 0, 0, 0, 146, 1207, 1, 0, 0, 0, 148, 1209, 1, 0, 0, 0, 150, 1211, 1, 0, 0, 0, 152, 1215, 1, 0, 0, 0, 154, 1220, 1, 0, 0, 0, 156, 1222, 1, 0, 0, 0, 158, 1226, 1, 0, 0, 0, 160, 1232, 1, 0, 0, 0, 162, 1234, 1, 0, 0, 0, 164, 1248, 1, 0, 0, 0, 166, 1250, 1, 0, 0, 0, 168, 1264, 1, 0, 0, 0, 170, 172, 3, 2, 1, 0, 171, 170, 1, 0, 0, 0, 172, 175, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 173, 174, 1, 0, 0, 0, 174, 176, 1, 0, 0, 0, 175, 173, 1, 0, 0, 0, 176, 177, 5, 0, 0, 1, 177, 1, 1, 0, 0, 0, 178, 181, 3, 6, 3, 0, 179, 181, 3, 10, 5, 0, 180, 178, 1, 0, 0, 0, 180, 179, 1, 0, 0, 0, 181, 3, 1, 0, 0, 0, 182, 183, 3, 110, 55, 0, 183, 5, 1, 0, 0, 0, 184, 185, 5, 50, 0, 0, 185, 189, 3, 154, 77, 0, 186, 187, 5, 111, 0, 0, 187, 188, 5, 118, 0, 0, 188, 190, 3, 4, 2, 0, 189, 186, 1, 0, 0, 0, 189, 190, 1, 0, 0, 0, 190, 7, 1, 0, 0, 0, 191, 196, 3, 154, 77, 0, 192, 193, 5, 112, 0, 0, 193, 195, 3, 154, 77, 0, 194, 192, 1, 0, 0, 0, 195, 198, 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 200, 1, 0, 0, 0, 198, 196, 1, 0, 0, 0, 199, 201, 5, 112, 0, 0, 200, 199, 1, 0, 0, 0, 200, 201, 1, 0, 0, 0, 201, 9, 1, 0, 0, 0, 202, 213, 3, 12, 6, 0, 203, 213, 3, 14, 7, 0, 204, 213, 3, 16, 8, 0, 205, 213, 3, 20, 10, 0, 206, 213, 3, 18, 9, 0, 207, 213, 3, 22, 11, 0, 208, 213, 3, 24, 12, 0, 209, 213, 3, 30, 15, 0, 210, 213, 3, 26, 13, 0, 211, 213, 3, 28, 14, 0, 212, 202, 1, 0, 0, 0, 212, 203, 1, 0, 0, 0, 212, 204, 1, 0, 0, 0, 212, 205, 1, 0, 0, 0, 212, 206, 1, 0, 0, 0, 212, 207, 1, 0, 0, 0, 212, 208, 1, 0, 0, 0, 212, 209, 1, 0, 0, 0, 212, 210, 1, 0, 0, 0, 212, 211, 1, 0, 0, 0, 213, 11, 1, 0, 0, 0, 214, 216, 5, 70, 0, 0, 215, 217, 3, 4, 2, 0, 216, 215, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 219, 1, 0, 0, 0, 218, 220, 5, 146, 0, 0, 219, 218, 1, 0, 0, 0, 219, 220, 1, 0, 0, 0, 220, 13, 1, 0, 0, 0, 221, 222, 5, 38, 0, 0, 222, 223, 5, 126, 0, 0, 223, 224, 3, 4, 2, 0, 224, 225, 5, 145, 0, 0, 225, 228, 3, 10, 5, 0, 226, 227, 5, 24, 0, 0, 227, 229, 3, 10, 5, 0, 228, 226, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 15, 1, 0, 0, 0, 230, 231, 5, 96, 0, 0, 231, 232, 5, 126, 0, 0, 232, 233, 3, 4, 2, 0, 233, 234, 5, 145, 0, 0, 234, 236, 3, 10, 5, 0, 235, 237, 5, 146, 0, 0, 236, 235, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 17, 1, 0, 0, 0, 238, 239, 5, 31, 0, 0, 239, 243, 5, 126, 0, 0, 240, 244, 3, 6, 3, 0, 241, 244, 3, 24, 12, 0, 242, 244, 3, 4, 2, 0, 243, 240, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 243, 242, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 247, 5, 146, 0, 0, 246, 248, 3, 4, 2, 0, 247, 246, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 253, 5, 146, 0, 0, 250, 254, 3, 6, 3, 0, 251, 254, 3, 24, 12, 0, 252, 254, 3, 4, 2, 0, 253, 250, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 252, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 256, 5, 145, 0, 0, 256, 258, 3, 10, 5, 0, 257, 259, 5, 146, 0, 0, 258, 257, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 19, 1, 0, 0, 0, 260, 261, 5, 31, 0, 0, 261, 262, 5, 126, 0, 0, 262, 263, 5, 50, 0, 0, 263, 266, 3, 154, 77, 0, 264, 265, 5, 112, 0, 0, 265, 267, 3, 154, 77, 0, 266, 264, 1, 0, 0, 0, 266, 267, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 269, 5, 40, 0, 0, 269, 270, 3, 4, 2, 0, 270, 271, 5, 145, 0, 0, 271, 273, 3, 10, 5, 0, 272, 274, 5, 146, 0, 0, 273, 272, 1, 0, 0, 0, 273, 274, 1, 0, 0, 0, 274, 21, 1, 0, 0, 0, 275, 276, 5, 29, 0, 0, 276, 277, 3, 154, 77, 0, 277, 279, 5, 126, 0, 0, 278, 280, 3, 8, 4, 0, 279, 278, 1, 0, 0, 0, 279, 280, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 282, 5, 145, 0, 0, 282, 283, 3, 30, 15, 0, 283, 23, 1, 0, 0, 0, 284, 285, 3, 4, 2, 0, 285, 286, 5, 111, 0, 0, 286, 287, 5, 118, 0, 0, 287, 288, 3, 4, 2, 0, 288, 25, 1, 0, 0, 0, 289, 291, 3, 4, 2, 0, 290, 292, 5, 146, 0, 0, 291, 290, 1, 0, 0, 0, 291, 292, 1, 0, 0, 0, 292, 27, 1, 0, 0, 0, 293, 294, 5, 146, 0, 0, 294, 29, 1, 0, 0, 0, 295, 299, 5, 124, 0, 0, 296, 298, 3, 2, 1, 0, 297, 296, 1, 0, 0, 0, 298, 301, 1, 0, 0, 0, 299, 297, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 302, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 302, 303, 5, 143, 0, 0, 303, 31, 1, 0, 0, 0, 304, 305, 3, 4, 2, 0, 305, 306, 5, 111, 0, 0, 306, 307, 3, 4, 2, 0, 307, 33, 1, 0, 0, 0, 308, 313, 3, 32, 16, 0, 309, 310, 5, 112, 0, 0, 310, 312, 3, 32, 16, 0, 311, 309, 1, 0, 0, 0, 312, 315, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 317, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 316, 318, 5, 112, 0, 0, 317, 316, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 35, 1, 0, 0, 0, 319, 323, 3, 38, 19, 0, 320, 323, 3, 42, 21, 0, 321, 323, 3, 118, 59, 0, 322, 319, 1, 0, 0, 0, 322, 320, 1, 0, 0, 0, 322, 321, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 325, 5, 0, 0, 1, 325, 37, 1, 0, 0, 0, 326, 332, 3, 40, 20, 0, 327, 328, 5, 91, 0, 0, 328, 329, 5, 1, 0, 0, 329, 331, 3, 40, 20, 0, 330, 327, 1, 0, 0, 0, 331, 334, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 39, 1, 0, 0, 0, 334, 332, 1, 0, 0, 0, 335, 342, 3, 42, 21, 0, 336, 337, 5, 126, 0, 0, 337, 338, 3, 38, 19, 0, 338, 339, 5, 145, 0, 0, 339, 342, 1, 0, 0, 0, 340, 342, 3, 158, 79, 0, 341, 335, 1, 0, 0, 0, 341, 336, 1, 0, 0, 0, 341, 340, 1, 0, 0, 0, 342, 41, 1, 0, 0, 0, 343, 345, 3, 44, 22, 0, 344, 343, 1, 0, 0, 0, 344, 345, 1, 0, 0, 0, 345, 346, 1, 0, 0, 0, 346, 348, 5, 77, 0, 0, 347, 349, 5, 23, 0, 0, 348, 347, 1, 0, 0, 0, 348, 349, 1, 0, 0, 0, 349, 351, 1, 0, 0, 0, 350, 352, 3, 46, 23, 0, 351, 350, 1, 0, 0, 0, 351, 352, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 355, 3, 108, 54, 0, 354, 356, 3, 48, 24, 0, 355, 354, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 358, 1, 0, 0, 0, 357, 359, 3, 50, 25, 0, 358, 357, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 361, 1, 0, 0, 0, 360, 362, 3, 54, 27, 0, 361, 360, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 364, 1, 0, 0, 0, 363, 365, 3, 56, 28, 0, 364, 363, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 367, 1, 0, 0, 0, 366, 368, 3, 58, 29, 0, 367, 366, 1, 0, 0, 0, 367, 368, 1, 0, 0, 0, 368, 371, 1, 0, 0, 0, 369, 370, 5, 98, 0, 0, 370, 372, 7, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 375, 1, 0, 0, 0, 373, 374, 5, 98, 0, 0, 374, 376, 5, 86, 0, 0, 375, 373, 1, 0, 0, 0, 375, 376, 1, 0, 0, 0, 376, 378, 1, 0, 0, 0, 377, 379, 3, 60, 30, 0, 378, 377, 1, 0, 0, 0, 378, 379, 1, 0, 0, 0, 379, 381, 1, 0, 0, 0, 380, 382, 3, 52, 26, 0, 381, 380, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 384, 1, 0, 0, 0, 383, 385, 3, 62, 31, 0, 384, 383, 1, 0, 0, 0, 384, 385, 1, 0, 0, 0, 385, 388, 1, 0, 0, 0, 386, 389, 3, 66, 33, 0, 387, 389, 3, 68, 34, 0, 388, 386, 1, 0, 0, 0, 388, 387, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 391, 1, 0, 0, 0, 390, 392, 3, 70, 35, 0, 391, 390, 1, 0, 0, 0, 391, 392, 1, 0, 0, 0, 392, 43, 1, 0, 0, 0, 393, 394, 5, 98, 0, 0, 394, 395, 3, 122, 61, 0, 395, 45, 1, 0, 0, 0, 396, 397, 5, 85, 0, 0, 397, 400, 5, 104, 0, 0, 398, 399, 5, 98, 0, 0, 399, 401, 5, 82, 0, 0, 400, 398, 1, 0, 0, 0, 400, 401, 1, 0, 0, 0, 401, 47, 1, 0, 0, 0, 402, 403, 5, 32, 0, 0, 403, 404, 3, 72, 36, 0, 404, 49, 1, 0, 0, 0, 405, 407, 7, 1, 0, 0, 406, 405, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 408, 1, 0, 0, 0, 408, 409, 5, 5, 0, 0, 409, 410, 5, 45, 0, 0, 410, 411, 3, 108, 54, 0, 411, 51, 1, 0, 0, 0, 412, 413, 5, 97, 0, 0, 413, 414, 3, 154, 77, 0, 414, 415, 5, 6, 0, 0, 415, 416, 5, 126, 0, 0, 416, 417, 3, 92, 46, 0, 417, 427, 5, 145, 0, 0, 418, 419, 5, 112, 0, 0, 419, 420, 3, 154, 77, 0, 420, 421, 5, 6, 0, 0, 421, 422, 5, 126, 0, 0, 422, 423, 3, 92, 46, 0, 423, 424, 5, 145, 0, 0, 424, 426, 1, 0, 0, 0, 425, 418, 1, 0, 0, 0, 426, 429, 1, 0, 0, 0, 427, 425, 1, 0, 0, 0, 427, 428, 1, 0, 0, 0, 428, 53, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 430, 431, 5, 67, 0, 0, 431, 432, 3, 110, 55, 0, 432, 55, 1, 0, 0, 0, 433, 434, 5, 95, 0, 0, 434, 435, 3, 110, 55, 0, 435, 57, 1, 0, 0, 0, 436, 437, 5, 34, 0, 0, 437, 444, 5, 11, 0, 0, 438, 439, 7, 0, 0, 0, 439, 440, 5, 126, 0, 0, 440, 441, 3, 108, 54, 0, 441, 442, 5, 145, 0, 0, 442, 445, 1, 0, 0, 0, 443, 445, 3, 108, 54, 0, 444, 438, 1, 0, 0, 0, 444, 443, 1, 0, 0, 0, 445, 59, 1, 0, 0, 0, 446, 447, 5, 35, 0, 0, 447, 448, 3, 110, 55, 0, 448, 61, 1, 0, 0, 0, 449, 450, 5, 62, 0, 0, 450, 451, 5, 11, 0, 0, 451, 452, 3, 82, 41, 0, 452, 63, 1, 0, 0, 0, 453, 454, 5, 62, 0, 0, 454, 455, 5, 11, 0, 0, 455, 456, 3, 108, 54, 0, 456, 65, 1, 0, 0, 0, 457, 458, 5, 52, 0, 0, 458, 461, 3, 110, 55, 0, 459, 460, 5, 112, 0, 0, 460, 462, 3, 110, 55, 0, 461, 459, 1, 0, 0, 0, 461, 462, 1, 0, 0, 0, 462, 467, 1, 0, 0, 0, 463, 464, 5, 98, 0, 0, 464, 468, 5, 82, 0, 0, 465, 466, 5, 11, 0, 0, 466, 468, 3, 108, 54, 0, 467, 463, 1, 0, 0, 0, 467, 465, 1, 0, 0, 0, 467, 468, 1, 0, 0, 0, 468, 487, 1, 0, 0, 0, 469, 470, 5, 52, 0, 0, 470, 473, 3, 110, 55, 0, 471, 472, 5, 98, 0, 0, 472, 474, 5, 82, 0, 0, 473, 471, 1, 0, 0, 0, 473, 474, 1, 0, 0, 0, 474, 475, 1, 0, 0, 0, 475, 476, 5, 59, 0, 0, 476, 477, 3, 110, 55, 0, 477, 487, 1, 0, 0, 0, 478, 479, 5, 52, 0, 0, 479, 480, 3, 110, 55, 0, 480, 481, 5, 59, 0, 0, 481, 484, 3, 110, 55, 0, 482, 483, 5, 11, 0, 0, 483, 485, 3, 108, 54, 0, 484, 482, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 487, 1, 0, 0, 0, 486, 457, 1, 0, 0, 0, 486, 469, 1, 0, 0, 0, 486, 478, 1, 0, 0, 0, 487, 67, 1, 0, 0, 0, 488, 489, 5, 59, 0, 0, 489, 490, 3, 110, 55, 0, 490, 69, 1, 0, 0, 0, 491, 492, 5, 79, 0, 0, 492, 493, 3, 88, 44, 0, 493, 71, 1, 0, 0, 0, 494, 495, 6, 36, -1, 0, 495, 497, 3, 130, 65, 0, 496, 498, 5, 27, 0, 0, 497, 496, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 500, 1, 0, 0, 0, 499, 501, 3, 80, 40, 0, 500, 499, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 507, 1, 0, 0, 0, 502, 503, 5, 126, 0, 0, 503, 504, 3, 72, 36, 0, 504, 505, 5, 145, 0, 0, 505, 507, 1, 0, 0, 0, 506, 494, 1, 0, 0, 0, 506, 502, 1, 0, 0, 0, 507, 522, 1, 0, 0, 0, 508, 509, 10, 3, 0, 0, 509, 510, 3, 76, 38, 0, 510, 511, 3, 72, 36, 4, 511, 521, 1, 0, 0, 0, 512, 514, 10, 4, 0, 0, 513, 515, 3, 74, 37, 0, 514, 513, 1, 0, 0, 0, 514, 515, 1, 0, 0, 0, 515, 516, 1, 0, 0, 0, 516, 517, 5, 45, 0, 0, 517, 518, 3, 72, 36, 0, 518, 519, 3, 78, 39, 0, 519, 521, 1, 0, 0, 0, 520, 508, 1, 0, 0, 0, 520, 512, 1, 0, 0, 0, 521, 524, 1, 0, 0, 0, 522, 520, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 73, 1, 0, 0, 0, 524, 522, 1, 0, 0, 0, 525, 527, 7, 2, 0, 0, 526, 525, 1, 0, 0, 0, 526, 527, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 535, 5, 42, 0, 0, 529, 531, 5, 42, 0, 0, 530, 532, 7, 2, 0, 0, 531, 530, 1, 0, 0, 0, 531, 532, 1, 0, 0, 0, 532, 535, 1, 0, 0, 0, 533, 535, 7, 2, 0, 0, 534, 526, 1, 0, 0, 0, 534, 529, 1, 0, 0, 0, 534, 533, 1, 0, 0, 0, 535, 569, 1, 0, 0, 0, 536, 538, 7, 3, 0, 0, 537, 536, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 539, 1, 0, 0, 0, 539, 541, 7, 4, 0, 0, 540, 542, 5, 63, 0, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 551, 1, 0, 0, 0, 543, 545, 7, 4, 0, 0, 544, 546, 5, 63, 0, 0, 545, 544, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 548, 1, 0, 0, 0, 547, 549, 7, 3, 0, 0, 548, 547, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 551, 1, 0, 0, 0, 550, 537, 1, 0, 0, 0, 550, 543, 1, 0, 0, 0, 551, 569, 1, 0, 0, 0, 552, 554, 7, 5, 0, 0, 553, 552, 1, 0, 0, 0, 553, 554, 1, 0, 0, 0, 554, 555, 1, 0, 0, 0, 555, 557, 5, 33, 0, 0, 556, 558, 5, 63, 0, 0, 557, 556, 1, 0, 0, 0, 557, 558, 1, 0, 0, 0, 558, 567, 1, 0, 0, 0, 559, 561, 5, 33, 0, 0, 560, 562, 5, 63, 0, 0, 561, 560, 1, 0, 0, 0, 561, 562, 1, 0, 0, 0, 562, 564, 1, 0, 0, 0, 563, 565, 7, 5, 0, 0, 564, 563, 1, 0, 0, 0, 564, 565, 1, 0, 0, 0, 565, 567, 1, 0, 0, 0, 566, 553, 1, 0, 0, 0, 566, 559, 1, 0, 0, 0, 567, 569, 1, 0, 0, 0, 568, 534, 1, 0, 0, 0, 568, 550, 1, 0, 0, 0, 568, 566, 1, 0, 0, 0, 569, 75, 1, 0, 0, 0, 570, 571, 5, 16, 0, 0, 571, 574, 5, 45, 0, 0, 572, 574, 5, 112, 0, 0, 573, 570, 1, 0, 0, 0, 573, 572, 1, 0, 0, 0, 574, 77, 1, 0, 0, 0, 575, 576, 5, 60, 0, 0, 576, 585, 3, 108, 54, 0, 577, 578, 5, 92, 0, 0, 578, 579, 5, 126, 0, 0, 579, 580, 3, 108, 54, 0, 580, 581, 5, 145, 0, 0, 581, 585, 1, 0, 0, 0, 582, 583, 5, 92, 0, 0, 583, 585, 3, 108, 54, 0, 584, 575, 1, 0, 0, 0, 584, 577, 1, 0, 0, 0, 584, 582, 1, 0, 0, 0, 585, 79, 1, 0, 0, 0, 586, 587, 5, 75, 0, 0, 587, 590, 3, 86, 43, 0, 588, 589, 5, 59, 0, 0, 589, 591, 3, 86, 43, 0, 590, 588, 1, 0, 0, 0, 590, 591, 1, 0, 0, 0, 591, 81, 1, 0, 0, 0, 592, 597, 3, 84, 42, 0, 593, 594, 5, 112, 0, 0, 594, 596, 3, 84, 42, 0, 595, 593, 1, 0, 0, 0, 596, 599, 1, 0, 0, 0, 597, 595, 1, 0, 0, 0, 597, 598, 1, 0, 0, 0, 598, 83, 1, 0, 0, 0, 599, 597, 1, 0, 0, 0, 600, 602, 3, 110, 55, 0, 601, 603, 7, 6, 0, 0, 602, 601, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 606, 1, 0, 0, 0, 604, 605, 5, 58, 0, 0, 605, 607, 7, 7, 0, 0, 606, 604, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 610, 1, 0, 0, 0, 608, 609, 5, 15, 0, 0, 609, 611, 5, 106, 0, 0, 610, 608, 1, 0, 0, 0, 610, 611, 1, 0, 0, 0, 611, 85, 1, 0, 0, 0, 612, 619, 3, 158, 79, 0, 613, 616, 3, 142, 71, 0, 614, 615, 5, 147, 0, 0, 615, 617, 3, 142, 71, 0, 616, 614, 1, 0, 0, 0, 616, 617, 1, 0, 0, 0, 617, 619, 1, 0, 0, 0, 618, 612, 1, 0, 0, 0, 618, 613, 1, 0, 0, 0, 619, 87, 1, 0, 0, 0, 620, 625, 3, 90, 45, 0, 621, 622, 5, 112, 0, 0, 622, 624, 3, 90, 45, 0, 623, 621, 1, 0, 0, 0, 624, 627, 1, 0, 0, 0, 625, 623, 1, 0, 0, 0, 625, 626, 1, 0, 0, 0, 626, 89, 1, 0, 0, 0, 627, 625, 1, 0, 0, 0, 628, 629, 3, 154, 77, 0, 629, 630, 5, 118, 0, 0, 630, 631, 3, 144, 72, 0, 631, 91, 1, 0, 0, 0, 632, 634, 3, 94, 47, 0, 633, 632, 1, 0, 0, 0, 633, 634, 1, 0, 0, 0, 634, 636, 1, 0, 0, 0, 635, 637, 3, 96, 48, 0, 636, 635, 1, 0, 0, 0, 636, 637, 1, 0, 0, 0, 637, 639, 1, 0, 0, 0, 638, 640, 3, 98, 49, 0, 639, 638, 1, 0, 0, 0, 639, 640, 1, 0, 0, 0, 640, 93, 1, 0, 0, 0, 641, 642, 5, 65, 0, 0, 642, 643, 5, 11, 0, 0, 643, 644, 3, 108, 54, 0, 644, 95, 1, 0, 0, 0, 645, 646, 5, 62, 0, 0, 646, 647, 5, 11, 0, 0, 647, 648, 3, 82, 41, 0, 648, 97, 1, 0, 0, 0, 649, 650, 7, 8, 0, 0, 650, 651, 3, 100, 50, 0, 651, 99, 1, 0, 0, 0, 652, 659, 3, 102, 51, 0, 653, 654, 5, 9, 0, 0, 654, 655, 3, 102, 51, 0, 655, 656, 5, 2, 0, 0, 656, 657, 3, 102, 51, 0, 657, 659, 1, 0, 0, 0, 658, 652, 1, 0, 0, 0, 658, 653, 1, 0, 0, 0, 659, 101, 1, 0, 0, 0, 660, 661, 5, 18, 0, 0, 661, 673, 5, 73, 0, 0, 662, 663, 5, 90, 0, 0, 663, 673, 5, 66, 0, 0, 664, 665, 5, 90, 0, 0, 665, 673, 5, 30, 0, 0, 666, 667, 3, 142, 71, 0, 667, 668, 5, 66, 0, 0, 668, 673, 1, 0, 0, 0, 669, 670, 3, 142, 71, 0, 670, 671, 5, 30, 0, 0, 671, 673, 1, 0, 0, 0, 672, 660, 1, 0, 0, 0, 672, 662, 1, 0, 0, 0, 672, 664, 1, 0, 0, 0, 672, 666, 1, 0, 0, 0, 672, 669, 1, 0, 0, 0, 673, 103, 1, 0, 0, 0, 674, 675, 3, 110, 55, 0, 675, 676, 5, 0, 0, 1, 676, 105, 1, 0, 0, 0, 677, 734, 3, 154, 77, 0, 678, 679, 3, 154, 77, 0, 679, 680, 5, 126, 0, 0, 680, 681, 3, 154, 77, 0, 681, 688, 3, 106, 53, 0, 682, 683, 5, 112, 0, 0, 683, 684, 3, 154, 77, 0, 684, 685, 3, 106, 53, 0, 685, 687, 1, 0, 0, 0, 686, 682, 1, 0, 0, 0, 687, 690, 1, 0, 0, 0, 688, 686, 1, 0, 0, 0, 688, 689, 1, 0, 0, 0, 689, 692, 1, 0, 0, 0, 690, 688, 1, 0, 0, 0, 691, 693, 5, 112, 0, 0, 692, 691, 1, 0, 0, 0, 692, 693, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 694, 695, 5, 145, 0, 0, 695, 734, 1, 0, 0, 0, 696, 697, 3, 154, 77, 0, 697, 698, 5, 126, 0, 0, 698, 703, 3, 156, 78, 0, 699, 700, 5, 112, 0, 0, 700, 702, 3, 156, 78, 0, 701, 699, 1, 0, 0, 0, 702, 705, 1, 0, 0, 0, 703, 701, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 704, 707, 1, 0, 0, 0, 705, 703, 1, 0, 0, 0, 706, 708, 5, 112, 0, 0, 707, 706, 1, 0, 0, 0, 707, 708, 1, 0, 0, 0, 708, 709, 1, 0, 0, 0, 709, 710, 5, 145, 0, 0, 710, 734, 1, 0, 0, 0, 711, 712, 3, 154, 77, 0, 712, 713, 5, 126, 0, 0, 713, 718, 3, 106, 53, 0, 714, 715, 5, 112, 0, 0, 715, 717, 3, 106, 53, 0, 716, 714, 1, 0, 0, 0, 717, 720, 1, 0, 0, 0, 718, 716, 1, 0, 0, 0, 718, 719, 1, 0, 0, 0, 719, 722, 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 721, 723, 5, 112, 0, 0, 722, 721, 1, 0, 0, 0, 722, 723, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 725, 5, 145, 0, 0, 725, 734, 1, 0, 0, 0, 726, 727, 3, 154, 77, 0, 727, 729, 5, 126, 0, 0, 728, 730, 3, 108, 54, 0, 729, 728, 1, 0, 0, 0, 729, 730, 1, 0, 0, 0, 730, 731, 1, 0, 0, 0, 731, 732, 5, 145, 0, 0, 732, 734, 1, 0, 0, 0, 733, 677, 1, 0, 0, 0, 733, 678, 1, 0, 0, 0, 733, 696, 1, 0, 0, 0, 733, 711, 1, 0, 0, 0, 733, 726, 1, 0, 0, 0, 734, 107, 1, 0, 0, 0, 735, 740, 3, 110, 55, 0, 736, 737, 5, 112, 0, 0, 737, 739, 3, 110, 55, 0, 738, 736, 1, 0, 0, 0, 739, 742, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 744, 1, 0, 0, 0, 742, 740, 1, 0, 0, 0, 743, 745, 5, 112, 0, 0, 744, 743, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 109, 1, 0, 0, 0, 746, 747, 6, 55, -1, 0, 747, 749, 5, 12, 0, 0, 748, 750, 3, 110, 55, 0, 749, 748, 1, 0, 0, 0, 749, 750, 1, 0, 0, 0, 750, 756, 1, 0, 0, 0, 751, 752, 5, 94, 0, 0, 752, 753, 3, 110, 55, 0, 753, 754, 5, 81, 0, 0, 754, 755, 3, 110, 55, 0, 755, 757, 1, 0, 0, 0, 756, 751, 1, 0, 0, 0, 757, 758, 1, 0, 0, 0, 758, 756, 1, 0, 0, 0, 758, 759, 1, 0, 0, 0, 759, 762, 1, 0, 0, 0, 760, 761, 5, 24, 0, 0, 761, 763, 3, 110, 55, 0, 762, 760, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 764, 1, 0, 0, 0, 764, 765, 5, 25, 0, 0, 765, 896, 1, 0, 0, 0, 766, 767, 5, 13, 0, 0, 767, 768, 5, 126, 0, 0, 768, 769, 3, 110, 55, 0, 769, 770, 5, 6, 0, 0, 770, 771, 3, 106, 53, 0, 771, 772, 5, 145, 0, 0, 772, 896, 1, 0, 0, 0, 773, 774, 5, 19, 0, 0, 774, 896, 5, 106, 0, 0, 775, 776, 5, 43, 0, 0, 776, 777, 3, 110, 55, 0, 777, 778, 3, 146, 73, 0, 778, 896, 1, 0, 0, 0, 779, 780, 5, 80, 0, 0, 780, 781, 5, 126, 0, 0, 781, 782, 3, 110, 55, 0, 782, 783, 5, 32, 0, 0, 783, 786, 3, 110, 55, 0, 784, 785, 5, 31, 0, 0, 785, 787, 3, 110, 55, 0, 786, 784, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, 1, 0, 0, 0, 788, 789, 5, 145, 0, 0, 789, 896, 1, 0, 0, 0, 790, 791, 5, 83, 0, 0, 791, 896, 5, 106, 0, 0, 792, 793, 5, 88, 0, 0, 793, 794, 5, 126, 0, 0, 794, 795, 7, 9, 0, 0, 795, 796, 3, 160, 80, 0, 796, 797, 5, 32, 0, 0, 797, 798, 3, 110, 55, 0, 798, 799, 5, 145, 0, 0, 799, 896, 1, 0, 0, 0, 800, 801, 3, 154, 77, 0, 801, 803, 5, 126, 0, 0, 802, 804, 3, 108, 54, 0, 803, 802, 1, 0, 0, 0, 803, 804, 1, 0, 0, 0, 804, 805, 1, 0, 0, 0, 805, 806, 5, 145, 0, 0, 806, 815, 1, 0, 0, 0, 807, 809, 5, 126, 0, 0, 808, 810, 5, 23, 0, 0, 809, 808, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 812, 1, 0, 0, 0, 811, 813, 3, 112, 56, 0, 812, 811, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 816, 5, 145, 0, 0, 815, 807, 1, 0, 0, 0, 815, 816, 1, 0, 0, 0, 816, 817, 1, 0, 0, 0, 817, 818, 5, 64, 0, 0, 818, 819, 5, 126, 0, 0, 819, 820, 3, 92, 46, 0, 820, 821, 5, 145, 0, 0, 821, 896, 1, 0, 0, 0, 822, 823, 3, 154, 77, 0, 823, 825, 5, 126, 0, 0, 824, 826, 3, 108, 54, 0, 825, 824, 1, 0, 0, 0, 825, 826, 1, 0, 0, 0, 826, 827, 1, 0, 0, 0, 827, 828, 5, 145, 0, 0, 828, 837, 1, 0, 0, 0, 829, 831, 5, 126, 0, 0, 830, 832, 5, 23, 0, 0, 831, 830, 1, 0, 0, 0, 831, 832, 1, 0, 0, 0, 832, 834, 1, 0, 0, 0, 833, 835, 3, 112, 56, 0, 834, 833, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 836, 1, 0, 0, 0, 836, 838, 5, 145, 0, 0, 837, 829, 1, 0, 0, 0, 837, 838, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 840, 5, 64, 0, 0, 840, 841, 3, 154, 77, 0, 841, 896, 1, 0, 0, 0, 842, 848, 3, 154, 77, 0, 843, 845, 5, 126, 0, 0, 844, 846, 3, 108, 54, 0, 845, 844, 1, 0, 0, 0, 845, 846, 1, 0, 0, 0, 846, 847, 1, 0, 0, 0, 847, 849, 5, 145, 0, 0, 848, 843, 1, 0, 0, 0, 848, 849, 1, 0, 0, 0, 849, 850, 1, 0, 0, 0, 850, 852, 5, 126, 0, 0, 851, 853, 5, 23, 0, 0, 852, 851, 1, 0, 0, 0, 852, 853, 1, 0, 0, 0, 853, 855, 1, 0, 0, 0, 854, 856, 3, 112, 56, 0, 855, 854, 1, 0, 0, 0, 855, 856, 1, 0, 0, 0, 856, 857, 1, 0, 0, 0, 857, 858, 5, 145, 0, 0, 858, 896, 1, 0, 0, 0, 859, 896, 3, 118, 59, 0, 860, 896, 3, 162, 81, 0, 861, 896, 3, 144, 72, 0, 862, 863, 5, 114, 0, 0, 863, 896, 3, 110, 55, 19, 864, 865, 5, 56, 0, 0, 865, 896, 3, 110, 55, 13, 866, 867, 3, 134, 67, 0, 867, 868, 5, 116, 0, 0, 868, 870, 1, 0, 0, 0, 869, 866, 1, 0, 0, 0, 869, 870, 1, 0, 0, 0, 870, 871, 1, 0, 0, 0, 871, 896, 5, 108, 0, 0, 872, 873, 5, 126, 0, 0, 873, 874, 3, 38, 19, 0, 874, 875, 5, 145, 0, 0, 875, 896, 1, 0, 0, 0, 876, 877, 5, 126, 0, 0, 877, 878, 3, 110, 55, 0, 878, 879, 5, 145, 0, 0, 879, 896, 1, 0, 0, 0, 880, 881, 5, 126, 0, 0, 881, 882, 3, 108, 54, 0, 882, 883, 5, 145, 0, 0, 883, 896, 1, 0, 0, 0, 884, 886, 5, 125, 0, 0, 885, 887, 3, 108, 54, 0, 886, 885, 1, 0, 0, 0, 886, 887, 1, 0, 0, 0, 887, 888, 1, 0, 0, 0, 888, 896, 5, 144, 0, 0, 889, 891, 5, 124, 0, 0, 890, 892, 3, 34, 17, 0, 891, 890, 1, 0, 0, 0, 891, 892, 1, 0, 0, 0, 892, 893, 1, 0, 0, 0, 893, 896, 5, 143, 0, 0, 894, 896, 3, 126, 63, 0, 895, 746, 1, 0, 0, 0, 895, 766, 1, 0, 0, 0, 895, 773, 1, 0, 0, 0, 895, 775, 1, 0, 0, 0, 895, 779, 1, 0, 0, 0, 895, 790, 1, 0, 0, 0, 895, 792, 1, 0, 0, 0, 895, 800, 1, 0, 0, 0, 895, 822, 1, 0, 0, 0, 895, 842, 1, 0, 0, 0, 895, 859, 1, 0, 0, 0, 895, 860, 1, 0, 0, 0, 895, 861, 1, 0, 0, 0, 895, 862, 1, 0, 0, 0, 895, 864, 1, 0, 0, 0, 895, 869, 1, 0, 0, 0, 895, 872, 1, 0, 0, 0, 895, 876, 1, 0, 0, 0, 895, 880, 1, 0, 0, 0, 895, 884, 1, 0, 0, 0, 895, 889, 1, 0, 0, 0, 895, 894, 1, 0, 0, 0, 896, 1001, 1, 0, 0, 0, 897, 901, 10, 18, 0, 0, 898, 902, 5, 108, 0, 0, 899, 902, 5, 147, 0, 0, 900, 902, 5, 134, 0, 0, 901, 898, 1, 0, 0, 0, 901, 899, 1, 0, 0, 0, 901, 900, 1, 0, 0, 0, 902, 903, 1, 0, 0, 0, 903, 1000, 3, 110, 55, 19, 904, 908, 10, 17, 0, 0, 905, 909, 5, 135, 0, 0, 906, 909, 5, 114, 0, 0, 907, 909, 5, 113, 0, 0, 908, 905, 1, 0, 0, 0, 908, 906, 1, 0, 0, 0, 908, 907, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 1000, 3, 110, 55, 18, 911, 936, 10, 16, 0, 0, 912, 937, 5, 117, 0, 0, 913, 937, 5, 118, 0, 0, 914, 937, 5, 129, 0, 0, 915, 937, 5, 127, 0, 0, 916, 937, 5, 128, 0, 0, 917, 937, 5, 119, 0, 0, 918, 937, 5, 120, 0, 0, 919, 921, 5, 56, 0, 0, 920, 919, 1, 0, 0, 0, 920, 921, 1, 0, 0, 0, 921, 922, 1, 0, 0, 0, 922, 924, 5, 40, 0, 0, 923, 925, 5, 14, 0, 0, 924, 923, 1, 0, 0, 0, 924, 925, 1, 0, 0, 0, 925, 937, 1, 0, 0, 0, 926, 928, 5, 56, 0, 0, 927, 926, 1, 0, 0, 0, 927, 928, 1, 0, 0, 0, 928, 929, 1, 0, 0, 0, 929, 937, 7, 10, 0, 0, 930, 937, 5, 141, 0, 0, 931, 937, 5, 142, 0, 0, 932, 937, 5, 131, 0, 0, 933, 937, 5, 122, 0, 0, 934, 937, 5, 123, 0, 0, 935, 937, 5, 130, 0, 0, 936, 912, 1, 0, 0, 0, 936, 913, 1, 0, 0, 0, 936, 914, 1, 0, 0, 0, 936, 915, 1, 0, 0, 0, 936, 916, 1, 0, 0, 0, 936, 917, 1, 0, 0, 0, 936, 918, 1, 0, 0, 0, 936, 920, 1, 0, 0, 0, 936, 927, 1, 0, 0, 0, 936, 930, 1, 0, 0, 0, 936, 931, 1, 0, 0, 0, 936, 932, 1, 0, 0, 0, 936, 933, 1, 0, 0, 0, 936, 934, 1, 0, 0, 0, 936, 935, 1, 0, 0, 0, 937, 938, 1, 0, 0, 0, 938, 1000, 3, 110, 55, 17, 939, 940, 10, 14, 0, 0, 940, 941, 5, 133, 0, 0, 941, 1000, 3, 110, 55, 15, 942, 943, 10, 12, 0, 0, 943, 944, 5, 2, 0, 0, 944, 1000, 3, 110, 55, 13, 945, 946, 10, 11, 0, 0, 946, 947, 5, 61, 0, 0, 947, 1000, 3, 110, 55, 12, 948, 950, 10, 10, 0, 0, 949, 951, 5, 56, 0, 0, 950, 949, 1, 0, 0, 0, 950, 951, 1, 0, 0, 0, 951, 952, 1, 0, 0, 0, 952, 953, 5, 9, 0, 0, 953, 954, 3, 110, 55, 0, 954, 955, 5, 2, 0, 0, 955, 956, 3, 110, 55, 11, 956, 1000, 1, 0, 0, 0, 957, 958, 10, 9, 0, 0, 958, 959, 5, 136, 0, 0, 959, 960, 3, 110, 55, 0, 960, 961, 5, 111, 0, 0, 961, 962, 3, 110, 55, 9, 962, 1000, 1, 0, 0, 0, 963, 964, 10, 25, 0, 0, 964, 965, 5, 125, 0, 0, 965, 966, 3, 110, 55, 0, 966, 967, 5, 144, 0, 0, 967, 1000, 1, 0, 0, 0, 968, 969, 10, 24, 0, 0, 969, 970, 5, 116, 0, 0, 970, 1000, 5, 104, 0, 0, 971, 972, 10, 23, 0, 0, 972, 973, 5, 116, 0, 0, 973, 1000, 3, 154, 77, 0, 974, 975, 10, 22, 0, 0, 975, 976, 5, 132, 0, 0, 976, 977, 5, 125, 0, 0, 977, 978, 3, 110, 55, 0, 978, 979, 5, 144, 0, 0, 979, 1000, 1, 0, 0, 0, 980, 981, 10, 21, 0, 0, 981, 982, 5, 132, 0, 0, 982, 1000, 5, 104, 0, 0, 983, 984, 10, 20, 0, 0, 984, 985, 5, 132, 0, 0, 985, 1000, 3, 154, 77, 0, 986, 987, 10, 15, 0, 0, 987, 989, 5, 44, 0, 0, 988, 990, 5, 56, 0, 0, 989, 988, 1, 0, 0, 0, 989, 990, 1, 0, 0, 0, 990, 991, 1, 0, 0, 0, 991, 1000, 5, 57, 0, 0, 992, 997, 10, 8, 0, 0, 993, 994, 5, 6, 0, 0, 994, 998, 3, 154, 77, 0, 995, 996, 5, 6, 0, 0, 996, 998, 5, 106, 0, 0, 997, 993, 1, 0, 0, 0, 997, 995, 1, 0, 0, 0, 998, 1000, 1, 0, 0, 0, 999, 897, 1, 0, 0, 0, 999, 904, 1, 0, 0, 0, 999, 911, 1, 0, 0, 0, 999, 939, 1, 0, 0, 0, 999, 942, 1, 0, 0, 0, 999, 945, 1, 0, 0, 0, 999, 948, 1, 0, 0, 0, 999, 957, 1, 0, 0, 0, 999, 963, 1, 0, 0, 0, 999, 968, 1, 0, 0, 0, 999, 971, 1, 0, 0, 0, 999, 974, 1, 0, 0, 0, 999, 980, 1, 0, 0, 0, 999, 983, 1, 0, 0, 0, 999, 986, 1, 0, 0, 0, 999, 992, 1, 0, 0, 0, 1000, 1003, 1, 0, 0, 0, 1001, 999, 1, 0, 0, 0, 1001, 1002, 1, 0, 0, 0, 1002, 111, 1, 0, 0, 0, 1003, 1001, 1, 0, 0, 0, 1004, 1009, 3, 114, 57, 0, 1005, 1006, 5, 112, 0, 0, 1006, 1008, 3, 114, 57, 0, 1007, 1005, 1, 0, 0, 0, 1008, 1011, 1, 0, 0, 0, 1009, 1007, 1, 0, 0, 0, 1009, 1010, 1, 0, 0, 0, 1010, 1013, 1, 0, 0, 0, 1011, 1009, 1, 0, 0, 0, 1012, 1014, 5, 112, 0, 0, 1013, 1012, 1, 0, 0, 0, 1013, 1014, 1, 0, 0, 0, 1014, 113, 1, 0, 0, 0, 1015, 1018, 3, 116, 58, 0, 1016, 1018, 3, 110, 55, 0, 1017, 1015, 1, 0, 0, 0, 1017, 1016, 1, 0, 0, 0, 1018, 115, 1, 0, 0, 0, 1019, 1020, 5, 126, 0, 0, 1020, 1025, 3, 154, 77, 0, 1021, 1022, 5, 112, 0, 0, 1022, 1024, 3, 154, 77, 0, 1023, 1021, 1, 0, 0, 0, 1024, 1027, 1, 0, 0, 0, 1025, 1023, 1, 0, 0, 0, 1025, 1026, 1, 0, 0, 0, 1026, 1029, 1, 0, 0, 0, 1027, 1025, 1, 0, 0, 0, 1028, 1030, 5, 112, 0, 0, 1029, 1028, 1, 0, 0, 0, 1029, 1030, 1, 0, 0, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1032, 5, 145, 0, 0, 1032, 1045, 1, 0, 0, 0, 1033, 1038, 3, 154, 77, 0, 1034, 1035, 5, 112, 0, 0, 1035, 1037, 3, 154, 77, 0, 1036, 1034, 1, 0, 0, 0, 1037, 1040, 1, 0, 0, 0, 1038, 1036, 1, 0, 0, 0, 1038, 1039, 1, 0, 0, 0, 1039, 1042, 1, 0, 0, 0, 1040, 1038, 1, 0, 0, 0, 1041, 1043, 5, 112, 0, 0, 1042, 1041, 1, 0, 0, 0, 1042, 1043, 1, 0, 0, 0, 1043, 1045, 1, 0, 0, 0, 1044, 1019, 1, 0, 0, 0, 1044, 1033, 1, 0, 0, 0, 1045, 1046, 1, 0, 0, 0, 1046, 1047, 5, 107, 0, 0, 1047, 1048, 3, 110, 55, 0, 1048, 117, 1, 0, 0, 0, 1049, 1050, 5, 128, 0, 0, 1050, 1054, 3, 154, 77, 0, 1051, 1053, 3, 120, 60, 0, 1052, 1051, 1, 0, 0, 0, 1053, 1056, 1, 0, 0, 0, 1054, 1052, 1, 0, 0, 0, 1054, 1055, 1, 0, 0, 0, 1055, 1057, 1, 0, 0, 0, 1056, 1054, 1, 0, 0, 0, 1057, 1058, 5, 147, 0, 0, 1058, 1059, 5, 120, 0, 0, 1059, 1078, 1, 0, 0, 0, 1060, 1061, 5, 128, 0, 0, 1061, 1065, 3, 154, 77, 0, 1062, 1064, 3, 120, 60, 0, 1063, 1062, 1, 0, 0, 0, 1064, 1067, 1, 0, 0, 0, 1065, 1063, 1, 0, 0, 0, 1065, 1066, 1, 0, 0, 0, 1066, 1068, 1, 0, 0, 0, 1067, 1065, 1, 0, 0, 0, 1068, 1070, 5, 120, 0, 0, 1069, 1071, 3, 118, 59, 0, 1070, 1069, 1, 0, 0, 0, 1070, 1071, 1, 0, 0, 0, 1071, 1072, 1, 0, 0, 0, 1072, 1073, 5, 128, 0, 0, 1073, 1074, 5, 147, 0, 0, 1074, 1075, 3, 154, 77, 0, 1075, 1076, 5, 120, 0, 0, 1076, 1078, 1, 0, 0, 0, 1077, 1049, 1, 0, 0, 0, 1077, 1060, 1, 0, 0, 0, 1078, 119, 1, 0, 0, 0, 1079, 1080, 3, 154, 77, 0, 1080, 1081, 5, 118, 0, 0, 1081, 1082, 3, 160, 80, 0, 1082, 1091, 1, 0, 0, 0, 1083, 1084, 3, 154, 77, 0, 1084, 1085, 5, 118, 0, 0, 1085, 1086, 5, 124, 0, 0, 1086, 1087, 3, 110, 55, 0, 1087, 1088, 5, 143, 0, 0, 1088, 1091, 1, 0, 0, 0, 1089, 1091, 3, 154, 77, 0, 1090, 1079, 1, 0, 0, 0, 1090, 1083, 1, 0, 0, 0, 1090, 1089, 1, 0, 0, 0, 1091, 121, 1, 0, 0, 0, 1092, 1097, 3, 124, 62, 0, 1093, 1094, 5, 112, 0, 0, 1094, 1096, 3, 124, 62, 0, 1095, 1093, 1, 0, 0, 0, 1096, 1099, 1, 0, 0, 0, 1097, 1095, 1, 0, 0, 0, 1097, 1098, 1, 0, 0, 0, 1098, 1101, 1, 0, 0, 0, 1099, 1097, 1, 0, 0, 0, 1100, 1102, 5, 112, 0, 0, 1101, 1100, 1, 0, 0, 0, 1101, 1102, 1, 0, 0, 0, 1102, 123, 1, 0, 0, 0, 1103, 1104, 3, 154, 77, 0, 1104, 1105, 5, 6, 0, 0, 1105, 1106, 5, 126, 0, 0, 1106, 1107, 3, 38, 19, 0, 1107, 1108, 5, 145, 0, 0, 1108, 1114, 1, 0, 0, 0, 1109, 1110, 3, 110, 55, 0, 1110, 1111, 5, 6, 0, 0, 1111, 1112, 3, 154, 77, 0, 1112, 1114, 1, 0, 0, 0, 1113, 1103, 1, 0, 0, 0, 1113, 1109, 1, 0, 0, 0, 1114, 125, 1, 0, 0, 0, 1115, 1123, 3, 158, 79, 0, 1116, 1117, 3, 134, 67, 0, 1117, 1118, 5, 116, 0, 0, 1118, 1120, 1, 0, 0, 0, 1119, 1116, 1, 0, 0, 0, 1119, 1120, 1, 0, 0, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1123, 3, 128, 64, 0, 1122, 1115, 1, 0, 0, 0, 1122, 1119, 1, 0, 0, 0, 1123, 127, 1, 0, 0, 0, 1124, 1129, 3, 154, 77, 0, 1125, 1126, 5, 116, 0, 0, 1126, 1128, 3, 154, 77, 0, 1127, 1125, 1, 0, 0, 0, 1128, 1131, 1, 0, 0, 0, 1129, 1127, 1, 0, 0, 0, 1129, 1130, 1, 0, 0, 0, 1130, 129, 1, 0, 0, 0, 1131, 1129, 1, 0, 0, 0, 1132, 1133, 6, 65, -1, 0, 1133, 1142, 3, 134, 67, 0, 1134, 1142, 3, 132, 66, 0, 1135, 1136, 5, 126, 0, 0, 1136, 1137, 3, 38, 19, 0, 1137, 1138, 5, 145, 0, 0, 1138, 1142, 1, 0, 0, 0, 1139, 1142, 3, 118, 59, 0, 1140, 1142, 3, 158, 79, 0, 1141, 1132, 1, 0, 0, 0, 1141, 1134, 1, 0, 0, 0, 1141, 1135, 1, 0, 0, 0, 1141, 1139, 1, 0, 0, 0, 1141, 1140, 1, 0, 0, 0, 1142, 1151, 1, 0, 0, 0, 1143, 1147, 10, 3, 0, 0, 1144, 1148, 3, 152, 76, 0, 1145, 1146, 5, 6, 0, 0, 1146, 1148, 3, 154, 77, 0, 1147, 1144, 1, 0, 0, 0, 1147, 1145, 1, 0, 0, 0, 1148, 1150, 1, 0, 0, 0, 1149, 1143, 1, 0, 0, 0, 1150, 1153, 1, 0, 0, 0, 1151, 1149, 1, 0, 0, 0, 1151, 1152, 1, 0, 0, 0, 1152, 131, 1, 0, 0, 0, 1153, 1151, 1, 0, 0, 0, 1154, 1155, 3, 154, 77, 0, 1155, 1157, 5, 126, 0, 0, 1156, 1158, 3, 136, 68, 0, 1157, 1156, 1, 0, 0, 0, 1157, 1158, 1, 0, 0, 0, 1158, 1159, 1, 0, 0, 0, 1159, 1160, 5, 145, 0, 0, 1160, 133, 1, 0, 0, 0, 1161, 1162, 3, 138, 69, 0, 1162, 1163, 5, 116, 0, 0, 1163, 1165, 1, 0, 0, 0, 1164, 1161, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 1166, 1, 0, 0, 0, 1166, 1167, 3, 154, 77, 0, 1167, 135, 1, 0, 0, 0, 1168, 1173, 3, 110, 55, 0, 1169, 1170, 5, 112, 0, 0, 1170, 1172, 3, 110, 55, 0, 1171, 1169, 1, 0, 0, 0, 1172, 1175, 1, 0, 0, 0, 1173, 1171, 1, 0, 0, 0, 1173, 1174, 1, 0, 0, 0, 1174, 1177, 1, 0, 0, 0, 1175, 1173, 1, 0, 0, 0, 1176, 1178, 5, 112, 0, 0, 1177, 1176, 1, 0, 0, 0, 1177, 1178, 1, 0, 0, 0, 1178, 137, 1, 0, 0, 0, 1179, 1180, 3, 154, 77, 0, 1180, 139, 1, 0, 0, 0, 1181, 1190, 5, 102, 0, 0, 1182, 1183, 5, 116, 0, 0, 1183, 1190, 7, 11, 0, 0, 1184, 1185, 5, 104, 0, 0, 1185, 1187, 5, 116, 0, 0, 1186, 1188, 7, 11, 0, 0, 1187, 1186, 1, 0, 0, 0, 1187, 1188, 1, 0, 0, 0, 1188, 1190, 1, 0, 0, 0, 1189, 1181, 1, 0, 0, 0, 1189, 1182, 1, 0, 0, 0, 1189, 1184, 1, 0, 0, 0, 1190, 141, 1, 0, 0, 0, 1191, 1193, 7, 12, 0, 0, 1192, 1191, 1, 0, 0, 0, 1192, 1193, 1, 0, 0, 0, 1193, 1200, 1, 0, 0, 0, 1194, 1201, 3, 140, 70, 0, 1195, 1201, 5, 103, 0, 0, 1196, 1201, 5, 104, 0, 0, 1197, 1201, 5, 105, 0, 0, 1198, 1201, 5, 41, 0, 0, 1199, 1201, 5, 55, 0, 0, 1200, 1194, 1, 0, 0, 0, 1200, 1195, 1, 0, 0, 0, 1200, 1196, 1, 0, 0, 0, 1200, 1197, 1, 0, 0, 0, 1200, 1198, 1, 0, 0, 0, 1200, 1199, 1, 0, 0, 0, 1201, 143, 1, 0, 0, 0, 1202, 1206, 3, 142, 71, 0, 1203, 1206, 5, 106, 0, 0, 1204, 1206, 5, 57, 0, 0, 1205, 1202, 1, 0, 0, 0, 1205, 1203, 1, 0, 0, 0, 1205, 1204, 1, 0, 0, 0, 1206, 145, 1, 0, 0, 0, 1207, 1208, 7, 13, 0, 0, 1208, 147, 1, 0, 0, 0, 1209, 1210, 7, 14, 0, 0, 1210, 149, 1, 0, 0, 0, 1211, 1212, 7, 15, 0, 0, 1212, 151, 1, 0, 0, 0, 1213, 1216, 5, 101, 0, 0, 1214, 1216, 3, 150, 75, 0, 1215, 1213, 1, 0, 0, 0, 1215, 1214, 1, 0, 0, 0, 1216, 153, 1, 0, 0, 0, 1217, 1221, 5, 101, 0, 0, 1218, 1221, 3, 146, 73, 0, 1219, 1221, 3, 148, 74, 0, 1220, 1217, 1, 0, 0, 0, 1220, 1218, 1, 0, 0, 0, 1220, 1219, 1, 0, 0, 0, 1221, 155, 1, 0, 0, 0, 1222, 1223, 3, 160, 80, 0, 1223, 1224, 5, 118, 0, 0, 1224, 1225, 3, 142, 71, 0, 1225, 157, 1, 0, 0, 0, 1226, 1227, 5, 124, 0, 0, 1227, 1228, 3, 154, 77, 0, 1228, 1229, 5, 143, 0, 0, 1229, 159, 1, 0, 0, 0, 1230, 1233, 5, 106, 0, 0, 1231, 1233, 3, 162, 81, 0, 1232, 1230, 1, 0, 0, 0, 1232, 1231, 1, 0, 0, 0, 1233, 161, 1, 0, 0, 0, 1234, 1238, 5, 138, 0, 0, 1235, 1237, 3, 164, 82, 0, 1236, 1235, 1, 0, 0, 0, 1237, 1240, 1, 0, 0, 0, 1238, 1236, 1, 0, 0, 0, 1238, 1239, 1, 0, 0, 0, 1239, 1241, 1, 0, 0, 0, 1240, 1238, 1, 0, 0, 0, 1241, 1242, 5, 140, 0, 0, 1242, 163, 1, 0, 0, 0, 1243, 1244, 5, 153, 0, 0, 1244, 1245, 3, 110, 55, 0, 1245, 1246, 5, 143, 0, 0, 1246, 1249, 1, 0, 0, 0, 1247, 1249, 5, 152, 0, 0, 1248, 1243, 1, 0, 0, 0, 1248, 1247, 1, 0, 0, 0, 1249, 165, 1, 0, 0, 0, 1250, 1254, 5, 139, 0, 0, 1251, 1253, 3, 168, 84, 0, 1252, 1251, 1, 0, 0, 0, 1253, 1256, 1, 0, 0, 0, 1254, 1252, 1, 0, 0, 0, 1254, 1255, 1, 0, 0, 0, 1255, 1257, 1, 0, 0, 0, 1256, 1254, 1, 0, 0, 0, 1257, 1258, 5, 0, 0, 1, 1258, 167, 1, 0, 0, 0, 1259, 1260, 5, 155, 0, 0, 1260, 1261, 3, 110, 55, 0, 1261, 1262, 5, 143, 0, 0, 1262, 1265, 1, 0, 0, 0, 1263, 1265, 5, 154, 0, 0, 1264, 1259, 1, 0, 0, 0, 1264, 1263, 1, 0, 0, 0, 1265, 169, 1, 0, 0, 0, 162, 173, 180, 189, 196, 200, 212, 216, 219, 228, 236, 243, 247, 253, 258, 266, 273, 279, 291, 299, 313, 317, 322, 332, 341, 344, 348, 351, 355, 358, 361, 364, 367, 371, 375, 378, 381, 384, 388, 391, 400, 406, 427, 444, 461, 467, 473, 484, 486, 497, 500, 506, 514, 520, 522, 526, 531, 534, 537, 541, 545, 548, 550, 553, 557, 561, 564, 566, 568, 573, 584, 590, 597, 602, 606, 610, 616, 618, 625, 633, 636, 639, 658, 672, 688, 692, 703, 707, 718, 722, 729, 733, 740, 744, 749, 758, 762, 786, 803, 809, 812, 815, 825, 831, 834, 837, 845, 848, 852, 855, 869, 886, 891, 895, 901, 908, 920, 924, 927, 936, 950, 989, 997, 999, 1001, 1009, 1013, 1017, 1025, 1029, 1038, 1042, 1044, 1054, 1065, 1070, 1077, 1090, 1097, 1101, 1113, 1119, 1122, 1129, 1141, 1147, 1151, 1157, 1164, 1173, 1177, 1187, 1189, 1192, 1200, 1205, 1215, 1220, 1232, 1238, 1248, 1254, 1264] \ No newline at end of file diff --git a/posthog/hogql/grammar/HogQLParser.py b/posthog/hogql/grammar/HogQLParser.py index 7491060e6a44c..39024c561054f 100644 --- a/posthog/hogql/grammar/HogQLParser.py +++ b/posthog/hogql/grammar/HogQLParser.py @@ -10,7 +10,7 @@ def serializedATN(): return [ - 4,1,155,1249,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, + 4,1,155,1267,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, 7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7, 13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2, 20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7, @@ -22,495 +22,502 @@ def serializedATN(): 59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,65,7, 65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,71,2, 72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77,2,78,7, - 78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,1,0,5,0,170, - 8,0,10,0,12,0,173,9,0,1,0,1,0,1,1,1,1,3,1,179,8,1,1,2,1,2,1,3,1, - 3,1,3,1,3,1,3,3,3,188,8,3,1,4,1,4,1,4,5,4,193,8,4,10,4,12,4,196, - 9,4,1,4,3,4,199,8,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,210, - 8,5,1,6,1,6,3,6,214,8,6,1,6,3,6,217,8,6,1,7,1,7,1,7,1,7,1,7,1,7, - 1,7,3,7,226,8,7,1,8,1,8,1,8,1,8,1,8,1,8,3,8,234,8,8,1,9,1,9,1,9, - 1,9,1,9,3,9,241,8,9,1,9,1,9,3,9,245,8,9,1,9,1,9,1,9,1,9,3,9,251, - 8,9,1,9,1,9,1,9,3,9,256,8,9,1,10,1,10,1,10,1,10,3,10,262,8,10,1, - 10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,12,1,12,3,12,274,8,12,1, - 13,1,13,1,14,1,14,5,14,280,8,14,10,14,12,14,283,9,14,1,14,1,14,1, - 15,1,15,1,15,1,15,1,16,1,16,1,16,5,16,294,8,16,10,16,12,16,297,9, - 16,1,16,3,16,300,8,16,1,17,1,17,1,17,3,17,305,8,17,1,17,1,17,1,18, - 1,18,1,18,1,18,5,18,313,8,18,10,18,12,18,316,9,18,1,19,1,19,1,19, - 1,19,1,19,1,19,3,19,324,8,19,1,20,3,20,327,8,20,1,20,1,20,3,20,331, - 8,20,1,20,3,20,334,8,20,1,20,1,20,3,20,338,8,20,1,20,3,20,341,8, - 20,1,20,3,20,344,8,20,1,20,3,20,347,8,20,1,20,3,20,350,8,20,1,20, - 1,20,3,20,354,8,20,1,20,1,20,3,20,358,8,20,1,20,3,20,361,8,20,1, - 20,3,20,364,8,20,1,20,3,20,367,8,20,1,20,1,20,3,20,371,8,20,1,20, - 3,20,374,8,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22,3,22,383,8,22,1, - 23,1,23,1,23,1,24,3,24,389,8,24,1,24,1,24,1,24,1,24,1,25,1,25,1, - 25,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,25,5,25,408,8, - 25,10,25,12,25,411,9,25,1,26,1,26,1,26,1,27,1,27,1,27,1,28,1,28, - 1,28,1,28,1,28,1,28,1,28,1,28,3,28,427,8,28,1,29,1,29,1,29,1,30, - 1,30,1,30,1,30,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,32,3,32,444, - 8,32,1,32,1,32,1,32,1,32,3,32,450,8,32,1,32,1,32,1,32,1,32,3,32, - 456,8,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,3,32,467,8, - 32,3,32,469,8,32,1,33,1,33,1,33,1,34,1,34,1,34,1,35,1,35,1,35,3, - 35,480,8,35,1,35,3,35,483,8,35,1,35,1,35,1,35,1,35,3,35,489,8,35, - 1,35,1,35,1,35,1,35,1,35,1,35,3,35,497,8,35,1,35,1,35,1,35,1,35, - 5,35,503,8,35,10,35,12,35,506,9,35,1,36,3,36,509,8,36,1,36,1,36, - 1,36,3,36,514,8,36,1,36,3,36,517,8,36,1,36,3,36,520,8,36,1,36,1, - 36,3,36,524,8,36,1,36,1,36,3,36,528,8,36,1,36,3,36,531,8,36,3,36, - 533,8,36,1,36,3,36,536,8,36,1,36,1,36,3,36,540,8,36,1,36,1,36,3, - 36,544,8,36,1,36,3,36,547,8,36,3,36,549,8,36,3,36,551,8,36,1,37, - 1,37,1,37,3,37,556,8,37,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38, - 1,38,3,38,567,8,38,1,39,1,39,1,39,1,39,3,39,573,8,39,1,40,1,40,1, - 40,5,40,578,8,40,10,40,12,40,581,9,40,1,41,1,41,3,41,585,8,41,1, - 41,1,41,3,41,589,8,41,1,41,1,41,3,41,593,8,41,1,42,1,42,1,42,1,42, - 3,42,599,8,42,3,42,601,8,42,1,43,1,43,1,43,5,43,606,8,43,10,43,12, - 43,609,9,43,1,44,1,44,1,44,1,44,1,45,3,45,616,8,45,1,45,3,45,619, - 8,45,1,45,3,45,622,8,45,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47, - 1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,3,49,641,8,49,1,50, - 1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,3,50,655, - 8,50,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52, - 5,52,669,8,52,10,52,12,52,672,9,52,1,52,3,52,675,8,52,1,52,1,52, - 1,52,1,52,1,52,1,52,1,52,5,52,684,8,52,10,52,12,52,687,9,52,1,52, - 3,52,690,8,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52,5,52,699,8,52,10, - 52,12,52,702,9,52,1,52,3,52,705,8,52,1,52,1,52,1,52,1,52,1,52,3, - 52,712,8,52,1,52,1,52,3,52,716,8,52,1,53,1,53,1,53,5,53,721,8,53, - 10,53,12,53,724,9,53,1,53,3,53,727,8,53,1,54,1,54,1,54,3,54,732, - 8,54,1,54,1,54,1,54,1,54,1,54,4,54,739,8,54,11,54,12,54,740,1,54, - 1,54,3,54,745,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 3,54,769,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,1,54,1,54,3,54,786,8,54,1,54,1,54,1,54,1,54,3,54, - 792,8,54,1,54,3,54,795,8,54,1,54,3,54,798,8,54,1,54,1,54,1,54,1, - 54,1,54,1,54,1,54,1,54,3,54,808,8,54,1,54,1,54,1,54,1,54,3,54,814, - 8,54,1,54,3,54,817,8,54,1,54,3,54,820,8,54,1,54,1,54,1,54,1,54,1, - 54,1,54,3,54,828,8,54,1,54,3,54,831,8,54,1,54,1,54,3,54,835,8,54, - 1,54,3,54,838,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,3,54,852,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, - 1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,869,8,54,1,54,1,54, - 1,54,3,54,874,8,54,1,54,1,54,3,54,878,8,54,1,54,1,54,1,54,1,54,3, - 54,884,8,54,1,54,1,54,1,54,1,54,1,54,3,54,891,8,54,1,54,1,54,1,54, - 1,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,903,8,54,1,54,1,54,3,54, - 907,8,54,1,54,3,54,910,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,3, - 54,919,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1, - 54,1,54,3,54,933,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1, - 54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1, - 54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1, - 54,1,54,1,54,3,54,972,8,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,980, - 8,54,5,54,982,8,54,10,54,12,54,985,9,54,1,55,1,55,1,55,5,55,990, - 8,55,10,55,12,55,993,9,55,1,55,3,55,996,8,55,1,56,1,56,3,56,1000, - 8,56,1,57,1,57,1,57,1,57,5,57,1006,8,57,10,57,12,57,1009,9,57,1, - 57,3,57,1012,8,57,1,57,1,57,1,57,1,57,1,57,5,57,1019,8,57,10,57, - 12,57,1022,9,57,1,57,3,57,1025,8,57,3,57,1027,8,57,1,57,1,57,1,57, - 1,58,1,58,1,58,5,58,1035,8,58,10,58,12,58,1038,9,58,1,58,1,58,1, - 58,1,58,1,58,1,58,5,58,1046,8,58,10,58,12,58,1049,9,58,1,58,1,58, - 3,58,1053,8,58,1,58,1,58,1,58,1,58,1,58,3,58,1060,8,58,1,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1073,8,59,1,60, - 1,60,1,60,5,60,1078,8,60,10,60,12,60,1081,9,60,1,60,3,60,1084,8, - 60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,3,61,1096,8, - 61,1,62,1,62,1,62,1,62,3,62,1102,8,62,1,62,3,62,1105,8,62,1,63,1, - 63,1,63,5,63,1110,8,63,10,63,12,63,1113,9,63,1,64,1,64,1,64,1,64, - 1,64,1,64,1,64,1,64,1,64,3,64,1124,8,64,1,64,1,64,1,64,1,64,3,64, - 1130,8,64,5,64,1132,8,64,10,64,12,64,1135,9,64,1,65,1,65,1,65,3, - 65,1140,8,65,1,65,1,65,1,66,1,66,1,66,3,66,1147,8,66,1,66,1,66,1, - 67,1,67,1,67,5,67,1154,8,67,10,67,12,67,1157,9,67,1,67,3,67,1160, - 8,67,1,68,1,68,1,69,1,69,1,69,1,69,1,69,1,69,3,69,1170,8,69,3,69, - 1172,8,69,1,70,3,70,1175,8,70,1,70,1,70,1,70,1,70,1,70,1,70,3,70, - 1183,8,70,1,71,1,71,1,71,3,71,1188,8,71,1,72,1,72,1,73,1,73,1,74, - 1,74,1,75,1,75,3,75,1198,8,75,1,76,1,76,1,76,3,76,1203,8,76,1,77, - 1,77,1,77,1,77,1,78,1,78,1,78,1,78,1,79,1,79,3,79,1215,8,79,1,80, - 1,80,5,80,1219,8,80,10,80,12,80,1222,9,80,1,80,1,80,1,81,1,81,1, - 81,1,81,1,81,3,81,1231,8,81,1,82,1,82,5,82,1235,8,82,10,82,12,82, - 1238,9,82,1,82,1,82,1,83,1,83,1,83,1,83,1,83,3,83,1247,8,83,1,83, - 0,3,70,108,128,84,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32, - 34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76, - 78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114, - 116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146, - 148,150,152,154,156,158,160,162,164,166,0,16,2,0,17,17,72,72,2,0, - 42,42,49,49,3,0,1,1,4,4,8,8,4,0,1,1,3,4,8,8,78,78,2,0,49,49,71,71, - 2,0,1,1,4,4,2,0,7,7,21,22,2,0,28,28,47,47,2,0,69,69,74,74,3,0,10, - 10,48,48,87,87,2,0,39,39,51,51,1,0,103,104,2,0,114,114,135,135,7, - 0,20,20,36,36,53,54,68,68,76,76,93,93,99,99,12,0,1,19,21,28,30,35, - 37,40,42,49,51,52,56,56,58,67,69,75,77,92,94,95,97,98,4,0,19,19, - 28,28,37,37,46,46,1409,0,171,1,0,0,0,2,178,1,0,0,0,4,180,1,0,0,0, - 6,182,1,0,0,0,8,189,1,0,0,0,10,209,1,0,0,0,12,211,1,0,0,0,14,218, - 1,0,0,0,16,227,1,0,0,0,18,235,1,0,0,0,20,257,1,0,0,0,22,266,1,0, - 0,0,24,271,1,0,0,0,26,275,1,0,0,0,28,277,1,0,0,0,30,286,1,0,0,0, - 32,290,1,0,0,0,34,304,1,0,0,0,36,308,1,0,0,0,38,323,1,0,0,0,40,326, - 1,0,0,0,42,375,1,0,0,0,44,378,1,0,0,0,46,384,1,0,0,0,48,388,1,0, - 0,0,50,394,1,0,0,0,52,412,1,0,0,0,54,415,1,0,0,0,56,418,1,0,0,0, - 58,428,1,0,0,0,60,431,1,0,0,0,62,435,1,0,0,0,64,468,1,0,0,0,66,470, - 1,0,0,0,68,473,1,0,0,0,70,488,1,0,0,0,72,550,1,0,0,0,74,555,1,0, - 0,0,76,566,1,0,0,0,78,568,1,0,0,0,80,574,1,0,0,0,82,582,1,0,0,0, - 84,600,1,0,0,0,86,602,1,0,0,0,88,610,1,0,0,0,90,615,1,0,0,0,92,623, - 1,0,0,0,94,627,1,0,0,0,96,631,1,0,0,0,98,640,1,0,0,0,100,654,1,0, - 0,0,102,656,1,0,0,0,104,715,1,0,0,0,106,717,1,0,0,0,108,877,1,0, - 0,0,110,986,1,0,0,0,112,999,1,0,0,0,114,1026,1,0,0,0,116,1059,1, - 0,0,0,118,1072,1,0,0,0,120,1074,1,0,0,0,122,1095,1,0,0,0,124,1104, - 1,0,0,0,126,1106,1,0,0,0,128,1123,1,0,0,0,130,1136,1,0,0,0,132,1146, - 1,0,0,0,134,1150,1,0,0,0,136,1161,1,0,0,0,138,1171,1,0,0,0,140,1174, - 1,0,0,0,142,1187,1,0,0,0,144,1189,1,0,0,0,146,1191,1,0,0,0,148,1193, - 1,0,0,0,150,1197,1,0,0,0,152,1202,1,0,0,0,154,1204,1,0,0,0,156,1208, - 1,0,0,0,158,1214,1,0,0,0,160,1216,1,0,0,0,162,1230,1,0,0,0,164,1232, - 1,0,0,0,166,1246,1,0,0,0,168,170,3,2,1,0,169,168,1,0,0,0,170,173, - 1,0,0,0,171,169,1,0,0,0,171,172,1,0,0,0,172,174,1,0,0,0,173,171, - 1,0,0,0,174,175,5,0,0,1,175,1,1,0,0,0,176,179,3,6,3,0,177,179,3, - 10,5,0,178,176,1,0,0,0,178,177,1,0,0,0,179,3,1,0,0,0,180,181,3,108, - 54,0,181,5,1,0,0,0,182,183,5,50,0,0,183,187,3,152,76,0,184,185,5, - 111,0,0,185,186,5,118,0,0,186,188,3,4,2,0,187,184,1,0,0,0,187,188, - 1,0,0,0,188,7,1,0,0,0,189,194,3,152,76,0,190,191,5,112,0,0,191,193, - 3,152,76,0,192,190,1,0,0,0,193,196,1,0,0,0,194,192,1,0,0,0,194,195, - 1,0,0,0,195,198,1,0,0,0,196,194,1,0,0,0,197,199,5,112,0,0,198,197, - 1,0,0,0,198,199,1,0,0,0,199,9,1,0,0,0,200,210,3,12,6,0,201,210,3, - 14,7,0,202,210,3,16,8,0,203,210,3,18,9,0,204,210,3,20,10,0,205,210, - 3,22,11,0,206,210,3,28,14,0,207,210,3,24,12,0,208,210,3,26,13,0, - 209,200,1,0,0,0,209,201,1,0,0,0,209,202,1,0,0,0,209,203,1,0,0,0, - 209,204,1,0,0,0,209,205,1,0,0,0,209,206,1,0,0,0,209,207,1,0,0,0, - 209,208,1,0,0,0,210,11,1,0,0,0,211,213,5,70,0,0,212,214,3,4,2,0, - 213,212,1,0,0,0,213,214,1,0,0,0,214,216,1,0,0,0,215,217,5,146,0, - 0,216,215,1,0,0,0,216,217,1,0,0,0,217,13,1,0,0,0,218,219,5,38,0, - 0,219,220,5,126,0,0,220,221,3,4,2,0,221,222,5,145,0,0,222,225,3, - 10,5,0,223,224,5,24,0,0,224,226,3,10,5,0,225,223,1,0,0,0,225,226, - 1,0,0,0,226,15,1,0,0,0,227,228,5,96,0,0,228,229,5,126,0,0,229,230, - 3,4,2,0,230,231,5,145,0,0,231,233,3,10,5,0,232,234,5,146,0,0,233, - 232,1,0,0,0,233,234,1,0,0,0,234,17,1,0,0,0,235,236,5,31,0,0,236, - 240,5,126,0,0,237,241,3,6,3,0,238,241,3,22,11,0,239,241,3,4,2,0, - 240,237,1,0,0,0,240,238,1,0,0,0,240,239,1,0,0,0,240,241,1,0,0,0, - 241,242,1,0,0,0,242,244,5,146,0,0,243,245,3,4,2,0,244,243,1,0,0, - 0,244,245,1,0,0,0,245,246,1,0,0,0,246,250,5,146,0,0,247,251,3,6, - 3,0,248,251,3,22,11,0,249,251,3,4,2,0,250,247,1,0,0,0,250,248,1, - 0,0,0,250,249,1,0,0,0,250,251,1,0,0,0,251,252,1,0,0,0,252,253,5, - 145,0,0,253,255,3,10,5,0,254,256,5,146,0,0,255,254,1,0,0,0,255,256, - 1,0,0,0,256,19,1,0,0,0,257,258,5,29,0,0,258,259,3,152,76,0,259,261, - 5,126,0,0,260,262,3,8,4,0,261,260,1,0,0,0,261,262,1,0,0,0,262,263, - 1,0,0,0,263,264,5,145,0,0,264,265,3,28,14,0,265,21,1,0,0,0,266,267, - 3,4,2,0,267,268,5,111,0,0,268,269,5,118,0,0,269,270,3,4,2,0,270, - 23,1,0,0,0,271,273,3,4,2,0,272,274,5,146,0,0,273,272,1,0,0,0,273, - 274,1,0,0,0,274,25,1,0,0,0,275,276,5,146,0,0,276,27,1,0,0,0,277, - 281,5,124,0,0,278,280,3,2,1,0,279,278,1,0,0,0,280,283,1,0,0,0,281, - 279,1,0,0,0,281,282,1,0,0,0,282,284,1,0,0,0,283,281,1,0,0,0,284, - 285,5,143,0,0,285,29,1,0,0,0,286,287,3,4,2,0,287,288,5,111,0,0,288, - 289,3,4,2,0,289,31,1,0,0,0,290,295,3,30,15,0,291,292,5,112,0,0,292, - 294,3,30,15,0,293,291,1,0,0,0,294,297,1,0,0,0,295,293,1,0,0,0,295, - 296,1,0,0,0,296,299,1,0,0,0,297,295,1,0,0,0,298,300,5,112,0,0,299, - 298,1,0,0,0,299,300,1,0,0,0,300,33,1,0,0,0,301,305,3,36,18,0,302, - 305,3,40,20,0,303,305,3,116,58,0,304,301,1,0,0,0,304,302,1,0,0,0, - 304,303,1,0,0,0,305,306,1,0,0,0,306,307,5,0,0,1,307,35,1,0,0,0,308, - 314,3,38,19,0,309,310,5,91,0,0,310,311,5,1,0,0,311,313,3,38,19,0, - 312,309,1,0,0,0,313,316,1,0,0,0,314,312,1,0,0,0,314,315,1,0,0,0, - 315,37,1,0,0,0,316,314,1,0,0,0,317,324,3,40,20,0,318,319,5,126,0, - 0,319,320,3,36,18,0,320,321,5,145,0,0,321,324,1,0,0,0,322,324,3, - 156,78,0,323,317,1,0,0,0,323,318,1,0,0,0,323,322,1,0,0,0,324,39, - 1,0,0,0,325,327,3,42,21,0,326,325,1,0,0,0,326,327,1,0,0,0,327,328, - 1,0,0,0,328,330,5,77,0,0,329,331,5,23,0,0,330,329,1,0,0,0,330,331, - 1,0,0,0,331,333,1,0,0,0,332,334,3,44,22,0,333,332,1,0,0,0,333,334, - 1,0,0,0,334,335,1,0,0,0,335,337,3,106,53,0,336,338,3,46,23,0,337, - 336,1,0,0,0,337,338,1,0,0,0,338,340,1,0,0,0,339,341,3,48,24,0,340, - 339,1,0,0,0,340,341,1,0,0,0,341,343,1,0,0,0,342,344,3,52,26,0,343, - 342,1,0,0,0,343,344,1,0,0,0,344,346,1,0,0,0,345,347,3,54,27,0,346, - 345,1,0,0,0,346,347,1,0,0,0,347,349,1,0,0,0,348,350,3,56,28,0,349, - 348,1,0,0,0,349,350,1,0,0,0,350,353,1,0,0,0,351,352,5,98,0,0,352, - 354,7,0,0,0,353,351,1,0,0,0,353,354,1,0,0,0,354,357,1,0,0,0,355, - 356,5,98,0,0,356,358,5,86,0,0,357,355,1,0,0,0,357,358,1,0,0,0,358, - 360,1,0,0,0,359,361,3,58,29,0,360,359,1,0,0,0,360,361,1,0,0,0,361, - 363,1,0,0,0,362,364,3,50,25,0,363,362,1,0,0,0,363,364,1,0,0,0,364, - 366,1,0,0,0,365,367,3,60,30,0,366,365,1,0,0,0,366,367,1,0,0,0,367, - 370,1,0,0,0,368,371,3,64,32,0,369,371,3,66,33,0,370,368,1,0,0,0, - 370,369,1,0,0,0,370,371,1,0,0,0,371,373,1,0,0,0,372,374,3,68,34, - 0,373,372,1,0,0,0,373,374,1,0,0,0,374,41,1,0,0,0,375,376,5,98,0, - 0,376,377,3,120,60,0,377,43,1,0,0,0,378,379,5,85,0,0,379,382,5,104, - 0,0,380,381,5,98,0,0,381,383,5,82,0,0,382,380,1,0,0,0,382,383,1, - 0,0,0,383,45,1,0,0,0,384,385,5,32,0,0,385,386,3,70,35,0,386,47,1, - 0,0,0,387,389,7,1,0,0,388,387,1,0,0,0,388,389,1,0,0,0,389,390,1, - 0,0,0,390,391,5,5,0,0,391,392,5,45,0,0,392,393,3,106,53,0,393,49, - 1,0,0,0,394,395,5,97,0,0,395,396,3,152,76,0,396,397,5,6,0,0,397, - 398,5,126,0,0,398,399,3,90,45,0,399,409,5,145,0,0,400,401,5,112, - 0,0,401,402,3,152,76,0,402,403,5,6,0,0,403,404,5,126,0,0,404,405, - 3,90,45,0,405,406,5,145,0,0,406,408,1,0,0,0,407,400,1,0,0,0,408, - 411,1,0,0,0,409,407,1,0,0,0,409,410,1,0,0,0,410,51,1,0,0,0,411,409, - 1,0,0,0,412,413,5,67,0,0,413,414,3,108,54,0,414,53,1,0,0,0,415,416, - 5,95,0,0,416,417,3,108,54,0,417,55,1,0,0,0,418,419,5,34,0,0,419, - 426,5,11,0,0,420,421,7,0,0,0,421,422,5,126,0,0,422,423,3,106,53, - 0,423,424,5,145,0,0,424,427,1,0,0,0,425,427,3,106,53,0,426,420,1, - 0,0,0,426,425,1,0,0,0,427,57,1,0,0,0,428,429,5,35,0,0,429,430,3, - 108,54,0,430,59,1,0,0,0,431,432,5,62,0,0,432,433,5,11,0,0,433,434, - 3,80,40,0,434,61,1,0,0,0,435,436,5,62,0,0,436,437,5,11,0,0,437,438, - 3,106,53,0,438,63,1,0,0,0,439,440,5,52,0,0,440,443,3,108,54,0,441, - 442,5,112,0,0,442,444,3,108,54,0,443,441,1,0,0,0,443,444,1,0,0,0, - 444,449,1,0,0,0,445,446,5,98,0,0,446,450,5,82,0,0,447,448,5,11,0, - 0,448,450,3,106,53,0,449,445,1,0,0,0,449,447,1,0,0,0,449,450,1,0, - 0,0,450,469,1,0,0,0,451,452,5,52,0,0,452,455,3,108,54,0,453,454, - 5,98,0,0,454,456,5,82,0,0,455,453,1,0,0,0,455,456,1,0,0,0,456,457, - 1,0,0,0,457,458,5,59,0,0,458,459,3,108,54,0,459,469,1,0,0,0,460, - 461,5,52,0,0,461,462,3,108,54,0,462,463,5,59,0,0,463,466,3,108,54, - 0,464,465,5,11,0,0,465,467,3,106,53,0,466,464,1,0,0,0,466,467,1, - 0,0,0,467,469,1,0,0,0,468,439,1,0,0,0,468,451,1,0,0,0,468,460,1, - 0,0,0,469,65,1,0,0,0,470,471,5,59,0,0,471,472,3,108,54,0,472,67, - 1,0,0,0,473,474,5,79,0,0,474,475,3,86,43,0,475,69,1,0,0,0,476,477, - 6,35,-1,0,477,479,3,128,64,0,478,480,5,27,0,0,479,478,1,0,0,0,479, - 480,1,0,0,0,480,482,1,0,0,0,481,483,3,78,39,0,482,481,1,0,0,0,482, - 483,1,0,0,0,483,489,1,0,0,0,484,485,5,126,0,0,485,486,3,70,35,0, - 486,487,5,145,0,0,487,489,1,0,0,0,488,476,1,0,0,0,488,484,1,0,0, - 0,489,504,1,0,0,0,490,491,10,3,0,0,491,492,3,74,37,0,492,493,3,70, - 35,4,493,503,1,0,0,0,494,496,10,4,0,0,495,497,3,72,36,0,496,495, - 1,0,0,0,496,497,1,0,0,0,497,498,1,0,0,0,498,499,5,45,0,0,499,500, - 3,70,35,0,500,501,3,76,38,0,501,503,1,0,0,0,502,490,1,0,0,0,502, - 494,1,0,0,0,503,506,1,0,0,0,504,502,1,0,0,0,504,505,1,0,0,0,505, - 71,1,0,0,0,506,504,1,0,0,0,507,509,7,2,0,0,508,507,1,0,0,0,508,509, - 1,0,0,0,509,510,1,0,0,0,510,517,5,42,0,0,511,513,5,42,0,0,512,514, - 7,2,0,0,513,512,1,0,0,0,513,514,1,0,0,0,514,517,1,0,0,0,515,517, - 7,2,0,0,516,508,1,0,0,0,516,511,1,0,0,0,516,515,1,0,0,0,517,551, - 1,0,0,0,518,520,7,3,0,0,519,518,1,0,0,0,519,520,1,0,0,0,520,521, - 1,0,0,0,521,523,7,4,0,0,522,524,5,63,0,0,523,522,1,0,0,0,523,524, - 1,0,0,0,524,533,1,0,0,0,525,527,7,4,0,0,526,528,5,63,0,0,527,526, - 1,0,0,0,527,528,1,0,0,0,528,530,1,0,0,0,529,531,7,3,0,0,530,529, - 1,0,0,0,530,531,1,0,0,0,531,533,1,0,0,0,532,519,1,0,0,0,532,525, - 1,0,0,0,533,551,1,0,0,0,534,536,7,5,0,0,535,534,1,0,0,0,535,536, - 1,0,0,0,536,537,1,0,0,0,537,539,5,33,0,0,538,540,5,63,0,0,539,538, - 1,0,0,0,539,540,1,0,0,0,540,549,1,0,0,0,541,543,5,33,0,0,542,544, - 5,63,0,0,543,542,1,0,0,0,543,544,1,0,0,0,544,546,1,0,0,0,545,547, - 7,5,0,0,546,545,1,0,0,0,546,547,1,0,0,0,547,549,1,0,0,0,548,535, - 1,0,0,0,548,541,1,0,0,0,549,551,1,0,0,0,550,516,1,0,0,0,550,532, - 1,0,0,0,550,548,1,0,0,0,551,73,1,0,0,0,552,553,5,16,0,0,553,556, - 5,45,0,0,554,556,5,112,0,0,555,552,1,0,0,0,555,554,1,0,0,0,556,75, - 1,0,0,0,557,558,5,60,0,0,558,567,3,106,53,0,559,560,5,92,0,0,560, - 561,5,126,0,0,561,562,3,106,53,0,562,563,5,145,0,0,563,567,1,0,0, - 0,564,565,5,92,0,0,565,567,3,106,53,0,566,557,1,0,0,0,566,559,1, - 0,0,0,566,564,1,0,0,0,567,77,1,0,0,0,568,569,5,75,0,0,569,572,3, - 84,42,0,570,571,5,59,0,0,571,573,3,84,42,0,572,570,1,0,0,0,572,573, - 1,0,0,0,573,79,1,0,0,0,574,579,3,82,41,0,575,576,5,112,0,0,576,578, - 3,82,41,0,577,575,1,0,0,0,578,581,1,0,0,0,579,577,1,0,0,0,579,580, - 1,0,0,0,580,81,1,0,0,0,581,579,1,0,0,0,582,584,3,108,54,0,583,585, - 7,6,0,0,584,583,1,0,0,0,584,585,1,0,0,0,585,588,1,0,0,0,586,587, - 5,58,0,0,587,589,7,7,0,0,588,586,1,0,0,0,588,589,1,0,0,0,589,592, - 1,0,0,0,590,591,5,15,0,0,591,593,5,106,0,0,592,590,1,0,0,0,592,593, - 1,0,0,0,593,83,1,0,0,0,594,601,3,156,78,0,595,598,3,140,70,0,596, - 597,5,147,0,0,597,599,3,140,70,0,598,596,1,0,0,0,598,599,1,0,0,0, - 599,601,1,0,0,0,600,594,1,0,0,0,600,595,1,0,0,0,601,85,1,0,0,0,602, - 607,3,88,44,0,603,604,5,112,0,0,604,606,3,88,44,0,605,603,1,0,0, - 0,606,609,1,0,0,0,607,605,1,0,0,0,607,608,1,0,0,0,608,87,1,0,0,0, - 609,607,1,0,0,0,610,611,3,152,76,0,611,612,5,118,0,0,612,613,3,142, - 71,0,613,89,1,0,0,0,614,616,3,92,46,0,615,614,1,0,0,0,615,616,1, - 0,0,0,616,618,1,0,0,0,617,619,3,94,47,0,618,617,1,0,0,0,618,619, - 1,0,0,0,619,621,1,0,0,0,620,622,3,96,48,0,621,620,1,0,0,0,621,622, - 1,0,0,0,622,91,1,0,0,0,623,624,5,65,0,0,624,625,5,11,0,0,625,626, - 3,106,53,0,626,93,1,0,0,0,627,628,5,62,0,0,628,629,5,11,0,0,629, - 630,3,80,40,0,630,95,1,0,0,0,631,632,7,8,0,0,632,633,3,98,49,0,633, - 97,1,0,0,0,634,641,3,100,50,0,635,636,5,9,0,0,636,637,3,100,50,0, - 637,638,5,2,0,0,638,639,3,100,50,0,639,641,1,0,0,0,640,634,1,0,0, - 0,640,635,1,0,0,0,641,99,1,0,0,0,642,643,5,18,0,0,643,655,5,73,0, - 0,644,645,5,90,0,0,645,655,5,66,0,0,646,647,5,90,0,0,647,655,5,30, - 0,0,648,649,3,140,70,0,649,650,5,66,0,0,650,655,1,0,0,0,651,652, - 3,140,70,0,652,653,5,30,0,0,653,655,1,0,0,0,654,642,1,0,0,0,654, - 644,1,0,0,0,654,646,1,0,0,0,654,648,1,0,0,0,654,651,1,0,0,0,655, - 101,1,0,0,0,656,657,3,108,54,0,657,658,5,0,0,1,658,103,1,0,0,0,659, - 716,3,152,76,0,660,661,3,152,76,0,661,662,5,126,0,0,662,663,3,152, - 76,0,663,670,3,104,52,0,664,665,5,112,0,0,665,666,3,152,76,0,666, - 667,3,104,52,0,667,669,1,0,0,0,668,664,1,0,0,0,669,672,1,0,0,0,670, - 668,1,0,0,0,670,671,1,0,0,0,671,674,1,0,0,0,672,670,1,0,0,0,673, - 675,5,112,0,0,674,673,1,0,0,0,674,675,1,0,0,0,675,676,1,0,0,0,676, - 677,5,145,0,0,677,716,1,0,0,0,678,679,3,152,76,0,679,680,5,126,0, - 0,680,685,3,154,77,0,681,682,5,112,0,0,682,684,3,154,77,0,683,681, - 1,0,0,0,684,687,1,0,0,0,685,683,1,0,0,0,685,686,1,0,0,0,686,689, - 1,0,0,0,687,685,1,0,0,0,688,690,5,112,0,0,689,688,1,0,0,0,689,690, - 1,0,0,0,690,691,1,0,0,0,691,692,5,145,0,0,692,716,1,0,0,0,693,694, - 3,152,76,0,694,695,5,126,0,0,695,700,3,104,52,0,696,697,5,112,0, - 0,697,699,3,104,52,0,698,696,1,0,0,0,699,702,1,0,0,0,700,698,1,0, - 0,0,700,701,1,0,0,0,701,704,1,0,0,0,702,700,1,0,0,0,703,705,5,112, - 0,0,704,703,1,0,0,0,704,705,1,0,0,0,705,706,1,0,0,0,706,707,5,145, - 0,0,707,716,1,0,0,0,708,709,3,152,76,0,709,711,5,126,0,0,710,712, - 3,106,53,0,711,710,1,0,0,0,711,712,1,0,0,0,712,713,1,0,0,0,713,714, - 5,145,0,0,714,716,1,0,0,0,715,659,1,0,0,0,715,660,1,0,0,0,715,678, - 1,0,0,0,715,693,1,0,0,0,715,708,1,0,0,0,716,105,1,0,0,0,717,722, - 3,108,54,0,718,719,5,112,0,0,719,721,3,108,54,0,720,718,1,0,0,0, - 721,724,1,0,0,0,722,720,1,0,0,0,722,723,1,0,0,0,723,726,1,0,0,0, - 724,722,1,0,0,0,725,727,5,112,0,0,726,725,1,0,0,0,726,727,1,0,0, - 0,727,107,1,0,0,0,728,729,6,54,-1,0,729,731,5,12,0,0,730,732,3,108, - 54,0,731,730,1,0,0,0,731,732,1,0,0,0,732,738,1,0,0,0,733,734,5,94, - 0,0,734,735,3,108,54,0,735,736,5,81,0,0,736,737,3,108,54,0,737,739, - 1,0,0,0,738,733,1,0,0,0,739,740,1,0,0,0,740,738,1,0,0,0,740,741, - 1,0,0,0,741,744,1,0,0,0,742,743,5,24,0,0,743,745,3,108,54,0,744, - 742,1,0,0,0,744,745,1,0,0,0,745,746,1,0,0,0,746,747,5,25,0,0,747, - 878,1,0,0,0,748,749,5,13,0,0,749,750,5,126,0,0,750,751,3,108,54, - 0,751,752,5,6,0,0,752,753,3,104,52,0,753,754,5,145,0,0,754,878,1, - 0,0,0,755,756,5,19,0,0,756,878,5,106,0,0,757,758,5,43,0,0,758,759, - 3,108,54,0,759,760,3,144,72,0,760,878,1,0,0,0,761,762,5,80,0,0,762, - 763,5,126,0,0,763,764,3,108,54,0,764,765,5,32,0,0,765,768,3,108, - 54,0,766,767,5,31,0,0,767,769,3,108,54,0,768,766,1,0,0,0,768,769, - 1,0,0,0,769,770,1,0,0,0,770,771,5,145,0,0,771,878,1,0,0,0,772,773, - 5,83,0,0,773,878,5,106,0,0,774,775,5,88,0,0,775,776,5,126,0,0,776, - 777,7,9,0,0,777,778,3,158,79,0,778,779,5,32,0,0,779,780,3,108,54, - 0,780,781,5,145,0,0,781,878,1,0,0,0,782,783,3,152,76,0,783,785,5, - 126,0,0,784,786,3,106,53,0,785,784,1,0,0,0,785,786,1,0,0,0,786,787, - 1,0,0,0,787,788,5,145,0,0,788,797,1,0,0,0,789,791,5,126,0,0,790, - 792,5,23,0,0,791,790,1,0,0,0,791,792,1,0,0,0,792,794,1,0,0,0,793, - 795,3,110,55,0,794,793,1,0,0,0,794,795,1,0,0,0,795,796,1,0,0,0,796, - 798,5,145,0,0,797,789,1,0,0,0,797,798,1,0,0,0,798,799,1,0,0,0,799, - 800,5,64,0,0,800,801,5,126,0,0,801,802,3,90,45,0,802,803,5,145,0, - 0,803,878,1,0,0,0,804,805,3,152,76,0,805,807,5,126,0,0,806,808,3, - 106,53,0,807,806,1,0,0,0,807,808,1,0,0,0,808,809,1,0,0,0,809,810, - 5,145,0,0,810,819,1,0,0,0,811,813,5,126,0,0,812,814,5,23,0,0,813, - 812,1,0,0,0,813,814,1,0,0,0,814,816,1,0,0,0,815,817,3,110,55,0,816, - 815,1,0,0,0,816,817,1,0,0,0,817,818,1,0,0,0,818,820,5,145,0,0,819, - 811,1,0,0,0,819,820,1,0,0,0,820,821,1,0,0,0,821,822,5,64,0,0,822, - 823,3,152,76,0,823,878,1,0,0,0,824,830,3,152,76,0,825,827,5,126, - 0,0,826,828,3,106,53,0,827,826,1,0,0,0,827,828,1,0,0,0,828,829,1, - 0,0,0,829,831,5,145,0,0,830,825,1,0,0,0,830,831,1,0,0,0,831,832, - 1,0,0,0,832,834,5,126,0,0,833,835,5,23,0,0,834,833,1,0,0,0,834,835, - 1,0,0,0,835,837,1,0,0,0,836,838,3,110,55,0,837,836,1,0,0,0,837,838, - 1,0,0,0,838,839,1,0,0,0,839,840,5,145,0,0,840,878,1,0,0,0,841,878, - 3,116,58,0,842,878,3,160,80,0,843,878,3,142,71,0,844,845,5,114,0, - 0,845,878,3,108,54,19,846,847,5,56,0,0,847,878,3,108,54,13,848,849, - 3,132,66,0,849,850,5,116,0,0,850,852,1,0,0,0,851,848,1,0,0,0,851, - 852,1,0,0,0,852,853,1,0,0,0,853,878,5,108,0,0,854,855,5,126,0,0, - 855,856,3,36,18,0,856,857,5,145,0,0,857,878,1,0,0,0,858,859,5,126, - 0,0,859,860,3,108,54,0,860,861,5,145,0,0,861,878,1,0,0,0,862,863, - 5,126,0,0,863,864,3,106,53,0,864,865,5,145,0,0,865,878,1,0,0,0,866, - 868,5,125,0,0,867,869,3,106,53,0,868,867,1,0,0,0,868,869,1,0,0,0, - 869,870,1,0,0,0,870,878,5,144,0,0,871,873,5,124,0,0,872,874,3,32, - 16,0,873,872,1,0,0,0,873,874,1,0,0,0,874,875,1,0,0,0,875,878,5,143, - 0,0,876,878,3,124,62,0,877,728,1,0,0,0,877,748,1,0,0,0,877,755,1, - 0,0,0,877,757,1,0,0,0,877,761,1,0,0,0,877,772,1,0,0,0,877,774,1, - 0,0,0,877,782,1,0,0,0,877,804,1,0,0,0,877,824,1,0,0,0,877,841,1, - 0,0,0,877,842,1,0,0,0,877,843,1,0,0,0,877,844,1,0,0,0,877,846,1, - 0,0,0,877,851,1,0,0,0,877,854,1,0,0,0,877,858,1,0,0,0,877,862,1, - 0,0,0,877,866,1,0,0,0,877,871,1,0,0,0,877,876,1,0,0,0,878,983,1, - 0,0,0,879,883,10,18,0,0,880,884,5,108,0,0,881,884,5,147,0,0,882, - 884,5,134,0,0,883,880,1,0,0,0,883,881,1,0,0,0,883,882,1,0,0,0,884, - 885,1,0,0,0,885,982,3,108,54,19,886,890,10,17,0,0,887,891,5,135, - 0,0,888,891,5,114,0,0,889,891,5,113,0,0,890,887,1,0,0,0,890,888, - 1,0,0,0,890,889,1,0,0,0,891,892,1,0,0,0,892,982,3,108,54,18,893, - 918,10,16,0,0,894,919,5,117,0,0,895,919,5,118,0,0,896,919,5,129, - 0,0,897,919,5,127,0,0,898,919,5,128,0,0,899,919,5,119,0,0,900,919, - 5,120,0,0,901,903,5,56,0,0,902,901,1,0,0,0,902,903,1,0,0,0,903,904, - 1,0,0,0,904,906,5,40,0,0,905,907,5,14,0,0,906,905,1,0,0,0,906,907, - 1,0,0,0,907,919,1,0,0,0,908,910,5,56,0,0,909,908,1,0,0,0,909,910, - 1,0,0,0,910,911,1,0,0,0,911,919,7,10,0,0,912,919,5,141,0,0,913,919, - 5,142,0,0,914,919,5,131,0,0,915,919,5,122,0,0,916,919,5,123,0,0, - 917,919,5,130,0,0,918,894,1,0,0,0,918,895,1,0,0,0,918,896,1,0,0, - 0,918,897,1,0,0,0,918,898,1,0,0,0,918,899,1,0,0,0,918,900,1,0,0, - 0,918,902,1,0,0,0,918,909,1,0,0,0,918,912,1,0,0,0,918,913,1,0,0, - 0,918,914,1,0,0,0,918,915,1,0,0,0,918,916,1,0,0,0,918,917,1,0,0, - 0,919,920,1,0,0,0,920,982,3,108,54,17,921,922,10,14,0,0,922,923, - 5,133,0,0,923,982,3,108,54,15,924,925,10,12,0,0,925,926,5,2,0,0, - 926,982,3,108,54,13,927,928,10,11,0,0,928,929,5,61,0,0,929,982,3, - 108,54,12,930,932,10,10,0,0,931,933,5,56,0,0,932,931,1,0,0,0,932, - 933,1,0,0,0,933,934,1,0,0,0,934,935,5,9,0,0,935,936,3,108,54,0,936, - 937,5,2,0,0,937,938,3,108,54,11,938,982,1,0,0,0,939,940,10,9,0,0, - 940,941,5,136,0,0,941,942,3,108,54,0,942,943,5,111,0,0,943,944,3, - 108,54,9,944,982,1,0,0,0,945,946,10,25,0,0,946,947,5,125,0,0,947, - 948,3,108,54,0,948,949,5,144,0,0,949,982,1,0,0,0,950,951,10,24,0, - 0,951,952,5,116,0,0,952,982,5,104,0,0,953,954,10,23,0,0,954,955, - 5,116,0,0,955,982,3,152,76,0,956,957,10,22,0,0,957,958,5,132,0,0, - 958,959,5,125,0,0,959,960,3,108,54,0,960,961,5,144,0,0,961,982,1, - 0,0,0,962,963,10,21,0,0,963,964,5,132,0,0,964,982,5,104,0,0,965, - 966,10,20,0,0,966,967,5,132,0,0,967,982,3,152,76,0,968,969,10,15, - 0,0,969,971,5,44,0,0,970,972,5,56,0,0,971,970,1,0,0,0,971,972,1, - 0,0,0,972,973,1,0,0,0,973,982,5,57,0,0,974,979,10,8,0,0,975,976, - 5,6,0,0,976,980,3,152,76,0,977,978,5,6,0,0,978,980,5,106,0,0,979, - 975,1,0,0,0,979,977,1,0,0,0,980,982,1,0,0,0,981,879,1,0,0,0,981, - 886,1,0,0,0,981,893,1,0,0,0,981,921,1,0,0,0,981,924,1,0,0,0,981, - 927,1,0,0,0,981,930,1,0,0,0,981,939,1,0,0,0,981,945,1,0,0,0,981, - 950,1,0,0,0,981,953,1,0,0,0,981,956,1,0,0,0,981,962,1,0,0,0,981, - 965,1,0,0,0,981,968,1,0,0,0,981,974,1,0,0,0,982,985,1,0,0,0,983, - 981,1,0,0,0,983,984,1,0,0,0,984,109,1,0,0,0,985,983,1,0,0,0,986, - 991,3,112,56,0,987,988,5,112,0,0,988,990,3,112,56,0,989,987,1,0, - 0,0,990,993,1,0,0,0,991,989,1,0,0,0,991,992,1,0,0,0,992,995,1,0, - 0,0,993,991,1,0,0,0,994,996,5,112,0,0,995,994,1,0,0,0,995,996,1, - 0,0,0,996,111,1,0,0,0,997,1000,3,114,57,0,998,1000,3,108,54,0,999, - 997,1,0,0,0,999,998,1,0,0,0,1000,113,1,0,0,0,1001,1002,5,126,0,0, - 1002,1007,3,152,76,0,1003,1004,5,112,0,0,1004,1006,3,152,76,0,1005, - 1003,1,0,0,0,1006,1009,1,0,0,0,1007,1005,1,0,0,0,1007,1008,1,0,0, - 0,1008,1011,1,0,0,0,1009,1007,1,0,0,0,1010,1012,5,112,0,0,1011,1010, - 1,0,0,0,1011,1012,1,0,0,0,1012,1013,1,0,0,0,1013,1014,5,145,0,0, - 1014,1027,1,0,0,0,1015,1020,3,152,76,0,1016,1017,5,112,0,0,1017, - 1019,3,152,76,0,1018,1016,1,0,0,0,1019,1022,1,0,0,0,1020,1018,1, - 0,0,0,1020,1021,1,0,0,0,1021,1024,1,0,0,0,1022,1020,1,0,0,0,1023, - 1025,5,112,0,0,1024,1023,1,0,0,0,1024,1025,1,0,0,0,1025,1027,1,0, - 0,0,1026,1001,1,0,0,0,1026,1015,1,0,0,0,1027,1028,1,0,0,0,1028,1029, - 5,107,0,0,1029,1030,3,108,54,0,1030,115,1,0,0,0,1031,1032,5,128, - 0,0,1032,1036,3,152,76,0,1033,1035,3,118,59,0,1034,1033,1,0,0,0, - 1035,1038,1,0,0,0,1036,1034,1,0,0,0,1036,1037,1,0,0,0,1037,1039, - 1,0,0,0,1038,1036,1,0,0,0,1039,1040,5,147,0,0,1040,1041,5,120,0, - 0,1041,1060,1,0,0,0,1042,1043,5,128,0,0,1043,1047,3,152,76,0,1044, - 1046,3,118,59,0,1045,1044,1,0,0,0,1046,1049,1,0,0,0,1047,1045,1, - 0,0,0,1047,1048,1,0,0,0,1048,1050,1,0,0,0,1049,1047,1,0,0,0,1050, - 1052,5,120,0,0,1051,1053,3,116,58,0,1052,1051,1,0,0,0,1052,1053, - 1,0,0,0,1053,1054,1,0,0,0,1054,1055,5,128,0,0,1055,1056,5,147,0, - 0,1056,1057,3,152,76,0,1057,1058,5,120,0,0,1058,1060,1,0,0,0,1059, - 1031,1,0,0,0,1059,1042,1,0,0,0,1060,117,1,0,0,0,1061,1062,3,152, - 76,0,1062,1063,5,118,0,0,1063,1064,3,158,79,0,1064,1073,1,0,0,0, - 1065,1066,3,152,76,0,1066,1067,5,118,0,0,1067,1068,5,124,0,0,1068, - 1069,3,108,54,0,1069,1070,5,143,0,0,1070,1073,1,0,0,0,1071,1073, - 3,152,76,0,1072,1061,1,0,0,0,1072,1065,1,0,0,0,1072,1071,1,0,0,0, - 1073,119,1,0,0,0,1074,1079,3,122,61,0,1075,1076,5,112,0,0,1076,1078, - 3,122,61,0,1077,1075,1,0,0,0,1078,1081,1,0,0,0,1079,1077,1,0,0,0, - 1079,1080,1,0,0,0,1080,1083,1,0,0,0,1081,1079,1,0,0,0,1082,1084, - 5,112,0,0,1083,1082,1,0,0,0,1083,1084,1,0,0,0,1084,121,1,0,0,0,1085, - 1086,3,152,76,0,1086,1087,5,6,0,0,1087,1088,5,126,0,0,1088,1089, - 3,36,18,0,1089,1090,5,145,0,0,1090,1096,1,0,0,0,1091,1092,3,108, - 54,0,1092,1093,5,6,0,0,1093,1094,3,152,76,0,1094,1096,1,0,0,0,1095, - 1085,1,0,0,0,1095,1091,1,0,0,0,1096,123,1,0,0,0,1097,1105,3,156, - 78,0,1098,1099,3,132,66,0,1099,1100,5,116,0,0,1100,1102,1,0,0,0, - 1101,1098,1,0,0,0,1101,1102,1,0,0,0,1102,1103,1,0,0,0,1103,1105, - 3,126,63,0,1104,1097,1,0,0,0,1104,1101,1,0,0,0,1105,125,1,0,0,0, - 1106,1111,3,152,76,0,1107,1108,5,116,0,0,1108,1110,3,152,76,0,1109, - 1107,1,0,0,0,1110,1113,1,0,0,0,1111,1109,1,0,0,0,1111,1112,1,0,0, - 0,1112,127,1,0,0,0,1113,1111,1,0,0,0,1114,1115,6,64,-1,0,1115,1124, - 3,132,66,0,1116,1124,3,130,65,0,1117,1118,5,126,0,0,1118,1119,3, - 36,18,0,1119,1120,5,145,0,0,1120,1124,1,0,0,0,1121,1124,3,116,58, - 0,1122,1124,3,156,78,0,1123,1114,1,0,0,0,1123,1116,1,0,0,0,1123, - 1117,1,0,0,0,1123,1121,1,0,0,0,1123,1122,1,0,0,0,1124,1133,1,0,0, - 0,1125,1129,10,3,0,0,1126,1130,3,150,75,0,1127,1128,5,6,0,0,1128, - 1130,3,152,76,0,1129,1126,1,0,0,0,1129,1127,1,0,0,0,1130,1132,1, - 0,0,0,1131,1125,1,0,0,0,1132,1135,1,0,0,0,1133,1131,1,0,0,0,1133, - 1134,1,0,0,0,1134,129,1,0,0,0,1135,1133,1,0,0,0,1136,1137,3,152, - 76,0,1137,1139,5,126,0,0,1138,1140,3,134,67,0,1139,1138,1,0,0,0, - 1139,1140,1,0,0,0,1140,1141,1,0,0,0,1141,1142,5,145,0,0,1142,131, - 1,0,0,0,1143,1144,3,136,68,0,1144,1145,5,116,0,0,1145,1147,1,0,0, - 0,1146,1143,1,0,0,0,1146,1147,1,0,0,0,1147,1148,1,0,0,0,1148,1149, - 3,152,76,0,1149,133,1,0,0,0,1150,1155,3,108,54,0,1151,1152,5,112, - 0,0,1152,1154,3,108,54,0,1153,1151,1,0,0,0,1154,1157,1,0,0,0,1155, - 1153,1,0,0,0,1155,1156,1,0,0,0,1156,1159,1,0,0,0,1157,1155,1,0,0, - 0,1158,1160,5,112,0,0,1159,1158,1,0,0,0,1159,1160,1,0,0,0,1160,135, - 1,0,0,0,1161,1162,3,152,76,0,1162,137,1,0,0,0,1163,1172,5,102,0, - 0,1164,1165,5,116,0,0,1165,1172,7,11,0,0,1166,1167,5,104,0,0,1167, - 1169,5,116,0,0,1168,1170,7,11,0,0,1169,1168,1,0,0,0,1169,1170,1, - 0,0,0,1170,1172,1,0,0,0,1171,1163,1,0,0,0,1171,1164,1,0,0,0,1171, - 1166,1,0,0,0,1172,139,1,0,0,0,1173,1175,7,12,0,0,1174,1173,1,0,0, - 0,1174,1175,1,0,0,0,1175,1182,1,0,0,0,1176,1183,3,138,69,0,1177, - 1183,5,103,0,0,1178,1183,5,104,0,0,1179,1183,5,105,0,0,1180,1183, - 5,41,0,0,1181,1183,5,55,0,0,1182,1176,1,0,0,0,1182,1177,1,0,0,0, - 1182,1178,1,0,0,0,1182,1179,1,0,0,0,1182,1180,1,0,0,0,1182,1181, - 1,0,0,0,1183,141,1,0,0,0,1184,1188,3,140,70,0,1185,1188,5,106,0, - 0,1186,1188,5,57,0,0,1187,1184,1,0,0,0,1187,1185,1,0,0,0,1187,1186, - 1,0,0,0,1188,143,1,0,0,0,1189,1190,7,13,0,0,1190,145,1,0,0,0,1191, - 1192,7,14,0,0,1192,147,1,0,0,0,1193,1194,7,15,0,0,1194,149,1,0,0, - 0,1195,1198,5,101,0,0,1196,1198,3,148,74,0,1197,1195,1,0,0,0,1197, - 1196,1,0,0,0,1198,151,1,0,0,0,1199,1203,5,101,0,0,1200,1203,3,144, - 72,0,1201,1203,3,146,73,0,1202,1199,1,0,0,0,1202,1200,1,0,0,0,1202, - 1201,1,0,0,0,1203,153,1,0,0,0,1204,1205,3,158,79,0,1205,1206,5,118, - 0,0,1206,1207,3,140,70,0,1207,155,1,0,0,0,1208,1209,5,124,0,0,1209, - 1210,3,152,76,0,1210,1211,5,143,0,0,1211,157,1,0,0,0,1212,1215,5, - 106,0,0,1213,1215,3,160,80,0,1214,1212,1,0,0,0,1214,1213,1,0,0,0, - 1215,159,1,0,0,0,1216,1220,5,138,0,0,1217,1219,3,162,81,0,1218,1217, - 1,0,0,0,1219,1222,1,0,0,0,1220,1218,1,0,0,0,1220,1221,1,0,0,0,1221, - 1223,1,0,0,0,1222,1220,1,0,0,0,1223,1224,5,140,0,0,1224,161,1,0, - 0,0,1225,1226,5,153,0,0,1226,1227,3,108,54,0,1227,1228,5,143,0,0, - 1228,1231,1,0,0,0,1229,1231,5,152,0,0,1230,1225,1,0,0,0,1230,1229, - 1,0,0,0,1231,163,1,0,0,0,1232,1236,5,139,0,0,1233,1235,3,166,83, - 0,1234,1233,1,0,0,0,1235,1238,1,0,0,0,1236,1234,1,0,0,0,1236,1237, - 1,0,0,0,1237,1239,1,0,0,0,1238,1236,1,0,0,0,1239,1240,5,0,0,1,1240, - 165,1,0,0,0,1241,1242,5,155,0,0,1242,1243,3,108,54,0,1243,1244,5, - 143,0,0,1244,1247,1,0,0,0,1245,1247,5,154,0,0,1246,1241,1,0,0,0, - 1246,1245,1,0,0,0,1247,167,1,0,0,0,160,171,178,187,194,198,209,213, - 216,225,233,240,244,250,255,261,273,281,295,299,304,314,323,326, - 330,333,337,340,343,346,349,353,357,360,363,366,370,373,382,388, - 409,426,443,449,455,466,468,479,482,488,496,502,504,508,513,516, - 519,523,527,530,532,535,539,543,546,548,550,555,566,572,579,584, - 588,592,598,600,607,615,618,621,640,654,670,674,685,689,700,704, - 711,715,722,726,731,740,744,768,785,791,794,797,807,813,816,819, - 827,830,834,837,851,868,873,877,883,890,902,906,909,918,932,971, - 979,981,983,991,995,999,1007,1011,1020,1024,1026,1036,1047,1052, - 1059,1072,1079,1083,1095,1101,1104,1111,1123,1129,1133,1139,1146, - 1155,1159,1169,1171,1174,1182,1187,1197,1202,1214,1220,1230,1236, - 1246 + 78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,7,84,1, + 0,5,0,172,8,0,10,0,12,0,175,9,0,1,0,1,0,1,1,1,1,3,1,181,8,1,1,2, + 1,2,1,3,1,3,1,3,1,3,1,3,3,3,190,8,3,1,4,1,4,1,4,5,4,195,8,4,10,4, + 12,4,198,9,4,1,4,3,4,201,8,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5, + 1,5,3,5,213,8,5,1,6,1,6,3,6,217,8,6,1,6,3,6,220,8,6,1,7,1,7,1,7, + 1,7,1,7,1,7,1,7,3,7,229,8,7,1,8,1,8,1,8,1,8,1,8,1,8,3,8,237,8,8, + 1,9,1,9,1,9,1,9,1,9,3,9,244,8,9,1,9,1,9,3,9,248,8,9,1,9,1,9,1,9, + 1,9,3,9,254,8,9,1,9,1,9,1,9,3,9,259,8,9,1,10,1,10,1,10,1,10,1,10, + 1,10,3,10,267,8,10,1,10,1,10,1,10,1,10,1,10,3,10,274,8,10,1,11,1, + 11,1,11,1,11,3,11,280,8,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1, + 12,1,13,1,13,3,13,292,8,13,1,14,1,14,1,15,1,15,5,15,298,8,15,10, + 15,12,15,301,9,15,1,15,1,15,1,16,1,16,1,16,1,16,1,17,1,17,1,17,5, + 17,312,8,17,10,17,12,17,315,9,17,1,17,3,17,318,8,17,1,18,1,18,1, + 18,3,18,323,8,18,1,18,1,18,1,19,1,19,1,19,1,19,5,19,331,8,19,10, + 19,12,19,334,9,19,1,20,1,20,1,20,1,20,1,20,1,20,3,20,342,8,20,1, + 21,3,21,345,8,21,1,21,1,21,3,21,349,8,21,1,21,3,21,352,8,21,1,21, + 1,21,3,21,356,8,21,1,21,3,21,359,8,21,1,21,3,21,362,8,21,1,21,3, + 21,365,8,21,1,21,3,21,368,8,21,1,21,1,21,3,21,372,8,21,1,21,1,21, + 3,21,376,8,21,1,21,3,21,379,8,21,1,21,3,21,382,8,21,1,21,3,21,385, + 8,21,1,21,1,21,3,21,389,8,21,1,21,3,21,392,8,21,1,22,1,22,1,22,1, + 23,1,23,1,23,1,23,3,23,401,8,23,1,24,1,24,1,24,1,25,3,25,407,8,25, + 1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26, + 1,26,1,26,1,26,1,26,5,26,426,8,26,10,26,12,26,429,9,26,1,27,1,27, + 1,27,1,28,1,28,1,28,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,3,29, + 445,8,29,1,30,1,30,1,30,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,32, + 1,33,1,33,1,33,1,33,3,33,462,8,33,1,33,1,33,1,33,1,33,3,33,468,8, + 33,1,33,1,33,1,33,1,33,3,33,474,8,33,1,33,1,33,1,33,1,33,1,33,1, + 33,1,33,1,33,1,33,3,33,485,8,33,3,33,487,8,33,1,34,1,34,1,34,1,35, + 1,35,1,35,1,36,1,36,1,36,3,36,498,8,36,1,36,3,36,501,8,36,1,36,1, + 36,1,36,1,36,3,36,507,8,36,1,36,1,36,1,36,1,36,1,36,1,36,3,36,515, + 8,36,1,36,1,36,1,36,1,36,5,36,521,8,36,10,36,12,36,524,9,36,1,37, + 3,37,527,8,37,1,37,1,37,1,37,3,37,532,8,37,1,37,3,37,535,8,37,1, + 37,3,37,538,8,37,1,37,1,37,3,37,542,8,37,1,37,1,37,3,37,546,8,37, + 1,37,3,37,549,8,37,3,37,551,8,37,1,37,3,37,554,8,37,1,37,1,37,3, + 37,558,8,37,1,37,1,37,3,37,562,8,37,1,37,3,37,565,8,37,3,37,567, + 8,37,3,37,569,8,37,1,38,1,38,1,38,3,38,574,8,38,1,39,1,39,1,39,1, + 39,1,39,1,39,1,39,1,39,1,39,3,39,585,8,39,1,40,1,40,1,40,1,40,3, + 40,591,8,40,1,41,1,41,1,41,5,41,596,8,41,10,41,12,41,599,9,41,1, + 42,1,42,3,42,603,8,42,1,42,1,42,3,42,607,8,42,1,42,1,42,3,42,611, + 8,42,1,43,1,43,1,43,1,43,3,43,617,8,43,3,43,619,8,43,1,44,1,44,1, + 44,5,44,624,8,44,10,44,12,44,627,9,44,1,45,1,45,1,45,1,45,1,46,3, + 46,634,8,46,1,46,3,46,637,8,46,1,46,3,46,640,8,46,1,47,1,47,1,47, + 1,47,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50, + 1,50,3,50,659,8,50,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,51, + 1,51,1,51,1,51,3,51,673,8,51,1,52,1,52,1,52,1,53,1,53,1,53,1,53, + 1,53,1,53,1,53,1,53,1,53,5,53,687,8,53,10,53,12,53,690,9,53,1,53, + 3,53,693,8,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,5,53,702,8,53,10, + 53,12,53,705,9,53,1,53,3,53,708,8,53,1,53,1,53,1,53,1,53,1,53,1, + 53,1,53,5,53,717,8,53,10,53,12,53,720,9,53,1,53,3,53,723,8,53,1, + 53,1,53,1,53,1,53,1,53,3,53,730,8,53,1,53,1,53,3,53,734,8,53,1,54, + 1,54,1,54,5,54,739,8,54,10,54,12,54,742,9,54,1,54,3,54,745,8,54, + 1,55,1,55,1,55,3,55,750,8,55,1,55,1,55,1,55,1,55,1,55,4,55,757,8, + 55,11,55,12,55,758,1,55,1,55,3,55,763,8,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,3,55,787,8,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,804,8,55,1, + 55,1,55,1,55,1,55,3,55,810,8,55,1,55,3,55,813,8,55,1,55,3,55,816, + 8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,826,8,55,1,55, + 1,55,1,55,1,55,3,55,832,8,55,1,55,3,55,835,8,55,1,55,3,55,838,8, + 55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,846,8,55,1,55,3,55,849,8,55, + 1,55,1,55,3,55,853,8,55,1,55,3,55,856,8,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,870,8,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3, + 55,887,8,55,1,55,1,55,1,55,3,55,892,8,55,1,55,1,55,3,55,896,8,55, + 1,55,1,55,1,55,1,55,3,55,902,8,55,1,55,1,55,1,55,1,55,1,55,3,55, + 909,8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55, + 921,8,55,1,55,1,55,3,55,925,8,55,1,55,3,55,928,8,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,3,55,937,8,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,951,8,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,990,8,55,1,55,1,55,1, + 55,1,55,1,55,1,55,3,55,998,8,55,5,55,1000,8,55,10,55,12,55,1003, + 9,55,1,56,1,56,1,56,5,56,1008,8,56,10,56,12,56,1011,9,56,1,56,3, + 56,1014,8,56,1,57,1,57,3,57,1018,8,57,1,58,1,58,1,58,1,58,5,58,1024, + 8,58,10,58,12,58,1027,9,58,1,58,3,58,1030,8,58,1,58,1,58,1,58,1, + 58,1,58,5,58,1037,8,58,10,58,12,58,1040,9,58,1,58,3,58,1043,8,58, + 3,58,1045,8,58,1,58,1,58,1,58,1,59,1,59,1,59,5,59,1053,8,59,10,59, + 12,59,1056,9,59,1,59,1,59,1,59,1,59,1,59,1,59,5,59,1064,8,59,10, + 59,12,59,1067,9,59,1,59,1,59,3,59,1071,8,59,1,59,1,59,1,59,1,59, + 1,59,3,59,1078,8,59,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60, + 1,60,1,60,3,60,1091,8,60,1,61,1,61,1,61,5,61,1096,8,61,10,61,12, + 61,1099,9,61,1,61,3,61,1102,8,61,1,62,1,62,1,62,1,62,1,62,1,62,1, + 62,1,62,1,62,1,62,3,62,1114,8,62,1,63,1,63,1,63,1,63,3,63,1120,8, + 63,1,63,3,63,1123,8,63,1,64,1,64,1,64,5,64,1128,8,64,10,64,12,64, + 1131,9,64,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,3,65,1142, + 8,65,1,65,1,65,1,65,1,65,3,65,1148,8,65,5,65,1150,8,65,10,65,12, + 65,1153,9,65,1,66,1,66,1,66,3,66,1158,8,66,1,66,1,66,1,67,1,67,1, + 67,3,67,1165,8,67,1,67,1,67,1,68,1,68,1,68,5,68,1172,8,68,10,68, + 12,68,1175,9,68,1,68,3,68,1178,8,68,1,69,1,69,1,70,1,70,1,70,1,70, + 1,70,1,70,3,70,1188,8,70,3,70,1190,8,70,1,71,3,71,1193,8,71,1,71, + 1,71,1,71,1,71,1,71,1,71,3,71,1201,8,71,1,72,1,72,1,72,3,72,1206, + 8,72,1,73,1,73,1,74,1,74,1,75,1,75,1,76,1,76,3,76,1216,8,76,1,77, + 1,77,1,77,3,77,1221,8,77,1,78,1,78,1,78,1,78,1,79,1,79,1,79,1,79, + 1,80,1,80,3,80,1233,8,80,1,81,1,81,5,81,1237,8,81,10,81,12,81,1240, + 9,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,3,82,1249,8,82,1,83,1,83, + 5,83,1253,8,83,10,83,12,83,1256,9,83,1,83,1,83,1,84,1,84,1,84,1, + 84,1,84,3,84,1265,8,84,1,84,0,3,72,110,130,85,0,2,4,6,8,10,12,14, + 16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58, + 60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100, + 102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132, + 134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164, + 166,168,0,16,2,0,17,17,72,72,2,0,42,42,49,49,3,0,1,1,4,4,8,8,4,0, + 1,1,3,4,8,8,78,78,2,0,49,49,71,71,2,0,1,1,4,4,2,0,7,7,21,22,2,0, + 28,28,47,47,2,0,69,69,74,74,3,0,10,10,48,48,87,87,2,0,39,39,51,51, + 1,0,103,104,2,0,114,114,135,135,7,0,20,20,36,36,53,54,68,68,76,76, + 93,93,99,99,12,0,1,19,21,28,30,35,37,40,42,49,51,52,56,56,58,67, + 69,75,77,92,94,95,97,98,4,0,19,19,28,28,37,37,46,46,1429,0,173,1, + 0,0,0,2,180,1,0,0,0,4,182,1,0,0,0,6,184,1,0,0,0,8,191,1,0,0,0,10, + 212,1,0,0,0,12,214,1,0,0,0,14,221,1,0,0,0,16,230,1,0,0,0,18,238, + 1,0,0,0,20,260,1,0,0,0,22,275,1,0,0,0,24,284,1,0,0,0,26,289,1,0, + 0,0,28,293,1,0,0,0,30,295,1,0,0,0,32,304,1,0,0,0,34,308,1,0,0,0, + 36,322,1,0,0,0,38,326,1,0,0,0,40,341,1,0,0,0,42,344,1,0,0,0,44,393, + 1,0,0,0,46,396,1,0,0,0,48,402,1,0,0,0,50,406,1,0,0,0,52,412,1,0, + 0,0,54,430,1,0,0,0,56,433,1,0,0,0,58,436,1,0,0,0,60,446,1,0,0,0, + 62,449,1,0,0,0,64,453,1,0,0,0,66,486,1,0,0,0,68,488,1,0,0,0,70,491, + 1,0,0,0,72,506,1,0,0,0,74,568,1,0,0,0,76,573,1,0,0,0,78,584,1,0, + 0,0,80,586,1,0,0,0,82,592,1,0,0,0,84,600,1,0,0,0,86,618,1,0,0,0, + 88,620,1,0,0,0,90,628,1,0,0,0,92,633,1,0,0,0,94,641,1,0,0,0,96,645, + 1,0,0,0,98,649,1,0,0,0,100,658,1,0,0,0,102,672,1,0,0,0,104,674,1, + 0,0,0,106,733,1,0,0,0,108,735,1,0,0,0,110,895,1,0,0,0,112,1004,1, + 0,0,0,114,1017,1,0,0,0,116,1044,1,0,0,0,118,1077,1,0,0,0,120,1090, + 1,0,0,0,122,1092,1,0,0,0,124,1113,1,0,0,0,126,1122,1,0,0,0,128,1124, + 1,0,0,0,130,1141,1,0,0,0,132,1154,1,0,0,0,134,1164,1,0,0,0,136,1168, + 1,0,0,0,138,1179,1,0,0,0,140,1189,1,0,0,0,142,1192,1,0,0,0,144,1205, + 1,0,0,0,146,1207,1,0,0,0,148,1209,1,0,0,0,150,1211,1,0,0,0,152,1215, + 1,0,0,0,154,1220,1,0,0,0,156,1222,1,0,0,0,158,1226,1,0,0,0,160,1232, + 1,0,0,0,162,1234,1,0,0,0,164,1248,1,0,0,0,166,1250,1,0,0,0,168,1264, + 1,0,0,0,170,172,3,2,1,0,171,170,1,0,0,0,172,175,1,0,0,0,173,171, + 1,0,0,0,173,174,1,0,0,0,174,176,1,0,0,0,175,173,1,0,0,0,176,177, + 5,0,0,1,177,1,1,0,0,0,178,181,3,6,3,0,179,181,3,10,5,0,180,178,1, + 0,0,0,180,179,1,0,0,0,181,3,1,0,0,0,182,183,3,110,55,0,183,5,1,0, + 0,0,184,185,5,50,0,0,185,189,3,154,77,0,186,187,5,111,0,0,187,188, + 5,118,0,0,188,190,3,4,2,0,189,186,1,0,0,0,189,190,1,0,0,0,190,7, + 1,0,0,0,191,196,3,154,77,0,192,193,5,112,0,0,193,195,3,154,77,0, + 194,192,1,0,0,0,195,198,1,0,0,0,196,194,1,0,0,0,196,197,1,0,0,0, + 197,200,1,0,0,0,198,196,1,0,0,0,199,201,5,112,0,0,200,199,1,0,0, + 0,200,201,1,0,0,0,201,9,1,0,0,0,202,213,3,12,6,0,203,213,3,14,7, + 0,204,213,3,16,8,0,205,213,3,20,10,0,206,213,3,18,9,0,207,213,3, + 22,11,0,208,213,3,24,12,0,209,213,3,30,15,0,210,213,3,26,13,0,211, + 213,3,28,14,0,212,202,1,0,0,0,212,203,1,0,0,0,212,204,1,0,0,0,212, + 205,1,0,0,0,212,206,1,0,0,0,212,207,1,0,0,0,212,208,1,0,0,0,212, + 209,1,0,0,0,212,210,1,0,0,0,212,211,1,0,0,0,213,11,1,0,0,0,214,216, + 5,70,0,0,215,217,3,4,2,0,216,215,1,0,0,0,216,217,1,0,0,0,217,219, + 1,0,0,0,218,220,5,146,0,0,219,218,1,0,0,0,219,220,1,0,0,0,220,13, + 1,0,0,0,221,222,5,38,0,0,222,223,5,126,0,0,223,224,3,4,2,0,224,225, + 5,145,0,0,225,228,3,10,5,0,226,227,5,24,0,0,227,229,3,10,5,0,228, + 226,1,0,0,0,228,229,1,0,0,0,229,15,1,0,0,0,230,231,5,96,0,0,231, + 232,5,126,0,0,232,233,3,4,2,0,233,234,5,145,0,0,234,236,3,10,5,0, + 235,237,5,146,0,0,236,235,1,0,0,0,236,237,1,0,0,0,237,17,1,0,0,0, + 238,239,5,31,0,0,239,243,5,126,0,0,240,244,3,6,3,0,241,244,3,24, + 12,0,242,244,3,4,2,0,243,240,1,0,0,0,243,241,1,0,0,0,243,242,1,0, + 0,0,243,244,1,0,0,0,244,245,1,0,0,0,245,247,5,146,0,0,246,248,3, + 4,2,0,247,246,1,0,0,0,247,248,1,0,0,0,248,249,1,0,0,0,249,253,5, + 146,0,0,250,254,3,6,3,0,251,254,3,24,12,0,252,254,3,4,2,0,253,250, + 1,0,0,0,253,251,1,0,0,0,253,252,1,0,0,0,253,254,1,0,0,0,254,255, + 1,0,0,0,255,256,5,145,0,0,256,258,3,10,5,0,257,259,5,146,0,0,258, + 257,1,0,0,0,258,259,1,0,0,0,259,19,1,0,0,0,260,261,5,31,0,0,261, + 262,5,126,0,0,262,263,5,50,0,0,263,266,3,154,77,0,264,265,5,112, + 0,0,265,267,3,154,77,0,266,264,1,0,0,0,266,267,1,0,0,0,267,268,1, + 0,0,0,268,269,5,40,0,0,269,270,3,4,2,0,270,271,5,145,0,0,271,273, + 3,10,5,0,272,274,5,146,0,0,273,272,1,0,0,0,273,274,1,0,0,0,274,21, + 1,0,0,0,275,276,5,29,0,0,276,277,3,154,77,0,277,279,5,126,0,0,278, + 280,3,8,4,0,279,278,1,0,0,0,279,280,1,0,0,0,280,281,1,0,0,0,281, + 282,5,145,0,0,282,283,3,30,15,0,283,23,1,0,0,0,284,285,3,4,2,0,285, + 286,5,111,0,0,286,287,5,118,0,0,287,288,3,4,2,0,288,25,1,0,0,0,289, + 291,3,4,2,0,290,292,5,146,0,0,291,290,1,0,0,0,291,292,1,0,0,0,292, + 27,1,0,0,0,293,294,5,146,0,0,294,29,1,0,0,0,295,299,5,124,0,0,296, + 298,3,2,1,0,297,296,1,0,0,0,298,301,1,0,0,0,299,297,1,0,0,0,299, + 300,1,0,0,0,300,302,1,0,0,0,301,299,1,0,0,0,302,303,5,143,0,0,303, + 31,1,0,0,0,304,305,3,4,2,0,305,306,5,111,0,0,306,307,3,4,2,0,307, + 33,1,0,0,0,308,313,3,32,16,0,309,310,5,112,0,0,310,312,3,32,16,0, + 311,309,1,0,0,0,312,315,1,0,0,0,313,311,1,0,0,0,313,314,1,0,0,0, + 314,317,1,0,0,0,315,313,1,0,0,0,316,318,5,112,0,0,317,316,1,0,0, + 0,317,318,1,0,0,0,318,35,1,0,0,0,319,323,3,38,19,0,320,323,3,42, + 21,0,321,323,3,118,59,0,322,319,1,0,0,0,322,320,1,0,0,0,322,321, + 1,0,0,0,323,324,1,0,0,0,324,325,5,0,0,1,325,37,1,0,0,0,326,332,3, + 40,20,0,327,328,5,91,0,0,328,329,5,1,0,0,329,331,3,40,20,0,330,327, + 1,0,0,0,331,334,1,0,0,0,332,330,1,0,0,0,332,333,1,0,0,0,333,39,1, + 0,0,0,334,332,1,0,0,0,335,342,3,42,21,0,336,337,5,126,0,0,337,338, + 3,38,19,0,338,339,5,145,0,0,339,342,1,0,0,0,340,342,3,158,79,0,341, + 335,1,0,0,0,341,336,1,0,0,0,341,340,1,0,0,0,342,41,1,0,0,0,343,345, + 3,44,22,0,344,343,1,0,0,0,344,345,1,0,0,0,345,346,1,0,0,0,346,348, + 5,77,0,0,347,349,5,23,0,0,348,347,1,0,0,0,348,349,1,0,0,0,349,351, + 1,0,0,0,350,352,3,46,23,0,351,350,1,0,0,0,351,352,1,0,0,0,352,353, + 1,0,0,0,353,355,3,108,54,0,354,356,3,48,24,0,355,354,1,0,0,0,355, + 356,1,0,0,0,356,358,1,0,0,0,357,359,3,50,25,0,358,357,1,0,0,0,358, + 359,1,0,0,0,359,361,1,0,0,0,360,362,3,54,27,0,361,360,1,0,0,0,361, + 362,1,0,0,0,362,364,1,0,0,0,363,365,3,56,28,0,364,363,1,0,0,0,364, + 365,1,0,0,0,365,367,1,0,0,0,366,368,3,58,29,0,367,366,1,0,0,0,367, + 368,1,0,0,0,368,371,1,0,0,0,369,370,5,98,0,0,370,372,7,0,0,0,371, + 369,1,0,0,0,371,372,1,0,0,0,372,375,1,0,0,0,373,374,5,98,0,0,374, + 376,5,86,0,0,375,373,1,0,0,0,375,376,1,0,0,0,376,378,1,0,0,0,377, + 379,3,60,30,0,378,377,1,0,0,0,378,379,1,0,0,0,379,381,1,0,0,0,380, + 382,3,52,26,0,381,380,1,0,0,0,381,382,1,0,0,0,382,384,1,0,0,0,383, + 385,3,62,31,0,384,383,1,0,0,0,384,385,1,0,0,0,385,388,1,0,0,0,386, + 389,3,66,33,0,387,389,3,68,34,0,388,386,1,0,0,0,388,387,1,0,0,0, + 388,389,1,0,0,0,389,391,1,0,0,0,390,392,3,70,35,0,391,390,1,0,0, + 0,391,392,1,0,0,0,392,43,1,0,0,0,393,394,5,98,0,0,394,395,3,122, + 61,0,395,45,1,0,0,0,396,397,5,85,0,0,397,400,5,104,0,0,398,399,5, + 98,0,0,399,401,5,82,0,0,400,398,1,0,0,0,400,401,1,0,0,0,401,47,1, + 0,0,0,402,403,5,32,0,0,403,404,3,72,36,0,404,49,1,0,0,0,405,407, + 7,1,0,0,406,405,1,0,0,0,406,407,1,0,0,0,407,408,1,0,0,0,408,409, + 5,5,0,0,409,410,5,45,0,0,410,411,3,108,54,0,411,51,1,0,0,0,412,413, + 5,97,0,0,413,414,3,154,77,0,414,415,5,6,0,0,415,416,5,126,0,0,416, + 417,3,92,46,0,417,427,5,145,0,0,418,419,5,112,0,0,419,420,3,154, + 77,0,420,421,5,6,0,0,421,422,5,126,0,0,422,423,3,92,46,0,423,424, + 5,145,0,0,424,426,1,0,0,0,425,418,1,0,0,0,426,429,1,0,0,0,427,425, + 1,0,0,0,427,428,1,0,0,0,428,53,1,0,0,0,429,427,1,0,0,0,430,431,5, + 67,0,0,431,432,3,110,55,0,432,55,1,0,0,0,433,434,5,95,0,0,434,435, + 3,110,55,0,435,57,1,0,0,0,436,437,5,34,0,0,437,444,5,11,0,0,438, + 439,7,0,0,0,439,440,5,126,0,0,440,441,3,108,54,0,441,442,5,145,0, + 0,442,445,1,0,0,0,443,445,3,108,54,0,444,438,1,0,0,0,444,443,1,0, + 0,0,445,59,1,0,0,0,446,447,5,35,0,0,447,448,3,110,55,0,448,61,1, + 0,0,0,449,450,5,62,0,0,450,451,5,11,0,0,451,452,3,82,41,0,452,63, + 1,0,0,0,453,454,5,62,0,0,454,455,5,11,0,0,455,456,3,108,54,0,456, + 65,1,0,0,0,457,458,5,52,0,0,458,461,3,110,55,0,459,460,5,112,0,0, + 460,462,3,110,55,0,461,459,1,0,0,0,461,462,1,0,0,0,462,467,1,0,0, + 0,463,464,5,98,0,0,464,468,5,82,0,0,465,466,5,11,0,0,466,468,3,108, + 54,0,467,463,1,0,0,0,467,465,1,0,0,0,467,468,1,0,0,0,468,487,1,0, + 0,0,469,470,5,52,0,0,470,473,3,110,55,0,471,472,5,98,0,0,472,474, + 5,82,0,0,473,471,1,0,0,0,473,474,1,0,0,0,474,475,1,0,0,0,475,476, + 5,59,0,0,476,477,3,110,55,0,477,487,1,0,0,0,478,479,5,52,0,0,479, + 480,3,110,55,0,480,481,5,59,0,0,481,484,3,110,55,0,482,483,5,11, + 0,0,483,485,3,108,54,0,484,482,1,0,0,0,484,485,1,0,0,0,485,487,1, + 0,0,0,486,457,1,0,0,0,486,469,1,0,0,0,486,478,1,0,0,0,487,67,1,0, + 0,0,488,489,5,59,0,0,489,490,3,110,55,0,490,69,1,0,0,0,491,492,5, + 79,0,0,492,493,3,88,44,0,493,71,1,0,0,0,494,495,6,36,-1,0,495,497, + 3,130,65,0,496,498,5,27,0,0,497,496,1,0,0,0,497,498,1,0,0,0,498, + 500,1,0,0,0,499,501,3,80,40,0,500,499,1,0,0,0,500,501,1,0,0,0,501, + 507,1,0,0,0,502,503,5,126,0,0,503,504,3,72,36,0,504,505,5,145,0, + 0,505,507,1,0,0,0,506,494,1,0,0,0,506,502,1,0,0,0,507,522,1,0,0, + 0,508,509,10,3,0,0,509,510,3,76,38,0,510,511,3,72,36,4,511,521,1, + 0,0,0,512,514,10,4,0,0,513,515,3,74,37,0,514,513,1,0,0,0,514,515, + 1,0,0,0,515,516,1,0,0,0,516,517,5,45,0,0,517,518,3,72,36,0,518,519, + 3,78,39,0,519,521,1,0,0,0,520,508,1,0,0,0,520,512,1,0,0,0,521,524, + 1,0,0,0,522,520,1,0,0,0,522,523,1,0,0,0,523,73,1,0,0,0,524,522,1, + 0,0,0,525,527,7,2,0,0,526,525,1,0,0,0,526,527,1,0,0,0,527,528,1, + 0,0,0,528,535,5,42,0,0,529,531,5,42,0,0,530,532,7,2,0,0,531,530, + 1,0,0,0,531,532,1,0,0,0,532,535,1,0,0,0,533,535,7,2,0,0,534,526, + 1,0,0,0,534,529,1,0,0,0,534,533,1,0,0,0,535,569,1,0,0,0,536,538, + 7,3,0,0,537,536,1,0,0,0,537,538,1,0,0,0,538,539,1,0,0,0,539,541, + 7,4,0,0,540,542,5,63,0,0,541,540,1,0,0,0,541,542,1,0,0,0,542,551, + 1,0,0,0,543,545,7,4,0,0,544,546,5,63,0,0,545,544,1,0,0,0,545,546, + 1,0,0,0,546,548,1,0,0,0,547,549,7,3,0,0,548,547,1,0,0,0,548,549, + 1,0,0,0,549,551,1,0,0,0,550,537,1,0,0,0,550,543,1,0,0,0,551,569, + 1,0,0,0,552,554,7,5,0,0,553,552,1,0,0,0,553,554,1,0,0,0,554,555, + 1,0,0,0,555,557,5,33,0,0,556,558,5,63,0,0,557,556,1,0,0,0,557,558, + 1,0,0,0,558,567,1,0,0,0,559,561,5,33,0,0,560,562,5,63,0,0,561,560, + 1,0,0,0,561,562,1,0,0,0,562,564,1,0,0,0,563,565,7,5,0,0,564,563, + 1,0,0,0,564,565,1,0,0,0,565,567,1,0,0,0,566,553,1,0,0,0,566,559, + 1,0,0,0,567,569,1,0,0,0,568,534,1,0,0,0,568,550,1,0,0,0,568,566, + 1,0,0,0,569,75,1,0,0,0,570,571,5,16,0,0,571,574,5,45,0,0,572,574, + 5,112,0,0,573,570,1,0,0,0,573,572,1,0,0,0,574,77,1,0,0,0,575,576, + 5,60,0,0,576,585,3,108,54,0,577,578,5,92,0,0,578,579,5,126,0,0,579, + 580,3,108,54,0,580,581,5,145,0,0,581,585,1,0,0,0,582,583,5,92,0, + 0,583,585,3,108,54,0,584,575,1,0,0,0,584,577,1,0,0,0,584,582,1,0, + 0,0,585,79,1,0,0,0,586,587,5,75,0,0,587,590,3,86,43,0,588,589,5, + 59,0,0,589,591,3,86,43,0,590,588,1,0,0,0,590,591,1,0,0,0,591,81, + 1,0,0,0,592,597,3,84,42,0,593,594,5,112,0,0,594,596,3,84,42,0,595, + 593,1,0,0,0,596,599,1,0,0,0,597,595,1,0,0,0,597,598,1,0,0,0,598, + 83,1,0,0,0,599,597,1,0,0,0,600,602,3,110,55,0,601,603,7,6,0,0,602, + 601,1,0,0,0,602,603,1,0,0,0,603,606,1,0,0,0,604,605,5,58,0,0,605, + 607,7,7,0,0,606,604,1,0,0,0,606,607,1,0,0,0,607,610,1,0,0,0,608, + 609,5,15,0,0,609,611,5,106,0,0,610,608,1,0,0,0,610,611,1,0,0,0,611, + 85,1,0,0,0,612,619,3,158,79,0,613,616,3,142,71,0,614,615,5,147,0, + 0,615,617,3,142,71,0,616,614,1,0,0,0,616,617,1,0,0,0,617,619,1,0, + 0,0,618,612,1,0,0,0,618,613,1,0,0,0,619,87,1,0,0,0,620,625,3,90, + 45,0,621,622,5,112,0,0,622,624,3,90,45,0,623,621,1,0,0,0,624,627, + 1,0,0,0,625,623,1,0,0,0,625,626,1,0,0,0,626,89,1,0,0,0,627,625,1, + 0,0,0,628,629,3,154,77,0,629,630,5,118,0,0,630,631,3,144,72,0,631, + 91,1,0,0,0,632,634,3,94,47,0,633,632,1,0,0,0,633,634,1,0,0,0,634, + 636,1,0,0,0,635,637,3,96,48,0,636,635,1,0,0,0,636,637,1,0,0,0,637, + 639,1,0,0,0,638,640,3,98,49,0,639,638,1,0,0,0,639,640,1,0,0,0,640, + 93,1,0,0,0,641,642,5,65,0,0,642,643,5,11,0,0,643,644,3,108,54,0, + 644,95,1,0,0,0,645,646,5,62,0,0,646,647,5,11,0,0,647,648,3,82,41, + 0,648,97,1,0,0,0,649,650,7,8,0,0,650,651,3,100,50,0,651,99,1,0,0, + 0,652,659,3,102,51,0,653,654,5,9,0,0,654,655,3,102,51,0,655,656, + 5,2,0,0,656,657,3,102,51,0,657,659,1,0,0,0,658,652,1,0,0,0,658,653, + 1,0,0,0,659,101,1,0,0,0,660,661,5,18,0,0,661,673,5,73,0,0,662,663, + 5,90,0,0,663,673,5,66,0,0,664,665,5,90,0,0,665,673,5,30,0,0,666, + 667,3,142,71,0,667,668,5,66,0,0,668,673,1,0,0,0,669,670,3,142,71, + 0,670,671,5,30,0,0,671,673,1,0,0,0,672,660,1,0,0,0,672,662,1,0,0, + 0,672,664,1,0,0,0,672,666,1,0,0,0,672,669,1,0,0,0,673,103,1,0,0, + 0,674,675,3,110,55,0,675,676,5,0,0,1,676,105,1,0,0,0,677,734,3,154, + 77,0,678,679,3,154,77,0,679,680,5,126,0,0,680,681,3,154,77,0,681, + 688,3,106,53,0,682,683,5,112,0,0,683,684,3,154,77,0,684,685,3,106, + 53,0,685,687,1,0,0,0,686,682,1,0,0,0,687,690,1,0,0,0,688,686,1,0, + 0,0,688,689,1,0,0,0,689,692,1,0,0,0,690,688,1,0,0,0,691,693,5,112, + 0,0,692,691,1,0,0,0,692,693,1,0,0,0,693,694,1,0,0,0,694,695,5,145, + 0,0,695,734,1,0,0,0,696,697,3,154,77,0,697,698,5,126,0,0,698,703, + 3,156,78,0,699,700,5,112,0,0,700,702,3,156,78,0,701,699,1,0,0,0, + 702,705,1,0,0,0,703,701,1,0,0,0,703,704,1,0,0,0,704,707,1,0,0,0, + 705,703,1,0,0,0,706,708,5,112,0,0,707,706,1,0,0,0,707,708,1,0,0, + 0,708,709,1,0,0,0,709,710,5,145,0,0,710,734,1,0,0,0,711,712,3,154, + 77,0,712,713,5,126,0,0,713,718,3,106,53,0,714,715,5,112,0,0,715, + 717,3,106,53,0,716,714,1,0,0,0,717,720,1,0,0,0,718,716,1,0,0,0,718, + 719,1,0,0,0,719,722,1,0,0,0,720,718,1,0,0,0,721,723,5,112,0,0,722, + 721,1,0,0,0,722,723,1,0,0,0,723,724,1,0,0,0,724,725,5,145,0,0,725, + 734,1,0,0,0,726,727,3,154,77,0,727,729,5,126,0,0,728,730,3,108,54, + 0,729,728,1,0,0,0,729,730,1,0,0,0,730,731,1,0,0,0,731,732,5,145, + 0,0,732,734,1,0,0,0,733,677,1,0,0,0,733,678,1,0,0,0,733,696,1,0, + 0,0,733,711,1,0,0,0,733,726,1,0,0,0,734,107,1,0,0,0,735,740,3,110, + 55,0,736,737,5,112,0,0,737,739,3,110,55,0,738,736,1,0,0,0,739,742, + 1,0,0,0,740,738,1,0,0,0,740,741,1,0,0,0,741,744,1,0,0,0,742,740, + 1,0,0,0,743,745,5,112,0,0,744,743,1,0,0,0,744,745,1,0,0,0,745,109, + 1,0,0,0,746,747,6,55,-1,0,747,749,5,12,0,0,748,750,3,110,55,0,749, + 748,1,0,0,0,749,750,1,0,0,0,750,756,1,0,0,0,751,752,5,94,0,0,752, + 753,3,110,55,0,753,754,5,81,0,0,754,755,3,110,55,0,755,757,1,0,0, + 0,756,751,1,0,0,0,757,758,1,0,0,0,758,756,1,0,0,0,758,759,1,0,0, + 0,759,762,1,0,0,0,760,761,5,24,0,0,761,763,3,110,55,0,762,760,1, + 0,0,0,762,763,1,0,0,0,763,764,1,0,0,0,764,765,5,25,0,0,765,896,1, + 0,0,0,766,767,5,13,0,0,767,768,5,126,0,0,768,769,3,110,55,0,769, + 770,5,6,0,0,770,771,3,106,53,0,771,772,5,145,0,0,772,896,1,0,0,0, + 773,774,5,19,0,0,774,896,5,106,0,0,775,776,5,43,0,0,776,777,3,110, + 55,0,777,778,3,146,73,0,778,896,1,0,0,0,779,780,5,80,0,0,780,781, + 5,126,0,0,781,782,3,110,55,0,782,783,5,32,0,0,783,786,3,110,55,0, + 784,785,5,31,0,0,785,787,3,110,55,0,786,784,1,0,0,0,786,787,1,0, + 0,0,787,788,1,0,0,0,788,789,5,145,0,0,789,896,1,0,0,0,790,791,5, + 83,0,0,791,896,5,106,0,0,792,793,5,88,0,0,793,794,5,126,0,0,794, + 795,7,9,0,0,795,796,3,160,80,0,796,797,5,32,0,0,797,798,3,110,55, + 0,798,799,5,145,0,0,799,896,1,0,0,0,800,801,3,154,77,0,801,803,5, + 126,0,0,802,804,3,108,54,0,803,802,1,0,0,0,803,804,1,0,0,0,804,805, + 1,0,0,0,805,806,5,145,0,0,806,815,1,0,0,0,807,809,5,126,0,0,808, + 810,5,23,0,0,809,808,1,0,0,0,809,810,1,0,0,0,810,812,1,0,0,0,811, + 813,3,112,56,0,812,811,1,0,0,0,812,813,1,0,0,0,813,814,1,0,0,0,814, + 816,5,145,0,0,815,807,1,0,0,0,815,816,1,0,0,0,816,817,1,0,0,0,817, + 818,5,64,0,0,818,819,5,126,0,0,819,820,3,92,46,0,820,821,5,145,0, + 0,821,896,1,0,0,0,822,823,3,154,77,0,823,825,5,126,0,0,824,826,3, + 108,54,0,825,824,1,0,0,0,825,826,1,0,0,0,826,827,1,0,0,0,827,828, + 5,145,0,0,828,837,1,0,0,0,829,831,5,126,0,0,830,832,5,23,0,0,831, + 830,1,0,0,0,831,832,1,0,0,0,832,834,1,0,0,0,833,835,3,112,56,0,834, + 833,1,0,0,0,834,835,1,0,0,0,835,836,1,0,0,0,836,838,5,145,0,0,837, + 829,1,0,0,0,837,838,1,0,0,0,838,839,1,0,0,0,839,840,5,64,0,0,840, + 841,3,154,77,0,841,896,1,0,0,0,842,848,3,154,77,0,843,845,5,126, + 0,0,844,846,3,108,54,0,845,844,1,0,0,0,845,846,1,0,0,0,846,847,1, + 0,0,0,847,849,5,145,0,0,848,843,1,0,0,0,848,849,1,0,0,0,849,850, + 1,0,0,0,850,852,5,126,0,0,851,853,5,23,0,0,852,851,1,0,0,0,852,853, + 1,0,0,0,853,855,1,0,0,0,854,856,3,112,56,0,855,854,1,0,0,0,855,856, + 1,0,0,0,856,857,1,0,0,0,857,858,5,145,0,0,858,896,1,0,0,0,859,896, + 3,118,59,0,860,896,3,162,81,0,861,896,3,144,72,0,862,863,5,114,0, + 0,863,896,3,110,55,19,864,865,5,56,0,0,865,896,3,110,55,13,866,867, + 3,134,67,0,867,868,5,116,0,0,868,870,1,0,0,0,869,866,1,0,0,0,869, + 870,1,0,0,0,870,871,1,0,0,0,871,896,5,108,0,0,872,873,5,126,0,0, + 873,874,3,38,19,0,874,875,5,145,0,0,875,896,1,0,0,0,876,877,5,126, + 0,0,877,878,3,110,55,0,878,879,5,145,0,0,879,896,1,0,0,0,880,881, + 5,126,0,0,881,882,3,108,54,0,882,883,5,145,0,0,883,896,1,0,0,0,884, + 886,5,125,0,0,885,887,3,108,54,0,886,885,1,0,0,0,886,887,1,0,0,0, + 887,888,1,0,0,0,888,896,5,144,0,0,889,891,5,124,0,0,890,892,3,34, + 17,0,891,890,1,0,0,0,891,892,1,0,0,0,892,893,1,0,0,0,893,896,5,143, + 0,0,894,896,3,126,63,0,895,746,1,0,0,0,895,766,1,0,0,0,895,773,1, + 0,0,0,895,775,1,0,0,0,895,779,1,0,0,0,895,790,1,0,0,0,895,792,1, + 0,0,0,895,800,1,0,0,0,895,822,1,0,0,0,895,842,1,0,0,0,895,859,1, + 0,0,0,895,860,1,0,0,0,895,861,1,0,0,0,895,862,1,0,0,0,895,864,1, + 0,0,0,895,869,1,0,0,0,895,872,1,0,0,0,895,876,1,0,0,0,895,880,1, + 0,0,0,895,884,1,0,0,0,895,889,1,0,0,0,895,894,1,0,0,0,896,1001,1, + 0,0,0,897,901,10,18,0,0,898,902,5,108,0,0,899,902,5,147,0,0,900, + 902,5,134,0,0,901,898,1,0,0,0,901,899,1,0,0,0,901,900,1,0,0,0,902, + 903,1,0,0,0,903,1000,3,110,55,19,904,908,10,17,0,0,905,909,5,135, + 0,0,906,909,5,114,0,0,907,909,5,113,0,0,908,905,1,0,0,0,908,906, + 1,0,0,0,908,907,1,0,0,0,909,910,1,0,0,0,910,1000,3,110,55,18,911, + 936,10,16,0,0,912,937,5,117,0,0,913,937,5,118,0,0,914,937,5,129, + 0,0,915,937,5,127,0,0,916,937,5,128,0,0,917,937,5,119,0,0,918,937, + 5,120,0,0,919,921,5,56,0,0,920,919,1,0,0,0,920,921,1,0,0,0,921,922, + 1,0,0,0,922,924,5,40,0,0,923,925,5,14,0,0,924,923,1,0,0,0,924,925, + 1,0,0,0,925,937,1,0,0,0,926,928,5,56,0,0,927,926,1,0,0,0,927,928, + 1,0,0,0,928,929,1,0,0,0,929,937,7,10,0,0,930,937,5,141,0,0,931,937, + 5,142,0,0,932,937,5,131,0,0,933,937,5,122,0,0,934,937,5,123,0,0, + 935,937,5,130,0,0,936,912,1,0,0,0,936,913,1,0,0,0,936,914,1,0,0, + 0,936,915,1,0,0,0,936,916,1,0,0,0,936,917,1,0,0,0,936,918,1,0,0, + 0,936,920,1,0,0,0,936,927,1,0,0,0,936,930,1,0,0,0,936,931,1,0,0, + 0,936,932,1,0,0,0,936,933,1,0,0,0,936,934,1,0,0,0,936,935,1,0,0, + 0,937,938,1,0,0,0,938,1000,3,110,55,17,939,940,10,14,0,0,940,941, + 5,133,0,0,941,1000,3,110,55,15,942,943,10,12,0,0,943,944,5,2,0,0, + 944,1000,3,110,55,13,945,946,10,11,0,0,946,947,5,61,0,0,947,1000, + 3,110,55,12,948,950,10,10,0,0,949,951,5,56,0,0,950,949,1,0,0,0,950, + 951,1,0,0,0,951,952,1,0,0,0,952,953,5,9,0,0,953,954,3,110,55,0,954, + 955,5,2,0,0,955,956,3,110,55,11,956,1000,1,0,0,0,957,958,10,9,0, + 0,958,959,5,136,0,0,959,960,3,110,55,0,960,961,5,111,0,0,961,962, + 3,110,55,9,962,1000,1,0,0,0,963,964,10,25,0,0,964,965,5,125,0,0, + 965,966,3,110,55,0,966,967,5,144,0,0,967,1000,1,0,0,0,968,969,10, + 24,0,0,969,970,5,116,0,0,970,1000,5,104,0,0,971,972,10,23,0,0,972, + 973,5,116,0,0,973,1000,3,154,77,0,974,975,10,22,0,0,975,976,5,132, + 0,0,976,977,5,125,0,0,977,978,3,110,55,0,978,979,5,144,0,0,979,1000, + 1,0,0,0,980,981,10,21,0,0,981,982,5,132,0,0,982,1000,5,104,0,0,983, + 984,10,20,0,0,984,985,5,132,0,0,985,1000,3,154,77,0,986,987,10,15, + 0,0,987,989,5,44,0,0,988,990,5,56,0,0,989,988,1,0,0,0,989,990,1, + 0,0,0,990,991,1,0,0,0,991,1000,5,57,0,0,992,997,10,8,0,0,993,994, + 5,6,0,0,994,998,3,154,77,0,995,996,5,6,0,0,996,998,5,106,0,0,997, + 993,1,0,0,0,997,995,1,0,0,0,998,1000,1,0,0,0,999,897,1,0,0,0,999, + 904,1,0,0,0,999,911,1,0,0,0,999,939,1,0,0,0,999,942,1,0,0,0,999, + 945,1,0,0,0,999,948,1,0,0,0,999,957,1,0,0,0,999,963,1,0,0,0,999, + 968,1,0,0,0,999,971,1,0,0,0,999,974,1,0,0,0,999,980,1,0,0,0,999, + 983,1,0,0,0,999,986,1,0,0,0,999,992,1,0,0,0,1000,1003,1,0,0,0,1001, + 999,1,0,0,0,1001,1002,1,0,0,0,1002,111,1,0,0,0,1003,1001,1,0,0,0, + 1004,1009,3,114,57,0,1005,1006,5,112,0,0,1006,1008,3,114,57,0,1007, + 1005,1,0,0,0,1008,1011,1,0,0,0,1009,1007,1,0,0,0,1009,1010,1,0,0, + 0,1010,1013,1,0,0,0,1011,1009,1,0,0,0,1012,1014,5,112,0,0,1013,1012, + 1,0,0,0,1013,1014,1,0,0,0,1014,113,1,0,0,0,1015,1018,3,116,58,0, + 1016,1018,3,110,55,0,1017,1015,1,0,0,0,1017,1016,1,0,0,0,1018,115, + 1,0,0,0,1019,1020,5,126,0,0,1020,1025,3,154,77,0,1021,1022,5,112, + 0,0,1022,1024,3,154,77,0,1023,1021,1,0,0,0,1024,1027,1,0,0,0,1025, + 1023,1,0,0,0,1025,1026,1,0,0,0,1026,1029,1,0,0,0,1027,1025,1,0,0, + 0,1028,1030,5,112,0,0,1029,1028,1,0,0,0,1029,1030,1,0,0,0,1030,1031, + 1,0,0,0,1031,1032,5,145,0,0,1032,1045,1,0,0,0,1033,1038,3,154,77, + 0,1034,1035,5,112,0,0,1035,1037,3,154,77,0,1036,1034,1,0,0,0,1037, + 1040,1,0,0,0,1038,1036,1,0,0,0,1038,1039,1,0,0,0,1039,1042,1,0,0, + 0,1040,1038,1,0,0,0,1041,1043,5,112,0,0,1042,1041,1,0,0,0,1042,1043, + 1,0,0,0,1043,1045,1,0,0,0,1044,1019,1,0,0,0,1044,1033,1,0,0,0,1045, + 1046,1,0,0,0,1046,1047,5,107,0,0,1047,1048,3,110,55,0,1048,117,1, + 0,0,0,1049,1050,5,128,0,0,1050,1054,3,154,77,0,1051,1053,3,120,60, + 0,1052,1051,1,0,0,0,1053,1056,1,0,0,0,1054,1052,1,0,0,0,1054,1055, + 1,0,0,0,1055,1057,1,0,0,0,1056,1054,1,0,0,0,1057,1058,5,147,0,0, + 1058,1059,5,120,0,0,1059,1078,1,0,0,0,1060,1061,5,128,0,0,1061,1065, + 3,154,77,0,1062,1064,3,120,60,0,1063,1062,1,0,0,0,1064,1067,1,0, + 0,0,1065,1063,1,0,0,0,1065,1066,1,0,0,0,1066,1068,1,0,0,0,1067,1065, + 1,0,0,0,1068,1070,5,120,0,0,1069,1071,3,118,59,0,1070,1069,1,0,0, + 0,1070,1071,1,0,0,0,1071,1072,1,0,0,0,1072,1073,5,128,0,0,1073,1074, + 5,147,0,0,1074,1075,3,154,77,0,1075,1076,5,120,0,0,1076,1078,1,0, + 0,0,1077,1049,1,0,0,0,1077,1060,1,0,0,0,1078,119,1,0,0,0,1079,1080, + 3,154,77,0,1080,1081,5,118,0,0,1081,1082,3,160,80,0,1082,1091,1, + 0,0,0,1083,1084,3,154,77,0,1084,1085,5,118,0,0,1085,1086,5,124,0, + 0,1086,1087,3,110,55,0,1087,1088,5,143,0,0,1088,1091,1,0,0,0,1089, + 1091,3,154,77,0,1090,1079,1,0,0,0,1090,1083,1,0,0,0,1090,1089,1, + 0,0,0,1091,121,1,0,0,0,1092,1097,3,124,62,0,1093,1094,5,112,0,0, + 1094,1096,3,124,62,0,1095,1093,1,0,0,0,1096,1099,1,0,0,0,1097,1095, + 1,0,0,0,1097,1098,1,0,0,0,1098,1101,1,0,0,0,1099,1097,1,0,0,0,1100, + 1102,5,112,0,0,1101,1100,1,0,0,0,1101,1102,1,0,0,0,1102,123,1,0, + 0,0,1103,1104,3,154,77,0,1104,1105,5,6,0,0,1105,1106,5,126,0,0,1106, + 1107,3,38,19,0,1107,1108,5,145,0,0,1108,1114,1,0,0,0,1109,1110,3, + 110,55,0,1110,1111,5,6,0,0,1111,1112,3,154,77,0,1112,1114,1,0,0, + 0,1113,1103,1,0,0,0,1113,1109,1,0,0,0,1114,125,1,0,0,0,1115,1123, + 3,158,79,0,1116,1117,3,134,67,0,1117,1118,5,116,0,0,1118,1120,1, + 0,0,0,1119,1116,1,0,0,0,1119,1120,1,0,0,0,1120,1121,1,0,0,0,1121, + 1123,3,128,64,0,1122,1115,1,0,0,0,1122,1119,1,0,0,0,1123,127,1,0, + 0,0,1124,1129,3,154,77,0,1125,1126,5,116,0,0,1126,1128,3,154,77, + 0,1127,1125,1,0,0,0,1128,1131,1,0,0,0,1129,1127,1,0,0,0,1129,1130, + 1,0,0,0,1130,129,1,0,0,0,1131,1129,1,0,0,0,1132,1133,6,65,-1,0,1133, + 1142,3,134,67,0,1134,1142,3,132,66,0,1135,1136,5,126,0,0,1136,1137, + 3,38,19,0,1137,1138,5,145,0,0,1138,1142,1,0,0,0,1139,1142,3,118, + 59,0,1140,1142,3,158,79,0,1141,1132,1,0,0,0,1141,1134,1,0,0,0,1141, + 1135,1,0,0,0,1141,1139,1,0,0,0,1141,1140,1,0,0,0,1142,1151,1,0,0, + 0,1143,1147,10,3,0,0,1144,1148,3,152,76,0,1145,1146,5,6,0,0,1146, + 1148,3,154,77,0,1147,1144,1,0,0,0,1147,1145,1,0,0,0,1148,1150,1, + 0,0,0,1149,1143,1,0,0,0,1150,1153,1,0,0,0,1151,1149,1,0,0,0,1151, + 1152,1,0,0,0,1152,131,1,0,0,0,1153,1151,1,0,0,0,1154,1155,3,154, + 77,0,1155,1157,5,126,0,0,1156,1158,3,136,68,0,1157,1156,1,0,0,0, + 1157,1158,1,0,0,0,1158,1159,1,0,0,0,1159,1160,5,145,0,0,1160,133, + 1,0,0,0,1161,1162,3,138,69,0,1162,1163,5,116,0,0,1163,1165,1,0,0, + 0,1164,1161,1,0,0,0,1164,1165,1,0,0,0,1165,1166,1,0,0,0,1166,1167, + 3,154,77,0,1167,135,1,0,0,0,1168,1173,3,110,55,0,1169,1170,5,112, + 0,0,1170,1172,3,110,55,0,1171,1169,1,0,0,0,1172,1175,1,0,0,0,1173, + 1171,1,0,0,0,1173,1174,1,0,0,0,1174,1177,1,0,0,0,1175,1173,1,0,0, + 0,1176,1178,5,112,0,0,1177,1176,1,0,0,0,1177,1178,1,0,0,0,1178,137, + 1,0,0,0,1179,1180,3,154,77,0,1180,139,1,0,0,0,1181,1190,5,102,0, + 0,1182,1183,5,116,0,0,1183,1190,7,11,0,0,1184,1185,5,104,0,0,1185, + 1187,5,116,0,0,1186,1188,7,11,0,0,1187,1186,1,0,0,0,1187,1188,1, + 0,0,0,1188,1190,1,0,0,0,1189,1181,1,0,0,0,1189,1182,1,0,0,0,1189, + 1184,1,0,0,0,1190,141,1,0,0,0,1191,1193,7,12,0,0,1192,1191,1,0,0, + 0,1192,1193,1,0,0,0,1193,1200,1,0,0,0,1194,1201,3,140,70,0,1195, + 1201,5,103,0,0,1196,1201,5,104,0,0,1197,1201,5,105,0,0,1198,1201, + 5,41,0,0,1199,1201,5,55,0,0,1200,1194,1,0,0,0,1200,1195,1,0,0,0, + 1200,1196,1,0,0,0,1200,1197,1,0,0,0,1200,1198,1,0,0,0,1200,1199, + 1,0,0,0,1201,143,1,0,0,0,1202,1206,3,142,71,0,1203,1206,5,106,0, + 0,1204,1206,5,57,0,0,1205,1202,1,0,0,0,1205,1203,1,0,0,0,1205,1204, + 1,0,0,0,1206,145,1,0,0,0,1207,1208,7,13,0,0,1208,147,1,0,0,0,1209, + 1210,7,14,0,0,1210,149,1,0,0,0,1211,1212,7,15,0,0,1212,151,1,0,0, + 0,1213,1216,5,101,0,0,1214,1216,3,150,75,0,1215,1213,1,0,0,0,1215, + 1214,1,0,0,0,1216,153,1,0,0,0,1217,1221,5,101,0,0,1218,1221,3,146, + 73,0,1219,1221,3,148,74,0,1220,1217,1,0,0,0,1220,1218,1,0,0,0,1220, + 1219,1,0,0,0,1221,155,1,0,0,0,1222,1223,3,160,80,0,1223,1224,5,118, + 0,0,1224,1225,3,142,71,0,1225,157,1,0,0,0,1226,1227,5,124,0,0,1227, + 1228,3,154,77,0,1228,1229,5,143,0,0,1229,159,1,0,0,0,1230,1233,5, + 106,0,0,1231,1233,3,162,81,0,1232,1230,1,0,0,0,1232,1231,1,0,0,0, + 1233,161,1,0,0,0,1234,1238,5,138,0,0,1235,1237,3,164,82,0,1236,1235, + 1,0,0,0,1237,1240,1,0,0,0,1238,1236,1,0,0,0,1238,1239,1,0,0,0,1239, + 1241,1,0,0,0,1240,1238,1,0,0,0,1241,1242,5,140,0,0,1242,163,1,0, + 0,0,1243,1244,5,153,0,0,1244,1245,3,110,55,0,1245,1246,5,143,0,0, + 1246,1249,1,0,0,0,1247,1249,5,152,0,0,1248,1243,1,0,0,0,1248,1247, + 1,0,0,0,1249,165,1,0,0,0,1250,1254,5,139,0,0,1251,1253,3,168,84, + 0,1252,1251,1,0,0,0,1253,1256,1,0,0,0,1254,1252,1,0,0,0,1254,1255, + 1,0,0,0,1255,1257,1,0,0,0,1256,1254,1,0,0,0,1257,1258,5,0,0,1,1258, + 167,1,0,0,0,1259,1260,5,155,0,0,1260,1261,3,110,55,0,1261,1262,5, + 143,0,0,1262,1265,1,0,0,0,1263,1265,5,154,0,0,1264,1259,1,0,0,0, + 1264,1263,1,0,0,0,1265,169,1,0,0,0,162,173,180,189,196,200,212,216, + 219,228,236,243,247,253,258,266,273,279,291,299,313,317,322,332, + 341,344,348,351,355,358,361,364,367,371,375,378,381,384,388,391, + 400,406,427,444,461,467,473,484,486,497,500,506,514,520,522,526, + 531,534,537,541,545,548,550,553,557,561,564,566,568,573,584,590, + 597,602,606,610,616,618,625,633,636,639,658,672,688,692,703,707, + 718,722,729,733,740,744,749,758,762,786,803,809,812,815,825,831, + 834,837,845,848,852,855,869,886,891,895,901,908,920,924,927,936, + 950,989,997,999,1001,1009,1013,1017,1025,1029,1038,1042,1044,1054, + 1065,1070,1077,1090,1097,1101,1113,1119,1122,1129,1141,1147,1151, + 1157,1164,1173,1177,1187,1189,1192,1200,1205,1215,1220,1232,1238, + 1248,1254,1264 ] class HogQLParser ( Parser ): @@ -599,96 +606,97 @@ class HogQLParser ( Parser ): RULE_ifStmt = 7 RULE_whileStmt = 8 RULE_forStmt = 9 - RULE_funcStmt = 10 - RULE_varAssignment = 11 - RULE_exprStmt = 12 - RULE_emptyStmt = 13 - RULE_block = 14 - RULE_kvPair = 15 - RULE_kvPairList = 16 - RULE_select = 17 - RULE_selectUnionStmt = 18 - RULE_selectStmtWithParens = 19 - RULE_selectStmt = 20 - RULE_withClause = 21 - RULE_topClause = 22 - RULE_fromClause = 23 - RULE_arrayJoinClause = 24 - RULE_windowClause = 25 - RULE_prewhereClause = 26 - RULE_whereClause = 27 - RULE_groupByClause = 28 - RULE_havingClause = 29 - RULE_orderByClause = 30 - RULE_projectionOrderByClause = 31 - RULE_limitAndOffsetClause = 32 - RULE_offsetOnlyClause = 33 - RULE_settingsClause = 34 - RULE_joinExpr = 35 - RULE_joinOp = 36 - RULE_joinOpCross = 37 - RULE_joinConstraintClause = 38 - RULE_sampleClause = 39 - RULE_orderExprList = 40 - RULE_orderExpr = 41 - RULE_ratioExpr = 42 - RULE_settingExprList = 43 - RULE_settingExpr = 44 - RULE_windowExpr = 45 - RULE_winPartitionByClause = 46 - RULE_winOrderByClause = 47 - RULE_winFrameClause = 48 - RULE_winFrameExtend = 49 - RULE_winFrameBound = 50 - RULE_expr = 51 - RULE_columnTypeExpr = 52 - RULE_columnExprList = 53 - RULE_columnExpr = 54 - RULE_columnArgList = 55 - RULE_columnArgExpr = 56 - RULE_columnLambdaExpr = 57 - RULE_hogqlxTagElement = 58 - RULE_hogqlxTagAttribute = 59 - RULE_withExprList = 60 - RULE_withExpr = 61 - RULE_columnIdentifier = 62 - RULE_nestedIdentifier = 63 - RULE_tableExpr = 64 - RULE_tableFunctionExpr = 65 - RULE_tableIdentifier = 66 - RULE_tableArgList = 67 - RULE_databaseIdentifier = 68 - RULE_floatingLiteral = 69 - RULE_numberLiteral = 70 - RULE_literal = 71 - RULE_interval = 72 - RULE_keyword = 73 - RULE_keywordForAlias = 74 - RULE_alias = 75 - RULE_identifier = 76 - RULE_enumValue = 77 - RULE_placeholder = 78 - RULE_string = 79 - RULE_templateString = 80 - RULE_stringContents = 81 - RULE_fullTemplateString = 82 - RULE_stringContentsFull = 83 + RULE_forInStmt = 10 + RULE_funcStmt = 11 + RULE_varAssignment = 12 + RULE_exprStmt = 13 + RULE_emptyStmt = 14 + RULE_block = 15 + RULE_kvPair = 16 + RULE_kvPairList = 17 + RULE_select = 18 + RULE_selectUnionStmt = 19 + RULE_selectStmtWithParens = 20 + RULE_selectStmt = 21 + RULE_withClause = 22 + RULE_topClause = 23 + RULE_fromClause = 24 + RULE_arrayJoinClause = 25 + RULE_windowClause = 26 + RULE_prewhereClause = 27 + RULE_whereClause = 28 + RULE_groupByClause = 29 + RULE_havingClause = 30 + RULE_orderByClause = 31 + RULE_projectionOrderByClause = 32 + RULE_limitAndOffsetClause = 33 + RULE_offsetOnlyClause = 34 + RULE_settingsClause = 35 + RULE_joinExpr = 36 + RULE_joinOp = 37 + RULE_joinOpCross = 38 + RULE_joinConstraintClause = 39 + RULE_sampleClause = 40 + RULE_orderExprList = 41 + RULE_orderExpr = 42 + RULE_ratioExpr = 43 + RULE_settingExprList = 44 + RULE_settingExpr = 45 + RULE_windowExpr = 46 + RULE_winPartitionByClause = 47 + RULE_winOrderByClause = 48 + RULE_winFrameClause = 49 + RULE_winFrameExtend = 50 + RULE_winFrameBound = 51 + RULE_expr = 52 + RULE_columnTypeExpr = 53 + RULE_columnExprList = 54 + RULE_columnExpr = 55 + RULE_columnArgList = 56 + RULE_columnArgExpr = 57 + RULE_columnLambdaExpr = 58 + RULE_hogqlxTagElement = 59 + RULE_hogqlxTagAttribute = 60 + RULE_withExprList = 61 + RULE_withExpr = 62 + RULE_columnIdentifier = 63 + RULE_nestedIdentifier = 64 + RULE_tableExpr = 65 + RULE_tableFunctionExpr = 66 + RULE_tableIdentifier = 67 + RULE_tableArgList = 68 + RULE_databaseIdentifier = 69 + RULE_floatingLiteral = 70 + RULE_numberLiteral = 71 + RULE_literal = 72 + RULE_interval = 73 + RULE_keyword = 74 + RULE_keywordForAlias = 75 + RULE_alias = 76 + RULE_identifier = 77 + RULE_enumValue = 78 + RULE_placeholder = 79 + RULE_string = 80 + RULE_templateString = 81 + RULE_stringContents = 82 + RULE_fullTemplateString = 83 + RULE_stringContentsFull = 84 ruleNames = [ "program", "declaration", "expression", "varDecl", "identifierList", "statement", "returnStmt", "ifStmt", "whileStmt", "forStmt", - "funcStmt", "varAssignment", "exprStmt", "emptyStmt", - "block", "kvPair", "kvPairList", "select", "selectUnionStmt", - "selectStmtWithParens", "selectStmt", "withClause", "topClause", - "fromClause", "arrayJoinClause", "windowClause", "prewhereClause", - "whereClause", "groupByClause", "havingClause", "orderByClause", - "projectionOrderByClause", "limitAndOffsetClause", "offsetOnlyClause", - "settingsClause", "joinExpr", "joinOp", "joinOpCross", - "joinConstraintClause", "sampleClause", "orderExprList", - "orderExpr", "ratioExpr", "settingExprList", "settingExpr", - "windowExpr", "winPartitionByClause", "winOrderByClause", - "winFrameClause", "winFrameExtend", "winFrameBound", - "expr", "columnTypeExpr", "columnExprList", "columnExpr", - "columnArgList", "columnArgExpr", "columnLambdaExpr", + "forInStmt", "funcStmt", "varAssignment", "exprStmt", + "emptyStmt", "block", "kvPair", "kvPairList", "select", + "selectUnionStmt", "selectStmtWithParens", "selectStmt", + "withClause", "topClause", "fromClause", "arrayJoinClause", + "windowClause", "prewhereClause", "whereClause", "groupByClause", + "havingClause", "orderByClause", "projectionOrderByClause", + "limitAndOffsetClause", "offsetOnlyClause", "settingsClause", + "joinExpr", "joinOp", "joinOpCross", "joinConstraintClause", + "sampleClause", "orderExprList", "orderExpr", "ratioExpr", + "settingExprList", "settingExpr", "windowExpr", "winPartitionByClause", + "winOrderByClause", "winFrameClause", "winFrameExtend", + "winFrameBound", "expr", "columnTypeExpr", "columnExprList", + "columnExpr", "columnArgList", "columnArgExpr", "columnLambdaExpr", "hogqlxTagElement", "hogqlxTagAttribute", "withExprList", "withExpr", "columnIdentifier", "nestedIdentifier", "tableExpr", "tableFunctionExpr", "tableIdentifier", "tableArgList", @@ -900,17 +908,17 @@ def program(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 171 + self.state = 173 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -2) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106351341731839) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 263297) != 0): - self.state = 168 + self.state = 170 self.declaration() - self.state = 173 + self.state = 175 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 174 + self.state = 176 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -953,17 +961,17 @@ def declaration(self): localctx = HogQLParser.DeclarationContext(self, self._ctx, self.state) self.enterRule(localctx, 2, self.RULE_declaration) try: - self.state = 178 + self.state = 180 self._errHandler.sync(self) token = self._input.LA(1) if token in [50]: self.enterOuterAlt(localctx, 1) - self.state = 176 + self.state = 178 self.varDecl() pass elif token in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 101, 102, 103, 104, 105, 106, 108, 114, 116, 124, 125, 126, 128, 135, 138, 146]: self.enterOuterAlt(localctx, 2) - self.state = 177 + self.state = 179 self.statement() pass else: @@ -1007,7 +1015,7 @@ def expression(self): self.enterRule(localctx, 4, self.RULE_expression) try: self.enterOuterAlt(localctx, 1) - self.state = 180 + self.state = 182 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -1061,19 +1069,19 @@ def varDecl(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 182 + self.state = 184 self.match(HogQLParser.LET) - self.state = 183 + self.state = 185 self.identifier() - self.state = 187 + self.state = 189 self._errHandler.sync(self) _la = self._input.LA(1) if _la==111: - self.state = 184 + self.state = 186 self.match(HogQLParser.COLON) - self.state = 185 + self.state = 187 self.match(HogQLParser.EQ_SINGLE) - self.state = 186 + self.state = 188 self.expression() @@ -1125,26 +1133,26 @@ def identifierList(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 189 + self.state = 191 self.identifier() - self.state = 194 + self.state = 196 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,3,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 190 + self.state = 192 self.match(HogQLParser.COMMA) - self.state = 191 + self.state = 193 self.identifier() - self.state = 196 + self.state = 198 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,3,self._ctx) - self.state = 198 + self.state = 200 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 197 + self.state = 199 self.match(HogQLParser.COMMA) @@ -1176,6 +1184,10 @@ def whileStmt(self): return self.getTypedRuleContext(HogQLParser.WhileStmtContext,0) + def forInStmt(self): + return self.getTypedRuleContext(HogQLParser.ForInStmtContext,0) + + def forStmt(self): return self.getTypedRuleContext(HogQLParser.ForStmtContext,0) @@ -1217,60 +1229,66 @@ def statement(self): localctx = HogQLParser.StatementContext(self, self._ctx, self.state) self.enterRule(localctx, 10, self.RULE_statement) try: - self.state = 209 + self.state = 212 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,5,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 200 + self.state = 202 self.returnStmt() pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 201 + self.state = 203 self.ifStmt() pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 202 + self.state = 204 self.whileStmt() pass elif la_ == 4: self.enterOuterAlt(localctx, 4) - self.state = 203 - self.forStmt() + self.state = 205 + self.forInStmt() pass elif la_ == 5: self.enterOuterAlt(localctx, 5) - self.state = 204 - self.funcStmt() + self.state = 206 + self.forStmt() pass elif la_ == 6: self.enterOuterAlt(localctx, 6) - self.state = 205 - self.varAssignment() + self.state = 207 + self.funcStmt() pass elif la_ == 7: self.enterOuterAlt(localctx, 7) - self.state = 206 - self.block() + self.state = 208 + self.varAssignment() pass elif la_ == 8: self.enterOuterAlt(localctx, 8) - self.state = 207 - self.exprStmt() + self.state = 209 + self.block() pass elif la_ == 9: self.enterOuterAlt(localctx, 9) - self.state = 208 + self.state = 210 + self.exprStmt() + pass + + elif la_ == 10: + self.enterOuterAlt(localctx, 10) + self.state = 211 self.emptyStmt() pass @@ -1319,21 +1337,21 @@ def returnStmt(self): self.enterRule(localctx, 12, self.RULE_returnStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 211 + self.state = 214 self.match(HogQLParser.RETURN) - self.state = 213 + self.state = 216 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,6,self._ctx) if la_ == 1: - self.state = 212 + self.state = 215 self.expression() - self.state = 216 + self.state = 219 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,7,self._ctx) if la_ == 1: - self.state = 215 + self.state = 218 self.match(HogQLParser.SEMICOLON) @@ -1394,23 +1412,23 @@ def ifStmt(self): self.enterRule(localctx, 14, self.RULE_ifStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 218 + self.state = 221 self.match(HogQLParser.IF) - self.state = 219 + self.state = 222 self.match(HogQLParser.LPAREN) - self.state = 220 + self.state = 223 self.expression() - self.state = 221 + self.state = 224 self.match(HogQLParser.RPAREN) - self.state = 222 - self.statement() self.state = 225 + self.statement() + self.state = 228 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,8,self._ctx) if la_ == 1: - self.state = 223 + self.state = 226 self.match(HogQLParser.ELSE) - self.state = 224 + self.state = 227 self.statement() @@ -1468,21 +1486,21 @@ def whileStmt(self): self.enterRule(localctx, 16, self.RULE_whileStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 227 + self.state = 230 self.match(HogQLParser.WHILE) - self.state = 228 + self.state = 231 self.match(HogQLParser.LPAREN) - self.state = 229 + self.state = 232 self.expression() - self.state = 230 + self.state = 233 self.match(HogQLParser.RPAREN) - self.state = 231 + self.state = 234 self.statement() - self.state = 233 + self.state = 236 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,9,self._ctx) if la_ == 1: - self.state = 232 + self.state = 235 self.match(HogQLParser.SEMICOLON) @@ -1568,63 +1586,168 @@ def forStmt(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 235 + self.state = 238 self.match(HogQLParser.FOR) - self.state = 236 + self.state = 239 self.match(HogQLParser.LPAREN) - self.state = 240 + self.state = 243 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,10,self._ctx) if la_ == 1: - self.state = 237 + self.state = 240 localctx.initializerVarDeclr = self.varDecl() elif la_ == 2: - self.state = 238 + self.state = 241 localctx.initializerVarAssignment = self.varAssignment() elif la_ == 3: - self.state = 239 + self.state = 242 localctx.initializerExpression = self.expression() - self.state = 242 + self.state = 245 self.match(HogQLParser.SEMICOLON) - self.state = 244 + self.state = 247 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 243 + self.state = 246 localctx.condition = self.expression() - self.state = 246 + self.state = 249 self.match(HogQLParser.SEMICOLON) - self.state = 250 + self.state = 253 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,12,self._ctx) if la_ == 1: - self.state = 247 + self.state = 250 localctx.incrementVarDeclr = self.varDecl() elif la_ == 2: - self.state = 248 + self.state = 251 localctx.incrementVarAssignment = self.varAssignment() elif la_ == 3: - self.state = 249 + self.state = 252 localctx.incrementExpression = self.expression() - self.state = 252 + self.state = 255 self.match(HogQLParser.RPAREN) - self.state = 253 + self.state = 256 self.statement() - self.state = 255 + self.state = 258 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,13,self._ctx) if la_ == 1: - self.state = 254 + self.state = 257 + self.match(HogQLParser.SEMICOLON) + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + + class ForInStmtContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def FOR(self): + return self.getToken(HogQLParser.FOR, 0) + + def LPAREN(self): + return self.getToken(HogQLParser.LPAREN, 0) + + def LET(self): + return self.getToken(HogQLParser.LET, 0) + + def identifier(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(HogQLParser.IdentifierContext) + else: + return self.getTypedRuleContext(HogQLParser.IdentifierContext,i) + + + def IN(self): + return self.getToken(HogQLParser.IN, 0) + + def expression(self): + return self.getTypedRuleContext(HogQLParser.ExpressionContext,0) + + + def RPAREN(self): + return self.getToken(HogQLParser.RPAREN, 0) + + def statement(self): + return self.getTypedRuleContext(HogQLParser.StatementContext,0) + + + def COMMA(self): + return self.getToken(HogQLParser.COMMA, 0) + + def SEMICOLON(self): + return self.getToken(HogQLParser.SEMICOLON, 0) + + def getRuleIndex(self): + return HogQLParser.RULE_forInStmt + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitForInStmt" ): + return visitor.visitForInStmt(self) + else: + return visitor.visitChildren(self) + + + + + def forInStmt(self): + + localctx = HogQLParser.ForInStmtContext(self, self._ctx, self.state) + self.enterRule(localctx, 20, self.RULE_forInStmt) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 260 + self.match(HogQLParser.FOR) + self.state = 261 + self.match(HogQLParser.LPAREN) + self.state = 262 + self.match(HogQLParser.LET) + self.state = 263 + self.identifier() + self.state = 266 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la==112: + self.state = 264 + self.match(HogQLParser.COMMA) + self.state = 265 + self.identifier() + + + self.state = 268 + self.match(HogQLParser.IN) + self.state = 269 + self.expression() + self.state = 270 + self.match(HogQLParser.RPAREN) + self.state = 271 + self.statement() + self.state = 273 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input,15,self._ctx) + if la_ == 1: + self.state = 272 self.match(HogQLParser.SEMICOLON) @@ -1680,27 +1803,27 @@ def accept(self, visitor:ParseTreeVisitor): def funcStmt(self): localctx = HogQLParser.FuncStmtContext(self, self._ctx, self.state) - self.enterRule(localctx, 20, self.RULE_funcStmt) + self.enterRule(localctx, 22, self.RULE_funcStmt) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 257 + self.state = 275 self.match(HogQLParser.FN) - self.state = 258 + self.state = 276 self.identifier() - self.state = 259 + self.state = 277 self.match(HogQLParser.LPAREN) - self.state = 261 + self.state = 279 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -181272084561788930) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 201863462911) != 0): - self.state = 260 + self.state = 278 self.identifierList() - self.state = 263 + self.state = 281 self.match(HogQLParser.RPAREN) - self.state = 264 + self.state = 282 self.block() except RecognitionException as re: localctx.exception = re @@ -1746,16 +1869,16 @@ def accept(self, visitor:ParseTreeVisitor): def varAssignment(self): localctx = HogQLParser.VarAssignmentContext(self, self._ctx, self.state) - self.enterRule(localctx, 22, self.RULE_varAssignment) + self.enterRule(localctx, 24, self.RULE_varAssignment) try: self.enterOuterAlt(localctx, 1) - self.state = 266 + self.state = 284 self.expression() - self.state = 267 + self.state = 285 self.match(HogQLParser.COLON) - self.state = 268 + self.state = 286 self.match(HogQLParser.EQ_SINGLE) - self.state = 269 + self.state = 287 self.expression() except RecognitionException as re: localctx.exception = re @@ -1795,16 +1918,16 @@ def accept(self, visitor:ParseTreeVisitor): def exprStmt(self): localctx = HogQLParser.ExprStmtContext(self, self._ctx, self.state) - self.enterRule(localctx, 24, self.RULE_exprStmt) + self.enterRule(localctx, 26, self.RULE_exprStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 271 + self.state = 289 self.expression() - self.state = 273 + self.state = 291 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,15,self._ctx) + la_ = self._interp.adaptivePredict(self._input,17,self._ctx) if la_ == 1: - self.state = 272 + self.state = 290 self.match(HogQLParser.SEMICOLON) @@ -1842,10 +1965,10 @@ def accept(self, visitor:ParseTreeVisitor): def emptyStmt(self): localctx = HogQLParser.EmptyStmtContext(self, self._ctx, self.state) - self.enterRule(localctx, 26, self.RULE_emptyStmt) + self.enterRule(localctx, 28, self.RULE_emptyStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 275 + self.state = 293 self.match(HogQLParser.SEMICOLON) except RecognitionException as re: localctx.exception = re @@ -1891,23 +2014,23 @@ def accept(self, visitor:ParseTreeVisitor): def block(self): localctx = HogQLParser.BlockContext(self, self._ctx, self.state) - self.enterRule(localctx, 28, self.RULE_block) + self.enterRule(localctx, 30, self.RULE_block) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 277 + self.state = 295 self.match(HogQLParser.LBRACE) - self.state = 281 + self.state = 299 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -2) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106351341731839) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 263297) != 0): - self.state = 278 + self.state = 296 self.declaration() - self.state = 283 + self.state = 301 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 284 + self.state = 302 self.match(HogQLParser.RBRACE) except RecognitionException as re: localctx.exception = re @@ -1950,14 +2073,14 @@ def accept(self, visitor:ParseTreeVisitor): def kvPair(self): localctx = HogQLParser.KvPairContext(self, self._ctx, self.state) - self.enterRule(localctx, 30, self.RULE_kvPair) + self.enterRule(localctx, 32, self.RULE_kvPair) try: self.enterOuterAlt(localctx, 1) - self.state = 286 + self.state = 304 self.expression() - self.state = 287 + self.state = 305 self.match(HogQLParser.COLON) - self.state = 288 + self.state = 306 self.expression() except RecognitionException as re: localctx.exception = re @@ -2003,30 +2126,30 @@ def accept(self, visitor:ParseTreeVisitor): def kvPairList(self): localctx = HogQLParser.KvPairListContext(self, self._ctx, self.state) - self.enterRule(localctx, 32, self.RULE_kvPairList) + self.enterRule(localctx, 34, self.RULE_kvPairList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 290 + self.state = 308 self.kvPair() - self.state = 295 + self.state = 313 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,17,self._ctx) + _alt = self._interp.adaptivePredict(self._input,19,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 291 + self.state = 309 self.match(HogQLParser.COMMA) - self.state = 292 + self.state = 310 self.kvPair() - self.state = 297 + self.state = 315 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,17,self._ctx) + _alt = self._interp.adaptivePredict(self._input,19,self._ctx) - self.state = 299 + self.state = 317 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 298 + self.state = 316 self.match(HogQLParser.COMMA) @@ -2076,29 +2199,29 @@ def accept(self, visitor:ParseTreeVisitor): def select(self): localctx = HogQLParser.SelectContext(self, self._ctx, self.state) - self.enterRule(localctx, 34, self.RULE_select) + self.enterRule(localctx, 36, self.RULE_select) try: self.enterOuterAlt(localctx, 1) - self.state = 304 + self.state = 322 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,19,self._ctx) + la_ = self._interp.adaptivePredict(self._input,21,self._ctx) if la_ == 1: - self.state = 301 + self.state = 319 self.selectUnionStmt() pass elif la_ == 2: - self.state = 302 + self.state = 320 self.selectStmt() pass elif la_ == 3: - self.state = 303 + self.state = 321 self.hogqlxTagElement() pass - self.state = 306 + self.state = 324 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -2150,23 +2273,23 @@ def accept(self, visitor:ParseTreeVisitor): def selectUnionStmt(self): localctx = HogQLParser.SelectUnionStmtContext(self, self._ctx, self.state) - self.enterRule(localctx, 36, self.RULE_selectUnionStmt) + self.enterRule(localctx, 38, self.RULE_selectUnionStmt) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 308 + self.state = 326 self.selectStmtWithParens() - self.state = 314 + self.state = 332 self._errHandler.sync(self) _la = self._input.LA(1) while _la==91: - self.state = 309 + self.state = 327 self.match(HogQLParser.UNION) - self.state = 310 + self.state = 328 self.match(HogQLParser.ALL) - self.state = 311 + self.state = 329 self.selectStmtWithParens() - self.state = 316 + self.state = 334 self._errHandler.sync(self) _la = self._input.LA(1) @@ -2219,28 +2342,28 @@ def accept(self, visitor:ParseTreeVisitor): def selectStmtWithParens(self): localctx = HogQLParser.SelectStmtWithParensContext(self, self._ctx, self.state) - self.enterRule(localctx, 38, self.RULE_selectStmtWithParens) + self.enterRule(localctx, 40, self.RULE_selectStmtWithParens) try: - self.state = 323 + self.state = 341 self._errHandler.sync(self) token = self._input.LA(1) if token in [77, 98]: self.enterOuterAlt(localctx, 1) - self.state = 317 + self.state = 335 self.selectStmt() pass elif token in [126]: self.enterOuterAlt(localctx, 2) - self.state = 318 + self.state = 336 self.match(HogQLParser.LPAREN) - self.state = 319 + self.state = 337 self.selectUnionStmt() - self.state = 320 + self.state = 338 self.match(HogQLParser.RPAREN) pass elif token in [124]: self.enterOuterAlt(localctx, 3) - self.state = 322 + self.state = 340 self.placeholder() pass else: @@ -2358,85 +2481,85 @@ def accept(self, visitor:ParseTreeVisitor): def selectStmt(self): localctx = HogQLParser.SelectStmtContext(self, self._ctx, self.state) - self.enterRule(localctx, 40, self.RULE_selectStmt) + self.enterRule(localctx, 42, self.RULE_selectStmt) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 326 + self.state = 344 self._errHandler.sync(self) _la = self._input.LA(1) if _la==98: - self.state = 325 + self.state = 343 localctx.with_ = self.withClause() - self.state = 328 + self.state = 346 self.match(HogQLParser.SELECT) - self.state = 330 + self.state = 348 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,23,self._ctx) + la_ = self._interp.adaptivePredict(self._input,25,self._ctx) if la_ == 1: - self.state = 329 + self.state = 347 self.match(HogQLParser.DISTINCT) - self.state = 333 + self.state = 351 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,24,self._ctx) + la_ = self._interp.adaptivePredict(self._input,26,self._ctx) if la_ == 1: - self.state = 332 + self.state = 350 self.topClause() - self.state = 335 + self.state = 353 localctx.columns = self.columnExprList() - self.state = 337 + self.state = 355 self._errHandler.sync(self) _la = self._input.LA(1) if _la==32: - self.state = 336 + self.state = 354 localctx.from_ = self.fromClause() - self.state = 340 + self.state = 358 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 567347999932448) != 0): - self.state = 339 + self.state = 357 self.arrayJoinClause() - self.state = 343 + self.state = 361 self._errHandler.sync(self) _la = self._input.LA(1) if _la==67: - self.state = 342 + self.state = 360 self.prewhereClause() - self.state = 346 + self.state = 364 self._errHandler.sync(self) _la = self._input.LA(1) if _la==95: - self.state = 345 + self.state = 363 localctx.where = self.whereClause() - self.state = 349 + self.state = 367 self._errHandler.sync(self) _la = self._input.LA(1) if _la==34: - self.state = 348 + self.state = 366 self.groupByClause() - self.state = 353 + self.state = 371 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,30,self._ctx) + la_ = self._interp.adaptivePredict(self._input,32,self._ctx) if la_ == 1: - self.state = 351 + self.state = 369 self.match(HogQLParser.WITH) - self.state = 352 + self.state = 370 _la = self._input.LA(1) if not(_la==17 or _la==72): self._errHandler.recoverInline(self) @@ -2445,60 +2568,60 @@ def selectStmt(self): self.consume() - self.state = 357 + self.state = 375 self._errHandler.sync(self) _la = self._input.LA(1) if _la==98: - self.state = 355 + self.state = 373 self.match(HogQLParser.WITH) - self.state = 356 + self.state = 374 self.match(HogQLParser.TOTALS) - self.state = 360 + self.state = 378 self._errHandler.sync(self) _la = self._input.LA(1) if _la==35: - self.state = 359 + self.state = 377 self.havingClause() - self.state = 363 + self.state = 381 self._errHandler.sync(self) _la = self._input.LA(1) if _la==97: - self.state = 362 + self.state = 380 self.windowClause() - self.state = 366 + self.state = 384 self._errHandler.sync(self) _la = self._input.LA(1) if _la==62: - self.state = 365 + self.state = 383 self.orderByClause() - self.state = 370 + self.state = 388 self._errHandler.sync(self) token = self._input.LA(1) if token in [52]: - self.state = 368 + self.state = 386 self.limitAndOffsetClause() pass elif token in [59]: - self.state = 369 + self.state = 387 self.offsetOnlyClause() pass elif token in [-1, 79, 91, 145]: pass else: pass - self.state = 373 + self.state = 391 self._errHandler.sync(self) _la = self._input.LA(1) if _la==79: - self.state = 372 + self.state = 390 self.settingsClause() @@ -2540,12 +2663,12 @@ def accept(self, visitor:ParseTreeVisitor): def withClause(self): localctx = HogQLParser.WithClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 42, self.RULE_withClause) + self.enterRule(localctx, 44, self.RULE_withClause) try: self.enterOuterAlt(localctx, 1) - self.state = 375 + self.state = 393 self.match(HogQLParser.WITH) - self.state = 376 + self.state = 394 self.withExprList() except RecognitionException as re: localctx.exception = re @@ -2590,20 +2713,20 @@ def accept(self, visitor:ParseTreeVisitor): def topClause(self): localctx = HogQLParser.TopClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 44, self.RULE_topClause) + self.enterRule(localctx, 46, self.RULE_topClause) try: self.enterOuterAlt(localctx, 1) - self.state = 378 + self.state = 396 self.match(HogQLParser.TOP) - self.state = 379 + self.state = 397 self.match(HogQLParser.DECIMAL_LITERAL) - self.state = 382 + self.state = 400 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,37,self._ctx) + la_ = self._interp.adaptivePredict(self._input,39,self._ctx) if la_ == 1: - self.state = 380 + self.state = 398 self.match(HogQLParser.WITH) - self.state = 381 + self.state = 399 self.match(HogQLParser.TIES) @@ -2645,12 +2768,12 @@ def accept(self, visitor:ParseTreeVisitor): def fromClause(self): localctx = HogQLParser.FromClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 46, self.RULE_fromClause) + self.enterRule(localctx, 48, self.RULE_fromClause) try: self.enterOuterAlt(localctx, 1) - self.state = 384 + self.state = 402 self.match(HogQLParser.FROM) - self.state = 385 + self.state = 403 self.joinExpr(0) except RecognitionException as re: localctx.exception = re @@ -2699,15 +2822,15 @@ def accept(self, visitor:ParseTreeVisitor): def arrayJoinClause(self): localctx = HogQLParser.ArrayJoinClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 48, self.RULE_arrayJoinClause) + self.enterRule(localctx, 50, self.RULE_arrayJoinClause) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 388 + self.state = 406 self._errHandler.sync(self) _la = self._input.LA(1) if _la==42 or _la==49: - self.state = 387 + self.state = 405 _la = self._input.LA(1) if not(_la==42 or _la==49): self._errHandler.recoverInline(self) @@ -2716,11 +2839,11 @@ def arrayJoinClause(self): self.consume() - self.state = 390 + self.state = 408 self.match(HogQLParser.ARRAY) - self.state = 391 + self.state = 409 self.match(HogQLParser.JOIN) - self.state = 392 + self.state = 410 self.columnExprList() except RecognitionException as re: localctx.exception = re @@ -2794,39 +2917,39 @@ def accept(self, visitor:ParseTreeVisitor): def windowClause(self): localctx = HogQLParser.WindowClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 50, self.RULE_windowClause) + self.enterRule(localctx, 52, self.RULE_windowClause) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 394 + self.state = 412 self.match(HogQLParser.WINDOW) - self.state = 395 + self.state = 413 self.identifier() - self.state = 396 + self.state = 414 self.match(HogQLParser.AS) - self.state = 397 + self.state = 415 self.match(HogQLParser.LPAREN) - self.state = 398 + self.state = 416 self.windowExpr() - self.state = 399 + self.state = 417 self.match(HogQLParser.RPAREN) - self.state = 409 + self.state = 427 self._errHandler.sync(self) _la = self._input.LA(1) while _la==112: - self.state = 400 + self.state = 418 self.match(HogQLParser.COMMA) - self.state = 401 + self.state = 419 self.identifier() - self.state = 402 + self.state = 420 self.match(HogQLParser.AS) - self.state = 403 + self.state = 421 self.match(HogQLParser.LPAREN) - self.state = 404 + self.state = 422 self.windowExpr() - self.state = 405 + self.state = 423 self.match(HogQLParser.RPAREN) - self.state = 411 + self.state = 429 self._errHandler.sync(self) _la = self._input.LA(1) @@ -2868,12 +2991,12 @@ def accept(self, visitor:ParseTreeVisitor): def prewhereClause(self): localctx = HogQLParser.PrewhereClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 52, self.RULE_prewhereClause) + self.enterRule(localctx, 54, self.RULE_prewhereClause) try: self.enterOuterAlt(localctx, 1) - self.state = 412 + self.state = 430 self.match(HogQLParser.PREWHERE) - self.state = 413 + self.state = 431 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -2913,12 +3036,12 @@ def accept(self, visitor:ParseTreeVisitor): def whereClause(self): localctx = HogQLParser.WhereClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 54, self.RULE_whereClause) + self.enterRule(localctx, 56, self.RULE_whereClause) try: self.enterOuterAlt(localctx, 1) - self.state = 415 + self.state = 433 self.match(HogQLParser.WHERE) - self.state = 416 + self.state = 434 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -2973,35 +3096,35 @@ def accept(self, visitor:ParseTreeVisitor): def groupByClause(self): localctx = HogQLParser.GroupByClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 56, self.RULE_groupByClause) + self.enterRule(localctx, 58, self.RULE_groupByClause) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 418 + self.state = 436 self.match(HogQLParser.GROUP) - self.state = 419 + self.state = 437 self.match(HogQLParser.BY) - self.state = 426 + self.state = 444 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,40,self._ctx) + la_ = self._interp.adaptivePredict(self._input,42,self._ctx) if la_ == 1: - self.state = 420 + self.state = 438 _la = self._input.LA(1) if not(_la==17 or _la==72): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 421 + self.state = 439 self.match(HogQLParser.LPAREN) - self.state = 422 + self.state = 440 self.columnExprList() - self.state = 423 + self.state = 441 self.match(HogQLParser.RPAREN) pass elif la_ == 2: - self.state = 425 + self.state = 443 self.columnExprList() pass @@ -3044,12 +3167,12 @@ def accept(self, visitor:ParseTreeVisitor): def havingClause(self): localctx = HogQLParser.HavingClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 58, self.RULE_havingClause) + self.enterRule(localctx, 60, self.RULE_havingClause) try: self.enterOuterAlt(localctx, 1) - self.state = 428 + self.state = 446 self.match(HogQLParser.HAVING) - self.state = 429 + self.state = 447 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -3092,14 +3215,14 @@ def accept(self, visitor:ParseTreeVisitor): def orderByClause(self): localctx = HogQLParser.OrderByClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 60, self.RULE_orderByClause) + self.enterRule(localctx, 62, self.RULE_orderByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 431 + self.state = 449 self.match(HogQLParser.ORDER) - self.state = 432 + self.state = 450 self.match(HogQLParser.BY) - self.state = 433 + self.state = 451 self.orderExprList() except RecognitionException as re: localctx.exception = re @@ -3142,14 +3265,14 @@ def accept(self, visitor:ParseTreeVisitor): def projectionOrderByClause(self): localctx = HogQLParser.ProjectionOrderByClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 62, self.RULE_projectionOrderByClause) + self.enterRule(localctx, 64, self.RULE_projectionOrderByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 435 + self.state = 453 self.match(HogQLParser.ORDER) - self.state = 436 + self.state = 454 self.match(HogQLParser.BY) - self.state = 437 + self.state = 455 self.columnExprList() except RecognitionException as re: localctx.exception = re @@ -3211,41 +3334,41 @@ def accept(self, visitor:ParseTreeVisitor): def limitAndOffsetClause(self): localctx = HogQLParser.LimitAndOffsetClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 64, self.RULE_limitAndOffsetClause) + self.enterRule(localctx, 66, self.RULE_limitAndOffsetClause) self._la = 0 # Token type try: - self.state = 468 + self.state = 486 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,45,self._ctx) + la_ = self._interp.adaptivePredict(self._input,47,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 439 + self.state = 457 self.match(HogQLParser.LIMIT) - self.state = 440 + self.state = 458 self.columnExpr(0) - self.state = 443 + self.state = 461 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 441 + self.state = 459 self.match(HogQLParser.COMMA) - self.state = 442 + self.state = 460 self.columnExpr(0) - self.state = 449 + self.state = 467 self._errHandler.sync(self) token = self._input.LA(1) if token in [98]: - self.state = 445 + self.state = 463 self.match(HogQLParser.WITH) - self.state = 446 + self.state = 464 self.match(HogQLParser.TIES) pass elif token in [11]: - self.state = 447 + self.state = 465 self.match(HogQLParser.BY) - self.state = 448 + self.state = 466 self.columnExprList() pass elif token in [-1, 79, 91, 145]: @@ -3256,43 +3379,43 @@ def limitAndOffsetClause(self): elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 451 + self.state = 469 self.match(HogQLParser.LIMIT) - self.state = 452 + self.state = 470 self.columnExpr(0) - self.state = 455 + self.state = 473 self._errHandler.sync(self) _la = self._input.LA(1) if _la==98: - self.state = 453 + self.state = 471 self.match(HogQLParser.WITH) - self.state = 454 + self.state = 472 self.match(HogQLParser.TIES) - self.state = 457 + self.state = 475 self.match(HogQLParser.OFFSET) - self.state = 458 + self.state = 476 self.columnExpr(0) pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 460 + self.state = 478 self.match(HogQLParser.LIMIT) - self.state = 461 + self.state = 479 self.columnExpr(0) - self.state = 462 + self.state = 480 self.match(HogQLParser.OFFSET) - self.state = 463 + self.state = 481 self.columnExpr(0) - self.state = 466 + self.state = 484 self._errHandler.sync(self) _la = self._input.LA(1) if _la==11: - self.state = 464 + self.state = 482 self.match(HogQLParser.BY) - self.state = 465 + self.state = 483 self.columnExprList() @@ -3337,12 +3460,12 @@ def accept(self, visitor:ParseTreeVisitor): def offsetOnlyClause(self): localctx = HogQLParser.OffsetOnlyClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 66, self.RULE_offsetOnlyClause) + self.enterRule(localctx, 68, self.RULE_offsetOnlyClause) try: self.enterOuterAlt(localctx, 1) - self.state = 470 + self.state = 488 self.match(HogQLParser.OFFSET) - self.state = 471 + self.state = 489 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -3382,12 +3505,12 @@ def accept(self, visitor:ParseTreeVisitor): def settingsClause(self): localctx = HogQLParser.SettingsClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 68, self.RULE_settingsClause) + self.enterRule(localctx, 70, self.RULE_settingsClause) try: self.enterOuterAlt(localctx, 1) - self.state = 473 + self.state = 491 self.match(HogQLParser.SETTINGS) - self.state = 474 + self.state = 492 self.settingExprList() except RecognitionException as re: localctx.exception = re @@ -3514,34 +3637,34 @@ def joinExpr(self, _p:int=0): _parentState = self.state localctx = HogQLParser.JoinExprContext(self, self._ctx, _parentState) _prevctx = localctx - _startState = 70 - self.enterRecursionRule(localctx, 70, self.RULE_joinExpr, _p) + _startState = 72 + self.enterRecursionRule(localctx, 72, self.RULE_joinExpr, _p) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 488 + self.state = 506 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,48,self._ctx) + la_ = self._interp.adaptivePredict(self._input,50,self._ctx) if la_ == 1: localctx = HogQLParser.JoinExprTableContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 477 + self.state = 495 self.tableExpr(0) - self.state = 479 + self.state = 497 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,46,self._ctx) + la_ = self._interp.adaptivePredict(self._input,48,self._ctx) if la_ == 1: - self.state = 478 + self.state = 496 self.match(HogQLParser.FINAL) - self.state = 482 + self.state = 500 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,47,self._ctx) + la_ = self._interp.adaptivePredict(self._input,49,self._ctx) if la_ == 1: - self.state = 481 + self.state = 499 self.sampleClause() @@ -3551,67 +3674,67 @@ def joinExpr(self, _p:int=0): localctx = HogQLParser.JoinExprParensContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 484 + self.state = 502 self.match(HogQLParser.LPAREN) - self.state = 485 + self.state = 503 self.joinExpr(0) - self.state = 486 + self.state = 504 self.match(HogQLParser.RPAREN) pass self._ctx.stop = self._input.LT(-1) - self.state = 504 + self.state = 522 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,51,self._ctx) + _alt = self._interp.adaptivePredict(self._input,53,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: if self._parseListeners is not None: self.triggerExitRuleEvent() _prevctx = localctx - self.state = 502 + self.state = 520 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,50,self._ctx) + la_ = self._interp.adaptivePredict(self._input,52,self._ctx) if la_ == 1: localctx = HogQLParser.JoinExprCrossOpContext(self, HogQLParser.JoinExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_joinExpr) - self.state = 490 + self.state = 508 if not self.precpred(self._ctx, 3): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 3)") - self.state = 491 + self.state = 509 self.joinOpCross() - self.state = 492 + self.state = 510 self.joinExpr(4) pass elif la_ == 2: localctx = HogQLParser.JoinExprOpContext(self, HogQLParser.JoinExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_joinExpr) - self.state = 494 + self.state = 512 if not self.precpred(self._ctx, 4): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 4)") - self.state = 496 + self.state = 514 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 567356589867290) != 0) or _la==71 or _la==78: - self.state = 495 + self.state = 513 self.joinOp() - self.state = 498 + self.state = 516 self.match(HogQLParser.JOIN) - self.state = 499 + self.state = 517 self.joinExpr(0) - self.state = 500 + self.state = 518 self.joinConstraintClause() pass - self.state = 506 + self.state = 524 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,51,self._ctx) + _alt = self._interp.adaptivePredict(self._input,53,self._ctx) except RecognitionException as re: localctx.exception = re @@ -3717,24 +3840,24 @@ def accept(self, visitor:ParseTreeVisitor): def joinOp(self): localctx = HogQLParser.JoinOpContext(self, self._ctx, self.state) - self.enterRule(localctx, 72, self.RULE_joinOp) + self.enterRule(localctx, 74, self.RULE_joinOp) self._la = 0 # Token type try: - self.state = 550 + self.state = 568 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,65,self._ctx) + la_ = self._interp.adaptivePredict(self._input,67,self._ctx) if la_ == 1: localctx = HogQLParser.JoinOpInnerContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 516 + self.state = 534 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,54,self._ctx) + la_ = self._interp.adaptivePredict(self._input,56,self._ctx) if la_ == 1: - self.state = 508 + self.state = 526 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0): - self.state = 507 + self.state = 525 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0)): self._errHandler.recoverInline(self) @@ -3743,18 +3866,18 @@ def joinOp(self): self.consume() - self.state = 510 + self.state = 528 self.match(HogQLParser.INNER) pass elif la_ == 2: - self.state = 511 + self.state = 529 self.match(HogQLParser.INNER) - self.state = 513 + self.state = 531 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0): - self.state = 512 + self.state = 530 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0)): self._errHandler.recoverInline(self) @@ -3766,7 +3889,7 @@ def joinOp(self): pass elif la_ == 3: - self.state = 515 + self.state = 533 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0)): self._errHandler.recoverInline(self) @@ -3781,15 +3904,15 @@ def joinOp(self): elif la_ == 2: localctx = HogQLParser.JoinOpLeftRightContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 532 + self.state = 550 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,59,self._ctx) + la_ = self._interp.adaptivePredict(self._input,61,self._ctx) if la_ == 1: - self.state = 519 + self.state = 537 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==78: - self.state = 518 + self.state = 536 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==78): self._errHandler.recoverInline(self) @@ -3798,44 +3921,44 @@ def joinOp(self): self.consume() - self.state = 521 + self.state = 539 _la = self._input.LA(1) if not(_la==49 or _la==71): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 523 + self.state = 541 self._errHandler.sync(self) _la = self._input.LA(1) if _la==63: - self.state = 522 + self.state = 540 self.match(HogQLParser.OUTER) pass elif la_ == 2: - self.state = 525 + self.state = 543 _la = self._input.LA(1) if not(_la==49 or _la==71): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 527 + self.state = 545 self._errHandler.sync(self) _la = self._input.LA(1) if _la==63: - self.state = 526 + self.state = 544 self.match(HogQLParser.OUTER) - self.state = 530 + self.state = 548 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==78: - self.state = 529 + self.state = 547 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==78): self._errHandler.recoverInline(self) @@ -3852,15 +3975,15 @@ def joinOp(self): elif la_ == 3: localctx = HogQLParser.JoinOpFullContext(self, localctx) self.enterOuterAlt(localctx, 3) - self.state = 548 + self.state = 566 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,64,self._ctx) + la_ = self._interp.adaptivePredict(self._input,66,self._ctx) if la_ == 1: - self.state = 535 + self.state = 553 self._errHandler.sync(self) _la = self._input.LA(1) if _la==1 or _la==4: - self.state = 534 + self.state = 552 _la = self._input.LA(1) if not(_la==1 or _la==4): self._errHandler.recoverInline(self) @@ -3869,34 +3992,34 @@ def joinOp(self): self.consume() - self.state = 537 + self.state = 555 self.match(HogQLParser.FULL) - self.state = 539 + self.state = 557 self._errHandler.sync(self) _la = self._input.LA(1) if _la==63: - self.state = 538 + self.state = 556 self.match(HogQLParser.OUTER) pass elif la_ == 2: - self.state = 541 + self.state = 559 self.match(HogQLParser.FULL) - self.state = 543 + self.state = 561 self._errHandler.sync(self) _la = self._input.LA(1) if _la==63: - self.state = 542 + self.state = 560 self.match(HogQLParser.OUTER) - self.state = 546 + self.state = 564 self._errHandler.sync(self) _la = self._input.LA(1) if _la==1 or _la==4: - self.state = 545 + self.state = 563 _la = self._input.LA(1) if not(_la==1 or _la==4): self._errHandler.recoverInline(self) @@ -3951,21 +4074,21 @@ def accept(self, visitor:ParseTreeVisitor): def joinOpCross(self): localctx = HogQLParser.JoinOpCrossContext(self, self._ctx, self.state) - self.enterRule(localctx, 74, self.RULE_joinOpCross) + self.enterRule(localctx, 76, self.RULE_joinOpCross) try: - self.state = 555 + self.state = 573 self._errHandler.sync(self) token = self._input.LA(1) if token in [16]: self.enterOuterAlt(localctx, 1) - self.state = 552 + self.state = 570 self.match(HogQLParser.CROSS) - self.state = 553 + self.state = 571 self.match(HogQLParser.JOIN) pass elif token in [112]: self.enterOuterAlt(localctx, 2) - self.state = 554 + self.state = 572 self.match(HogQLParser.COMMA) pass else: @@ -4018,36 +4141,36 @@ def accept(self, visitor:ParseTreeVisitor): def joinConstraintClause(self): localctx = HogQLParser.JoinConstraintClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 76, self.RULE_joinConstraintClause) + self.enterRule(localctx, 78, self.RULE_joinConstraintClause) try: - self.state = 566 + self.state = 584 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,67,self._ctx) + la_ = self._interp.adaptivePredict(self._input,69,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 557 + self.state = 575 self.match(HogQLParser.ON) - self.state = 558 + self.state = 576 self.columnExprList() pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 559 + self.state = 577 self.match(HogQLParser.USING) - self.state = 560 + self.state = 578 self.match(HogQLParser.LPAREN) - self.state = 561 + self.state = 579 self.columnExprList() - self.state = 562 + self.state = 580 self.match(HogQLParser.RPAREN) pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 564 + self.state = 582 self.match(HogQLParser.USING) - self.state = 565 + self.state = 583 self.columnExprList() pass @@ -4096,20 +4219,20 @@ def accept(self, visitor:ParseTreeVisitor): def sampleClause(self): localctx = HogQLParser.SampleClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 78, self.RULE_sampleClause) + self.enterRule(localctx, 80, self.RULE_sampleClause) try: self.enterOuterAlt(localctx, 1) - self.state = 568 + self.state = 586 self.match(HogQLParser.SAMPLE) - self.state = 569 + self.state = 587 self.ratioExpr() - self.state = 572 + self.state = 590 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,68,self._ctx) + la_ = self._interp.adaptivePredict(self._input,70,self._ctx) if la_ == 1: - self.state = 570 + self.state = 588 self.match(HogQLParser.OFFSET) - self.state = 571 + self.state = 589 self.ratioExpr() @@ -4157,21 +4280,21 @@ def accept(self, visitor:ParseTreeVisitor): def orderExprList(self): localctx = HogQLParser.OrderExprListContext(self, self._ctx, self.state) - self.enterRule(localctx, 80, self.RULE_orderExprList) + self.enterRule(localctx, 82, self.RULE_orderExprList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 574 + self.state = 592 self.orderExpr() - self.state = 579 + self.state = 597 self._errHandler.sync(self) _la = self._input.LA(1) while _la==112: - self.state = 575 + self.state = 593 self.match(HogQLParser.COMMA) - self.state = 576 + self.state = 594 self.orderExpr() - self.state = 581 + self.state = 599 self._errHandler.sync(self) _la = self._input.LA(1) @@ -4234,17 +4357,17 @@ def accept(self, visitor:ParseTreeVisitor): def orderExpr(self): localctx = HogQLParser.OrderExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 82, self.RULE_orderExpr) + self.enterRule(localctx, 84, self.RULE_orderExpr) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 582 + self.state = 600 self.columnExpr(0) - self.state = 584 + self.state = 602 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 6291584) != 0): - self.state = 583 + self.state = 601 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 6291584) != 0)): self._errHandler.recoverInline(self) @@ -4253,13 +4376,13 @@ def orderExpr(self): self.consume() - self.state = 588 + self.state = 606 self._errHandler.sync(self) _la = self._input.LA(1) if _la==58: - self.state = 586 + self.state = 604 self.match(HogQLParser.NULLS) - self.state = 587 + self.state = 605 _la = self._input.LA(1) if not(_la==28 or _la==47): self._errHandler.recoverInline(self) @@ -4268,13 +4391,13 @@ def orderExpr(self): self.consume() - self.state = 592 + self.state = 610 self._errHandler.sync(self) _la = self._input.LA(1) if _la==15: - self.state = 590 + self.state = 608 self.match(HogQLParser.COLLATE) - self.state = 591 + self.state = 609 self.match(HogQLParser.STRING_LITERAL) @@ -4323,27 +4446,27 @@ def accept(self, visitor:ParseTreeVisitor): def ratioExpr(self): localctx = HogQLParser.RatioExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 84, self.RULE_ratioExpr) + self.enterRule(localctx, 86, self.RULE_ratioExpr) try: - self.state = 600 + self.state = 618 self._errHandler.sync(self) token = self._input.LA(1) if token in [124]: self.enterOuterAlt(localctx, 1) - self.state = 594 + self.state = 612 self.placeholder() pass elif token in [41, 55, 102, 103, 104, 105, 114, 116, 135]: self.enterOuterAlt(localctx, 2) - self.state = 595 + self.state = 613 self.numberLiteral() - self.state = 598 + self.state = 616 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,73,self._ctx) + la_ = self._interp.adaptivePredict(self._input,75,self._ctx) if la_ == 1: - self.state = 596 + self.state = 614 self.match(HogQLParser.SLASH) - self.state = 597 + self.state = 615 self.numberLiteral() @@ -4395,21 +4518,21 @@ def accept(self, visitor:ParseTreeVisitor): def settingExprList(self): localctx = HogQLParser.SettingExprListContext(self, self._ctx, self.state) - self.enterRule(localctx, 86, self.RULE_settingExprList) + self.enterRule(localctx, 88, self.RULE_settingExprList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 602 + self.state = 620 self.settingExpr() - self.state = 607 + self.state = 625 self._errHandler.sync(self) _la = self._input.LA(1) while _la==112: - self.state = 603 + self.state = 621 self.match(HogQLParser.COMMA) - self.state = 604 + self.state = 622 self.settingExpr() - self.state = 609 + self.state = 627 self._errHandler.sync(self) _la = self._input.LA(1) @@ -4455,14 +4578,14 @@ def accept(self, visitor:ParseTreeVisitor): def settingExpr(self): localctx = HogQLParser.SettingExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 88, self.RULE_settingExpr) + self.enterRule(localctx, 90, self.RULE_settingExpr) try: self.enterOuterAlt(localctx, 1) - self.state = 610 + self.state = 628 self.identifier() - self.state = 611 + self.state = 629 self.match(HogQLParser.EQ_SINGLE) - self.state = 612 + self.state = 630 self.literal() except RecognitionException as re: localctx.exception = re @@ -4507,31 +4630,31 @@ def accept(self, visitor:ParseTreeVisitor): def windowExpr(self): localctx = HogQLParser.WindowExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 90, self.RULE_windowExpr) + self.enterRule(localctx, 92, self.RULE_windowExpr) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 615 + self.state = 633 self._errHandler.sync(self) _la = self._input.LA(1) if _la==65: - self.state = 614 + self.state = 632 self.winPartitionByClause() - self.state = 618 + self.state = 636 self._errHandler.sync(self) _la = self._input.LA(1) if _la==62: - self.state = 617 + self.state = 635 self.winOrderByClause() - self.state = 621 + self.state = 639 self._errHandler.sync(self) _la = self._input.LA(1) if _la==69 or _la==74: - self.state = 620 + self.state = 638 self.winFrameClause() @@ -4576,14 +4699,14 @@ def accept(self, visitor:ParseTreeVisitor): def winPartitionByClause(self): localctx = HogQLParser.WinPartitionByClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 92, self.RULE_winPartitionByClause) + self.enterRule(localctx, 94, self.RULE_winPartitionByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 623 + self.state = 641 self.match(HogQLParser.PARTITION) - self.state = 624 + self.state = 642 self.match(HogQLParser.BY) - self.state = 625 + self.state = 643 self.columnExprList() except RecognitionException as re: localctx.exception = re @@ -4626,14 +4749,14 @@ def accept(self, visitor:ParseTreeVisitor): def winOrderByClause(self): localctx = HogQLParser.WinOrderByClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 94, self.RULE_winOrderByClause) + self.enterRule(localctx, 96, self.RULE_winOrderByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 627 + self.state = 645 self.match(HogQLParser.ORDER) - self.state = 628 + self.state = 646 self.match(HogQLParser.BY) - self.state = 629 + self.state = 647 self.orderExprList() except RecognitionException as re: localctx.exception = re @@ -4676,18 +4799,18 @@ def accept(self, visitor:ParseTreeVisitor): def winFrameClause(self): localctx = HogQLParser.WinFrameClauseContext(self, self._ctx, self.state) - self.enterRule(localctx, 96, self.RULE_winFrameClause) + self.enterRule(localctx, 98, self.RULE_winFrameClause) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 631 + self.state = 649 _la = self._input.LA(1) if not(_la==69 or _la==74): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 632 + self.state = 650 self.winFrameExtend() except RecognitionException as re: localctx.exception = re @@ -4760,27 +4883,27 @@ def accept(self, visitor:ParseTreeVisitor): def winFrameExtend(self): localctx = HogQLParser.WinFrameExtendContext(self, self._ctx, self.state) - self.enterRule(localctx, 98, self.RULE_winFrameExtend) + self.enterRule(localctx, 100, self.RULE_winFrameExtend) try: - self.state = 640 + self.state = 658 self._errHandler.sync(self) token = self._input.LA(1) if token in [18, 41, 55, 90, 102, 103, 104, 105, 114, 116, 135]: localctx = HogQLParser.FrameStartContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 634 + self.state = 652 self.winFrameBound() pass elif token in [9]: localctx = HogQLParser.FrameBetweenContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 635 + self.state = 653 self.match(HogQLParser.BETWEEN) - self.state = 636 + self.state = 654 self.winFrameBound() - self.state = 637 + self.state = 655 self.match(HogQLParser.AND) - self.state = 638 + self.state = 656 self.winFrameBound() pass else: @@ -4836,44 +4959,44 @@ def accept(self, visitor:ParseTreeVisitor): def winFrameBound(self): localctx = HogQLParser.WinFrameBoundContext(self, self._ctx, self.state) - self.enterRule(localctx, 100, self.RULE_winFrameBound) + self.enterRule(localctx, 102, self.RULE_winFrameBound) try: self.enterOuterAlt(localctx, 1) - self.state = 654 + self.state = 672 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,80,self._ctx) + la_ = self._interp.adaptivePredict(self._input,82,self._ctx) if la_ == 1: - self.state = 642 + self.state = 660 self.match(HogQLParser.CURRENT) - self.state = 643 + self.state = 661 self.match(HogQLParser.ROW) pass elif la_ == 2: - self.state = 644 + self.state = 662 self.match(HogQLParser.UNBOUNDED) - self.state = 645 + self.state = 663 self.match(HogQLParser.PRECEDING) pass elif la_ == 3: - self.state = 646 + self.state = 664 self.match(HogQLParser.UNBOUNDED) - self.state = 647 + self.state = 665 self.match(HogQLParser.FOLLOWING) pass elif la_ == 4: - self.state = 648 + self.state = 666 self.numberLiteral() - self.state = 649 + self.state = 667 self.match(HogQLParser.PRECEDING) pass elif la_ == 5: - self.state = 651 + self.state = 669 self.numberLiteral() - self.state = 652 + self.state = 670 self.match(HogQLParser.FOLLOWING) pass @@ -4916,12 +5039,12 @@ def accept(self, visitor:ParseTreeVisitor): def expr(self): localctx = HogQLParser.ExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 102, self.RULE_expr) + self.enterRule(localctx, 104, self.RULE_expr) try: self.enterOuterAlt(localctx, 1) - self.state = 656 + self.state = 674 self.columnExpr(0) - self.state = 657 + self.state = 675 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -5093,141 +5216,141 @@ def accept(self, visitor:ParseTreeVisitor): def columnTypeExpr(self): localctx = HogQLParser.ColumnTypeExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 104, self.RULE_columnTypeExpr) + self.enterRule(localctx, 106, self.RULE_columnTypeExpr) self._la = 0 # Token type try: - self.state = 715 + self.state = 733 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,88,self._ctx) + la_ = self._interp.adaptivePredict(self._input,90,self._ctx) if la_ == 1: localctx = HogQLParser.ColumnTypeExprSimpleContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 659 + self.state = 677 self.identifier() pass elif la_ == 2: localctx = HogQLParser.ColumnTypeExprNestedContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 660 + self.state = 678 self.identifier() - self.state = 661 + self.state = 679 self.match(HogQLParser.LPAREN) - self.state = 662 + self.state = 680 self.identifier() - self.state = 663 + self.state = 681 self.columnTypeExpr() - self.state = 670 + self.state = 688 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,81,self._ctx) + _alt = self._interp.adaptivePredict(self._input,83,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 664 + self.state = 682 self.match(HogQLParser.COMMA) - self.state = 665 + self.state = 683 self.identifier() - self.state = 666 + self.state = 684 self.columnTypeExpr() - self.state = 672 + self.state = 690 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,81,self._ctx) + _alt = self._interp.adaptivePredict(self._input,83,self._ctx) - self.state = 674 + self.state = 692 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 673 + self.state = 691 self.match(HogQLParser.COMMA) - self.state = 676 + self.state = 694 self.match(HogQLParser.RPAREN) pass elif la_ == 3: localctx = HogQLParser.ColumnTypeExprEnumContext(self, localctx) self.enterOuterAlt(localctx, 3) - self.state = 678 + self.state = 696 self.identifier() - self.state = 679 + self.state = 697 self.match(HogQLParser.LPAREN) - self.state = 680 + self.state = 698 self.enumValue() - self.state = 685 + self.state = 703 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,83,self._ctx) + _alt = self._interp.adaptivePredict(self._input,85,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 681 + self.state = 699 self.match(HogQLParser.COMMA) - self.state = 682 + self.state = 700 self.enumValue() - self.state = 687 + self.state = 705 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,83,self._ctx) + _alt = self._interp.adaptivePredict(self._input,85,self._ctx) - self.state = 689 + self.state = 707 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 688 + self.state = 706 self.match(HogQLParser.COMMA) - self.state = 691 + self.state = 709 self.match(HogQLParser.RPAREN) pass elif la_ == 4: localctx = HogQLParser.ColumnTypeExprComplexContext(self, localctx) self.enterOuterAlt(localctx, 4) - self.state = 693 + self.state = 711 self.identifier() - self.state = 694 + self.state = 712 self.match(HogQLParser.LPAREN) - self.state = 695 + self.state = 713 self.columnTypeExpr() - self.state = 700 + self.state = 718 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,85,self._ctx) + _alt = self._interp.adaptivePredict(self._input,87,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 696 + self.state = 714 self.match(HogQLParser.COMMA) - self.state = 697 + self.state = 715 self.columnTypeExpr() - self.state = 702 + self.state = 720 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,85,self._ctx) + _alt = self._interp.adaptivePredict(self._input,87,self._ctx) - self.state = 704 + self.state = 722 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 703 + self.state = 721 self.match(HogQLParser.COMMA) - self.state = 706 + self.state = 724 self.match(HogQLParser.RPAREN) pass elif la_ == 5: localctx = HogQLParser.ColumnTypeExprParamContext(self, localctx) self.enterOuterAlt(localctx, 5) - self.state = 708 + self.state = 726 self.identifier() - self.state = 709 + self.state = 727 self.match(HogQLParser.LPAREN) - self.state = 711 + self.state = 729 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 710 + self.state = 728 self.columnExprList() - self.state = 713 + self.state = 731 self.match(HogQLParser.RPAREN) pass @@ -5276,29 +5399,29 @@ def accept(self, visitor:ParseTreeVisitor): def columnExprList(self): localctx = HogQLParser.ColumnExprListContext(self, self._ctx, self.state) - self.enterRule(localctx, 106, self.RULE_columnExprList) + self.enterRule(localctx, 108, self.RULE_columnExprList) try: self.enterOuterAlt(localctx, 1) - self.state = 717 + self.state = 735 self.columnExpr(0) - self.state = 722 + self.state = 740 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,89,self._ctx) + _alt = self._interp.adaptivePredict(self._input,91,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 718 + self.state = 736 self.match(HogQLParser.COMMA) - self.state = 719 + self.state = 737 self.columnExpr(0) - self.state = 724 + self.state = 742 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,89,self._ctx) + _alt = self._interp.adaptivePredict(self._input,91,self._ctx) - self.state = 726 + self.state = 744 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,90,self._ctx) + la_ = self._interp.adaptivePredict(self._input,92,self._ctx) if la_ == 1: - self.state = 725 + self.state = 743 self.match(HogQLParser.COMMA) @@ -6286,58 +6409,58 @@ def columnExpr(self, _p:int=0): _parentState = self.state localctx = HogQLParser.ColumnExprContext(self, self._ctx, _parentState) _prevctx = localctx - _startState = 108 - self.enterRecursionRule(localctx, 108, self.RULE_columnExpr, _p) + _startState = 110 + self.enterRecursionRule(localctx, 110, self.RULE_columnExpr, _p) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 877 + self.state = 895 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,110,self._ctx) + la_ = self._interp.adaptivePredict(self._input,112,self._ctx) if la_ == 1: localctx = HogQLParser.ColumnExprCaseContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 729 + self.state = 747 self.match(HogQLParser.CASE) - self.state = 731 + self.state = 749 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,91,self._ctx) + la_ = self._interp.adaptivePredict(self._input,93,self._ctx) if la_ == 1: - self.state = 730 + self.state = 748 localctx.caseExpr = self.columnExpr(0) - self.state = 738 + self.state = 756 self._errHandler.sync(self) _la = self._input.LA(1) while True: - self.state = 733 + self.state = 751 self.match(HogQLParser.WHEN) - self.state = 734 + self.state = 752 localctx.whenExpr = self.columnExpr(0) - self.state = 735 + self.state = 753 self.match(HogQLParser.THEN) - self.state = 736 + self.state = 754 localctx.thenExpr = self.columnExpr(0) - self.state = 740 + self.state = 758 self._errHandler.sync(self) _la = self._input.LA(1) if not (_la==94): break - self.state = 744 + self.state = 762 self._errHandler.sync(self) _la = self._input.LA(1) if _la==24: - self.state = 742 + self.state = 760 self.match(HogQLParser.ELSE) - self.state = 743 + self.state = 761 localctx.elseExpr = self.columnExpr(0) - self.state = 746 + self.state = 764 self.match(HogQLParser.END) pass @@ -6345,17 +6468,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprCastContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 748 + self.state = 766 self.match(HogQLParser.CAST) - self.state = 749 + self.state = 767 self.match(HogQLParser.LPAREN) - self.state = 750 + self.state = 768 self.columnExpr(0) - self.state = 751 + self.state = 769 self.match(HogQLParser.AS) - self.state = 752 + self.state = 770 self.columnTypeExpr() - self.state = 753 + self.state = 771 self.match(HogQLParser.RPAREN) pass @@ -6363,9 +6486,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprDateContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 755 + self.state = 773 self.match(HogQLParser.DATE) - self.state = 756 + self.state = 774 self.match(HogQLParser.STRING_LITERAL) pass @@ -6373,11 +6496,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprIntervalContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 757 + self.state = 775 self.match(HogQLParser.INTERVAL) - self.state = 758 + self.state = 776 self.columnExpr(0) - self.state = 759 + self.state = 777 self.interval() pass @@ -6385,27 +6508,27 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprSubstringContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 761 + self.state = 779 self.match(HogQLParser.SUBSTRING) - self.state = 762 + self.state = 780 self.match(HogQLParser.LPAREN) - self.state = 763 + self.state = 781 self.columnExpr(0) - self.state = 764 + self.state = 782 self.match(HogQLParser.FROM) - self.state = 765 + self.state = 783 self.columnExpr(0) - self.state = 768 + self.state = 786 self._errHandler.sync(self) _la = self._input.LA(1) if _la==31: - self.state = 766 + self.state = 784 self.match(HogQLParser.FOR) - self.state = 767 + self.state = 785 self.columnExpr(0) - self.state = 770 + self.state = 788 self.match(HogQLParser.RPAREN) pass @@ -6413,9 +6536,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTimestampContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 772 + self.state = 790 self.match(HogQLParser.TIMESTAMP) - self.state = 773 + self.state = 791 self.match(HogQLParser.STRING_LITERAL) pass @@ -6423,24 +6546,24 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTrimContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 774 + self.state = 792 self.match(HogQLParser.TRIM) - self.state = 775 + self.state = 793 self.match(HogQLParser.LPAREN) - self.state = 776 + self.state = 794 _la = self._input.LA(1) if not(_la==10 or _la==48 or _la==87): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 777 + self.state = 795 self.string() - self.state = 778 + self.state = 796 self.match(HogQLParser.FROM) - self.state = 779 + self.state = 797 self.columnExpr(0) - self.state = 780 + self.state = 798 self.match(HogQLParser.RPAREN) pass @@ -6448,54 +6571,54 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprWinFunctionContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 782 + self.state = 800 self.identifier() - self.state = 783 + self.state = 801 self.match(HogQLParser.LPAREN) - self.state = 785 + self.state = 803 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 784 + self.state = 802 self.columnExprList() - self.state = 787 + self.state = 805 self.match(HogQLParser.RPAREN) - self.state = 797 + self.state = 815 self._errHandler.sync(self) _la = self._input.LA(1) if _la==126: - self.state = 789 + self.state = 807 self.match(HogQLParser.LPAREN) - self.state = 791 + self.state = 809 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,96,self._ctx) + la_ = self._interp.adaptivePredict(self._input,98,self._ctx) if la_ == 1: - self.state = 790 + self.state = 808 self.match(HogQLParser.DISTINCT) - self.state = 794 + self.state = 812 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 793 + self.state = 811 self.columnArgList() - self.state = 796 + self.state = 814 self.match(HogQLParser.RPAREN) - self.state = 799 + self.state = 817 self.match(HogQLParser.OVER) - self.state = 800 + self.state = 818 self.match(HogQLParser.LPAREN) - self.state = 801 + self.state = 819 self.windowExpr() - self.state = 802 + self.state = 820 self.match(HogQLParser.RPAREN) pass @@ -6503,50 +6626,50 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprWinFunctionTargetContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 804 + self.state = 822 self.identifier() - self.state = 805 + self.state = 823 self.match(HogQLParser.LPAREN) - self.state = 807 + self.state = 825 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 806 + self.state = 824 self.columnExprList() - self.state = 809 + self.state = 827 self.match(HogQLParser.RPAREN) - self.state = 819 + self.state = 837 self._errHandler.sync(self) _la = self._input.LA(1) if _la==126: - self.state = 811 + self.state = 829 self.match(HogQLParser.LPAREN) - self.state = 813 + self.state = 831 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,100,self._ctx) + la_ = self._interp.adaptivePredict(self._input,102,self._ctx) if la_ == 1: - self.state = 812 + self.state = 830 self.match(HogQLParser.DISTINCT) - self.state = 816 + self.state = 834 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 815 + self.state = 833 self.columnArgList() - self.state = 818 + self.state = 836 self.match(HogQLParser.RPAREN) - self.state = 821 + self.state = 839 self.match(HogQLParser.OVER) - self.state = 822 + self.state = 840 self.identifier() pass @@ -6554,45 +6677,45 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprFunctionContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 824 + self.state = 842 self.identifier() - self.state = 830 + self.state = 848 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,104,self._ctx) + la_ = self._interp.adaptivePredict(self._input,106,self._ctx) if la_ == 1: - self.state = 825 + self.state = 843 self.match(HogQLParser.LPAREN) - self.state = 827 + self.state = 845 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 826 + self.state = 844 self.columnExprList() - self.state = 829 + self.state = 847 self.match(HogQLParser.RPAREN) - self.state = 832 + self.state = 850 self.match(HogQLParser.LPAREN) - self.state = 834 + self.state = 852 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,105,self._ctx) + la_ = self._interp.adaptivePredict(self._input,107,self._ctx) if la_ == 1: - self.state = 833 + self.state = 851 self.match(HogQLParser.DISTINCT) - self.state = 837 + self.state = 855 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 836 + self.state = 854 self.columnArgList() - self.state = 839 + self.state = 857 self.match(HogQLParser.RPAREN) pass @@ -6600,7 +6723,7 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTagElementContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 841 + self.state = 859 self.hogqlxTagElement() pass @@ -6608,7 +6731,7 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTemplateStringContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 842 + self.state = 860 self.templateString() pass @@ -6616,7 +6739,7 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprLiteralContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 843 + self.state = 861 self.literal() pass @@ -6624,9 +6747,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprNegateContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 844 + self.state = 862 self.match(HogQLParser.DASH) - self.state = 845 + self.state = 863 self.columnExpr(19) pass @@ -6634,9 +6757,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprNotContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 846 + self.state = 864 self.match(HogQLParser.NOT) - self.state = 847 + self.state = 865 self.columnExpr(13) pass @@ -6644,17 +6767,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprAsteriskContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 851 + self.state = 869 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -181272084561788930) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 201863462911) != 0): - self.state = 848 + self.state = 866 self.tableIdentifier() - self.state = 849 + self.state = 867 self.match(HogQLParser.DOT) - self.state = 853 + self.state = 871 self.match(HogQLParser.ASTERISK) pass @@ -6662,11 +6785,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprSubqueryContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 854 + self.state = 872 self.match(HogQLParser.LPAREN) - self.state = 855 + self.state = 873 self.selectUnionStmt() - self.state = 856 + self.state = 874 self.match(HogQLParser.RPAREN) pass @@ -6674,11 +6797,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprParensContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 858 + self.state = 876 self.match(HogQLParser.LPAREN) - self.state = 859 + self.state = 877 self.columnExpr(0) - self.state = 860 + self.state = 878 self.match(HogQLParser.RPAREN) pass @@ -6686,11 +6809,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTupleContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 862 + self.state = 880 self.match(HogQLParser.LPAREN) - self.state = 863 + self.state = 881 self.columnExprList() - self.state = 864 + self.state = 882 self.match(HogQLParser.RPAREN) pass @@ -6698,17 +6821,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprArrayContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 866 + self.state = 884 self.match(HogQLParser.LBRACKET) - self.state = 868 + self.state = 886 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 867 + self.state = 885 self.columnExprList() - self.state = 870 + self.state = 888 self.match(HogQLParser.RBRACKET) pass @@ -6716,17 +6839,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprDictContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 871 + self.state = 889 self.match(HogQLParser.LBRACE) - self.state = 873 + self.state = 891 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 872 + self.state = 890 self.kvPairList() - self.state = 875 + self.state = 893 self.match(HogQLParser.RBRACE) pass @@ -6734,50 +6857,50 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprIdentifierContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 876 + self.state = 894 self.columnIdentifier() pass self._ctx.stop = self._input.LT(-1) - self.state = 983 + self.state = 1001 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,121,self._ctx) + _alt = self._interp.adaptivePredict(self._input,123,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: if self._parseListeners is not None: self.triggerExitRuleEvent() _prevctx = localctx - self.state = 981 + self.state = 999 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,120,self._ctx) + la_ = self._interp.adaptivePredict(self._input,122,self._ctx) if la_ == 1: localctx = HogQLParser.ColumnExprPrecedence1Context(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) localctx.left = _prevctx self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 879 + self.state = 897 if not self.precpred(self._ctx, 18): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 18)") - self.state = 883 + self.state = 901 self._errHandler.sync(self) token = self._input.LA(1) if token in [108]: - self.state = 880 + self.state = 898 localctx.operator = self.match(HogQLParser.ASTERISK) pass elif token in [147]: - self.state = 881 + self.state = 899 localctx.operator = self.match(HogQLParser.SLASH) pass elif token in [134]: - self.state = 882 + self.state = 900 localctx.operator = self.match(HogQLParser.PERCENT) pass else: raise NoViableAltException(self) - self.state = 885 + self.state = 903 localctx.right = self.columnExpr(19) pass @@ -6785,29 +6908,29 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprPrecedence2Context(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) localctx.left = _prevctx self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 886 + self.state = 904 if not self.precpred(self._ctx, 17): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 17)") - self.state = 890 + self.state = 908 self._errHandler.sync(self) token = self._input.LA(1) if token in [135]: - self.state = 887 + self.state = 905 localctx.operator = self.match(HogQLParser.PLUS) pass elif token in [114]: - self.state = 888 + self.state = 906 localctx.operator = self.match(HogQLParser.DASH) pass elif token in [113]: - self.state = 889 + self.state = 907 localctx.operator = self.match(HogQLParser.CONCAT) pass else: raise NoViableAltException(self) - self.state = 892 + self.state = 910 localctx.right = self.columnExpr(18) pass @@ -6815,79 +6938,79 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprPrecedence3Context(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) localctx.left = _prevctx self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 893 + self.state = 911 if not self.precpred(self._ctx, 16): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 16)") - self.state = 918 + self.state = 936 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,116,self._ctx) + la_ = self._interp.adaptivePredict(self._input,118,self._ctx) if la_ == 1: - self.state = 894 + self.state = 912 localctx.operator = self.match(HogQLParser.EQ_DOUBLE) pass elif la_ == 2: - self.state = 895 + self.state = 913 localctx.operator = self.match(HogQLParser.EQ_SINGLE) pass elif la_ == 3: - self.state = 896 + self.state = 914 localctx.operator = self.match(HogQLParser.NOT_EQ) pass elif la_ == 4: - self.state = 897 + self.state = 915 localctx.operator = self.match(HogQLParser.LT_EQ) pass elif la_ == 5: - self.state = 898 + self.state = 916 localctx.operator = self.match(HogQLParser.LT) pass elif la_ == 6: - self.state = 899 + self.state = 917 localctx.operator = self.match(HogQLParser.GT_EQ) pass elif la_ == 7: - self.state = 900 + self.state = 918 localctx.operator = self.match(HogQLParser.GT) pass elif la_ == 8: - self.state = 902 + self.state = 920 self._errHandler.sync(self) _la = self._input.LA(1) if _la==56: - self.state = 901 + self.state = 919 localctx.operator = self.match(HogQLParser.NOT) - self.state = 904 + self.state = 922 self.match(HogQLParser.IN) - self.state = 906 + self.state = 924 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,114,self._ctx) + la_ = self._interp.adaptivePredict(self._input,116,self._ctx) if la_ == 1: - self.state = 905 + self.state = 923 self.match(HogQLParser.COHORT) pass elif la_ == 9: - self.state = 909 + self.state = 927 self._errHandler.sync(self) _la = self._input.LA(1) if _la==56: - self.state = 908 + self.state = 926 localctx.operator = self.match(HogQLParser.NOT) - self.state = 911 + self.state = 929 _la = self._input.LA(1) if not(_la==39 or _la==51): self._errHandler.recoverInline(self) @@ -6897,247 +7020,247 @@ def columnExpr(self, _p:int=0): pass elif la_ == 10: - self.state = 912 + self.state = 930 localctx.operator = self.match(HogQLParser.REGEX_SINGLE) pass elif la_ == 11: - self.state = 913 + self.state = 931 localctx.operator = self.match(HogQLParser.REGEX_DOUBLE) pass elif la_ == 12: - self.state = 914 + self.state = 932 localctx.operator = self.match(HogQLParser.NOT_REGEX) pass elif la_ == 13: - self.state = 915 + self.state = 933 localctx.operator = self.match(HogQLParser.IREGEX_SINGLE) pass elif la_ == 14: - self.state = 916 + self.state = 934 localctx.operator = self.match(HogQLParser.IREGEX_DOUBLE) pass elif la_ == 15: - self.state = 917 + self.state = 935 localctx.operator = self.match(HogQLParser.NOT_IREGEX) pass - self.state = 920 + self.state = 938 localctx.right = self.columnExpr(17) pass elif la_ == 4: localctx = HogQLParser.ColumnExprNullishContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 921 + self.state = 939 if not self.precpred(self._ctx, 14): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 14)") - self.state = 922 + self.state = 940 self.match(HogQLParser.NULLISH) - self.state = 923 + self.state = 941 self.columnExpr(15) pass elif la_ == 5: localctx = HogQLParser.ColumnExprAndContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 924 + self.state = 942 if not self.precpred(self._ctx, 12): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 12)") - self.state = 925 + self.state = 943 self.match(HogQLParser.AND) - self.state = 926 + self.state = 944 self.columnExpr(13) pass elif la_ == 6: localctx = HogQLParser.ColumnExprOrContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 927 + self.state = 945 if not self.precpred(self._ctx, 11): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 11)") - self.state = 928 + self.state = 946 self.match(HogQLParser.OR) - self.state = 929 + self.state = 947 self.columnExpr(12) pass elif la_ == 7: localctx = HogQLParser.ColumnExprBetweenContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 930 + self.state = 948 if not self.precpred(self._ctx, 10): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 10)") - self.state = 932 + self.state = 950 self._errHandler.sync(self) _la = self._input.LA(1) if _la==56: - self.state = 931 + self.state = 949 self.match(HogQLParser.NOT) - self.state = 934 + self.state = 952 self.match(HogQLParser.BETWEEN) - self.state = 935 + self.state = 953 self.columnExpr(0) - self.state = 936 + self.state = 954 self.match(HogQLParser.AND) - self.state = 937 + self.state = 955 self.columnExpr(11) pass elif la_ == 8: localctx = HogQLParser.ColumnExprTernaryOpContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 939 + self.state = 957 if not self.precpred(self._ctx, 9): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 9)") - self.state = 940 + self.state = 958 self.match(HogQLParser.QUERY) - self.state = 941 + self.state = 959 self.columnExpr(0) - self.state = 942 + self.state = 960 self.match(HogQLParser.COLON) - self.state = 943 + self.state = 961 self.columnExpr(9) pass elif la_ == 9: localctx = HogQLParser.ColumnExprArrayAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 945 + self.state = 963 if not self.precpred(self._ctx, 25): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 25)") - self.state = 946 + self.state = 964 self.match(HogQLParser.LBRACKET) - self.state = 947 + self.state = 965 self.columnExpr(0) - self.state = 948 + self.state = 966 self.match(HogQLParser.RBRACKET) pass elif la_ == 10: localctx = HogQLParser.ColumnExprTupleAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 950 + self.state = 968 if not self.precpred(self._ctx, 24): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 24)") - self.state = 951 + self.state = 969 self.match(HogQLParser.DOT) - self.state = 952 + self.state = 970 self.match(HogQLParser.DECIMAL_LITERAL) pass elif la_ == 11: localctx = HogQLParser.ColumnExprPropertyAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 953 + self.state = 971 if not self.precpred(self._ctx, 23): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 23)") - self.state = 954 + self.state = 972 self.match(HogQLParser.DOT) - self.state = 955 + self.state = 973 self.identifier() pass elif la_ == 12: localctx = HogQLParser.ColumnExprNullArrayAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 956 + self.state = 974 if not self.precpred(self._ctx, 22): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 22)") - self.state = 957 + self.state = 975 self.match(HogQLParser.NULL_PROPERTY) - self.state = 958 + self.state = 976 self.match(HogQLParser.LBRACKET) - self.state = 959 + self.state = 977 self.columnExpr(0) - self.state = 960 + self.state = 978 self.match(HogQLParser.RBRACKET) pass elif la_ == 13: localctx = HogQLParser.ColumnExprNullTupleAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 962 + self.state = 980 if not self.precpred(self._ctx, 21): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 21)") - self.state = 963 + self.state = 981 self.match(HogQLParser.NULL_PROPERTY) - self.state = 964 + self.state = 982 self.match(HogQLParser.DECIMAL_LITERAL) pass elif la_ == 14: localctx = HogQLParser.ColumnExprNullPropertyAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 965 + self.state = 983 if not self.precpred(self._ctx, 20): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 20)") - self.state = 966 + self.state = 984 self.match(HogQLParser.NULL_PROPERTY) - self.state = 967 + self.state = 985 self.identifier() pass elif la_ == 15: localctx = HogQLParser.ColumnExprIsNullContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 968 + self.state = 986 if not self.precpred(self._ctx, 15): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 15)") - self.state = 969 + self.state = 987 self.match(HogQLParser.IS) - self.state = 971 + self.state = 989 self._errHandler.sync(self) _la = self._input.LA(1) if _la==56: - self.state = 970 + self.state = 988 self.match(HogQLParser.NOT) - self.state = 973 + self.state = 991 self.match(HogQLParser.NULL_SQL) pass elif la_ == 16: localctx = HogQLParser.ColumnExprAliasContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 974 + self.state = 992 if not self.precpred(self._ctx, 8): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 8)") - self.state = 979 + self.state = 997 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,119,self._ctx) + la_ = self._interp.adaptivePredict(self._input,121,self._ctx) if la_ == 1: - self.state = 975 + self.state = 993 self.match(HogQLParser.AS) - self.state = 976 + self.state = 994 self.identifier() pass elif la_ == 2: - self.state = 977 + self.state = 995 self.match(HogQLParser.AS) - self.state = 978 + self.state = 996 self.match(HogQLParser.STRING_LITERAL) pass @@ -7145,9 +7268,9 @@ def columnExpr(self, _p:int=0): pass - self.state = 985 + self.state = 1003 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,121,self._ctx) + _alt = self._interp.adaptivePredict(self._input,123,self._ctx) except RecognitionException as re: localctx.exception = re @@ -7193,30 +7316,30 @@ def accept(self, visitor:ParseTreeVisitor): def columnArgList(self): localctx = HogQLParser.ColumnArgListContext(self, self._ctx, self.state) - self.enterRule(localctx, 110, self.RULE_columnArgList) + self.enterRule(localctx, 112, self.RULE_columnArgList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 986 + self.state = 1004 self.columnArgExpr() - self.state = 991 + self.state = 1009 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,122,self._ctx) + _alt = self._interp.adaptivePredict(self._input,124,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 987 + self.state = 1005 self.match(HogQLParser.COMMA) - self.state = 988 + self.state = 1006 self.columnArgExpr() - self.state = 993 + self.state = 1011 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,122,self._ctx) + _alt = self._interp.adaptivePredict(self._input,124,self._ctx) - self.state = 995 + self.state = 1013 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 994 + self.state = 1012 self.match(HogQLParser.COMMA) @@ -7259,20 +7382,20 @@ def accept(self, visitor:ParseTreeVisitor): def columnArgExpr(self): localctx = HogQLParser.ColumnArgExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 112, self.RULE_columnArgExpr) + self.enterRule(localctx, 114, self.RULE_columnArgExpr) try: - self.state = 999 + self.state = 1017 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,124,self._ctx) + la_ = self._interp.adaptivePredict(self._input,126,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 997 + self.state = 1015 self.columnLambdaExpr() pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 998 + self.state = 1016 self.columnExpr(0) pass @@ -7334,63 +7457,63 @@ def accept(self, visitor:ParseTreeVisitor): def columnLambdaExpr(self): localctx = HogQLParser.ColumnLambdaExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 114, self.RULE_columnLambdaExpr) + self.enterRule(localctx, 116, self.RULE_columnLambdaExpr) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1026 + self.state = 1044 self._errHandler.sync(self) token = self._input.LA(1) if token in [126]: - self.state = 1001 + self.state = 1019 self.match(HogQLParser.LPAREN) - self.state = 1002 + self.state = 1020 self.identifier() - self.state = 1007 + self.state = 1025 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,125,self._ctx) + _alt = self._interp.adaptivePredict(self._input,127,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1003 + self.state = 1021 self.match(HogQLParser.COMMA) - self.state = 1004 + self.state = 1022 self.identifier() - self.state = 1009 + self.state = 1027 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,125,self._ctx) + _alt = self._interp.adaptivePredict(self._input,127,self._ctx) - self.state = 1011 + self.state = 1029 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 1010 + self.state = 1028 self.match(HogQLParser.COMMA) - self.state = 1013 + self.state = 1031 self.match(HogQLParser.RPAREN) pass elif token in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 97, 98, 99, 101]: - self.state = 1015 + self.state = 1033 self.identifier() - self.state = 1020 + self.state = 1038 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,127,self._ctx) + _alt = self._interp.adaptivePredict(self._input,129,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1016 + self.state = 1034 self.match(HogQLParser.COMMA) - self.state = 1017 + self.state = 1035 self.identifier() - self.state = 1022 + self.state = 1040 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,127,self._ctx) + _alt = self._interp.adaptivePredict(self._input,129,self._ctx) - self.state = 1024 + self.state = 1042 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 1023 + self.state = 1041 self.match(HogQLParser.COMMA) @@ -7398,9 +7521,9 @@ def columnLambdaExpr(self): else: raise NoViableAltException(self) - self.state = 1028 + self.state = 1046 self.match(HogQLParser.ARROW) - self.state = 1029 + self.state = 1047 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -7502,69 +7625,69 @@ def accept(self, visitor:ParseTreeVisitor): def hogqlxTagElement(self): localctx = HogQLParser.HogqlxTagElementContext(self, self._ctx, self.state) - self.enterRule(localctx, 116, self.RULE_hogqlxTagElement) + self.enterRule(localctx, 118, self.RULE_hogqlxTagElement) self._la = 0 # Token type try: - self.state = 1059 + self.state = 1077 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,133,self._ctx) + la_ = self._interp.adaptivePredict(self._input,135,self._ctx) if la_ == 1: localctx = HogQLParser.HogqlxTagElementClosedContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 1031 + self.state = 1049 self.match(HogQLParser.LT) - self.state = 1032 + self.state = 1050 self.identifier() - self.state = 1036 + self.state = 1054 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -181272084561788930) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 201863462911) != 0): - self.state = 1033 + self.state = 1051 self.hogqlxTagAttribute() - self.state = 1038 + self.state = 1056 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1039 + self.state = 1057 self.match(HogQLParser.SLASH) - self.state = 1040 + self.state = 1058 self.match(HogQLParser.GT) pass elif la_ == 2: localctx = HogQLParser.HogqlxTagElementNestedContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 1042 + self.state = 1060 self.match(HogQLParser.LT) - self.state = 1043 + self.state = 1061 self.identifier() - self.state = 1047 + self.state = 1065 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -181272084561788930) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 201863462911) != 0): - self.state = 1044 + self.state = 1062 self.hogqlxTagAttribute() - self.state = 1049 + self.state = 1067 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1050 + self.state = 1068 self.match(HogQLParser.GT) - self.state = 1052 + self.state = 1070 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,132,self._ctx) + la_ = self._interp.adaptivePredict(self._input,134,self._ctx) if la_ == 1: - self.state = 1051 + self.state = 1069 self.hogqlxTagElement() - self.state = 1054 + self.state = 1072 self.match(HogQLParser.LT) - self.state = 1055 + self.state = 1073 self.match(HogQLParser.SLASH) - self.state = 1056 + self.state = 1074 self.identifier() - self.state = 1057 + self.state = 1075 self.match(HogQLParser.GT) pass @@ -7621,38 +7744,38 @@ def accept(self, visitor:ParseTreeVisitor): def hogqlxTagAttribute(self): localctx = HogQLParser.HogqlxTagAttributeContext(self, self._ctx, self.state) - self.enterRule(localctx, 118, self.RULE_hogqlxTagAttribute) + self.enterRule(localctx, 120, self.RULE_hogqlxTagAttribute) try: - self.state = 1072 + self.state = 1090 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,134,self._ctx) + la_ = self._interp.adaptivePredict(self._input,136,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 1061 + self.state = 1079 self.identifier() - self.state = 1062 + self.state = 1080 self.match(HogQLParser.EQ_SINGLE) - self.state = 1063 + self.state = 1081 self.string() pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 1065 + self.state = 1083 self.identifier() - self.state = 1066 + self.state = 1084 self.match(HogQLParser.EQ_SINGLE) - self.state = 1067 + self.state = 1085 self.match(HogQLParser.LBRACE) - self.state = 1068 + self.state = 1086 self.columnExpr(0) - self.state = 1069 + self.state = 1087 self.match(HogQLParser.RBRACE) pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 1071 + self.state = 1089 self.identifier() pass @@ -7701,30 +7824,30 @@ def accept(self, visitor:ParseTreeVisitor): def withExprList(self): localctx = HogQLParser.WithExprListContext(self, self._ctx, self.state) - self.enterRule(localctx, 120, self.RULE_withExprList) + self.enterRule(localctx, 122, self.RULE_withExprList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1074 + self.state = 1092 self.withExpr() - self.state = 1079 + self.state = 1097 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,135,self._ctx) + _alt = self._interp.adaptivePredict(self._input,137,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1075 + self.state = 1093 self.match(HogQLParser.COMMA) - self.state = 1076 + self.state = 1094 self.withExpr() - self.state = 1081 + self.state = 1099 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,135,self._ctx) + _alt = self._interp.adaptivePredict(self._input,137,self._ctx) - self.state = 1083 + self.state = 1101 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 1082 + self.state = 1100 self.match(HogQLParser.COMMA) @@ -7806,34 +7929,34 @@ def accept(self, visitor:ParseTreeVisitor): def withExpr(self): localctx = HogQLParser.WithExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 122, self.RULE_withExpr) + self.enterRule(localctx, 124, self.RULE_withExpr) try: - self.state = 1095 + self.state = 1113 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,137,self._ctx) + la_ = self._interp.adaptivePredict(self._input,139,self._ctx) if la_ == 1: localctx = HogQLParser.WithExprSubqueryContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 1085 + self.state = 1103 self.identifier() - self.state = 1086 + self.state = 1104 self.match(HogQLParser.AS) - self.state = 1087 + self.state = 1105 self.match(HogQLParser.LPAREN) - self.state = 1088 + self.state = 1106 self.selectUnionStmt() - self.state = 1089 + self.state = 1107 self.match(HogQLParser.RPAREN) pass elif la_ == 2: localctx = HogQLParser.WithExprColumnContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 1091 + self.state = 1109 self.columnExpr(0) - self.state = 1092 + self.state = 1110 self.match(HogQLParser.AS) - self.state = 1093 + self.state = 1111 self.identifier() pass @@ -7884,29 +8007,29 @@ def accept(self, visitor:ParseTreeVisitor): def columnIdentifier(self): localctx = HogQLParser.ColumnIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 124, self.RULE_columnIdentifier) + self.enterRule(localctx, 126, self.RULE_columnIdentifier) try: - self.state = 1104 + self.state = 1122 self._errHandler.sync(self) token = self._input.LA(1) if token in [124]: self.enterOuterAlt(localctx, 1) - self.state = 1097 + self.state = 1115 self.placeholder() pass elif token in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 97, 98, 99, 101]: self.enterOuterAlt(localctx, 2) - self.state = 1101 + self.state = 1119 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,138,self._ctx) + la_ = self._interp.adaptivePredict(self._input,140,self._ctx) if la_ == 1: - self.state = 1098 + self.state = 1116 self.tableIdentifier() - self.state = 1099 + self.state = 1117 self.match(HogQLParser.DOT) - self.state = 1103 + self.state = 1121 self.nestedIdentifier() pass else: @@ -7956,23 +8079,23 @@ def accept(self, visitor:ParseTreeVisitor): def nestedIdentifier(self): localctx = HogQLParser.NestedIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 126, self.RULE_nestedIdentifier) + self.enterRule(localctx, 128, self.RULE_nestedIdentifier) try: self.enterOuterAlt(localctx, 1) - self.state = 1106 + self.state = 1124 self.identifier() - self.state = 1111 + self.state = 1129 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,140,self._ctx) + _alt = self._interp.adaptivePredict(self._input,142,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1107 + self.state = 1125 self.match(HogQLParser.DOT) - self.state = 1108 + self.state = 1126 self.identifier() - self.state = 1113 + self.state = 1131 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,140,self._ctx) + _alt = self._interp.adaptivePredict(self._input,142,self._ctx) except RecognitionException as re: localctx.exception = re @@ -8119,19 +8242,19 @@ def tableExpr(self, _p:int=0): _parentState = self.state localctx = HogQLParser.TableExprContext(self, self._ctx, _parentState) _prevctx = localctx - _startState = 128 - self.enterRecursionRule(localctx, 128, self.RULE_tableExpr, _p) + _startState = 130 + self.enterRecursionRule(localctx, 130, self.RULE_tableExpr, _p) try: self.enterOuterAlt(localctx, 1) - self.state = 1123 + self.state = 1141 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,141,self._ctx) + la_ = self._interp.adaptivePredict(self._input,143,self._ctx) if la_ == 1: localctx = HogQLParser.TableExprIdentifierContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1115 + self.state = 1133 self.tableIdentifier() pass @@ -8139,7 +8262,7 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprFunctionContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1116 + self.state = 1134 self.tableFunctionExpr() pass @@ -8147,11 +8270,11 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprSubqueryContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1117 + self.state = 1135 self.match(HogQLParser.LPAREN) - self.state = 1118 + self.state = 1136 self.selectUnionStmt() - self.state = 1119 + self.state = 1137 self.match(HogQLParser.RPAREN) pass @@ -8159,7 +8282,7 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprTagContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1121 + self.state = 1139 self.hogqlxTagElement() pass @@ -8167,15 +8290,15 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprPlaceholderContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1122 + self.state = 1140 self.placeholder() pass self._ctx.stop = self._input.LT(-1) - self.state = 1133 + self.state = 1151 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,143,self._ctx) + _alt = self._interp.adaptivePredict(self._input,145,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: if self._parseListeners is not None: @@ -8183,29 +8306,29 @@ def tableExpr(self, _p:int=0): _prevctx = localctx localctx = HogQLParser.TableExprAliasContext(self, HogQLParser.TableExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_tableExpr) - self.state = 1125 + self.state = 1143 if not self.precpred(self._ctx, 3): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 3)") - self.state = 1129 + self.state = 1147 self._errHandler.sync(self) token = self._input.LA(1) if token in [19, 28, 37, 46, 101]: - self.state = 1126 + self.state = 1144 self.alias() pass elif token in [6]: - self.state = 1127 + self.state = 1145 self.match(HogQLParser.AS) - self.state = 1128 + self.state = 1146 self.identifier() pass else: raise NoViableAltException(self) - self.state = 1135 + self.state = 1153 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,143,self._ctx) + _alt = self._interp.adaptivePredict(self._input,145,self._ctx) except RecognitionException as re: localctx.exception = re @@ -8252,23 +8375,23 @@ def accept(self, visitor:ParseTreeVisitor): def tableFunctionExpr(self): localctx = HogQLParser.TableFunctionExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 130, self.RULE_tableFunctionExpr) + self.enterRule(localctx, 132, self.RULE_tableFunctionExpr) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1136 + self.state = 1154 self.identifier() - self.state = 1137 + self.state = 1155 self.match(HogQLParser.LPAREN) - self.state = 1139 + self.state = 1157 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -1125900443713538) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 8076106347046764543) != 0) or ((((_la - 128)) & ~0x3f) == 0 and ((1 << (_la - 128)) & 1153) != 0): - self.state = 1138 + self.state = 1156 self.tableArgList() - self.state = 1141 + self.state = 1159 self.match(HogQLParser.RPAREN) except RecognitionException as re: localctx.exception = re @@ -8312,20 +8435,20 @@ def accept(self, visitor:ParseTreeVisitor): def tableIdentifier(self): localctx = HogQLParser.TableIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 132, self.RULE_tableIdentifier) + self.enterRule(localctx, 134, self.RULE_tableIdentifier) try: self.enterOuterAlt(localctx, 1) - self.state = 1146 + self.state = 1164 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,145,self._ctx) + la_ = self._interp.adaptivePredict(self._input,147,self._ctx) if la_ == 1: - self.state = 1143 + self.state = 1161 self.databaseIdentifier() - self.state = 1144 + self.state = 1162 self.match(HogQLParser.DOT) - self.state = 1148 + self.state = 1166 self.identifier() except RecognitionException as re: localctx.exception = re @@ -8371,30 +8494,30 @@ def accept(self, visitor:ParseTreeVisitor): def tableArgList(self): localctx = HogQLParser.TableArgListContext(self, self._ctx, self.state) - self.enterRule(localctx, 134, self.RULE_tableArgList) + self.enterRule(localctx, 136, self.RULE_tableArgList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1150 + self.state = 1168 self.columnExpr(0) - self.state = 1155 + self.state = 1173 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,146,self._ctx) + _alt = self._interp.adaptivePredict(self._input,148,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1151 + self.state = 1169 self.match(HogQLParser.COMMA) - self.state = 1152 + self.state = 1170 self.columnExpr(0) - self.state = 1157 + self.state = 1175 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,146,self._ctx) + _alt = self._interp.adaptivePredict(self._input,148,self._ctx) - self.state = 1159 + self.state = 1177 self._errHandler.sync(self) _la = self._input.LA(1) if _la==112: - self.state = 1158 + self.state = 1176 self.match(HogQLParser.COMMA) @@ -8433,10 +8556,10 @@ def accept(self, visitor:ParseTreeVisitor): def databaseIdentifier(self): localctx = HogQLParser.DatabaseIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 136, self.RULE_databaseIdentifier) + self.enterRule(localctx, 138, self.RULE_databaseIdentifier) try: self.enterOuterAlt(localctx, 1) - self.state = 1161 + self.state = 1179 self.identifier() except RecognitionException as re: localctx.exception = re @@ -8484,22 +8607,22 @@ def accept(self, visitor:ParseTreeVisitor): def floatingLiteral(self): localctx = HogQLParser.FloatingLiteralContext(self, self._ctx, self.state) - self.enterRule(localctx, 138, self.RULE_floatingLiteral) + self.enterRule(localctx, 140, self.RULE_floatingLiteral) self._la = 0 # Token type try: - self.state = 1171 + self.state = 1189 self._errHandler.sync(self) token = self._input.LA(1) if token in [102]: self.enterOuterAlt(localctx, 1) - self.state = 1163 + self.state = 1181 self.match(HogQLParser.FLOATING_LITERAL) pass elif token in [116]: self.enterOuterAlt(localctx, 2) - self.state = 1164 + self.state = 1182 self.match(HogQLParser.DOT) - self.state = 1165 + self.state = 1183 _la = self._input.LA(1) if not(_la==103 or _la==104): self._errHandler.recoverInline(self) @@ -8509,15 +8632,15 @@ def floatingLiteral(self): pass elif token in [104]: self.enterOuterAlt(localctx, 3) - self.state = 1166 + self.state = 1184 self.match(HogQLParser.DECIMAL_LITERAL) - self.state = 1167 + self.state = 1185 self.match(HogQLParser.DOT) - self.state = 1169 + self.state = 1187 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,148,self._ctx) + la_ = self._interp.adaptivePredict(self._input,150,self._ctx) if la_ == 1: - self.state = 1168 + self.state = 1186 _la = self._input.LA(1) if not(_la==103 or _la==104): self._errHandler.recoverInline(self) @@ -8586,15 +8709,15 @@ def accept(self, visitor:ParseTreeVisitor): def numberLiteral(self): localctx = HogQLParser.NumberLiteralContext(self, self._ctx, self.state) - self.enterRule(localctx, 140, self.RULE_numberLiteral) + self.enterRule(localctx, 142, self.RULE_numberLiteral) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1174 + self.state = 1192 self._errHandler.sync(self) _la = self._input.LA(1) if _la==114 or _la==135: - self.state = 1173 + self.state = 1191 _la = self._input.LA(1) if not(_la==114 or _la==135): self._errHandler.recoverInline(self) @@ -8603,36 +8726,36 @@ def numberLiteral(self): self.consume() - self.state = 1182 + self.state = 1200 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,151,self._ctx) + la_ = self._interp.adaptivePredict(self._input,153,self._ctx) if la_ == 1: - self.state = 1176 + self.state = 1194 self.floatingLiteral() pass elif la_ == 2: - self.state = 1177 + self.state = 1195 self.match(HogQLParser.OCTAL_LITERAL) pass elif la_ == 3: - self.state = 1178 + self.state = 1196 self.match(HogQLParser.DECIMAL_LITERAL) pass elif la_ == 4: - self.state = 1179 + self.state = 1197 self.match(HogQLParser.HEXADECIMAL_LITERAL) pass elif la_ == 5: - self.state = 1180 + self.state = 1198 self.match(HogQLParser.INF) pass elif la_ == 6: - self.state = 1181 + self.state = 1199 self.match(HogQLParser.NAN_SQL) pass @@ -8678,24 +8801,24 @@ def accept(self, visitor:ParseTreeVisitor): def literal(self): localctx = HogQLParser.LiteralContext(self, self._ctx, self.state) - self.enterRule(localctx, 142, self.RULE_literal) + self.enterRule(localctx, 144, self.RULE_literal) try: - self.state = 1187 + self.state = 1205 self._errHandler.sync(self) token = self._input.LA(1) if token in [41, 55, 102, 103, 104, 105, 114, 116, 135]: self.enterOuterAlt(localctx, 1) - self.state = 1184 + self.state = 1202 self.numberLiteral() pass elif token in [106]: self.enterOuterAlt(localctx, 2) - self.state = 1185 + self.state = 1203 self.match(HogQLParser.STRING_LITERAL) pass elif token in [57]: self.enterOuterAlt(localctx, 3) - self.state = 1186 + self.state = 1204 self.match(HogQLParser.NULL_SQL) pass else: @@ -8756,11 +8879,11 @@ def accept(self, visitor:ParseTreeVisitor): def interval(self): localctx = HogQLParser.IntervalContext(self, self._ctx, self.state) - self.enterRule(localctx, 144, self.RULE_interval) + self.enterRule(localctx, 146, self.RULE_interval) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1189 + self.state = 1207 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 27021666484748288) != 0) or ((((_la - 68)) & ~0x3f) == 0 and ((1 << (_la - 68)) & 2181038337) != 0)): self._errHandler.recoverInline(self) @@ -9053,11 +9176,11 @@ def accept(self, visitor:ParseTreeVisitor): def keyword(self): localctx = HogQLParser.KeywordContext(self, self._ctx, self.state) - self.enterRule(localctx, 146, self.RULE_keyword) + self.enterRule(localctx, 148, self.RULE_keyword) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1191 + self.state = 1209 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & -208293751046537218) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 29527896047) != 0)): self._errHandler.recoverInline(self) @@ -9107,11 +9230,11 @@ def accept(self, visitor:ParseTreeVisitor): def keywordForAlias(self): localctx = HogQLParser.KeywordForAliasContext(self, self._ctx, self.state) - self.enterRule(localctx, 148, self.RULE_keywordForAlias) + self.enterRule(localctx, 150, self.RULE_keywordForAlias) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1193 + self.state = 1211 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 70506452090880) != 0)): self._errHandler.recoverInline(self) @@ -9156,19 +9279,19 @@ def accept(self, visitor:ParseTreeVisitor): def alias(self): localctx = HogQLParser.AliasContext(self, self._ctx, self.state) - self.enterRule(localctx, 150, self.RULE_alias) + self.enterRule(localctx, 152, self.RULE_alias) try: - self.state = 1197 + self.state = 1215 self._errHandler.sync(self) token = self._input.LA(1) if token in [101]: self.enterOuterAlt(localctx, 1) - self.state = 1195 + self.state = 1213 self.match(HogQLParser.IDENTIFIER) pass elif token in [19, 28, 37, 46]: self.enterOuterAlt(localctx, 2) - self.state = 1196 + self.state = 1214 self.keywordForAlias() pass else: @@ -9216,24 +9339,24 @@ def accept(self, visitor:ParseTreeVisitor): def identifier(self): localctx = HogQLParser.IdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 152, self.RULE_identifier) + self.enterRule(localctx, 154, self.RULE_identifier) try: - self.state = 1202 + self.state = 1220 self._errHandler.sync(self) token = self._input.LA(1) if token in [101]: self.enterOuterAlt(localctx, 1) - self.state = 1199 + self.state = 1217 self.match(HogQLParser.IDENTIFIER) pass elif token in [20, 36, 53, 54, 68, 76, 93, 99]: self.enterOuterAlt(localctx, 2) - self.state = 1200 + self.state = 1218 self.interval() pass elif token in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 51, 52, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 70, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 94, 95, 97, 98]: self.enterOuterAlt(localctx, 3) - self.state = 1201 + self.state = 1219 self.keyword() pass else: @@ -9281,14 +9404,14 @@ def accept(self, visitor:ParseTreeVisitor): def enumValue(self): localctx = HogQLParser.EnumValueContext(self, self._ctx, self.state) - self.enterRule(localctx, 154, self.RULE_enumValue) + self.enterRule(localctx, 156, self.RULE_enumValue) try: self.enterOuterAlt(localctx, 1) - self.state = 1204 + self.state = 1222 self.string() - self.state = 1205 + self.state = 1223 self.match(HogQLParser.EQ_SINGLE) - self.state = 1206 + self.state = 1224 self.numberLiteral() except RecognitionException as re: localctx.exception = re @@ -9331,14 +9454,14 @@ def accept(self, visitor:ParseTreeVisitor): def placeholder(self): localctx = HogQLParser.PlaceholderContext(self, self._ctx, self.state) - self.enterRule(localctx, 156, self.RULE_placeholder) + self.enterRule(localctx, 158, self.RULE_placeholder) try: self.enterOuterAlt(localctx, 1) - self.state = 1208 + self.state = 1226 self.match(HogQLParser.LBRACE) - self.state = 1209 + self.state = 1227 self.identifier() - self.state = 1210 + self.state = 1228 self.match(HogQLParser.RBRACE) except RecognitionException as re: localctx.exception = re @@ -9378,19 +9501,19 @@ def accept(self, visitor:ParseTreeVisitor): def string(self): localctx = HogQLParser.StringContext(self, self._ctx, self.state) - self.enterRule(localctx, 158, self.RULE_string) + self.enterRule(localctx, 160, self.RULE_string) try: - self.state = 1214 + self.state = 1232 self._errHandler.sync(self) token = self._input.LA(1) if token in [106]: self.enterOuterAlt(localctx, 1) - self.state = 1212 + self.state = 1230 self.match(HogQLParser.STRING_LITERAL) pass elif token in [138]: self.enterOuterAlt(localctx, 2) - self.state = 1213 + self.state = 1231 self.templateString() pass else: @@ -9440,23 +9563,23 @@ def accept(self, visitor:ParseTreeVisitor): def templateString(self): localctx = HogQLParser.TemplateStringContext(self, self._ctx, self.state) - self.enterRule(localctx, 160, self.RULE_templateString) + self.enterRule(localctx, 162, self.RULE_templateString) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1216 + self.state = 1234 self.match(HogQLParser.QUOTE_SINGLE_TEMPLATE) - self.state = 1220 + self.state = 1238 self._errHandler.sync(self) _la = self._input.LA(1) while _la==152 or _la==153: - self.state = 1217 + self.state = 1235 self.stringContents() - self.state = 1222 + self.state = 1240 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1223 + self.state = 1241 self.match(HogQLParser.QUOTE_SINGLE) except RecognitionException as re: localctx.exception = re @@ -9502,23 +9625,23 @@ def accept(self, visitor:ParseTreeVisitor): def stringContents(self): localctx = HogQLParser.StringContentsContext(self, self._ctx, self.state) - self.enterRule(localctx, 162, self.RULE_stringContents) + self.enterRule(localctx, 164, self.RULE_stringContents) try: - self.state = 1230 + self.state = 1248 self._errHandler.sync(self) token = self._input.LA(1) if token in [153]: self.enterOuterAlt(localctx, 1) - self.state = 1225 + self.state = 1243 self.match(HogQLParser.STRING_ESCAPE_TRIGGER) - self.state = 1226 + self.state = 1244 self.columnExpr(0) - self.state = 1227 + self.state = 1245 self.match(HogQLParser.RBRACE) pass elif token in [152]: self.enterOuterAlt(localctx, 2) - self.state = 1229 + self.state = 1247 self.match(HogQLParser.STRING_TEXT) pass else: @@ -9568,23 +9691,23 @@ def accept(self, visitor:ParseTreeVisitor): def fullTemplateString(self): localctx = HogQLParser.FullTemplateStringContext(self, self._ctx, self.state) - self.enterRule(localctx, 164, self.RULE_fullTemplateString) + self.enterRule(localctx, 166, self.RULE_fullTemplateString) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1232 + self.state = 1250 self.match(HogQLParser.QUOTE_SINGLE_TEMPLATE_FULL) - self.state = 1236 + self.state = 1254 self._errHandler.sync(self) _la = self._input.LA(1) while _la==154 or _la==155: - self.state = 1233 + self.state = 1251 self.stringContentsFull() - self.state = 1238 + self.state = 1256 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1239 + self.state = 1257 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -9630,23 +9753,23 @@ def accept(self, visitor:ParseTreeVisitor): def stringContentsFull(self): localctx = HogQLParser.StringContentsFullContext(self, self._ctx, self.state) - self.enterRule(localctx, 166, self.RULE_stringContentsFull) + self.enterRule(localctx, 168, self.RULE_stringContentsFull) try: - self.state = 1246 + self.state = 1264 self._errHandler.sync(self) token = self._input.LA(1) if token in [155]: self.enterOuterAlt(localctx, 1) - self.state = 1241 + self.state = 1259 self.match(HogQLParser.FULL_STRING_ESCAPE_TRIGGER) - self.state = 1242 + self.state = 1260 self.columnExpr(0) - self.state = 1243 + self.state = 1261 self.match(HogQLParser.RBRACE) pass elif token in [154]: self.enterOuterAlt(localctx, 2) - self.state = 1245 + self.state = 1263 self.match(HogQLParser.FULL_STRING_TEXT) pass else: @@ -9665,9 +9788,9 @@ def stringContentsFull(self): def sempred(self, localctx:RuleContext, ruleIndex:int, predIndex:int): if self._predicates == None: self._predicates = dict() - self._predicates[35] = self.joinExpr_sempred - self._predicates[54] = self.columnExpr_sempred - self._predicates[64] = self.tableExpr_sempred + self._predicates[36] = self.joinExpr_sempred + self._predicates[55] = self.columnExpr_sempred + self._predicates[65] = self.tableExpr_sempred pred = self._predicates.get(ruleIndex, None) if pred is None: raise Exception("No predicate with index:" + str(ruleIndex)) diff --git a/posthog/hogql/grammar/HogQLParserVisitor.py b/posthog/hogql/grammar/HogQLParserVisitor.py index 6b0ffe0320d55..47f93b5fbfb16 100644 --- a/posthog/hogql/grammar/HogQLParserVisitor.py +++ b/posthog/hogql/grammar/HogQLParserVisitor.py @@ -59,6 +59,11 @@ def visitForStmt(self, ctx:HogQLParser.ForStmtContext): return self.visitChildren(ctx) + # Visit a parse tree produced by HogQLParser#forInStmt. + def visitForInStmt(self, ctx:HogQLParser.ForInStmtContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by HogQLParser#funcStmt. def visitFuncStmt(self, ctx:HogQLParser.FuncStmtContext): return self.visitChildren(ctx) diff --git a/posthog/hogql/parser.py b/posthog/hogql/parser.py index 44255bc82bfbd..0ca61a3117819 100644 --- a/posthog/hogql/parser.py +++ b/posthog/hogql/parser.py @@ -258,6 +258,16 @@ def visitWhileStmt(self, ctx: HogQLParser.WhileStmtContext): body=self.visit(ctx.statement()) if ctx.statement() else None, ) + def visitForInStmt(self, ctx: HogQLParser.ForInStmtContext): + first_identifier = ctx.identifier(0).getText() + second_identifier = ctx.identifier(1).getText() if ctx.identifier(1) else None + return ast.ForInStatement( + valueVar=second_identifier if second_identifier is not None else first_identifier, + keyVar=first_identifier if second_identifier is not None else None, + expr=self.visit(ctx.expression()), + body=self.visit(ctx.statement()), + ) + def visitForStmt(self, ctx: HogQLParser.ForStmtContext): initializer = ctx.initializerVarDeclr or ctx.initializerVarAssignment or ctx.initializerExpression increment = ctx.incrementVarDeclr or ctx.incrementVarAssignment or ctx.incrementExpression diff --git a/posthog/hogql/test/_test_parser.py b/posthog/hogql/test/_test_parser.py index 5a00005976ad8..f34be4753efba 100644 --- a/posthog/hogql/test/_test_parser.py +++ b/posthog/hogql/test/_test_parser.py @@ -2254,4 +2254,45 @@ def test_pop_empty_stack(self): self._select("select } from events") self.assertEqual(str(e.exception), "Unmatched curly bracket") + def test_for_in_loops(self): + code = """ + for (let i in [1, 2, 3]) { + print(a); + } + """ + program = self._program(code) + expected = ast.Program( + declarations=[ + ast.ForInStatement( + keyVar=None, + valueVar="i", + expr=ast.Array(exprs=[Constant(value=1), Constant(value=2), Constant(value=3)]), + body=ast.Block( + declarations=[ast.ExprStatement(expr=Call(name="print", args=[Field(chain=["a"])]))] + ), + ) + ] + ) + self.assertEqual(program, expected) + + code = """ + for (let key, value in [1, 2, 3]) { + print(a); + } + """ + program = self._program(code) + expected = ast.Program( + declarations=[ + ast.ForInStatement( + keyVar="key", + valueVar="value", + expr=ast.Array(exprs=[Constant(value=1), Constant(value=2), Constant(value=3)]), + body=ast.Block( + declarations=[ast.ExprStatement(expr=Call(name="print", args=[Field(chain=["a"])]))] + ), + ) + ] + ) + self.assertEqual(program, expected) + return TestParser diff --git a/posthog/hogql/test/test_autocomplete.py b/posthog/hogql/test/test_autocomplete.py index e1d19d6ce22f7..82a08eed3a111 100644 --- a/posthog/hogql/test/test_autocomplete.py +++ b/posthog/hogql/test/test_autocomplete.py @@ -63,7 +63,7 @@ def _program( kind="HogQLAutocomplete", query=query, language=HogLanguage.HOG, - sourceQuery=HogQLQuery(query="select * from events"), + globals={"event": "$pageview"}, startPosition=start, endPosition=end, ) @@ -314,7 +314,7 @@ def test_autocomplete_template_strings(self): database = create_hogql_database(team_id=self.team.pk, team_arg=self.team) query = "this isn't a string {concat(eve)} <- this is" - results = self._template(query=query, start=31, end=31, database=database) + results = self._template(query=query, start=28, end=31, database=database) suggestions = list(filter(lambda x: x.label == "event", results.suggestions)) assert len(suggestions) == 1 @@ -327,12 +327,26 @@ def test_autocomplete_template_strings(self): def test_autocomplete_hog(self): database = create_hogql_database(team_id=self.team.pk, team_arg=self.team) + # 1 query = "let var1 := 3; let otherVar := 5; print(v)" results = self._program(query=query, start=41, end=41, database=database) suggestions = list(filter(lambda x: x.kind == Kind.VARIABLE, results.suggestions)) - assert len(suggestions) == 2 - assert sorted([suggestion.label for suggestion in suggestions]) == ["otherVar", "var1"] + assert sorted([suggestion.label for suggestion in suggestions]) == ["event", "otherVar", "var1"] suggestions = list(filter(lambda x: x.kind == Kind.FUNCTION, results.suggestions)) assert len(suggestions) > 0 + + # 2 + query = "let var1 := 3; let otherVar := 5; print(v)" + results = self._program(query=query, start=16, end=16, database=database) + + suggestions = list(filter(lambda x: x.kind == Kind.VARIABLE, results.suggestions)) + assert sorted([suggestion.label for suggestion in suggestions]) == ["event", "var1"] + + # 3 + query = "let var1 := 3; let otherVar := 5; print(v)" + results = self._program(query=query, start=34, end=34, database=database) + + suggestions = list(filter(lambda x: x.kind == Kind.VARIABLE, results.suggestions)) + assert sorted([suggestion.label for suggestion in suggestions]) == ["event", "otherVar", "var1"] diff --git a/posthog/hogql/visitor.py b/posthog/hogql/visitor.py index d5e0bf11360b8..536a482c14236 100644 --- a/posthog/hogql/visitor.py +++ b/posthog/hogql/visitor.py @@ -299,6 +299,10 @@ def visit_for_statement(self, node: ast.ForStatement): self.visit(node.increment) self.visit(node.body) + def visit_for_in_statement(self, node: ast.ForInStatement): + self.visit(node.expr) + self.visit(node.body) + def visit_expr_statement(self, node: ast.ExprStatement): self.visit(node.expr) @@ -649,6 +653,16 @@ def visit_for_statement(self, node: ast.ForStatement): body=self.visit(node.body), ) + def visit_for_in_statement(self, node: ast.ForInStatement): + return ast.ForInStatement( + start=None if self.clear_locations else node.start, + end=None if self.clear_locations else node.end, + valueVar=node.valueVar, + keyVar=node.keyVar, + expr=self.visit(node.expr), + body=self.visit(node.body), + ) + def visit_expr_statement(self, node: ast.ExprStatement): return ast.ExprStatement( start=None if self.clear_locations else node.start, diff --git a/posthog/hogql_queries/insights/funnels/test/breakdown_cases.py b/posthog/hogql_queries/insights/funnels/test/breakdown_cases.py index 1f5beb9224729..cb38a0f91be71 100644 --- a/posthog/hogql_queries/insights/funnels/test/breakdown_cases.py +++ b/posthog/hogql_queries/insights/funnels/test/breakdown_cases.py @@ -2957,10 +2957,6 @@ def test_funnel_aggregate_by_groups_breakdown_group(self): ], ) - @also_test_with_materialized_columns( - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @also_test_with_person_on_events_v2 @snapshot_clickhouse_queries def test_funnel_aggregate_by_groups_breakdown_group_person_on_events(self): diff --git a/posthog/hogql_queries/insights/funnels/test/test_funnel_correlation.py b/posthog/hogql_queries/insights/funnels/test/test_funnel_correlation.py index 75daa35aa5cc5..da85943dcd894 100644 --- a/posthog/hogql_queries/insights/funnels/test/test_funnel_correlation.py +++ b/posthog/hogql_queries/insights/funnels/test/test_funnel_correlation.py @@ -865,7 +865,6 @@ def test_funnel_correlation_with_properties_and_groups(self): @also_test_with_materialized_columns( event_properties=[], person_properties=["$browser"], - group_properties=[(0, "industry")], verify_no_jsonextract=False, ) @also_test_with_person_on_events_v2 diff --git a/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py b/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py index 0c5e360f762cd..4b593c75e8e6d 100644 --- a/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py +++ b/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py @@ -3871,11 +3871,6 @@ def test_groups_filtering(self): ], ) - @also_test_with_materialized_columns( - ["$current_url", "$screen_name"], - group_properties=[(0, "industry"), (1, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_groups_filtering_person_on_events(self): self._create_groups() diff --git a/posthog/hogql_queries/insights/trends/test/test_trends.py b/posthog/hogql_queries/insights/trends/test/test_trends.py index 6a9458099cf0a..7cb7980cf68b3 100644 --- a/posthog/hogql_queries/insights/trends/test/test_trends.py +++ b/posthog/hogql_queries/insights/trends/test/test_trends.py @@ -8679,11 +8679,6 @@ def test_breakdown_with_filter_groups(self): self.assertEqual(response[1]["breakdown_value"], "uh") self.assertEqual(response[1]["count"], 1) - @also_test_with_materialized_columns( - event_properties=["key"], - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_breakdown_with_filter_groups_person_on_events(self): self._create_groups() @@ -8881,9 +8876,6 @@ def test_breakdown_by_group_props(self): self.assertEqual(res[0][0]["distinct_ids"], ["person1"]) @freeze_time("2020-01-01") - @also_test_with_materialized_columns( - group_properties=[(0, "industry")], materialize_only_with_person_on_events=True - ) @snapshot_clickhouse_queries def test_breakdown_by_group_props_person_on_events(self): self._create_groups() @@ -9099,11 +9091,6 @@ def test_filtering_with_group_props_event_with_no_group_data(self): # set to a value other than textiles AND events with no group at all self.assertEqual(response[0]["count"], 4) - @also_test_with_materialized_columns( - person_properties=["key"], - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_breakdown_by_group_props_with_person_filter_person_on_events(self): self._create_groups() @@ -9149,11 +9136,6 @@ def test_breakdown_by_group_props_with_person_filter_person_on_events(self): self.assertEqual(response[0]["breakdown_value"], "finance") self.assertEqual(response[0]["count"], 1) - @also_test_with_materialized_columns( - person_properties=["key"], - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_filtering_with_group_props_person_on_events(self): self._create_groups() @@ -9210,10 +9192,6 @@ def test_filtering_with_group_props_person_on_events(self): self.assertEqual(response[0]["count"], 1) @freeze_time("2020-01-01") - @also_test_with_materialized_columns( - group_properties=[(0, "industry"), (2, "name")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_filtering_by_multiple_groups_person_on_events(self): GroupTypeMapping.objects.create(team=self.team, group_type="organization", group_type_index=0) diff --git a/posthog/hogql_queries/query_runner.py b/posthog/hogql_queries/query_runner.py index db71cb40346b1..9ec423da20e96 100644 --- a/posthog/hogql_queries/query_runner.py +++ b/posthog/hogql_queries/query_runner.py @@ -53,6 +53,7 @@ QueryStatusResponse, GenericCachedQueryResponse, QueryStatus, + SessionAttributionExplorerQuery, ) from posthog.schema_helpers import to_dict, to_json from posthog.utils import generate_cache_key, get_from_dict_or_attr, get_safe_cache @@ -137,6 +138,7 @@ def shared_insights_execution_mode(execution_mode: ExecutionMode) -> ExecutionMo WebOverviewQuery, WebStatsTableQuery, WebTopClicksQuery, + SessionAttributionExplorerQuery, ] @@ -317,6 +319,17 @@ def get_query_runner( limit_context=limit_context, ) + if kind == "SessionAttributionExplorerQuery": + from .web_analytics.session_attribution_explorer_query_runner import SessionAttributionExplorerQueryRunner + + return SessionAttributionExplorerQueryRunner( + query=query, + team=team, + timings=timings, + modifiers=modifiers, + limit_context=limit_context, + ) + raise ValueError(f"Can't get a runner for an unknown query kind: {kind}") diff --git a/posthog/hogql_queries/web_analytics/session_attribution_explorer_query_runner.py b/posthog/hogql_queries/web_analytics/session_attribution_explorer_query_runner.py new file mode 100644 index 0000000000000..63401ebec93f7 --- /dev/null +++ b/posthog/hogql_queries/web_analytics/session_attribution_explorer_query_runner.py @@ -0,0 +1,118 @@ +from posthog.hogql import ast +from posthog.hogql.constants import LimitContext +from posthog.hogql.parser import parse_select +from posthog.hogql_queries.insights.paginators import HogQLHasMorePaginator +from posthog.hogql_queries.query_runner import QueryRunner +from posthog.schema import ( + CachedSessionAttributionExplorerQueryResponse, + SessionAttributionExplorerQuery, + SessionAttributionExplorerQueryResponse, + SessionAttributionGroupBy, + HogQLFilters, +) + +BREAKDOWN_NULL_DISPLAY = "(none)" + + +class SessionAttributionExplorerQueryRunner(QueryRunner): + query: SessionAttributionExplorerQuery + response: SessionAttributionExplorerQueryResponse + cached_response: CachedSessionAttributionExplorerQueryResponse + paginator: HogQLHasMorePaginator + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.paginator = HogQLHasMorePaginator.from_limit_context( + limit_context=LimitContext.QUERY, limit=self.query.limit if self.query.limit else None + ) + + def to_query(self) -> ast.SelectQuery: + with self.timings.measure("session_attribution_query_runner"): + # We use string concatenation here, so that the resultant hogql can be opened as its own hogql insight, + # so this part must never use user inputs. + # Note that {filters} is a placeholder that is left as is, and replaced with actual filters in + # execute_hogql_query. Those filters *are* user input, which is why placeholders are used. It helps that + # placeholders are valid in HogQL insights too, so it will "just work"! + + group_by = [] + + def group_or_agg(group_name, field, result): + if group_name in self.query.groupBy: + group_by.append(f'"{result}"') + return field + else: + return f"topK(10)({field})" + + channel_type = group_or_agg( + SessionAttributionGroupBy.CHANNEL_TYPE, "$channel_type", "context.columns.channel_type" + ) + referring_domain = group_or_agg( + SessionAttributionGroupBy.REFERRING_DOMAIN, + "$entry_referring_domain", + "context.columns.referring_domain", + ) + utm_source = group_or_agg( + SessionAttributionGroupBy.SOURCE, "$entry_utm_source", "context.columns.utm_source" + ) + utm_medium = group_or_agg( + SessionAttributionGroupBy.MEDIUM, "$entry_utm_medium", "context.columns.utm_medium" + ) + utm_campaign = group_or_agg( + SessionAttributionGroupBy.CAMPAIGN, "$entry_utm_campaign", "context.columns.utm_campaign" + ) + ad_ids = group_or_agg( + SessionAttributionGroupBy.AD_IDS, + "nullIf(arrayStringConcat([if(isNotNull($entry_gclid), 'glcid', NULL), if(isNotNull($entry_gad_source), 'gad_source', NULL)], ','), '')", + "context.columns.ad_ids", + ) + entry_url = group_or_agg( + SessionAttributionGroupBy.INITIAL_URL, "$entry_current_url", "context.columns.example_entry_urls" + ) + + filters = "{filters}" + group_by_str = ("GROUP BY" + ", ".join(group_by)) if group_by else "" + + query_str = f""" +SELECT + count() as "context.columns.count", + {channel_type} as "context.columns.channel_type", + {referring_domain} as "context.columns.referring_domain", + {utm_source} as "context.columns.utm_source", + {utm_medium} as "context.columns.utm_medium", + {utm_campaign} as "context.columns.utm_campaign", + {ad_ids} as "context.columns.ad_ids", + {entry_url} as "context.columns.example_entry_urls" +FROM sessions +WHERE {filters} +{group_by_str} +ORDER BY "context.columns.count" DESC +""" + + query = parse_select( + query_str, + timings=self.timings, + ) + assert isinstance(query, ast.SelectQuery) + return query + + def calculate(self): + response = self.paginator.execute_hogql_query( + query_type="session_attribution_query", + query=self.to_query(), + team=self.team, + timings=self.timings, + modifiers=self.modifiers, + filters=HogQLFilters(dateRange=self.query.filters.dateRange, properties=self.query.filters.properties) + if self.query.filters + else None, + ) + + return SessionAttributionExplorerQueryResponse( + columns=response.columns, + results=response.results, + timings=response.timings, + types=response.types, + hogql=response.hogql, + modifiers=self.modifiers, + **self.paginator.response_params(), + ) diff --git a/posthog/hogql_queries/web_analytics/test/test_session_attribution_explorer_query_runner.py b/posthog/hogql_queries/web_analytics/test/test_session_attribution_explorer_query_runner.py new file mode 100644 index 0000000000000..331e7dd806f2f --- /dev/null +++ b/posthog/hogql_queries/web_analytics/test/test_session_attribution_explorer_query_runner.py @@ -0,0 +1,221 @@ +from typing import Optional + +from parameterized import parameterized + +from posthog.hogql.constants import LimitContext +from posthog.hogql_queries.web_analytics.session_attribution_explorer_query_runner import ( + SessionAttributionExplorerQueryRunner, +) +from posthog.models.utils import uuid7 +from posthog.schema import ( + DateRange, + SessionTableVersion, + HogQLQueryModifiers, + SessionAttributionExplorerQuery, + Filters, + SessionAttributionGroupBy, + SessionPropertyFilter, + PropertyOperator, +) +from posthog.test.base import ( + APIBaseTest, + ClickhouseTestMixin, + _create_event, +) + + +class TestSessionAttributionQueryRunner(ClickhouseTestMixin, APIBaseTest): + def _create_session( + self, url=None, source=None, medium=None, campaign=None, gclid=None, gad_source=None, referring_domain="$direct" + ): + session_id = str(uuid7()) + _create_event( + team=self.team, + event="$pageview", + distinct_id=session_id, + properties={ + "$session_id": session_id, + "$current_url": url, + "utm_source": source, + "utm_medium": medium, + "utm_campaign": campaign, + "gclid": gclid, + "gad_source": gad_source, + "$referring_domain": referring_domain, + }, + ) + + def _create_data(self): + # use powers of 2 for the number of sessions, so that all orderings are unambiguous (to ensure the test is deterministic) + for _ in range(4): + self._create_session( + url="http://example.com/1a", + source="source1", + medium="medium1", + campaign="campaign1a", + referring_domain="referring_domain1a", + gclid="gclid1a", + gad_source="gad_source1a", + ) + for _ in range(2): + self._create_session( + url="http://example.com/1b", + source="source1", + medium="medium1", + campaign="campaign1b", + referring_domain="referring_domain1b", + gclid="gclid1b", + gad_source="gad_source1b", + ) + for _ in range(1): + self._create_session( + url="http://example.com/2", + source="source2", + medium="medium2", + campaign="campaign2", + referring_domain="referring_domain2", + ) + + def _run_session_attribution_query( + self, + date_from: Optional[str] = None, + date_to: Optional[str] = None, + session_table_version: SessionTableVersion = SessionTableVersion.V1, + group_by: Optional[list[SessionAttributionGroupBy]] = None, + limit_context: Optional[LimitContext] = None, + properties: Optional[list[SessionPropertyFilter]] = None, + ): + modifiers = HogQLQueryModifiers(sessionTableVersion=session_table_version) + query = SessionAttributionExplorerQuery( + filters=Filters(dateRange=DateRange(date_from=date_from, date_to=date_to), properties=properties or []), + groupBy=group_by or [], + modifiers=modifiers, + ) + runner = SessionAttributionExplorerQueryRunner(team=self.team, query=query, limit_context=limit_context) + return runner.calculate() + + @parameterized.expand([[SessionTableVersion.V1], [SessionTableVersion.V2]]) + def test_no_crash_when_no_data(self, session_table_version: SessionTableVersion): + results = self._run_session_attribution_query( + session_table_version=session_table_version, + ).results + assert results == [(0, [], [], [], [], [], [], [])] + + @parameterized.expand([[SessionTableVersion.V1], [SessionTableVersion.V2]]) + def test_group_by_nothing(self, session_table_version: SessionTableVersion): + self._create_data() + + results = self._run_session_attribution_query( + session_table_version=session_table_version, + ).results + + assert results == [ + ( + 7, + ["Paid Unknown", "Unknown"], + ["referring_domain1a", "referring_domain1b", "referring_domain2"], + ["source1", "source2"], + ["medium1", "medium2"], + ["campaign1a", "campaign1b", "campaign2"], + ["glcid,gad_source"], + ["http://example.com/1a", "http://example.com/1b", "http://example.com/2"], + ) + ] + + @parameterized.expand([[SessionTableVersion.V1], [SessionTableVersion.V2]]) + def test_group_by_initial_url(self, session_table_version: SessionTableVersion): + self._create_data() + + results = self._run_session_attribution_query( + session_table_version=session_table_version, + group_by=[SessionAttributionGroupBy.INITIAL_URL], + ).results + + assert results == [ + ( + 4, + ["Paid Unknown"], + ["referring_domain1a"], + ["source1"], + ["medium1"], + ["campaign1a"], + ["glcid,gad_source"], + "http://example.com/1a", + ), + ( + 2, + ["Paid Unknown"], + ["referring_domain1b"], + ["source1"], + ["medium1"], + ["campaign1b"], + ["glcid,gad_source"], + "http://example.com/1b", + ), + ( + 1, + ["Unknown"], + ["referring_domain2"], + ["source2"], + ["medium2"], + ["campaign2"], + [], + "http://example.com/2", + ), + ] + + @parameterized.expand([[SessionTableVersion.V1], [SessionTableVersion.V2]]) + def test_group_channel_medium_source(self, session_table_version: SessionTableVersion): + self._create_data() + + results = self._run_session_attribution_query( + session_table_version=session_table_version, + group_by=[ + SessionAttributionGroupBy.CHANNEL_TYPE, + SessionAttributionGroupBy.MEDIUM, + SessionAttributionGroupBy.SOURCE, + ], + ).results + + assert results == [ + ( + 6, + "Paid Unknown", + ["referring_domain1a", "referring_domain1b"], + "source1", + "medium1", + ["campaign1a", "campaign1b"], + ["glcid,gad_source"], + ["http://example.com/1a", "http://example.com/1b"], + ), + (1, "Unknown", ["referring_domain2"], "source2", "medium2", ["campaign2"], [], ["http://example.com/2"]), + ] + + @parameterized.expand([[SessionTableVersion.V1], [SessionTableVersion.V2]]) + def test_filters(self, session_table_version: SessionTableVersion): + self._create_data() + + results = self._run_session_attribution_query( + session_table_version=session_table_version, + group_by=[ + SessionAttributionGroupBy.CHANNEL_TYPE, + SessionAttributionGroupBy.MEDIUM, + SessionAttributionGroupBy.SOURCE, + ], + properties=[ + SessionPropertyFilter(key="$entry_utm_source", value="source1", operator=PropertyOperator.EXACT) + ], + ).results + + assert results == [ + ( + 6, + "Paid Unknown", + ["referring_domain1a", "referring_domain1b"], + "source1", + "medium1", + ["campaign1a", "campaign1b"], + ["glcid,gad_source"], + ["http://example.com/1a", "http://example.com/1b"], + ), + ] diff --git a/posthog/management/commands/run_autoreload_celery.py b/posthog/management/commands/run_autoreload_celery.py index 38758dfab517e..abd17fb9c1ab2 100644 --- a/posthog/management/commands/run_autoreload_celery.py +++ b/posthog/management/commands/run_autoreload_celery.py @@ -26,9 +26,6 @@ def run_celery_worker(self): "posthog", "worker", "-B", - "-S", - "redbeat.RedBeatScheduler", - "--without-mingle", "--pool=threads", f"--queues={','.join(queues)}", "-Ofair", diff --git a/posthog/migrations/0435_alter_action_slack_message_format.py b/posthog/migrations/0435_alter_action_slack_message_format.py new file mode 100644 index 0000000000000..136e6ace14936 --- /dev/null +++ b/posthog/migrations/0435_alter_action_slack_message_format.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.11 on 2024-07-11 10:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0434_add_web_vitals_opt_in"), + ] + + operations = [ + migrations.AlterField( + model_name="action", + name="slack_message_format", + field=models.CharField(blank=True, default="", max_length=1200), + ), + ] diff --git a/posthog/migrations/0436_alter_proxyrecord_status.py b/posthog/migrations/0436_alter_proxyrecord_status.py new file mode 100644 index 0000000000000..bd87e445dddf5 --- /dev/null +++ b/posthog/migrations/0436_alter_proxyrecord_status.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.11 on 2024-07-11 13:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0435_alter_action_slack_message_format"), + ] + + operations = [ + migrations.AlterField( + model_name="proxyrecord", + name="status", + field=models.CharField( + choices=[ + ("waiting", "Waiting"), + ("issuing", "Issuing"), + ("valid", "Valid"), + ("erroring", "Erroring"), + ("deleting", "Deleting"), + ("timed_out", "Timed Out"), + ], + default="waiting", + ), + ), + ] diff --git a/posthog/migrations/0437_externaldataschema_sync_frequency.py b/posthog/migrations/0437_externaldataschema_sync_frequency.py new file mode 100644 index 0000000000000..8b298032abcb8 --- /dev/null +++ b/posthog/migrations/0437_externaldataschema_sync_frequency.py @@ -0,0 +1,39 @@ +# Generated by Django 4.2.11 on 2024-07-03 17:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0436_alter_proxyrecord_status"), + ] + + operations = [ + migrations.AddField( + model_name="externaldataschema", + name="sync_frequency", + field=models.CharField( + blank=True, + choices=[("day", "Daily"), ("week", "Weekly"), ("month", "Monthly")], + default="day", + max_length=128, + ), + ), + migrations.AlterField( + model_name="externaldatajob", + name="pipeline", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name="jobs", to="posthog.externaldatasource" + ), + ), + migrations.RunSQL( + sql=""" + UPDATE posthog_externaldataschema AS schema + SET sync_frequency = source.sync_frequency + FROM posthog_externaldatasource AS source + WHERE schema.source_id = source.id + """, + reverse_sql=migrations.RunSQL.noop, + ), + ] diff --git a/posthog/models/action/action.py b/posthog/models/action/action.py index 929f2753bc02c..1d140dff91340 100644 --- a/posthog/models/action/action.py +++ b/posthog/models/action/action.py @@ -41,7 +41,7 @@ class Meta: deleted: models.BooleanField = models.BooleanField(default=False) events: models.ManyToManyField = models.ManyToManyField("Event", blank=True) post_to_slack: models.BooleanField = models.BooleanField(default=False) - slack_message_format: models.CharField = models.CharField(default="", max_length=600, blank=True) + slack_message_format: models.CharField = models.CharField(default="", max_length=1200, blank=True) updated_at: models.DateTimeField = models.DateTimeField(auto_now=True) bytecode: models.JSONField = models.JSONField(null=True, blank=True) bytecode_error: models.TextField = models.TextField(blank=True, null=True) diff --git a/posthog/models/property/property.py b/posthog/models/property/property.py index bb378b7616d43..5f62ef4f78cb1 100644 --- a/posthog/models/property/property.py +++ b/posthog/models/property/property.py @@ -49,11 +49,6 @@ class BehavioralPropertyType(StrEnum): "group_properties", # for groups table # all below are for person&groups on events table "person_properties", - "group0_properties", - "group1_properties", - "group2_properties", - "group3_properties", - "group4_properties", ] OperatorType = Literal[ "exact", diff --git a/posthog/models/proxy_record.py b/posthog/models/proxy_record.py index ce41004c1e86b..3c421f4a0bb7c 100644 --- a/posthog/models/proxy_record.py +++ b/posthog/models/proxy_record.py @@ -17,6 +17,7 @@ class Status(models.TextChoices): VALID = "valid" ERRORING = "erroring" DELETING = "deleting" + TIMED_OUT = "timed_out" status: models.CharField = models.CharField( choices=Status.choices, diff --git a/posthog/queries/column_optimizer/foss_column_optimizer.py b/posthog/queries/column_optimizer/foss_column_optimizer.py index b3e73d3178c5e..5553384132e37 100644 --- a/posthog/queries/column_optimizer/foss_column_optimizer.py +++ b/posthog/queries/column_optimizer/foss_column_optimizer.py @@ -7,7 +7,6 @@ from posthog.constants import TREND_FILTER_TYPE_ACTIONS, FunnelCorrelationType from posthog.models.action.util import ( get_action_tables_and_properties, - uses_elements_chain, ) from posthog.models.entity import Entity from posthog.models.filters import Filter @@ -96,32 +95,6 @@ def is_using_cohort_propertes(self) -> bool: def group_types_to_query(self) -> set[GroupTypeIndex]: return set() - @cached_property - def group_on_event_columns_to_query(self) -> set[ColumnName]: - return set() - - @cached_property - def should_query_elements_chain_column(self) -> bool: - "Returns whether this query uses elements_chain" - has_element_type_property = lambda properties: any(prop.type == "element" for prop in properties) - - if has_element_type_property(self.filter.property_groups.flat): - return True - - # Both entities and funnel exclusions can contain nested elements_chain inclusions - for entity in self.entities_used_in_filter(): - if has_element_type_property(entity.property_groups.flat): - return True - - # :TRICKY: Action definition may contain elements_chain usage - # - # See ee/clickhouse/models/action.py#format_action_filter for an example - if entity.type == TREND_FILTER_TYPE_ACTIONS: - if uses_elements_chain(entity.get_action()): - return True - - return False - @cached_property def properties_used_in_filter(self) -> TCounter[PropertyIdentifier]: "Returns collection of properties + types that this query would use" diff --git a/posthog/queries/properties_timeline/properties_timeline_event_query.py b/posthog/queries/properties_timeline/properties_timeline_event_query.py index b5e9a87d07c82..03f25ad018927 100644 --- a/posthog/queries/properties_timeline/properties_timeline_event_query.py +++ b/posthog/queries/properties_timeline/properties_timeline_event_query.py @@ -1,5 +1,5 @@ import datetime as dt -from typing import Any, Optional +from typing import Any from zoneinfo import ZoneInfo from posthog.models.entity.util import get_entity_filtering_params @@ -14,22 +14,12 @@ class PropertiesTimelineEventQuery(EventQuery): effective_date_to: dt.datetime _filter: PropertiesTimelineFilter - _group_type_index: Optional[int] # If the parent insight is group-based, this is the index of the group type - - def __init__(self, filter: PropertiesTimelineFilter, *args, **kwargs): - super().__init__(filter, *args, **kwargs) - self._group_type_index = filter.aggregation_group_type_index def get_query(self) -> tuple[str, dict[str, Any]]: real_fields = [f"{self.EVENT_TABLE_ALIAS}.timestamp AS timestamp"] sentinel_fields = ["NULL AS timestamp"] - if self._group_type_index is None: - columns_to_query = self._column_optimizer.person_on_event_columns_to_query | {"person_properties"} - else: - columns_to_query = self._column_optimizer.group_on_event_columns_to_query | { - f"group_{self._group_type_index}_properties" - } + columns_to_query = self._column_optimizer.person_on_event_columns_to_query | {"person_properties"} for column_name in sorted(columns_to_query): real_fields.append(f'{self.EVENT_TABLE_ALIAS}."{column_name}" AS "{column_name}"') @@ -44,15 +34,13 @@ def get_query(self) -> tuple[str, dict[str, Any]]: entity_query, entity_params = self._get_entity_query() self.params.update(entity_params) - actor_id_column = "person_id" if self._group_type_index is None else f"$group_{self._group_type_index}" - query = f""" ( SELECT {real_fields_combined} FROM events {self.EVENT_TABLE_ALIAS} WHERE team_id = %(team_id)s - AND {actor_id_column} = %(actor_id)s + AND person_id = %(actor_id)s {entity_query} {date_query} ORDER BY timestamp ASC diff --git a/posthog/queries/test/test_trends.py b/posthog/queries/test/test_trends.py index dfdbde33b81e0..ea8d5586f7415 100644 --- a/posthog/queries/test/test_trends.py +++ b/posthog/queries/test/test_trends.py @@ -7964,11 +7964,6 @@ def test_breakdown_with_filter_groups(self): self.assertEqual(response[1]["breakdown_value"], "uh") self.assertEqual(response[1]["count"], 1) - @also_test_with_materialized_columns( - event_properties=["key"], - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_breakdown_with_filter_groups_person_on_events(self): self._create_groups() @@ -8166,9 +8161,6 @@ def test_breakdown_by_group_props(self): self.assertEqual(res[0]["distinct_ids"], ["person1"]) - @also_test_with_materialized_columns( - group_properties=[(0, "industry")], materialize_only_with_person_on_events=True - ) @snapshot_clickhouse_queries def test_breakdown_by_group_props_person_on_events(self): self._create_groups() @@ -8385,11 +8377,6 @@ def test_filtering_with_group_props_event_with_no_group_data(self): # set to a value other than textiles AND events with no group at all self.assertEqual(response[0]["count"], 4) - @also_test_with_materialized_columns( - person_properties=["key"], - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_breakdown_by_group_props_with_person_filter_person_on_events(self): self._create_groups() @@ -8435,11 +8422,6 @@ def test_breakdown_by_group_props_with_person_filter_person_on_events(self): self.assertEqual(response[0]["breakdown_value"], "finance") self.assertEqual(response[0]["count"], 1) - @also_test_with_materialized_columns( - person_properties=["key"], - group_properties=[(0, "industry")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_filtering_with_group_props_person_on_events(self): self._create_groups() @@ -8495,10 +8477,6 @@ def test_filtering_with_group_props_person_on_events(self): response = Trends().run(filter, self.team) self.assertEqual(response[0]["count"], 1) - @also_test_with_materialized_columns( - group_properties=[(0, "industry"), (2, "name")], - materialize_only_with_person_on_events=True, - ) @snapshot_clickhouse_queries def test_filtering_by_multiple_groups_person_on_events(self): GroupTypeMapping.objects.create(team=self.team, group_type="organization", group_type_index=0) diff --git a/posthog/schema.py b/posthog/schema.py index 490a86759e5e3..a2e1f8c64dc5b 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -657,6 +657,7 @@ class NodeKind(StrEnum): FUNNELS_ACTORS_QUERY = "FunnelsActorsQuery" FUNNEL_CORRELATION_ACTORS_QUERY = "FunnelCorrelationActorsQuery" SESSIONS_TIMELINE_QUERY = "SessionsTimelineQuery" + SESSION_ATTRIBUTION_EXPLORER_QUERY = "SessionAttributionExplorerQuery" DATA_TABLE_NODE = "DataTableNode" DATA_VISUALIZATION_NODE = "DataVisualizationNode" SAVED_INSIGHT_NODE = "SavedInsightNode" @@ -898,6 +899,39 @@ class SamplingRate(BaseModel): numerator: float +class SessionAttributionExplorerQueryResponse(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) + hasMore: Optional[bool] = None + hogql: Optional[str] = Field(default=None, description="Generated HogQL query.") + limit: Optional[int] = None + modifiers: Optional[HogQLQueryModifiers] = Field( + default=None, description="Modifiers used when performing the query" + ) + offset: Optional[int] = None + results: Any + timings: Optional[list[QueryTiming]] = Field( + default=None, description="Measured timings for different parts of the query generation process" + ) + types: Optional[list] = None + + +class SessionAttributionGroupBy(StrEnum): + CHANNEL_TYPE = "ChannelType" + MEDIUM = "Medium" + SOURCE = "Source" + CAMPAIGN = "Campaign" + AD_IDS = "AdIds" + REFERRING_DOMAIN = "ReferringDomain" + INITIAL_URL = "InitialURL" + + class SessionPropertyFilter(BaseModel): model_config = ConfigDict( extra="forbid", @@ -1507,6 +1541,41 @@ class CachedPathsQueryResponse(BaseModel): ) +class CachedSessionAttributionExplorerQueryResponse(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + cache_key: str + cache_target_age: Optional[AwareDatetime] = None + calculation_trigger: Optional[str] = Field( + default=None, description="What triggered the calculation of the query, leave empty if user/immediate" + ) + columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) + hasMore: Optional[bool] = None + hogql: Optional[str] = Field(default=None, description="Generated HogQL query.") + is_cached: bool + last_refresh: AwareDatetime + limit: Optional[int] = None + modifiers: Optional[HogQLQueryModifiers] = Field( + default=None, description="Modifiers used when performing the query" + ) + next_allowed_client_refresh: AwareDatetime + offset: Optional[int] = None + query_status: Optional[QueryStatus] = Field( + default=None, description="Query status indicates whether next to the provided data, a query is still running." + ) + results: Any + timezone: str + timings: Optional[list[QueryTiming]] = Field( + default=None, description="Measured timings for different parts of the query generation process" + ) + types: Optional[list] = None + + class CachedSessionsTimelineQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", @@ -1813,6 +1882,29 @@ class Response5(BaseModel): types: Optional[list] = None +class Response6(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) + hasMore: Optional[bool] = None + hogql: Optional[str] = Field(default=None, description="Generated HogQL query.") + limit: Optional[int] = None + modifiers: Optional[HogQLQueryModifiers] = Field( + default=None, description="Modifiers used when performing the query" + ) + offset: Optional[int] = None + results: Any + timings: Optional[list[QueryTiming]] = Field( + default=None, description="Measured timings for different parts of the query generation process" + ) + types: Optional[list] = None + + class ChartSettings(BaseModel): model_config = ConfigDict( extra="forbid", @@ -2361,6 +2453,29 @@ class QueryResponseAlternative11(BaseModel): class QueryResponseAlternative12(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) + hasMore: Optional[bool] = None + hogql: Optional[str] = Field(default=None, description="Generated HogQL query.") + limit: Optional[int] = None + modifiers: Optional[HogQLQueryModifiers] = Field( + default=None, description="Modifiers used when performing the query" + ) + offset: Optional[int] = None + results: Any + timings: Optional[list[QueryTiming]] = Field( + default=None, description="Measured timings for different parts of the query generation process" + ) + types: Optional[list] = None + + +class QueryResponseAlternative13(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2383,7 +2498,7 @@ class QueryResponseAlternative12(BaseModel): types: list[str] -class QueryResponseAlternative13(BaseModel): +class QueryResponseAlternative14(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2407,7 +2522,7 @@ class QueryResponseAlternative13(BaseModel): types: list[str] -class QueryResponseAlternative14(BaseModel): +class QueryResponseAlternative15(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2434,7 +2549,7 @@ class QueryResponseAlternative14(BaseModel): types: Optional[list] = Field(default=None, description="Types of returned columns") -class QueryResponseAlternative15(BaseModel): +class QueryResponseAlternative16(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2455,7 +2570,7 @@ class QueryResponseAlternative15(BaseModel): ) -class QueryResponseAlternative16(BaseModel): +class QueryResponseAlternative17(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2479,7 +2594,7 @@ class QueryResponseAlternative16(BaseModel): types: Optional[list] = None -class QueryResponseAlternative17(BaseModel): +class QueryResponseAlternative18(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2500,7 +2615,30 @@ class QueryResponseAlternative17(BaseModel): types: Optional[list] = None -class QueryResponseAlternative18(BaseModel): +class QueryResponseAlternative19(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) + hasMore: Optional[bool] = None + hogql: Optional[str] = Field(default=None, description="Generated HogQL query.") + limit: Optional[int] = None + modifiers: Optional[HogQLQueryModifiers] = Field( + default=None, description="Modifiers used when performing the query" + ) + offset: Optional[int] = None + results: Any + timings: Optional[list[QueryTiming]] = Field( + default=None, description="Measured timings for different parts of the query generation process" + ) + types: Optional[list] = None + + +class QueryResponseAlternative20(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2518,7 +2656,7 @@ class QueryResponseAlternative18(BaseModel): ) -class QueryResponseAlternative19(BaseModel): +class QueryResponseAlternative21(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2536,7 +2674,7 @@ class QueryResponseAlternative19(BaseModel): ) -class QueryResponseAlternative21(BaseModel): +class QueryResponseAlternative23(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2554,7 +2692,7 @@ class QueryResponseAlternative21(BaseModel): ) -class QueryResponseAlternative24(BaseModel): +class QueryResponseAlternative26(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -2667,6 +2805,29 @@ class SavedInsightNode(BaseModel): vizSpecificOptions: Optional[VizSpecificOptions] = None +class Filters(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + dateRange: Optional[DateRange] = None + properties: Optional[list[SessionPropertyFilter]] = None + + +class SessionAttributionExplorerQuery(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + filters: Optional[Filters] = None + groupBy: list[SessionAttributionGroupBy] + kind: Literal["SessionAttributionExplorerQuery"] = "SessionAttributionExplorerQuery" + limit: Optional[int] = None + modifiers: Optional[HogQLQueryModifiers] = Field( + default=None, description="Modifiers used when performing the query" + ) + offset: Optional[int] = None + response: Optional[SessionAttributionExplorerQueryResponse] = None + + class SessionsTimelineQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", @@ -3421,7 +3582,7 @@ class PropertyGroupFilterValue(BaseModel): ] -class QueryResponseAlternative20(BaseModel): +class QueryResponseAlternative22(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -4011,7 +4172,7 @@ class LifecycleQuery(BaseModel): ) -class QueryResponseAlternative25(BaseModel): +class QueryResponseAlternative27(BaseModel): model_config = ConfigDict( extra="forbid", ) @@ -4041,8 +4202,8 @@ class QueryResponseAlternative( QueryResponseAlternative9, QueryResponseAlternative10, QueryResponseAlternative11, - Any, QueryResponseAlternative12, + Any, QueryResponseAlternative13, QueryResponseAlternative14, QueryResponseAlternative15, @@ -4052,8 +4213,10 @@ class QueryResponseAlternative( QueryResponseAlternative19, QueryResponseAlternative20, QueryResponseAlternative21, - QueryResponseAlternative24, - QueryResponseAlternative25, + QueryResponseAlternative22, + QueryResponseAlternative23, + QueryResponseAlternative26, + QueryResponseAlternative27, ] ] ): @@ -4070,8 +4233,8 @@ class QueryResponseAlternative( QueryResponseAlternative9, QueryResponseAlternative10, QueryResponseAlternative11, - Any, QueryResponseAlternative12, + Any, QueryResponseAlternative13, QueryResponseAlternative14, QueryResponseAlternative15, @@ -4081,8 +4244,10 @@ class QueryResponseAlternative( QueryResponseAlternative19, QueryResponseAlternative20, QueryResponseAlternative21, - QueryResponseAlternative24, - QueryResponseAlternative25, + QueryResponseAlternative22, + QueryResponseAlternative23, + QueryResponseAlternative26, + QueryResponseAlternative27, ] @@ -4360,7 +4525,9 @@ class DataTableNode(BaseModel): ) kind: Literal["DataTableNode"] = "DataTableNode" propertiesViaUrl: Optional[bool] = Field(default=None, description="Link properties via the URL (default: false)") - response: Optional[Union[dict[str, Any], Response, Response1, Response2, Response3, Response4, Response5]] = None + response: Optional[ + Union[dict[str, Any], Response, Response1, Response2, Response3, Response4, Response5, Response6] + ] = None showActions: Optional[bool] = Field(default=None, description="Show the kebab menu at the end of the row") showColumnConfigurator: Optional[bool] = Field( default=None, description="Show a button to configure the table's columns if possible" @@ -4396,6 +4563,7 @@ class DataTableNode(BaseModel): WebOverviewQuery, WebStatsTableQuery, WebTopClicksQuery, + SessionAttributionExplorerQuery, ] = Field(..., description="Source of the events") @@ -4430,6 +4598,7 @@ class HogQLAutocomplete(BaseModel): WebOverviewQuery, WebStatsTableQuery, WebTopClicksQuery, + SessionAttributionExplorerQuery, ] ] = Field(default=None, description="Query in whose context to validate.") startPosition: int = Field(..., description="Start position of the editor word") @@ -4468,6 +4637,7 @@ class HogQLMetadata(BaseModel): WebOverviewQuery, WebStatsTableQuery, WebTopClicksQuery, + SessionAttributionExplorerQuery, ] ] = Field( default=None, @@ -4508,6 +4678,7 @@ class QueryRequest(BaseModel): WebOverviewQuery, WebStatsTableQuery, WebTopClicksQuery, + SessionAttributionExplorerQuery, DataVisualizationNode, DataTableNode, SavedInsightNode, @@ -4552,6 +4723,7 @@ class QuerySchemaRoot( WebOverviewQuery, WebStatsTableQuery, WebTopClicksQuery, + SessionAttributionExplorerQuery, DataVisualizationNode, DataTableNode, SavedInsightNode, @@ -4584,6 +4756,7 @@ class QuerySchemaRoot( WebOverviewQuery, WebStatsTableQuery, WebTopClicksQuery, + SessionAttributionExplorerQuery, DataVisualizationNode, DataTableNode, SavedInsightNode, diff --git a/posthog/temporal/proxy_service/common.py b/posthog/temporal/proxy_service/common.py index 914ad2c7f5242..c842dec2e0883 100644 --- a/posthog/temporal/proxy_service/common.py +++ b/posthog/temporal/proxy_service/common.py @@ -23,6 +23,10 @@ class NonRetriableException(Exception): pass +class RecordDeletedException(NonRetriableException): + pass + + @dataclass class UpdateProxyRecordInputs: organization_id: uuid.UUID @@ -45,7 +49,10 @@ async def update_proxy_record(inputs: UpdateProxyRecordInputs): @sync_to_async def update_record(proxy_record_id): connection.connect() - pr = ProxyRecord.objects.get(id=proxy_record_id) + prs = ProxyRecord.objects.filter(id=proxy_record_id) + if len(prs) == 0: + raise RecordDeletedException("proxy record was deleted before workflow completed") + pr = prs[0] pr.status = inputs.status # clear message after every transition pr.message = "" diff --git a/posthog/temporal/proxy_service/create.py b/posthog/temporal/proxy_service/create.py index 0a0c0d10f22e0..7ce51cf96f577 100644 --- a/posthog/temporal/proxy_service/create.py +++ b/posthog/temporal/proxy_service/create.py @@ -11,7 +11,7 @@ from temporalio import activity, workflow import temporalio.common -from temporalio.exceptions import ApplicationError +from temporalio.exceptions import ActivityError, ApplicationError, RetryState from posthog.models import ProxyRecord from posthog.temporal.batch_exports.base import PostHogWorkflow @@ -20,6 +20,7 @@ from posthog.temporal.proxy_service.common import ( get_grpc_client, NonRetriableException, + RecordDeletedException, update_proxy_record, UpdateProxyRecordInputs, ) @@ -77,7 +78,7 @@ def update_record_message(*, proxy_record_id, message): pr.save() if not await record_exists(inputs.proxy_record_id): - raise NonRetriableException("proxy record was deleted while waiting for DNS records") + raise RecordDeletedException("proxy record was deleted while waiting for DNS records") try: cnames = dns.resolver.query(inputs.domain, "CNAME") @@ -107,7 +108,7 @@ def update_record_message(*, proxy_record_id, message): message="The DNS record appears to have Cloudflare proxying enabled - please disable this. For more information see [the docs](https://posthog.com/docs/advanced/proxy/managed-reverse-proxy)", ) raise - except (dns.resolver.NXDOMAIN, ApplicationError): + except (dns.resolver.NXDOMAIN, dns.resolver.Timeout, ApplicationError): # retriable raise except Exception as e: @@ -133,7 +134,7 @@ def record_exists(proxy_record_id) -> bool: return len(pr) > 0 if not await record_exists(inputs.proxy_record_id): - raise NonRetriableException("proxy record was deleted while waiting for certificate to be provisioned") + raise RecordDeletedException("proxy record was deleted while waiting for certificate to be provisioned") client = await get_grpc_client() @@ -200,29 +201,57 @@ def parse_inputs(inputs: list[str]) -> CreateManagedProxyInputs: @temporalio.workflow.run async def run(self, inputs: CreateManagedProxyInputs) -> None: """Workflow implementation to create a Managed reverse Proxy.""" - + logger = await bind_temporal_org_worker_logger(organization_id=inputs.organization_id) try: - # Wait for DNS record to be created. - # This will fail and retry infinitely until the expected resolution is found. - # Timeout after 7 days - users will need to delete and recreate after this time. - await temporalio.workflow.execute_activity( - wait_for_dns_records, - WaitForDNSRecordsInputs( - organization_id=inputs.organization_id, - proxy_record_id=inputs.proxy_record_id, - domain=inputs.domain, - target_cname=inputs.target_cname, - ), - schedule_to_close_timeout=dt.timedelta(days=7), - start_to_close_timeout=dt.timedelta(seconds=2), - retry_policy=temporalio.common.RetryPolicy( - backoff_coefficient=1.1, - initial_interval=dt.timedelta(seconds=3), - maximum_interval=dt.timedelta(seconds=3600), - maximum_attempts=0, - non_retryable_error_types=["NonRetriableException"], - ), - ) + try: + # Wait for DNS record to be created. + # This will fail and retry infinitely until the expected resolution is found. + # Timeout after 7 days - users will need to delete and recreate after this time. + await temporalio.workflow.execute_activity( + wait_for_dns_records, + WaitForDNSRecordsInputs( + organization_id=inputs.organization_id, + proxy_record_id=inputs.proxy_record_id, + domain=inputs.domain, + target_cname=inputs.target_cname, + ), + schedule_to_close_timeout=dt.timedelta(days=7), + start_to_close_timeout=dt.timedelta(seconds=10), + retry_policy=temporalio.common.RetryPolicy( + backoff_coefficient=1.1, + initial_interval=dt.timedelta(seconds=3), + maximum_interval=dt.timedelta(seconds=300), + maximum_attempts=0, + non_retryable_error_types=["NonRetriableException", "RecordDeletedException"], + ), + ) + except ActivityError as e: + if e.retry_state != RetryState.TIMEOUT: + raise + + # If we time out waiting for DNS records set to TIMED_OUT status + # This is not really an "error", as it's on the customer to set the DNS + # records and we have no control over it. + logger.info( + "Timed out waiting for DNS records for domain %s", + inputs.domain, + ) + + # Handle schedule-to-close timeout specifically + await temporalio.workflow.execute_activity( + update_proxy_record, + UpdateProxyRecordInputs( + organization_id=inputs.organization_id, + proxy_record_id=inputs.proxy_record_id, + status=ProxyRecord.Status.TIMED_OUT.value, + ), + start_to_close_timeout=dt.timedelta(seconds=60), + retry_policy=temporalio.common.RetryPolicy( + maximum_attempts=10, + non_retryable_error_types=["NonRetriableException", "RecordDeletedException"], + ), + ) + return # We've found the correct DNS record - update record to the ISSUING state await temporalio.workflow.execute_activity( @@ -235,6 +264,7 @@ async def run(self, inputs: CreateManagedProxyInputs) -> None: start_to_close_timeout=dt.timedelta(seconds=10), retry_policy=temporalio.common.RetryPolicy( maximum_attempts=2, + non_retryable_error_types=["NonRetriableException", "RecordDeletedException"], ), ) @@ -247,7 +277,7 @@ async def run(self, inputs: CreateManagedProxyInputs) -> None: retry_policy=temporalio.common.RetryPolicy( initial_interval=dt.timedelta(seconds=10), maximum_attempts=5, - non_retryable_error_types=["NonRetriableException"], + non_retryable_error_types=["NonRetriableException", "RecordDeletedException"], ), ) @@ -266,7 +296,7 @@ async def run(self, inputs: CreateManagedProxyInputs) -> None: initial_interval=dt.timedelta(seconds=1), maximum_interval=dt.timedelta(seconds=10), maximum_attempts=0, - non_retryable_error_types=["NonRetriableException"], + non_retryable_error_types=["NonRetriableException", "RecordDeletedException"], ), ) @@ -281,10 +311,36 @@ async def run(self, inputs: CreateManagedProxyInputs) -> None: start_to_close_timeout=dt.timedelta(seconds=10), retry_policy=temporalio.common.RetryPolicy( maximum_attempts=2, + non_retryable_error_types=["NonRetriableException", "RecordDeletedException"], ), ) - except Exception: + except RecordDeletedException: + logger.info( + "Record was deleted before completing provisioning for id %s (%s)", + inputs.proxy_record_id, + inputs.domain, + ) + + # if the record has been deleted don't error the workflow, just ignore + return + + except Exception as e: + logger.info( + "Exception caught during workflow run: %s (%s)", + e, + type(e), + ) + + if hasattr(e, "cause") and e.cause.type == "RecordDeletedException": + logger.info( + "Record was deleted before completing provisioning for id %s (%s)", + inputs.proxy_record_id, + inputs.domain, + ) + + # if the record has been deleted don't error the workflow, just ignore + return # Something went wrong - set the record to error state await temporalio.workflow.execute_activity( update_proxy_record, @@ -296,6 +352,7 @@ async def run(self, inputs: CreateManagedProxyInputs) -> None: start_to_close_timeout=dt.timedelta(seconds=60), retry_policy=temporalio.common.RetryPolicy( maximum_attempts=10, + non_retryable_error_types=["NonRetriableException", "RecordDeletedException"], ), ) raise diff --git a/posthog/test/base.py b/posthog/test/base.py index 829a3e5ebd4d0..321d2979431a2 100644 --- a/posthog/test/base.py +++ b/posthog/test/base.py @@ -89,7 +89,6 @@ DROP_SESSION_REPLAY_EVENTS_TABLE_SQL, SESSION_REPLAY_EVENTS_TABLE_SQL, ) -from posthog.settings.utils import get_from_env, str_to_bool from posthog.test.assert_faster_than import assert_faster_than # Make sure freezegun ignores our utils class that times functions @@ -446,10 +445,7 @@ def optionally_drop(table, filter=None): def also_test_with_materialized_columns( event_properties=None, person_properties=None, - group_properties=None, verify_no_jsonextract=True, - # :TODO: Remove this when groups-on-events is released - materialize_only_with_person_on_events=False, ): """ Runs the test twice on clickhouse - once verifying it works normally, once with materialized columns. @@ -457,8 +453,6 @@ def also_test_with_materialized_columns( Requires a unittest class with ClickhouseTestMixin mixed in """ - if group_properties is None: - group_properties = [] if person_properties is None: person_properties = [] if event_properties is None: @@ -469,12 +463,6 @@ def also_test_with_materialized_columns( # EE not available? Just run the main test return lambda fn: fn - if materialize_only_with_person_on_events and not get_from_env( - "PERSON_ON_EVENTS_ENABLED", False, type_cast=str_to_bool - ): - # Don't run materialized test unless PERSON_ON_EVENTS_ENABLED - return lambda fn: fn - def decorator(fn): @pytest.mark.ee def fn_with_materialized(self, *args, **kwargs): @@ -487,12 +475,6 @@ def fn_with_materialized(self, *args, **kwargs): for prop in person_properties: materialize("person", prop) materialize("events", prop, table_column="person_properties") - for group_type_index, prop in group_properties: - materialize( - "events", - prop, - table_column=f"group{group_type_index}_properties", - ) try: with self.capture_select_queries() as sqls: diff --git a/posthog/warehouse/api/external_data_schema.py b/posthog/warehouse/api/external_data_schema.py index dff4252b34a5c..0ee95d8f6bd0b 100644 --- a/posthog/warehouse/api/external_data_schema.py +++ b/posthog/warehouse/api/external_data_schema.py @@ -57,6 +57,7 @@ class Meta: "sync_type", "incremental_field", "incremental_field_type", + "sync_frequency", ] read_only_fields = [ @@ -131,6 +132,7 @@ def update(self, instance: ExternalDataSchema, validated_data: dict[str, Any]) - validated_data["sync_type_config"] = payload should_sync = validated_data.get("should_sync", None) + sync_frequency = validated_data.get("sync_frequency", None) if should_sync is True and sync_type is None and instance.sync_type is None: raise ValidationError("Sync type must be set up first before enabling schema") @@ -146,6 +148,9 @@ def update(self, instance: ExternalDataSchema, validated_data: dict[str, Any]) - if should_sync is True: sync_external_data_job_workflow(instance, create=True) + if sync_frequency: + sync_external_data_job_workflow(instance, create=False) + if trigger_refresh: source: ExternalDataSource = instance.source source.job_inputs.update({"reset_pipeline": True}) diff --git a/posthog/warehouse/api/external_data_source.py b/posthog/warehouse/api/external_data_source.py index ae0487d58f66d..1ef5ac2011c06 100644 --- a/posthog/warehouse/api/external_data_source.py +++ b/posthog/warehouse/api/external_data_source.py @@ -19,7 +19,7 @@ trigger_external_data_source_workflow, ) from posthog.warehouse.models import ExternalDataSource, ExternalDataSchema, ExternalDataJob -from posthog.warehouse.api.external_data_schema import ExternalDataSchemaSerializer +from posthog.warehouse.api.external_data_schema import ExternalDataSchemaSerializer, SimpleExternalDataSchemaSerializer from posthog.hogql.database.database import create_hogql_database from posthog.temporal.data_imports.pipelines.schemas import ( PIPELINE_TYPE_INCREMENTAL_ENDPOINTS_MAPPING, @@ -64,6 +64,20 @@ } +class ExternalDataJobSerializers(serializers.ModelSerializer): + schema = serializers.SerializerMethodField(read_only=True) + + class Meta: + model = ExternalDataJob + fields = ["id", "created_at", "created_by", "status", "schema", "rows_synced", "latest_error"] + read_only_fields = ["id", "created_at", "created_by", "status", "schema", "rows_synced", "latest_error"] + + def get_schema(self, instance: ExternalDataJob): + return SimpleExternalDataSchemaSerializer( + instance.schema, many=False, read_only=True, context=self.context + ).data + + class ExternalDataSourceSerializers(serializers.ModelSerializer): account_id = serializers.CharField(write_only=True) client_secret = serializers.CharField(write_only=True) @@ -84,7 +98,6 @@ class Meta: "prefix", "last_run_at", "schemas", - "sync_frequency", ] read_only_fields = [ "id", @@ -134,7 +147,6 @@ def get_schemas(self, instance: ExternalDataSource): def update(self, instance: ExternalDataSource, validated_data: Any) -> Any: updated_source: ExternalDataSource = super().update(instance, validated_data) - updated_source.update_schemas() return updated_source @@ -770,6 +782,19 @@ def source_prefix(self, request: Request, *arg: Any, **kwargs: Any): return Response(status=status.HTTP_200_OK) + @action(methods=["GET"], detail=True) + def jobs(self, request: Request, *arg: Any, **kwargs: Any): + instance: ExternalDataSource = self.get_object() + + jobs = instance.jobs.prefetch_related("schema").order_by("-created_at").all() + + return Response( + status=status.HTTP_200_OK, + data=ExternalDataJobSerializers( + jobs, many=True, read_only=True, context=self.get_serializer_context() + ).data, + ) + def _expose_postgres_error(self, error: OperationalError) -> str | None: error_msg = " ".join(str(n) for n in error.args) diff --git a/posthog/warehouse/api/test/test_external_data_schema.py b/posthog/warehouse/api/test/test_external_data_schema.py index e248204d880e4..44554f4dce17d 100644 --- a/posthog/warehouse/api/test/test_external_data_schema.py +++ b/posthog/warehouse/api/test/test_external_data_schema.py @@ -358,3 +358,38 @@ def test_update_schema_change_sync_type_with_invalid_type(self): ) assert response.status_code == 400 + + def test_update_schema_sync_frequency(self): + source = ExternalDataSource.objects.create( + team=self.team, source_type=ExternalDataSource.Type.STRIPE, job_inputs={} + ) + schema = ExternalDataSchema.objects.create( + name="BalanceTransaction", + team=self.team, + source=source, + should_sync=True, + status=ExternalDataSchema.Status.COMPLETED, + sync_type=ExternalDataSchema.SyncType.FULL_REFRESH, + sync_frequency=ExternalDataSchema.SyncFrequency.DAILY, + ) + + with ( + mock.patch( + "posthog.warehouse.api.external_data_schema.external_data_workflow_exists" + ) as mock_external_data_workflow_exists, + mock.patch( + "posthog.warehouse.api.external_data_schema.sync_external_data_job_workflow" + ) as mock_sync_external_data_job_workflow, + ): + mock_external_data_workflow_exists.return_value = True + + response = self.client.patch( + f"/api/projects/{self.team.pk}/external_data_schemas/{schema.id}", + data={"sync_frequency": "week"}, + ) + + assert response.status_code == 200 + mock_sync_external_data_job_workflow.assert_called_once() + + schema.refresh_from_db() + assert schema.sync_frequency == ExternalDataSchema.SyncFrequency.WEEKLY diff --git a/posthog/warehouse/api/test/test_external_data_source.py b/posthog/warehouse/api/test/test_external_data_source.py index 69a3ae2248205..63c7e9946af32 100644 --- a/posthog/warehouse/api/test/test_external_data_source.py +++ b/posthog/warehouse/api/test/test_external_data_source.py @@ -6,14 +6,14 @@ from posthog.temporal.data_imports.pipelines.schemas import ( PIPELINE_TYPE_SCHEMA_DEFAULT_MAPPING, ) -from posthog.warehouse.data_load.service import get_sync_schedule from django.test import override_settings from django.conf import settings from posthog.models import Team import psycopg from rest_framework import status -import datetime + +from posthog.warehouse.models.external_data_job import ExternalDataJob class TestExternalDataSource(APIBaseTest): @@ -396,7 +396,6 @@ def test_get_external_data_source_with_schema(self): "prefix", "last_run_at", "schemas", - "sync_frequency", ], ) self.assertEqual( @@ -414,6 +413,7 @@ def test_get_external_data_source_with_schema(self): "status": schema.status, "sync_type": schema.sync_type, "table": schema.table, + "sync_frequency": schema.sync_frequency, } ], ) @@ -598,35 +598,22 @@ def test_internal_postgres(self, patch_get_postgres_schemas): self.assertEqual(response.status_code, 400) self.assertEqual(response.json(), {"message": "Cannot use internal Postgres database"}) - @patch("posthog.warehouse.data_load.service.sync_external_data_job_workflow") - def test_update_source_sync_frequency(self, _patch_sync_external_data_job_workflow): + def test_source_jobs(self): source = self._create_external_data_source() schema = self._create_external_data_schema(source.pk) - - self.assertEqual(source.sync_frequency, ExternalDataSource.SyncFrequency.DAILY) - # test schedule - schedule = get_sync_schedule(schema) - self.assertEqual( - schedule.spec.intervals[0].every, - datetime.timedelta(days=1), + job = ExternalDataJob.objects.create( + team=self.team, pipeline=source, schema=schema, status=ExternalDataJob.Status.COMPLETED, rows_synced=100 ) - # test api - response = self.client.patch( - f"/api/projects/{self.team.pk}/external_data_sources/{source.pk}/", - data={"sync_frequency": ExternalDataSource.SyncFrequency.WEEKLY}, + response = self.client.get( + f"/api/projects/{self.team.pk}/external_data_sources/{source.pk}/jobs", ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - source.refresh_from_db() - schema.refresh_from_db() + data = response.json() - self.assertEqual(source.sync_frequency, ExternalDataSource.SyncFrequency.WEEKLY) - self.assertEqual(_patch_sync_external_data_job_workflow.call_count, 1) - - # test schedule - schedule = get_sync_schedule(schema) - self.assertEqual( - schedule.spec.intervals[0].every, - datetime.timedelta(days=7), - ) + assert response.status_code, status.HTTP_200_OK + assert len(data) == 1 + assert data[0]["id"] == str(job.pk) + assert data[0]["status"] == "Completed" + assert data[0]["rows_synced"] == 100 + assert data[0]["schema"]["id"] == str(schema.pk) diff --git a/posthog/warehouse/data_load/service.py b/posthog/warehouse/data_load/service.py index c3f97406c56cf..2425f186b5fa7 100644 --- a/posthog/warehouse/data_load/service.py +++ b/posthog/warehouse/data_load/service.py @@ -67,11 +67,11 @@ def get_sync_schedule(external_data_schema: ExternalDataSchema): def get_sync_frequency(external_data_schema: ExternalDataSchema): - if external_data_schema.source.sync_frequency == ExternalDataSource.SyncFrequency.DAILY: + if external_data_schema.sync_frequency == ExternalDataSchema.SyncFrequency.DAILY: return timedelta(days=1) - elif external_data_schema.source.sync_frequency == ExternalDataSource.SyncFrequency.WEEKLY: + elif external_data_schema.sync_frequency == ExternalDataSchema.SyncFrequency.WEEKLY: return timedelta(weeks=1) - elif external_data_schema.source.sync_frequency == ExternalDataSource.SyncFrequency.MONTHLY: + elif external_data_schema.sync_frequency == ExternalDataSchema.SyncFrequency.MONTHLY: return timedelta(days=30) else: raise ValueError(f"Unknown sync frequency: {external_data_schema.source.sync_frequency}") diff --git a/posthog/warehouse/models/external_data_job.py b/posthog/warehouse/models/external_data_job.py index 2c273a6589538..916f7704cf742 100644 --- a/posthog/warehouse/models/external_data_job.py +++ b/posthog/warehouse/models/external_data_job.py @@ -17,7 +17,9 @@ class Status(models.TextChoices): CANCELLED = "Cancelled", "Cancelled" team: models.ForeignKey = models.ForeignKey(Team, on_delete=models.CASCADE) - pipeline: models.ForeignKey = models.ForeignKey("posthog.ExternalDataSource", on_delete=models.CASCADE) + pipeline: models.ForeignKey = models.ForeignKey( + "posthog.ExternalDataSource", related_name="jobs", on_delete=models.CASCADE + ) schema: models.ForeignKey = models.ForeignKey( "posthog.ExternalDataSchema", on_delete=models.CASCADE, null=True, blank=True ) diff --git a/posthog/warehouse/models/external_data_schema.py b/posthog/warehouse/models/external_data_schema.py index 109c440eccc2c..b2c68fb9e8cab 100644 --- a/posthog/warehouse/models/external_data_schema.py +++ b/posthog/warehouse/models/external_data_schema.py @@ -23,6 +23,11 @@ class SyncType(models.TextChoices): FULL_REFRESH = "full_refresh", "full_refresh" INCREMENTAL = "incremental", "incremental" + class SyncFrequency(models.TextChoices): + DAILY = "day", "Daily" + WEEKLY = "week", "Weekly" + MONTHLY = "month", "Monthly" + name: models.CharField = models.CharField(max_length=400) team: models.ForeignKey = models.ForeignKey(Team, on_delete=models.CASCADE) source: models.ForeignKey = models.ForeignKey( @@ -42,6 +47,9 @@ class SyncType(models.TextChoices): default=dict, blank=True, ) + sync_frequency: models.CharField = models.CharField( + max_length=128, choices=SyncFrequency.choices, default=SyncFrequency.DAILY, blank=True + ) __repr__ = sane_repr("name") diff --git a/posthog/warehouse/models/external_data_source.py b/posthog/warehouse/models/external_data_source.py index 58aa891c34db0..dc21af8db26ad 100644 --- a/posthog/warehouse/models/external_data_source.py +++ b/posthog/warehouse/models/external_data_source.py @@ -27,6 +27,7 @@ class Status(models.TextChoices): COMPLETED = "Completed", "Completed" CANCELLED = "Cancelled", "Cancelled" + # Deprecated, use `ExternalDataSchema.SyncFrequency` class SyncFrequency(models.TextChoices): DAILY = "day", "Daily" WEEKLY = "week", "Weekly" @@ -38,6 +39,7 @@ class SyncFrequency(models.TextChoices): destination_id: models.CharField = models.CharField(max_length=400, null=True, blank=True) team: models.ForeignKey = models.ForeignKey(Team, on_delete=models.CASCADE) + # Deprecated, use `ExternalDataSchema.sync_frequency` sync_frequency: models.CharField = models.CharField( max_length=128, choices=SyncFrequency.choices, default=SyncFrequency.DAILY, blank=True ) @@ -69,15 +71,6 @@ def reload_schemas(self): except Exception as e: logger.exception(f"Could not trigger external data job for schema {schema.name}", exc_info=e) - def update_schemas(self): - from posthog.warehouse.models.external_data_schema import ExternalDataSchema - from posthog.warehouse.data_load.service import sync_external_data_job_workflow - - for schema in ExternalDataSchema.objects.filter( - team_id=self.team.pk, source_id=self.id, should_sync=True - ).all(): - sync_external_data_job_workflow(schema, create=False) - @database_sync_to_async def get_external_data_source(source_id: UUID) -> ExternalDataSource: diff --git a/requirements-dev.txt b/requirements-dev.txt index c534a931f0c92..b2dc70b72d1fc 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,7 +25,6 @@ asttokens==2.4.1 async-timeout==4.0.2 # via # -c requirements.txt - # aiohttp # redis attrs==23.2.0 # via @@ -38,7 +37,7 @@ black==23.9.1 # -r requirements-dev.in # datamodel-code-generator # inline-snapshot -boto3-stubs==1.34.84 +boto3-stubs[s3]==1.34.84 # via -r requirements-dev.in botocore-stubs==1.34.84 # via boto3-stubs @@ -63,7 +62,7 @@ click==8.1.7 # inline-snapshot colorama==0.4.4 # via pytest-watch -coverage==5.5 +coverage[toml]==5.5 # via pytest-cov cryptography==39.0.2 # via @@ -71,7 +70,7 @@ cryptography==39.0.2 # types-paramiko datamodel-code-generator==0.25.6 # via -r requirements-dev.in -django==4.2.11 +django==4.2.14 # via # -c requirements.txt # django-stubs @@ -92,17 +91,13 @@ docopt==0.6.2 # via pytest-watch email-validator==2.0.0.post2 # via pydantic -exceptiongroup==1.2.1 - # via - # -c requirements.txt - # pytest execnet==2.1.1 # via pytest-xdist executing==2.0.1 # via inline-snapshot faker==17.5.0 # via -r requirements-dev.in -fakeredis==2.11.0 +fakeredis[lua]==2.11.0 # via -r requirements-dev.in flaky==3.7.0 # via -r requirements-dev.in @@ -203,7 +198,7 @@ pycparser==2.20 # via # -c requirements.txt # cffi -pydantic==2.5.3 +pydantic[email]==2.5.3 # via # -c requirements.txt # datamodel-code-generator @@ -289,6 +284,7 @@ ruamel-yaml==0.18.6 ruamel-yaml-clib==0.2.8 # via ruamel-yaml ruff==0.4.10 + # via -r requirements-dev.in six==1.16.0 # via # -c requirements.txt @@ -309,14 +305,7 @@ syrupy==4.6.0 toml==0.10.2 # via # coverage - # datamodel-code-generator # inline-snapshot -tomli==2.0.1 - # via - # black - # django-stubs - # mypy - # pytest types-awscrt==0.20.9 # via botocore-stubs types-freezegun==1.1.10 @@ -355,8 +344,6 @@ types-tzlocal==5.1.0.1 typing-extensions==4.7.1 # via # -c requirements.txt - # asgiref - # black # boto3-stubs # django-stubs # django-stubs-ext @@ -371,7 +358,9 @@ urllib3==1.26.18 # requests # responses watchdog==2.1.8 - # via pytest-watch + # via + # -r requirements-dev.in + # pytest-watch yarl==1.9.4 # via # -c requirements.txt diff --git a/requirements.in b/requirements.in index 48c264e4131b2..5cd8ce747b335 100644 --- a/requirements.in +++ b/requirements.in @@ -16,7 +16,7 @@ clickhouse-driver==0.2.7 clickhouse-pool==0.5.3 cryptography==39.0.2 dj-database-url==0.5.0 -Django~=4.2.11 +Django~=4.2.14 django-axes==5.9.0 django-cors-headers==3.5.0 django-deprecate-fields==0.1.1 @@ -93,7 +93,7 @@ phonenumberslite==8.13.6 openai==1.10.0 tiktoken==0.6.0 nh3==0.2.14 -hogql-parser==1.0.27 +hogql-parser==1.0.28 zxcvbn==4.4.28 zstd==1.5.5.1 xmlsec==1.3.13 # Do not change this version - it will break SAML diff --git a/requirements.txt b/requirements.txt index 1fc2ba65c39cf..0aa73caba3e40 100644 --- a/requirements.txt +++ b/requirements.txt @@ -139,7 +139,7 @@ distro==1.9.0 # via openai dj-database-url==0.5.0 # via -r requirements.in -django==4.2.11 +django==4.2.14 # via # -r requirements.in # django-axes @@ -262,6 +262,8 @@ googleapis-common-protos==1.60.0 # via # google-api-core # grpcio-status +greenlet==3.0.3 + # via sqlalchemy grpcio==1.57.0 # via # google-api-core @@ -277,7 +279,7 @@ h11==0.13.0 # wsproto hexbytes==1.0.0 # via dlt -hogql-parser==1.0.27 +hogql-parser==1.0.28 # via -r requirements.in httpcore==1.0.2 # via httpx diff --git a/rust/capture/src/api.rs b/rust/capture/src/api.rs index 97d84857075e4..646bd2d3c9c8b 100644 --- a/rust/capture/src/api.rs +++ b/rust/capture/src/api.rs @@ -84,6 +84,9 @@ impl IntoResponse for CaptureError { pub enum DataType { AnalyticsMain, AnalyticsHistorical, + ClientIngestionWarning, + HeatmapMain, + ExceptionMain, } #[derive(Clone, Debug, Serialize, Eq, PartialEq)] pub struct ProcessedEvent { diff --git a/rust/capture/src/config.rs b/rust/capture/src/config.rs index cfc38877fec33..6c66d09e68454 100644 --- a/rust/capture/src/config.rs +++ b/rust/capture/src/config.rs @@ -54,6 +54,12 @@ pub struct KafkaConfig { pub kafka_topic: String, #[envconfig(default = "events_plugin_ingestion_historical")] pub kafka_historical_topic: String, + #[envconfig(default = "events_plugin_ingestion")] + pub kafka_client_ingestion_warning_topic: String, + #[envconfig(default = "events_plugin_ingestion")] + pub kafka_exceptions_topic: String, + #[envconfig(default = "events_plugin_ingestion")] + pub kafka_heatmaps_topic: String, #[envconfig(default = "false")] pub kafka_tls: bool, } diff --git a/rust/capture/src/sinks/kafka.rs b/rust/capture/src/sinks/kafka.rs index b82d3c342a115..760c6f31740ba 100644 --- a/rust/capture/src/sinks/kafka.rs +++ b/rust/capture/src/sinks/kafka.rs @@ -110,6 +110,9 @@ pub struct KafkaSink { partition: Option, main_topic: String, historical_topic: String, + client_ingestion_warning_topic: String, + exceptions_topic: String, + heatmaps_topic: String, } impl KafkaSink { @@ -158,6 +161,9 @@ impl KafkaSink { partition, main_topic: config.kafka_topic, historical_topic: config.kafka_historical_topic, + client_ingestion_warning_topic: config.kafka_client_ingestion_warning_topic, + exceptions_topic: config.kafka_exceptions_topic, + heatmaps_topic: config.kafka_heatmaps_topic, }) } @@ -187,6 +193,12 @@ impl KafkaSink { (&self.main_topic, Some(event_key.as_str())) } } + DataType::ClientIngestionWarning => ( + &self.client_ingestion_warning_topic, + Some(event_key.as_str()), + ), + DataType::HeatmapMain => (&self.heatmaps_topic, Some(event_key.as_str())), + DataType::ExceptionMain => (&self.exceptions_topic, Some(event_key.as_str())), }; match self.producer.send_result(FutureRecord { @@ -325,6 +337,9 @@ mod tests { kafka_hosts: cluster.bootstrap_servers(), kafka_topic: "events_plugin_ingestion".to_string(), kafka_historical_topic: "events_plugin_ingestion_historical".to_string(), + kafka_client_ingestion_warning_topic: "events_plugin_ingestion".to_string(), + kafka_exceptions_topic: "events_plugin_ingestion".to_string(), + kafka_heatmaps_topic: "events_plugin_ingestion".to_string(), kafka_tls: false, }; let sink = KafkaSink::new(config, handle, limiter).expect("failed to create sink"); diff --git a/rust/capture/src/v0_endpoint.rs b/rust/capture/src/v0_endpoint.rs index ff4b90f2662e2..83290e1016742 100644 --- a/rust/capture/src/v0_endpoint.rs +++ b/rust/capture/src/v0_endpoint.rs @@ -182,9 +182,12 @@ pub fn process_single_event( return Err(CaptureError::MissingEventName); } - let data_type = match context.historical_migration { - true => DataType::AnalyticsHistorical, - false => DataType::AnalyticsMain, + let data_type = match (event.event.as_str(), context.historical_migration) { + ("$$client_ingestion_warning", _) => DataType::ClientIngestionWarning, + ("$exception", _) => DataType::ExceptionMain, + ("$$heatmap", _) => DataType::HeatmapMain, + (_, true) => DataType::AnalyticsHistorical, + (_, false) => DataType::AnalyticsMain, }; let data = serde_json::to_string(&event).map_err(|e| { diff --git a/rust/capture/tests/common.rs b/rust/capture/tests/common.rs index 5dd4c639aa5f0..f469f8c6815a4 100644 --- a/rust/capture/tests/common.rs +++ b/rust/capture/tests/common.rs @@ -45,6 +45,9 @@ pub static DEFAULT_CONFIG: Lazy = Lazy::new(|| Config { kafka_hosts: "kafka:9092".to_string(), kafka_topic: "events_plugin_ingestion".to_string(), kafka_historical_topic: "events_plugin_ingestion_historical".to_string(), + kafka_client_ingestion_warning_topic: "events_plugin_ingestion".to_string(), + kafka_exceptions_topic: "events_plugin_ingestion".to_string(), + kafka_heatmaps_topic: "events_plugin_ingestion".to_string(), kafka_tls: false, }, otel_url: None, @@ -179,6 +182,15 @@ impl EphemeralTopic { } } + pub(crate) fn assert_empty(&self) { + assert!( + self.consumer + .poll(Timeout::After(Duration::from_secs(1))) + .is_none(), + "topic holds more messages" + ) + } + pub fn topic_name(&self) -> &str { &self.topic_name } diff --git a/rust/capture/tests/events.rs b/rust/capture/tests/events.rs index 0554aae905ec8..8fc95c10bc2b7 100644 --- a/rust/capture/tests/events.rs +++ b/rust/capture/tests/events.rs @@ -1,13 +1,13 @@ use std::num::NonZeroU32; use time::Duration; +use crate::common::*; use anyhow::Result; use assert_json_diff::assert_json_include; use capture::limiters::billing::QuotaResource; use reqwest::StatusCode; use serde_json::json; - -use crate::common::*; +use uuid::Uuid; mod common; #[tokio::test] @@ -411,3 +411,104 @@ async fn it_applies_billing_limits() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn it_routes_exceptions_and_heapmaps_to_separate_topics() -> Result<()> { + setup_tracing(); + + let token = random_string("token", 16); + let distinct_id = random_string("id", 16); + let uuids: [Uuid; 5] = core::array::from_fn(|_| Uuid::now_v7()); + + let main_topic = EphemeralTopic::new().await; + let warnings_topic = EphemeralTopic::new().await; + let exceptions_topic = EphemeralTopic::new().await; + let heatmaps_topic = EphemeralTopic::new().await; + + let mut config = DEFAULT_CONFIG.clone(); + config.kafka.kafka_topic = main_topic.topic_name().to_string(); + config.kafka.kafka_client_ingestion_warning_topic = warnings_topic.topic_name().to_string(); + config.kafka.kafka_exceptions_topic = exceptions_topic.topic_name().to_string(); + config.kafka.kafka_heatmaps_topic = heatmaps_topic.topic_name().to_string(); + + let server = ServerHandle::for_config(config).await; + + let event = json!([{ + "token": token, + "event": "$$client_ingestion_warning", + "uuid": uuids[4], + "distinct_id": distinct_id + },{ + "token": token, + "event": "event1", + "uuid": uuids[0], + "distinct_id": distinct_id + },{ + "token": token, + "event": "$$heatmap", + "uuid": uuids[1], + "distinct_id": distinct_id + },{ + "token": token, + "event": "$exception", + "uuid": uuids[2], + "distinct_id": distinct_id + },{ + "token": token, + "event": "event2", + "uuid": uuids[3], + "distinct_id": distinct_id + }]); + + let res = server.capture_events(event.to_string()).await; + assert_eq!(StatusCode::OK, res.status()); + + // Regular events are pushed to the main analytics topic + assert_json_include!( + actual: main_topic.next_event()?, + expected: json!({ + "token": token, + "uuid": uuids[0], + "distinct_id": distinct_id + }) + ); + assert_json_include!( + actual: main_topic.next_event()?, + expected: json!({ + "token": token, + "uuid": uuids[3], + "distinct_id": distinct_id + }) + ); + main_topic.assert_empty(); + + // Special-cased events are pushed to their own topics + assert_json_include!( + actual: exceptions_topic.next_event()?, + expected: json!({ + "token": token, + "uuid": uuids[2], + "distinct_id": distinct_id + }) + ); + exceptions_topic.assert_empty(); + assert_json_include!( + actual: heatmaps_topic.next_event()?, + expected: json!({ + "token": token, + "uuid": uuids[1], + "distinct_id": distinct_id + }) + ); + heatmaps_topic.assert_empty(); + assert_json_include!( + actual: warnings_topic.next_event()?, + expected: json!({ + "token": token, + "uuid": uuids[4], + "distinct_id": distinct_id + }) + ); + warnings_topic.assert_empty(); + Ok(()) +}