pytrilogy 0.3.331__tar.gz → 0.3.332__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/PKG-INFO +1 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/__init__.py +1 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/authoring/__init__.py +2 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/execute.py +17 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/union_dim_pushdown.py +35 -5
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +87 -29
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/strategy_builder.py +13 -1
- pytrilogy-0.3.332/trilogy/core/processing/v4_node_generators/basic.py +104 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/query_processor.py +70 -18
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/statements/author.py +24 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/statements/execute.py +10 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/base.py +18 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/python_source.py +7 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/AGENTS.md +6 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/__init__.py +2 -0
- pytrilogy-0.3.332/trilogy/execution/state/isolation.py +39 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/partitions.py +19 -19
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/state_store.py +10 -14
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/watermarks.py +72 -30
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/executor.py +104 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parser.py +2 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/parse_engine_v2.py +4 -3
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/render.py +79 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/trilogy.lark +6 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/hydration.py +24 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/chart_rules.py +9 -2
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/operational_rules.py +53 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/statement_planner.py +3 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/statement_plans.py +32 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/syntax.py +2 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/rendering/altair_renderer.py +17 -9
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_info_docs/content.py +19 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/common.py +18 -6
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/Cargo.lock +1 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/Cargo.toml +1 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/trilogy.pest +3 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display.py +2 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_parallel.py +67 -39
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/parallel_execution.py +11 -3
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/refresh.py +1 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/single_execution.py +1 -1
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/source.py +2 -5
- pytrilogy-0.3.331/trilogy/core/processing/v4_node_generators/basic.py +0 -53
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/.scripts/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/.scripts/build_backend.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/.scripts/sync_version.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/LICENSE.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/pyproject.toml +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/constants.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/conversation.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/enums.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/execute.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/models.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/prompts.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/anthropic.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/base.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/deepseek.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/google.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/openai.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/openrouter.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/providers/utils.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/ai/syntax_examples.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/authoring/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/constants.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/domain_graph.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/domain_validation.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/fingerprint.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/graph.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/having_normalization.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/author.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/collapse_single_parent.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/filtered_aggregate.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/filtered_count_join.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/full_join_lowering.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/hide_unused_concept.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/join_hoist.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/join_upgrade.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/keyless_full_join.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/merge_irrelevant_group_by.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/null_safe_join.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/order_inner_joins.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/semi_join_pushdown.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/strip_redundant_not_null.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/utils.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/optimizations/value_set_join_upgrade.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/VIRTUAL_UNNEST.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/aggregate_rollup.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/concept_strategies_v4.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/condition_utility.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/constants.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/discovery_utility.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/grain_utility.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/join_resolution.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/model_ambiguity.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/presence_probe.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/select_helpers/condition_routing.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/select_helpers/datasource_nodes.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/select_helpers/source_scoring.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/subselect_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/rowset_islanding.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/concept_graph.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/condition_injection.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/condition_placement.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/constants.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/edges.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/functional_dependency.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/group_behaviors.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/group_graph.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/group_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/history.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/models.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/network_build.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/network_coalescing.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/network_model.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/network_obligations.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/network_search.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/network_topology.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/projection.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/source_planning.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/staged_where.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/aggregate.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/common.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/condition_sources.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/constant.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/dispatch.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/filter.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/group_to.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/multiselect.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/nested_select.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/recursive.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/root.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/rowset.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/subselect.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/union.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/union_select.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/unnest.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_node_generators/window.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/scope_diagnostics.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/statements/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/table_processor.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/common.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/concept.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/datasource.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/environment.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/fix.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/validation/rows.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/where_scope_normalization.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/bigquery_engine.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/bigquery_persist.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/bigquery_staging.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/cancel.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/clickhouse.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/clickhouse_chdb.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/duckdb_uv.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/metadata.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/mock.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/mysql.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/results.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/source_pushdown.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/dialect/sqlite.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/engine.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/config.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/envs.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/model_fingerprint.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/report.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/CLAUDE.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/cache.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/exceptions.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/persistence.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/phases.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/execution/state/snapshot.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/adapters.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/arrow.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/click_support.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/contract.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/describe.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/errors.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/runner.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/io/sinks.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/pretty.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/concept_factory.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/concept_syntax.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/errors.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/function_syntax.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/import_service.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/lark_backend.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/model.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/pest_backend.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rowset_semantics.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/concept_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/conditional_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/datasource_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/expression_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/function_definition_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/function_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/import_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/merge_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/multiselect_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/order_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/persist_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/rawsql_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/rowset_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/select_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/select_statement_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/statement_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/subselect_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/token_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/tvf_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules/type_rules.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/rules_context.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/select_finalize.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/semantic_scope.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/semantic_state.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/parsing/v2/symbols.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/py.typed +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/render.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/rendering/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/rendering/base.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/rendering/chart_theme.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/rendering/rich_types.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/rendering/terminal_renderer.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/rendering/theme.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/backends/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/backends/base.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/backends/html.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/backends/png.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/charts.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/document.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/runner.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/report/tables.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/AGENTS.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/CLAUDE.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_info.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_info_docs/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_info_docs/cli.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_info_docs/directory.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_sessions.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_sql_tools.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/agent_tools.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/click_utils.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/cloud.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/cloud_models.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/database.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/README.md +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/build.sh +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/examples/basic_usage.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/directory_resolver.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/graph.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/lib.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/main.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/network_search.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/parser.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/python_bindings.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/resolver.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/src/trilogy_parser.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/base.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/cli_integration.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/customer.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/main.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/orders.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/public_api.rs +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/test_data/base.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency/tests/test_data/consumer.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/dependency.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_core.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_execution.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_ingest.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_init.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_models.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_refresh.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/display_validation.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/env_commands.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/environment.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/explore.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/explore_seen.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/file.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/file_helpers/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/file_helpers/backends.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/file_helpers/preql_description.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/file_helpers/preql_validation.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/fmt.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/ingest.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/ingest_helpers/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/ingest_helpers/fk_inference.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/ingest_helpers/foreign_keys.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/ingest_helpers/formatting.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/ingest_helpers/introspection.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/ingest_helpers/typing.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/init.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/plan.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/project_config.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/public.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/render.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/run.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/connection_spec.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/file_discovery.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/index_generation.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/jobs.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/models.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/state_cache.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/state_computation.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/serve_helpers/studio_bundle.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/source_identity.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/state.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/testing.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/scripts/validate_agent.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/staging.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/chart.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/color.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/currency.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/metric.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/std/semantic.preql +0 -0
- {pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/utility.py +0 -0
|
@@ -55,6 +55,7 @@ from trilogy.core.models.datasource import Address, Datasource, DatasourceMetada
|
|
|
55
55
|
from trilogy.core.models.environment import DictImportResolver, Environment
|
|
56
56
|
from trilogy.core.statements.author import (
|
|
57
57
|
STATEMENT_TYPES,
|
|
58
|
+
CallStatement,
|
|
58
59
|
ConceptDeclarationStatement,
|
|
59
60
|
ConceptTransform,
|
|
60
61
|
CopyStatement,
|
|
@@ -89,6 +90,7 @@ __all__ = [
|
|
|
89
90
|
"ArrayType",
|
|
90
91
|
# trilogy.core.enums
|
|
91
92
|
"BooleanOperator",
|
|
93
|
+
"CallStatement",
|
|
92
94
|
"CaseElse",
|
|
93
95
|
"CaseWhen",
|
|
94
96
|
"Comment",
|
|
@@ -1830,11 +1830,27 @@ class UnionCTE:
|
|
|
1830
1830
|
|
|
1831
1831
|
@property
|
|
1832
1832
|
def group_to_grain(self) -> bool:
|
|
1833
|
+
"""A union never dedupes itself — only its consumer can decide.
|
|
1834
|
+
|
|
1835
|
+
A UNION ALL whose projection drops its arms' grain keys does hold
|
|
1836
|
+
duplicate rows against its own declared grain, so this is NOT the
|
|
1837
|
+
"already a unique set" it looks like. But the duplicates are only noise
|
|
1838
|
+
to a consumer joining the union as a key set; to a consumer aggregating
|
|
1839
|
+
it they are the data. `select species, sum(dbh)` over two tree-grain
|
|
1840
|
+
partitions projects (species, dbh) and must keep all three of three
|
|
1841
|
+
identically-valued trees — deduping here silently under-counts the sum.
|
|
1842
|
+
|
|
1843
|
+
The union cannot tell those apart, so the obligation sits with whoever
|
|
1844
|
+
reads it at a coarser grain: that consumer must group. See
|
|
1845
|
+
`_grain_claim_needs_group` in v4_node_generators/basic.py, which is the
|
|
1846
|
+
projection-shaped case of exactly that.
|
|
1847
|
+
"""
|
|
1833
1848
|
return False
|
|
1834
1849
|
|
|
1835
1850
|
@property
|
|
1836
1851
|
def group_concepts(self) -> list[BuildConcept]:
|
|
1837
|
-
#
|
|
1852
|
+
# Nothing to group by: see group_to_grain — deduping is the consumer's
|
|
1853
|
+
# obligation, never the union's.
|
|
1838
1854
|
return []
|
|
1839
1855
|
|
|
1840
1856
|
def __add__(self, other):
|
|
@@ -192,6 +192,23 @@ def _datasource_matches_raw_id(
|
|
|
192
192
|
return isinstance(base, BuildDatasource) and base.identifier == source_id
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
def _derives_from(node: CTE | UnionCTE, container_name: str) -> bool:
|
|
196
|
+
"""``node`` IS ``container_name`` or reads from it, at any depth."""
|
|
197
|
+
seen: set[str] = set()
|
|
198
|
+
stack: list[CTE | UnionCTE] = [node]
|
|
199
|
+
while stack:
|
|
200
|
+
current = stack.pop()
|
|
201
|
+
if current.name == container_name:
|
|
202
|
+
return True
|
|
203
|
+
if current.name in seen:
|
|
204
|
+
continue
|
|
205
|
+
seen.add(current.name)
|
|
206
|
+
stack.extend(current.dependency_nodes())
|
|
207
|
+
if isinstance(current, UnionCTE):
|
|
208
|
+
stack.extend(current.internal_ctes)
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
|
|
195
212
|
def _dim_local_atoms(
|
|
196
213
|
cte: CTE,
|
|
197
214
|
dim_qds: BuildDatasource | QueryDatasource,
|
|
@@ -604,12 +621,25 @@ class UnionDimPushdown(OptimizationRule):
|
|
|
604
621
|
return needed.issubset(out)
|
|
605
622
|
|
|
606
623
|
def _resolve_push_context(
|
|
607
|
-
self, consumers: list[CTE], d: _DimDescriptor
|
|
624
|
+
self, consumers: list[CTE], d: _DimDescriptor, container: CTE | UnionCTE
|
|
608
625
|
) -> _PushContext | None:
|
|
609
626
|
for c in consumers:
|
|
610
627
|
found = _find_dim_cte_for_qds(c, d.join_qds_id)
|
|
611
|
-
if found is
|
|
612
|
-
|
|
628
|
+
if found is None or not self._dim_cte_exposes(found, d):
|
|
629
|
+
continue
|
|
630
|
+
# A "dim" carved OUT of the container (a single-key projection of the
|
|
631
|
+
# union, which the planner peels when the union is the only source of
|
|
632
|
+
# that key) cannot be computed before it. Pushing it in would make the
|
|
633
|
+
# container's branches depend on a CTE that depends on the container —
|
|
634
|
+
# `reorder_ctes` then fails the whole query with "CTE dependency graph
|
|
635
|
+
# contains a cycle".
|
|
636
|
+
if _derives_from(found, container.name):
|
|
637
|
+
self.log(
|
|
638
|
+
f"Skipping dim {d.dim_qds.identifier}: its CTE {found.name} "
|
|
639
|
+
f"is derived from {container.name}"
|
|
640
|
+
)
|
|
641
|
+
continue
|
|
642
|
+
return _PushContext(dim_cte=found, source_consumer=c)
|
|
613
643
|
return None
|
|
614
644
|
|
|
615
645
|
def _can_push_into_branch(self, branch: CTE, d: _DimDescriptor) -> bool:
|
|
@@ -634,7 +664,7 @@ class UnionDimPushdown(OptimizationRule):
|
|
|
634
664
|
if not d.strip_safe and not d.where_atoms:
|
|
635
665
|
return False
|
|
636
666
|
|
|
637
|
-
context = self._resolve_push_context(consumers, d)
|
|
667
|
+
context = self._resolve_push_context(consumers, d, union)
|
|
638
668
|
if context is None:
|
|
639
669
|
return False
|
|
640
670
|
|
|
@@ -707,7 +737,7 @@ class UnionDimPushdown(OptimizationRule):
|
|
|
707
737
|
"""
|
|
708
738
|
if not d.strip_safe or not d.where_atoms:
|
|
709
739
|
return False
|
|
710
|
-
context = self._resolve_push_context(consumers, d)
|
|
740
|
+
context = self._resolve_push_context(consumers, d, target)
|
|
711
741
|
if context is None:
|
|
712
742
|
return False
|
|
713
743
|
if not self._can_push_into_branch(target, d):
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
|
-
from itertools import product
|
|
3
2
|
|
|
4
3
|
from trilogy.core.enums import (
|
|
5
4
|
AddressType,
|
|
@@ -76,9 +75,12 @@ def _best_enum_union(
|
|
|
76
75
|
) -> list[list[BuildDatasource]] | None:
|
|
77
76
|
"""Find the best minimal covering combinations for an enum-partitioned key.
|
|
78
77
|
|
|
79
|
-
Groups by covered enum value,
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
Groups by covered enum value, then searches one-source-per-value combos via
|
|
79
|
+
a dynamic program over values whose state is the combo's concept overlap so
|
|
80
|
+
far (minus the merge key), keeping the highest-scoring combo per distinct
|
|
81
|
+
overlap signature. The score is separable (a per-source sum) and the
|
|
82
|
+
overlap is the only coupling between values, so this is exhaustive over
|
|
83
|
+
achievable signatures without enumerating the k^V combo product. Returning
|
|
82
84
|
one combo per overlap lets parallel partitionings (e.g., sales vs.
|
|
83
85
|
returns vs. dim, all keyed by the same channel enum) each contribute
|
|
84
86
|
their own union datasource instead of collapsing into the single best.
|
|
@@ -100,36 +102,92 @@ def _best_enum_union(
|
|
|
100
102
|
return None
|
|
101
103
|
|
|
102
104
|
values = list(by_value.keys())
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
# A union requires at least 2 distinct sources; a single source is not a union
|
|
106
|
+
if len(values) < 2:
|
|
107
|
+
return None
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
cols: dict[int, frozenset[str]] = {}
|
|
110
|
+
scores: dict[int, int] = {}
|
|
111
|
+
for candidates in by_value.values():
|
|
112
|
+
for ds in candidates:
|
|
113
|
+
cols[id(ds)] = frozenset(col.concept.address for col in ds.columns)
|
|
114
|
+
scores[id(ds)] = _datasource_score(ds)
|
|
109
115
|
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
# Members MAY disagree on intrinsic (~) partiality of a shared column
|
|
117
|
+
# (a "mixed-family" combo, e.g. web_sales + catalog/store_returns).
|
|
118
|
+
# Such a union is a legitimate per-channel provider of columns it binds
|
|
119
|
+
# complete (q05: a web return's return-site lives on web_sales), and
|
|
120
|
+
# union partial propagation keeps its ~-partial keys from ever outranking
|
|
121
|
+
# a pure family that binds them complete (q14) — so don't reject it here.
|
|
122
|
+
# An empty overlap beyond the merge key IS rejected, at the first step it
|
|
123
|
+
# appears: intersection only shrinks, so no continuation can revive it.
|
|
124
|
+
# Ties reproduce the old product enumeration exactly: per signature the
|
|
125
|
+
# winner is the first max-scoring combo in product order (`combo_key` =
|
|
126
|
+
# candidate index tuple, lexicographic = product order), and signatures
|
|
127
|
+
# order by their first-achieving combo (`min_key`, tracked over ALL combos
|
|
128
|
+
# reaching a signature, not just the best-scoring one).
|
|
129
|
+
merge_key_addr = merge_key.address
|
|
130
|
+
# signature -> (score, combo, combo_key, min_key)
|
|
131
|
+
states: dict[
|
|
132
|
+
frozenset[str],
|
|
133
|
+
tuple[int, list[BuildDatasource], tuple[int, ...], tuple[int, ...]],
|
|
134
|
+
] = {}
|
|
135
|
+
for idx, ds in enumerate(by_value[values[0]]):
|
|
136
|
+
sig = cols[id(ds)] - {merge_key_addr}
|
|
137
|
+
if not sig:
|
|
112
138
|
continue
|
|
139
|
+
existing = states.get(sig)
|
|
140
|
+
if existing is None:
|
|
141
|
+
states[sig] = (scores[id(ds)], [ds], (idx,), (idx,))
|
|
142
|
+
elif scores[id(ds)] > existing[0]:
|
|
143
|
+
states[sig] = (scores[id(ds)], [ds], (idx,), existing[3])
|
|
113
144
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
145
|
+
for v in values[1:]:
|
|
146
|
+
next_states: dict[
|
|
147
|
+
frozenset[str],
|
|
148
|
+
tuple[int, list[BuildDatasource], tuple[int, ...], tuple[int, ...]],
|
|
149
|
+
] = {}
|
|
150
|
+
for sig, (score, combo, combo_key, min_key) in states.items():
|
|
151
|
+
for idx, ds in enumerate(by_value[v]):
|
|
152
|
+
new_sig = sig & cols[id(ds)]
|
|
153
|
+
if not new_sig:
|
|
154
|
+
continue
|
|
155
|
+
new_score = score + scores[id(ds)]
|
|
156
|
+
new_key = combo_key + (idx,)
|
|
157
|
+
existing = next_states.get(new_sig)
|
|
158
|
+
if existing is None:
|
|
159
|
+
next_states[new_sig] = (
|
|
160
|
+
new_score,
|
|
161
|
+
combo + [ds],
|
|
162
|
+
new_key,
|
|
163
|
+
min_key + (idx,),
|
|
164
|
+
)
|
|
165
|
+
continue
|
|
166
|
+
new_min_key = min(existing[3], min_key + (idx,))
|
|
167
|
+
if new_score > existing[0] or (
|
|
168
|
+
new_score == existing[0] and new_key < existing[2]
|
|
169
|
+
):
|
|
170
|
+
next_states[new_sig] = (
|
|
171
|
+
new_score,
|
|
172
|
+
combo + [ds],
|
|
173
|
+
new_key,
|
|
174
|
+
new_min_key,
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
next_states[new_sig] = (
|
|
178
|
+
existing[0],
|
|
179
|
+
existing[1],
|
|
180
|
+
existing[2],
|
|
181
|
+
new_min_key,
|
|
182
|
+
)
|
|
183
|
+
states = next_states
|
|
184
|
+
if not states:
|
|
185
|
+
return None
|
|
132
186
|
|
|
187
|
+
best_per_overlap: dict[frozenset[str], tuple[list[BuildDatasource], int]] = {
|
|
188
|
+
sig: (state[1], state[0])
|
|
189
|
+
for sig, state in sorted(states.items(), key=lambda kv: kv[1][3])
|
|
190
|
+
}
|
|
133
191
|
if not best_per_overlap:
|
|
134
192
|
return None
|
|
135
193
|
# Keep only maximal overlap signatures: drop a signature whose concept set
|
{pytrilogy-0.3.331 → pytrilogy-0.3.332}/trilogy/core/processing/v4_helper/strategy_builder.py
RENAMED
|
@@ -2019,7 +2019,19 @@ def _satisfy_parent_projection_contract(
|
|
|
2019
2019
|
if not any(_contains_shape_barrier(parent) for parent in parents):
|
|
2020
2020
|
return parents
|
|
2021
2021
|
|
|
2022
|
+
# Two different questions, two different sets. "What is available TO this
|
|
2023
|
+
# parent" is its own parents' outputs (`parent_output_addresses`) — that is
|
|
2024
|
+
# what `parent_needed` asks, and a leaf scan's empty answer is what keeps
|
|
2025
|
+
# leaf scans out of this projection entirely (q66). "What does a SIBLING
|
|
2026
|
+
# supply to the merge" is that sibling's own projection: a WINDOW sibling
|
|
2027
|
+
# READS `event_time` and emits only `lag(event_time)`, so crediting it with
|
|
2028
|
+
# its inputs made this projection strip `event_time` off the dimension
|
|
2029
|
+
# parent — and the consumer computing `event_time - prior_event_time` then
|
|
2030
|
+
# had no source for it and was dropped from the plan outright.
|
|
2022
2031
|
outputs_by_parent = [parent_output_addresses(parent) for parent in parents]
|
|
2032
|
+
own_outputs_by_parent = [
|
|
2033
|
+
{output.address for output in parent.usable_outputs} for parent in parents
|
|
2034
|
+
]
|
|
2023
2035
|
projected: list[StrategyNode] = []
|
|
2024
2036
|
for idx, parent in enumerate(parents):
|
|
2025
2037
|
if _contains_shape_barrier(parent):
|
|
@@ -2027,7 +2039,7 @@ def _satisfy_parent_projection_contract(
|
|
|
2027
2039
|
continue
|
|
2028
2040
|
parent_needed = outputs_by_parent[idx] & needed
|
|
2029
2041
|
other_outputs = set().union(
|
|
2030
|
-
*(outputs for j, outputs in enumerate(
|
|
2042
|
+
*(outputs for j, outputs in enumerate(own_outputs_by_parent) if j != idx)
|
|
2031
2043
|
)
|
|
2032
2044
|
fd_candidates = {
|
|
2033
2045
|
addr
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from trilogy.core.models.build import BuildConcept, BuildGrain, BuildWhereClause
|
|
2
|
+
from trilogy.core.models.build_environment import BuildEnvironment
|
|
3
|
+
from trilogy.core.processing.nodes import (
|
|
4
|
+
ConstantNode,
|
|
5
|
+
MergeNode,
|
|
6
|
+
SelectNode,
|
|
7
|
+
StrategyNode,
|
|
8
|
+
)
|
|
9
|
+
from trilogy.core.processing.v4_helper.functional_dependency import (
|
|
10
|
+
build_fd_determines,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .common import outputs_with_parent_grain_keys, parent_outputs_needed
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _grain_claim_needs_group(
|
|
17
|
+
outputs: list[BuildConcept],
|
|
18
|
+
parent: StrategyNode,
|
|
19
|
+
environment: BuildEnvironment,
|
|
20
|
+
) -> bool:
|
|
21
|
+
"""True when this projection can only make its grain claim true by grouping.
|
|
22
|
+
|
|
23
|
+
A projection's grain is inferred from what it projects, but a plain SELECT
|
|
24
|
+
emits one row per PARENT row. Project `species` and `upper(species)` off a
|
|
25
|
+
`tree_id`-grain union and the node advertises grain `species` while emitting
|
|
26
|
+
one row per tree; a consumer then joins it on `species` and fans the other
|
|
27
|
+
side out — silently, since the SQL is valid.
|
|
28
|
+
|
|
29
|
+
Two things can make the claim true, and only the second is ours to do here:
|
|
30
|
+
|
|
31
|
+
- The claim is already true. Either the parent's grain keys survive in the
|
|
32
|
+
projection, or the projected grain functionally determines them (the
|
|
33
|
+
parent holds at most one row per it, so dropping them loses nothing).
|
|
34
|
+
- The projection can be widened back to the parent's grain. That is
|
|
35
|
+
``outputs_with_parent_grain_keys``'s job, and it needs the parent to
|
|
36
|
+
actually expose those keys.
|
|
37
|
+
|
|
38
|
+
When the parent does NOT expose its own grain there is nothing to widen with,
|
|
39
|
+
and grouping is the only repair left. The outputs are functionally determined
|
|
40
|
+
by the grouped set, so the dedupe cannot drop a row — it removes exactly the
|
|
41
|
+
duplicates the parent's finer grain introduced.
|
|
42
|
+
|
|
43
|
+
Returns SelectNode's own default (False) when it does not fire -- passing
|
|
44
|
+
None instead would flip every other basic projection onto ``_resolve``'s
|
|
45
|
+
grain-less branch, which is a plan change for nodes this has no business
|
|
46
|
+
touching.
|
|
47
|
+
"""
|
|
48
|
+
parent_grain = set(parent.resolve().grain.components)
|
|
49
|
+
if not parent_grain or parent_grain.issubset({c.address for c in outputs}):
|
|
50
|
+
return False
|
|
51
|
+
if parent_grain.issubset({c.address for c in parent.output_concepts}):
|
|
52
|
+
return False
|
|
53
|
+
claimed = BuildGrain.from_concepts(outputs).components
|
|
54
|
+
return not all(
|
|
55
|
+
component in claimed
|
|
56
|
+
or build_fd_determines(
|
|
57
|
+
environment, claimed, component, include_empty_grain=False
|
|
58
|
+
)
|
|
59
|
+
for component in parent_grain
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def gen_basic(
|
|
64
|
+
outputs: list[BuildConcept],
|
|
65
|
+
parents: list[StrategyNode],
|
|
66
|
+
environment: BuildEnvironment,
|
|
67
|
+
conditions: BuildWhereClause | None = None,
|
|
68
|
+
preexisting_conditions: BuildWhereClause | None = None,
|
|
69
|
+
) -> StrategyNode | None:
|
|
70
|
+
"""Projection of derived basic expressions over already-built parents.
|
|
71
|
+
|
|
72
|
+
Zero parents → ConstantNode. One parent → SelectNode (just projection).
|
|
73
|
+
Multiple parents → MergeNode, which auto-joins on shared output concepts
|
|
74
|
+
(typically the common grain). A SelectNode here would render with no
|
|
75
|
+
join and emit `INVALID_REFERENCE_BUG_<...>` for the unjoined parent."""
|
|
76
|
+
pre = preexisting_conditions.conditional if preexisting_conditions else None
|
|
77
|
+
if not parents:
|
|
78
|
+
return ConstantNode(
|
|
79
|
+
input_concepts=[],
|
|
80
|
+
output_concepts=outputs,
|
|
81
|
+
environment=environment,
|
|
82
|
+
conditions=conditions.conditional if conditions else None,
|
|
83
|
+
preexisting_conditions=pre,
|
|
84
|
+
)
|
|
85
|
+
full_outputs = outputs_with_parent_grain_keys(outputs, parents)
|
|
86
|
+
inputs = parent_outputs_needed(full_outputs, parents, conditions)
|
|
87
|
+
if len(parents) == 1:
|
|
88
|
+
return SelectNode(
|
|
89
|
+
input_concepts=inputs,
|
|
90
|
+
output_concepts=full_outputs,
|
|
91
|
+
environment=environment,
|
|
92
|
+
parents=parents,
|
|
93
|
+
conditions=conditions.conditional if conditions else None,
|
|
94
|
+
preexisting_conditions=pre,
|
|
95
|
+
force_group=_grain_claim_needs_group(full_outputs, parents[0], environment),
|
|
96
|
+
)
|
|
97
|
+
return MergeNode(
|
|
98
|
+
input_concepts=inputs,
|
|
99
|
+
output_concepts=full_outputs,
|
|
100
|
+
environment=environment,
|
|
101
|
+
parents=parents,
|
|
102
|
+
conditions=conditions.conditional if conditions else None,
|
|
103
|
+
preexisting_conditions=pre,
|
|
104
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from collections import defaultdict
|
|
1
|
+
from collections import OrderedDict, defaultdict
|
|
2
2
|
from dataclasses import replace
|
|
3
3
|
from math import ceil
|
|
4
4
|
|
|
@@ -89,6 +89,7 @@ from trilogy.core.scope_diagnostics import (
|
|
|
89
89
|
extract_derived_value_scopes,
|
|
90
90
|
)
|
|
91
91
|
from trilogy.core.statements.author import (
|
|
92
|
+
CallStatement,
|
|
92
93
|
ChartLayer,
|
|
93
94
|
ChartStatement,
|
|
94
95
|
ConceptDeclarationStatement,
|
|
@@ -99,6 +100,7 @@ from trilogy.core.statements.author import (
|
|
|
99
100
|
)
|
|
100
101
|
from trilogy.core.statements.execute import (
|
|
101
102
|
MaterializedDataset,
|
|
103
|
+
ProcessedCallStatement,
|
|
102
104
|
ProcessedChartCopyStatement,
|
|
103
105
|
ProcessedChartLayer,
|
|
104
106
|
ProcessedChartStatement,
|
|
@@ -872,10 +874,17 @@ def _authored_reference_addresses(
|
|
|
872
874
|
return closure
|
|
873
875
|
|
|
874
876
|
|
|
875
|
-
# id(environment) -> (weak handle, stamp
|
|
877
|
+
# id(environment) -> (weak handle, {stamp: {join key: BuildCaches}}). Same
|
|
876
878
|
# identity idiom + mutation stamp as domain_graph._MINTED_CACHE.
|
|
877
879
|
_SESSION_CACHE_STORE: dict[int, tuple] = {}
|
|
878
880
|
|
|
881
|
+
# Stamp generations retained per environment. More than one because refresh
|
|
882
|
+
# planning alternates between two long-lived states — the full environment and
|
|
883
|
+
# the probes' hidden-datasource window (see execution/state/isolation.py, which
|
|
884
|
+
# restores the stamp on exit exactly when content is restored) — and a
|
|
885
|
+
# single-generation store made every alternation a full baseline rebuild.
|
|
886
|
+
_SESSION_CACHE_GENERATIONS = 4
|
|
887
|
+
|
|
879
888
|
|
|
880
889
|
def _evict_session_caches(key: int, _dead) -> None:
|
|
881
890
|
_SESSION_CACHE_STORE.pop(key, None)
|
|
@@ -893,12 +902,16 @@ def _session_build_caches(
|
|
|
893
902
|
datasource dict effective-write counters (content_version, not mutations —
|
|
894
903
|
overlay push/pop and identical re-registrations from a re-parsed script
|
|
895
904
|
must not evict the bundle), in-place datasource status flips (persist
|
|
896
|
-
marks PUBLISHED without a dict write), and the
|
|
897
|
-
in the join key. If a concept overlay is
|
|
898
|
-
fall back to the raw mutation counter so
|
|
899
|
-
A statement with its own scoped joins
|
|
900
|
-
— address/grain-keyed cache entries are only shareable
|
|
901
|
-
(the same rule that gives nested arms fresh caches).
|
|
905
|
+
marks PUBLISHED without a dict write), datasource membership, and the
|
|
906
|
+
alias map; env merges ride in the join key. If a concept overlay is
|
|
907
|
+
somehow live at generation time, fall back to the raw mutation counter so
|
|
908
|
+
overlay-visible state is covered. A statement with its own scoped joins
|
|
909
|
+
gets its own bundle — address/grain-keyed cache entries are only shareable
|
|
910
|
+
under ONE join set (the same rule that gives nested arms fresh caches).
|
|
911
|
+
A few recent stamps are retained per environment so states an executor
|
|
912
|
+
alternates between (full env / probe-hidden env) keep their bundles;
|
|
913
|
+
correctness rests on a stamp identifying exactly one content state, which
|
|
914
|
+
holds because counters only ever rewind on provably identical content."""
|
|
902
915
|
from functools import partial
|
|
903
916
|
from weakref import ref
|
|
904
917
|
|
|
@@ -911,15 +924,22 @@ def _session_build_caches(
|
|
|
911
924
|
)
|
|
912
925
|
store_key = id(environment)
|
|
913
926
|
cached = _SESSION_CACHE_STORE.get(store_key)
|
|
914
|
-
if cached is None or cached[0]() is not environment
|
|
915
|
-
|
|
927
|
+
if cached is None or cached[0]() is not environment:
|
|
928
|
+
generations: OrderedDict[tuple, dict[tuple, BuildCaches]] = OrderedDict()
|
|
916
929
|
_SESSION_CACHE_STORE[store_key] = (
|
|
917
930
|
ref(environment, partial(_evict_session_caches, store_key)),
|
|
918
|
-
|
|
919
|
-
bundles,
|
|
931
|
+
generations,
|
|
920
932
|
)
|
|
921
933
|
else:
|
|
922
|
-
|
|
934
|
+
generations = cached[1]
|
|
935
|
+
bundles = generations.get(stamp)
|
|
936
|
+
if bundles is None:
|
|
937
|
+
bundles = {}
|
|
938
|
+
generations[stamp] = bundles
|
|
939
|
+
while len(generations) > _SESSION_CACHE_GENERATIONS:
|
|
940
|
+
generations.popitem(last=False)
|
|
941
|
+
else:
|
|
942
|
+
generations.move_to_end(stamp)
|
|
923
943
|
key = environment.materialize_join_key(scoped_joins)
|
|
924
944
|
caches = bundles.get(key)
|
|
925
945
|
if caches is None:
|
|
@@ -1266,17 +1286,45 @@ def process_copy(
|
|
|
1266
1286
|
)
|
|
1267
1287
|
|
|
1268
1288
|
|
|
1269
|
-
def
|
|
1289
|
+
def process_call(
|
|
1290
|
+
environment: Environment,
|
|
1291
|
+
statement: CallStatement,
|
|
1292
|
+
hooks: list[BaseHook] | None = None,
|
|
1293
|
+
) -> ProcessedCallStatement:
|
|
1294
|
+
if statement.select is None:
|
|
1295
|
+
return ProcessedCallStatement(target=statement.target)
|
|
1296
|
+
query = process_query(
|
|
1297
|
+
environment=environment, statement=statement.select, hooks=hooks
|
|
1298
|
+
)
|
|
1299
|
+
return ProcessedCallStatement(target=statement.target, query=query)
|
|
1300
|
+
|
|
1301
|
+
|
|
1302
|
+
def _binding_safe_address(
|
|
1303
|
+
binding, environment: Environment, output_fields: set[str]
|
|
1304
|
+
) -> str:
|
|
1305
|
+
"""The select-output field a binding names.
|
|
1306
|
+
|
|
1307
|
+
An `as` alias resolves to a projected output of that name when one exists;
|
|
1308
|
+
otherwise it is a display label only and the bound concept itself is the
|
|
1309
|
+
field. Computed expressions have no address, so they require a projected
|
|
1310
|
+
alias."""
|
|
1270
1311
|
from trilogy.core.models.author import compute_safe_address
|
|
1271
1312
|
|
|
1272
1313
|
if binding.alias is not None:
|
|
1273
1314
|
namespace = environment.namespace or DEFAULT_NAMESPACE
|
|
1274
|
-
|
|
1315
|
+
alias_safe = compute_safe_address(namespace, binding.alias)
|
|
1316
|
+
if alias_safe in output_fields:
|
|
1317
|
+
return alias_safe
|
|
1275
1318
|
if isinstance(binding.expr, ConceptRef):
|
|
1276
1319
|
return binding.expr.safe_address
|
|
1320
|
+
if binding.alias is None:
|
|
1321
|
+
raise ValueError(
|
|
1322
|
+
f"Chart binding for role '{binding.role}' has a computed expression"
|
|
1323
|
+
" without an alias"
|
|
1324
|
+
)
|
|
1277
1325
|
raise ValueError(
|
|
1278
|
-
f"Chart
|
|
1279
|
-
"
|
|
1326
|
+
f"Chart role '{binding.role}' aliases '{binding.alias}' but no select"
|
|
1327
|
+
f" output has that name: {output_fields}"
|
|
1280
1328
|
)
|
|
1281
1329
|
|
|
1282
1330
|
|
|
@@ -1291,14 +1339,17 @@ def _process_chart_layer(
|
|
|
1291
1339
|
output_fields = {c.safe_address for c in layer.select.output_components}
|
|
1292
1340
|
|
|
1293
1341
|
role_map: dict[str, str] = {}
|
|
1342
|
+
field_labels: dict[str, str] = {}
|
|
1294
1343
|
for binding in layer.bindings:
|
|
1295
|
-
safe = _binding_safe_address(binding, environment)
|
|
1344
|
+
safe = _binding_safe_address(binding, environment, output_fields)
|
|
1296
1345
|
if safe not in output_fields:
|
|
1297
1346
|
raise ValueError(
|
|
1298
1347
|
f"Chart role '{binding.role}' resolves to '{safe}' which is"
|
|
1299
1348
|
f" not in select output: {output_fields}"
|
|
1300
1349
|
)
|
|
1301
1350
|
role_map[binding.role] = safe
|
|
1351
|
+
if binding.alias is not None:
|
|
1352
|
+
field_labels[safe] = binding.alias
|
|
1302
1353
|
|
|
1303
1354
|
def _single(role: str) -> str | None:
|
|
1304
1355
|
return role_map.get(role)
|
|
@@ -1308,6 +1359,7 @@ def _process_chart_layer(
|
|
|
1308
1359
|
return ProcessedChartLayer(
|
|
1309
1360
|
layer_type=layer.layer_type,
|
|
1310
1361
|
query=select,
|
|
1362
|
+
field_labels=field_labels,
|
|
1311
1363
|
x_fields=[x_field] if x_field else [],
|
|
1312
1364
|
y_fields=[y_field] if y_field else [],
|
|
1313
1365
|
color_field=_single("color"),
|
|
@@ -240,7 +240,14 @@ class SelectStatement(HasUUID, SelectTypeMixin):
|
|
|
240
240
|
]
|
|
241
241
|
return SelectLineage(
|
|
242
242
|
selection=[
|
|
243
|
-
|
|
243
|
+
# An ephemeral parse never commits its select aliases, so a
|
|
244
|
+
# locally-derived output may exist only on the statement.
|
|
245
|
+
(
|
|
246
|
+
self.local_concepts[x.concept.address]
|
|
247
|
+
if x.concept.address not in environment.concepts
|
|
248
|
+
and x.concept.address in self.local_concepts
|
|
249
|
+
else environment.concepts[x.concept.address]
|
|
250
|
+
).reference
|
|
244
251
|
for x in self.selection
|
|
245
252
|
],
|
|
246
253
|
order_by=self.order_by,
|
|
@@ -552,6 +559,18 @@ class CopyStatement:
|
|
|
552
559
|
meta: Metadata | None = field(default_factory=Metadata)
|
|
553
560
|
|
|
554
561
|
|
|
562
|
+
@dataclass
|
|
563
|
+
class CallStatement:
|
|
564
|
+
target: str
|
|
565
|
+
select: SelectStatement | None = None
|
|
566
|
+
meta: Metadata | None = field(default_factory=Metadata)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def call_arg_name(address: str) -> str:
|
|
570
|
+
"""The ``--flag`` name a call select output supplies (validated at parse)."""
|
|
571
|
+
return address.rsplit(".", 1)[-1]
|
|
572
|
+
|
|
573
|
+
|
|
555
574
|
@dataclass
|
|
556
575
|
class MultiSelectStatement(HasUUID, SelectTypeMixin):
|
|
557
576
|
selects: list[SelectStatement]
|
|
@@ -841,6 +860,9 @@ class ChartLayer:
|
|
|
841
860
|
layer_type: ChartType
|
|
842
861
|
bindings: list[ChartLayerBinding] = field(default_factory=list)
|
|
843
862
|
select: SelectStatement | None = None
|
|
863
|
+
# True when the author wrote `from select ...`; an implicit select is
|
|
864
|
+
# rebuilt from the bindings, so rendering must not echo it back.
|
|
865
|
+
explicit_select: bool = False
|
|
844
866
|
|
|
845
867
|
|
|
846
868
|
@dataclass
|
|
@@ -865,6 +887,7 @@ STATEMENT_TYPES = (
|
|
|
865
887
|
SelectStatement
|
|
866
888
|
| RawSQLStatement
|
|
867
889
|
| CopyStatement
|
|
890
|
+
| CallStatement
|
|
868
891
|
| MultiSelectStatement
|
|
869
892
|
| RowsetDerivationStatement
|
|
870
893
|
| MergeStatementV2
|
|
@@ -93,6 +93,12 @@ class ProcessedRawSQLStatement:
|
|
|
93
93
|
text: str
|
|
94
94
|
|
|
95
95
|
|
|
96
|
+
@dataclass
|
|
97
|
+
class ProcessedCallStatement:
|
|
98
|
+
target: str
|
|
99
|
+
query: ProcessedQuery | None = None
|
|
100
|
+
|
|
101
|
+
|
|
96
102
|
@dataclass
|
|
97
103
|
class ProcessedValidateStatement:
|
|
98
104
|
scope: ValidationScope
|
|
@@ -175,6 +181,9 @@ class ProcessedShowStatement:
|
|
|
175
181
|
class ProcessedChartLayer:
|
|
176
182
|
layer_type: "ChartType"
|
|
177
183
|
query: ProcessedQuery | None = None
|
|
184
|
+
# display names for fields whose binding carried an `as` alias, keyed by
|
|
185
|
+
# the field's safe address; renderers fall back to the address itself
|
|
186
|
+
field_labels: dict[str, str] = field(default_factory=dict)
|
|
178
187
|
x_fields: list[str] = field(default_factory=list)
|
|
179
188
|
y_fields: list[str] = field(default_factory=list)
|
|
180
189
|
color_field: str | None = None
|
|
@@ -210,6 +219,7 @@ from trilogy.core.statements.author import ChartPlacement
|
|
|
210
219
|
|
|
211
220
|
PROCESSED_STATEMENT_TYPES = (
|
|
212
221
|
ProcessedCopyStatement
|
|
222
|
+
| ProcessedCallStatement
|
|
213
223
|
| ProcessedQuery
|
|
214
224
|
| ProcessedRawSQLStatement
|
|
215
225
|
| ProcessedQueryPersist
|