class_docstr: Generates an SQL query plan from a node in the a metric dataflow plan.
class_docstr_tok: ["Generates","an","SQL","query","plan","from","a","node","in","the","a","metric","dataflow","plan","."]
docstr: Generates the query that realizes the behavior of the JoinToStandardOutputNode.
docstr_tok: ["Generates","the","query","that","realizes","the","behavior","of","the","JoinToStandardOutputNode","."]
code: def visit_join_to_base_output_node(
      self, node: JoinToBaseOutputNode
  ) -> SqlDataSet:
      """Generates the query that realizes the behavior of the JoinToStandardOutputNode."""
      # Keep a mapping between the table aliases that would be used in the query and the MDO instances in that source.
      # e.g. when building "FROM from_table a JOIN right_table b", the value for key "a" would be the instances in
      # "from_table"
      table_alias_to_instance_set: OrderedDict[
          str, InstanceSet
      ] = OrderedDict()
  
      # Convert the dataflow from the left node to a DataSet and add context for it to table_alias_to_instance_set
      # A DataSet is a bundle of the SQL query (in object form) and the MDO instances that the SQL query contains.
      from_data_set = node.left_node.accept(self)
      from_data_set_alias = self._next_unique_table_alias()
      table_alias_to_instance_set[
          from_data_set_alias
      ] = from_data_set.instance_set
  
      # Build the join descriptions for the SqlQueryPlan - different from node.join_descriptions which are the join
      # descriptions from the dataflow plan.
      sql_join_descs: List[SqlJoinDescription] = []
  
      # The dataflow plan describes how the data sets coming from the parent nodes should be joined together. Use
      # those descriptions to convert them to join descriptions for the SQL query plan.
      for join_description in node.join_targets:
          join_on_entity = join_description.join_on_entity
  
          right_node_to_join: BaseOutput = join_description.join_node
          right_data_set: SqlDataSet = right_node_to_join.accept(self)
          right_data_set_alias = self._next_unique_table_alias()
  
          sql_join_descs.append(
              SqlQueryPlanJoinBuilder.make_base_output_join_description(
                  left_data_set=AnnotatedSqlDataSet(
                      data_set=from_data_set, alias=from_data_set_alias
                  ),
                  right_data_set=AnnotatedSqlDataSet(
                      data_set=right_data_set,
                      alias=right_data_set_alias,
                  ),
                  join_description=join_description,
              )
          )
  
          # Remove the linkable instances with the join_on_entity as the leading link as the next step adds the
          # link. This is to avoid cases where there is a primary entity and a dimension in the data set, and we
          # create an instance in the next step that has the same entity link.
          # e.g. a data set has the dimension "listing__country_latest" and "listing" is a primary entity in the
          # data set. The next step would create an instance like "listing__listing__country_latest" without this
          # filter.
          right_data_set_instance_set_filtered = (
              FilterLinkableInstancesWithLeadingLink(
                  entity_link=join_on_entity,
              ).transform(right_data_set.instance_set)
          )
  
          # After the right data set is joined to the "from" data set, we need to change the links for some of the
          # instances that represent the right data set. For example, if the "from" data set contains the "bookings"
          # measure instance and the right dataset contains the "country" dimension instance, then after the join,
          # the output data set should have the "country" dimension instance with the "user_id" entity link
          # (if "user_id" equality was the join condition). "country" -> "user_id__country"
          right_data_set_instance_set_after_join = (
              right_data_set_instance_set_filtered.transform(
                  AddLinkToLinkableElements(
                      join_on_entity=join_on_entity
                  )
              )
          )
          table_alias_to_instance_set[
              right_data_set_alias
          ] = right_data_set_instance_set_after_join
  
      from_data_set_output_instance_set = (
          from_data_set.instance_set.transform(
              FilterElements(
                  include_specs=from_data_set.instance_set.spec_set
              )
          )
      )
  
      # Change the aggregation state for the measures to be partially aggregated if it was previously aggregated
      # since we removed the entities and added the dimensions. The dimensions could have the same value for
      # multiple rows, so we'll need to re-aggregate.
      from_data_set_output_instance_set = from_data_set_output_instance_set.transform(
          ChangeMeasureAggregationState(
              {
                  AggregationState.NON_AGGREGATED: AggregationState.NON_AGGREGATED,
                  AggregationState.COMPLETE: AggregationState.PARTIAL,
                  AggregationState.PARTIAL: AggregationState.PARTIAL,
              }
          )
      )
  
      table_alias_to_instance_set[
          from_data_set_alias
      ] = from_data_set_output_instance_set
  
      # Construct the data set that contains the updated instances and the SQL nodes that should go in the various
      # clauses.
      return SqlDataSet(
          instance_set=InstanceSet.merge(
              list(table_alias_to_instance_set.values())
          ),
          sql_select_node=SqlSelectStatementNode(
              description=node.description,
              select_columns=create_select_columns_for_instance_sets(
                  self._column_association_resolver,
                  table_alias_to_instance_set,
              ),
              from_source=from_data_set.sql_select_node,
              from_source_alias=from_data_set_alias,
              joins_descs=tuple(sql_join_descs),
              group_bys=(),
              where=None,
              order_bys=(),
          ),
      )
  
imports: from __future__ import annotations
  import logging
  from collections import OrderedDict
  from typing import List, Optional, Sequence, Tuple, Union
  from dbt_semantic_interfaces.enum_extension import assert_values_exhausted
  from dbt_semantic_interfaces.protocols.metric import MetricInputMeasure, MetricType
  from dbt_semantic_interfaces.references import MetricModelReference
  from dbt_semantic_interfaces.type_enums.aggregation_type import AggregationType
  from dbt_semantic_interfaces.type_enums.conversion_calculation_type import ConversionCalculationType
  from dbt_semantic_interfaces.validations.unique_valid_name import MetricFlowReservedKeywords
  from metricflow.aggregation_properties import AggregationState
  from metricflow.dag.id_generation import IdGeneratorRegistry
  from metricflow.dataflow.dataflow_plan import AddGeneratedUuidColumnNode, AggregateMeasuresNode, BaseOutput, CombineAggregatedOutputsNode, ComputedMetricsOutput, ComputeMetricsNode, ConstrainTimeRangeNode, DataflowPlanNodeVisitor, FilterElementsNode, JoinConversionEventsNode, JoinOverTimeRangeNode, JoinToBaseOutputNode, JoinToTimeSpineNode, MetricTimeDimensionTransformNode, MinMaxNode, OrderByLimitNode, ReadSqlSourceNode, SemiAdditiveJoinNode, WhereConstraintNode, WriteToResultDataframeNode, WriteToResultTableNode
  from metricflow.dataset.dataset import DataSet
  from metricflow.dataset.sql_dataset import SqlDataSet
  from metricflow.filters.time_constraint import TimeRangeConstraint
  from metricflow.instances import InstanceSet, MetadataInstance, MetricInstance, TimeDimensionInstance
  from metricflow.model.semantic_manifest_lookup import SemanticManifestLookup
  from metricflow.plan_conversion.instance_converters import AddLinkToLinkableElements, AddMetadata, AddMetrics, AliasAggregatedMeasures, ChangeAssociatedColumns, ChangeMeasureAggregationState, ConvertToMetadata, CreateSelectColumnForCombineOutputNode, CreateSelectColumnsForInstances, CreateSelectColumnsWithMeasuresAggregated, CreateSqlColumnReferencesForInstances, FilterElements, FilterLinkableInstancesWithLeadingLink, RemoveMeasures, RemoveMetrics, UpdateMeasureFillNullsWith, create_select_columns_for_instance_sets
  from metricflow.plan_conversion.select_column_gen import SelectColumnSet
  from metricflow.plan_conversion.spec_transforms import CreateColumnAssociations, CreateSelectCoalescedColumnsForLinkableSpecs, SelectOnlyLinkableSpecs
  from metricflow.plan_conversion.sql_join_builder import AnnotatedSqlDataSet, ColumnEqualityDescription, SqlQueryPlanJoinBuilder
  from metricflow.plan_conversion.time_spine import TimeSpineSource
  from metricflow.protocols.sql_client import SqlEngine
  from metricflow.specs.column_assoc import ColumnAssociation, ColumnAssociationResolver, SingleColumnCorrelationKey
  from metricflow.specs.specs import InstanceSpecSet, MeasureSpec, MetadataSpec, MetricSpec, TimeDimensionSpec
  from metricflow.sql.optimizer.optimization_levels import SqlQueryOptimizationLevel, SqlQueryOptimizerConfiguration
  from metricflow.sql.sql_exprs import SqlAggregateFunctionExpression, SqlBetweenExpression, SqlColumnReference, SqlColumnReferenceExpression, SqlComparison, SqlComparisonExpression, SqlDateTruncExpression, SqlExpressionNode, SqlExtractExpression, SqlFunction, SqlFunctionExpression, SqlGenerateUuidExpression, SqlLogicalExpression, SqlLogicalOperator, SqlRatioComputationExpression, SqlStringExpression, SqlStringLiteralExpression, SqlWindowFunction, SqlWindowFunctionExpression, SqlWindowOrderByArgument
  from metricflow.sql.sql_plan import SqlJoinDescription, SqlJoinType, SqlOrderByDescription, SqlQueryPlan, SqlQueryPlanNode, SqlSelectColumn, SqlSelectStatementNode, SqlTableFromClauseNode
  from metricflow.time.time_constants import ISO8601_PYTHON_FORMAT
label_desc: Use image node_id 7 for calling the DataflowToSqlQueryPlanConverter obj's underlying member method code with example usage: obj.visit_join_to_base_output_node(node) and returns: SqlDataSet