Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

add macro_context_generator on adapter #9251

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2474722
moving types_pb2.py to common/events
colin-rogers-dbt Oct 25, 2023
07743b7
merge
colin-rogers-dbt Oct 25, 2023
252e3e3
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Oct 26, 2023
ff9d519
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Nov 2, 2023
bf079b1
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Nov 28, 2023
3187ded
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Nov 28, 2023
4da67bf
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Dec 5, 2023
c03823f
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Dec 6, 2023
2811ccd
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Dec 7, 2023
4dec100
remove manifest from adapter.execute_macro, replace with MacroResolve…
MichelleArk Dec 6, 2023
3e5515b
rename to MacroResolverProtocol
MichelleArk Dec 7, 2023
a70edd6
Merge branch 'feature/decouple-adapters-from-core' into macro-resolve…
MichelleArk Dec 7, 2023
7b1e332
pass MacroResolverProtcol in adapter.calculate_freshness_from_metadata
MichelleArk Dec 7, 2023
3620ce6
changelog entry
MichelleArk Dec 7, 2023
538481a
fix adapter.calculate_freshness call
MichelleArk Dec 7, 2023
f61da7b
add macro_context_generator on adapter
MichelleArk Dec 7, 2023
325717f
fix adapter test setup
MichelleArk Dec 7, 2023
bb12375
changelog entry
MichelleArk Dec 7, 2023
e88138a
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Dec 7, 2023
1740df5
Update parser to support conversion metrics (#9173)
WilliamDee Dec 7, 2023
977a842
Merge branch 'feature/decouple-adapters-from-core' of https://github.…
colin-rogers-dbt Dec 7, 2023
001bd51
merge feature/decouple-adapters-from-core
colin-rogers-dbt Dec 7, 2023
2bb2922
Add typing for macro_context_generator, fix query_header_context
colin-rogers-dbt Dec 8, 2023
003606a
merge main
colin-rogers-dbt Dec 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changes/unreleased/Features-20231206-181458.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Features
body: Adds support for parsing conversion metric related properties for the semantic
layer.
time: 2023-12-06T18:14:58.688221-05:00
custom:
Author: WilliamDee
Issue: "9203"
6 changes: 6 additions & 0 deletions .changes/unreleased/Under the Hood-20231208-004854.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Under the Hood
body: add macro_context_generator on adapter
time: 2023-12-08T00:48:54.506911+09:00
custom:
Author: michelleark
Issue: "9247"
36 changes: 22 additions & 14 deletions core/dbt/adapters/base/impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@
MacroResultError,
)

from dbt.adapters.protocol import AdapterConfig
from dbt.adapters.protocol import (
AdapterConfig,
MacroContextGeneratorCallable,
)
from dbt.common.clients.agate_helper import (
empty_table,
get_column_value_uncased,
Expand All @@ -76,7 +79,11 @@
from dbt.common.utils import filter_null_values, executor, cast_to_str, AttrDict

from dbt.adapters.contracts.relation import RelationConfig
from dbt.adapters.base.connections import Connection, AdapterResponse, BaseConnectionManager
from dbt.adapters.base.connections import (
Connection,
AdapterResponse,
BaseConnectionManager,
)
from dbt.adapters.base.meta import AdapterMeta, available
from dbt.adapters.base.relation import (
ComponentName,
Expand Down Expand Up @@ -258,6 +265,7 @@
self.cache = RelationsCache(log_cache_events=config.log_cache_events)
self.connections = self.ConnectionManager(config, mp_context)
self._macro_resolver: Optional[MacroResolverProtocol] = None
self._macro_context_generator: Optional[MacroContextGeneratorCallable] = None

###
# Methods to set / access a macro resolver
Expand All @@ -269,9 +277,15 @@
return self._macro_resolver

def clear_macro_resolver(self) -> None:
if self._macro_resolver is not None:
self._macro_resolver = None

Check warning on line 281 in core/dbt/adapters/base/impl.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/base/impl.py#L280-L281

Added lines #L280 - L281 were not covered by tests

def set_macro_context_generator(
self,
macro_context_generator: MacroContextGeneratorCallable,
) -> None:
self._macro_context_generator = macro_context_generator

###
# Methods that pass through to the connection manager
###
Expand Down Expand Up @@ -1057,7 +1071,10 @@

resolver = macro_resolver or self._macro_resolver
if resolver is None:
raise DbtInternalError("macro resolver was None when calling execute_macro!")
raise DbtInternalError("Macro resolver was None when calling execute_macro!")

Check warning on line 1074 in core/dbt/adapters/base/impl.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/base/impl.py#L1074

Added line #L1074 was not covered by tests

if self._macro_context_generator is None:
raise DbtInternalError("Macro context generator was None when calling execute_macro!")

Check warning on line 1077 in core/dbt/adapters/base/impl.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/base/impl.py#L1077

Added line #L1077 was not covered by tests

macro = resolver.find_macro_by_name(macro_name, self.config.project_name, project)
if macro is None:
Expand All @@ -1071,17 +1088,8 @@
macro_name, package_name
)
)
# This causes a reference cycle, as generate_runtime_macro_context()
# ends up calling get_adapter, so the import has to be here.
from dbt.context.providers import generate_runtime_macro_context

macro_context = generate_runtime_macro_context(
# TODO CT-211
macro=macro,
config=self.config,
manifest=resolver, # type: ignore[arg-type]
package_name=project,
)

macro_context = self._macro_context_generator(macro, self.config, resolver, project)
macro_context.update(context_override)

macro_function = CallableMacroGenerator(macro, macro_context)
Expand Down
18 changes: 18 additions & 0 deletions core/dbt/adapters/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from dbt.adapters.contracts.macros import MacroResolverProtocol
from dbt.adapters.contracts.relation import Policy, HasQuoting, RelationConfig
from dbt.common.contracts.config.base import BaseConfig
from dbt.common.clients.jinja import MacroProtocol


@dataclass
Expand Down Expand Up @@ -55,6 +56,17 @@
Column_T = TypeVar("Column_T", bound=ColumnProtocol)


class MacroContextGeneratorCallable(Protocol):
def __call__(
self,
macro_protocol: MacroProtocol,
config: AdapterRequiredConfig,
macro_resolver: MacroResolverProtocol,
package_name: Optional[str],
) -> Dict[str, Any]:
...

Check warning on line 67 in core/dbt/adapters/protocol.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/protocol.py#L67

Added line #L67 was not covered by tests


# TODO CT-211
class AdapterProtocol( # type: ignore[misc]
Protocol,
Expand All @@ -78,14 +90,20 @@
...

def set_macro_resolver(self, macro_resolver: MacroResolverProtocol) -> None:
...

Check warning on line 93 in core/dbt/adapters/protocol.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/protocol.py#L93

Added line #L93 was not covered by tests

def get_macro_resolver(self) -> Optional[MacroResolverProtocol]:
...

Check warning on line 96 in core/dbt/adapters/protocol.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/protocol.py#L96

Added line #L96 was not covered by tests

def clear_macro_resolver(self) -> None:
...

Check warning on line 99 in core/dbt/adapters/protocol.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/protocol.py#L99

Added line #L99 was not covered by tests

def set_macro_context_generator(
self,
macro_context_generator: MacroContextGeneratorCallable,
) -> None:
...

Check warning on line 105 in core/dbt/adapters/protocol.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/adapters/protocol.py#L105

Added line #L105 was not covered by tests

@classmethod
def type(cls) -> str:
pass
Expand Down
5 changes: 4 additions & 1 deletion core/dbt/cli/requires.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import dbt.tracking
from dbt.common.invocation import reset_invocation_id
from dbt.version import installed as installed_version
from dbt.adapters.factory import adapter_management, register_adapter
from dbt.adapters.factory import adapter_management, register_adapter, get_adapter
from dbt.flags import set_flags, get_flag_dict
from dbt.cli.exceptions import (
ExceptionExit,
Expand All @@ -10,6 +10,7 @@
from dbt.cli.flags import Flags
from dbt.config import RuntimeConfig
from dbt.config.runtime import load_project, load_profile, UnsetProfile
from dbt.context.providers import generate_runtime_macro_context

from dbt.common.events.base_types import EventLevel
from dbt.common.events.functions import (
Expand Down Expand Up @@ -274,6 +275,8 @@ def wrapper(*args, **kwargs):

runtime_config = ctx.obj["runtime_config"]
register_adapter(runtime_config)
adapter = get_adapter(runtime_config)
adapter.set_macro_context_generator(generate_runtime_macro_context)

# a manifest has already been set on the context, so don't overwrite it
if ctx.obj.get("manifest") is None:
Expand Down
2 changes: 1 addition & 1 deletion core/dbt/contracts/graph/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


DERIVED_METRICS = [MetricType.DERIVED, MetricType.RATIO]
BASE_METRICS = [MetricType.SIMPLE, MetricType.CUMULATIVE]
BASE_METRICS = [MetricType.SIMPLE, MetricType.CUMULATIVE, MetricType.CONVERSION]


class MetricReference(object):
Expand Down
18 changes: 17 additions & 1 deletion core/dbt/contracts/graph/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
SourceFileMetadata,
)
from dbt.contracts.graph.unparsed import (
ConstantPropertyInput,
Docs,
ExposureType,
ExternalTable,
Expand Down Expand Up @@ -64,7 +65,11 @@
TimeDimensionReference,
)
from dbt_semantic_interfaces.references import MetricReference as DSIMetricReference
from dbt_semantic_interfaces.type_enums import MetricType, TimeGranularity
from dbt_semantic_interfaces.type_enums import (
ConversionCalculationType,
MetricType,
TimeGranularity,
)

from .model_config import (
NodeConfig,
Expand Down Expand Up @@ -1409,6 +1414,16 @@ def post_aggregation_reference(self) -> DSIMetricReference:
return DSIMetricReference(element_name=self.alias or self.name)


@dataclass
class ConversionTypeParams(dbtClassMixin):
base_measure: MetricInputMeasure
conversion_measure: MetricInputMeasure
entity: str
calculation: ConversionCalculationType = ConversionCalculationType.CONVERSION_RATE
window: Optional[MetricTimeWindow] = None
constant_properties: Optional[List[ConstantPropertyInput]] = None


@dataclass
class MetricTypeParams(dbtClassMixin):
measure: Optional[MetricInputMeasure] = None
Expand All @@ -1419,6 +1434,7 @@ class MetricTypeParams(dbtClassMixin):
window: Optional[MetricTimeWindow] = None
grain_to_date: Optional[TimeGranularity] = None
metrics: Optional[List[MetricInput]] = None
conversion_type_params: Optional[ConversionTypeParams] = None


@dataclass
Expand Down
21 changes: 21 additions & 0 deletions core/dbt/contracts/graph/unparsed.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
ValidationError,
)

from dbt_semantic_interfaces.type_enums import ConversionCalculationType

from dataclasses import dataclass, field
from datetime import timedelta
from pathlib import Path
Expand Down Expand Up @@ -600,6 +602,24 @@ class UnparsedMetricInput(dbtClassMixin):
offset_to_grain: Optional[str] = None # str is really a TimeGranularity Enum


@dataclass
class ConstantPropertyInput(dbtClassMixin):
base_property: str
conversion_property: str


@dataclass
class UnparsedConversionTypeParams(dbtClassMixin):
base_measure: Union[UnparsedMetricInputMeasure, str]
conversion_measure: Union[UnparsedMetricInputMeasure, str]
entity: str
calculation: str = (
ConversionCalculationType.CONVERSION_RATE.value
) # ConversionCalculationType Enum
window: Optional[str] = None
constant_properties: Optional[List[ConstantPropertyInput]] = None


@dataclass
class UnparsedMetricTypeParams(dbtClassMixin):
measure: Optional[Union[UnparsedMetricInputMeasure, str]] = None
Expand All @@ -609,6 +629,7 @@ class UnparsedMetricTypeParams(dbtClassMixin):
window: Optional[str] = None
grain_to_date: Optional[str] = None # str is really a TimeGranularity Enum
metrics: Optional[List[Union[UnparsedMetricInput, str]]] = None
conversion_type_params: Optional[UnparsedConversionTypeParams] = None


@dataclass
Expand Down
69 changes: 47 additions & 22 deletions core/dbt/parser/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from itertools import chain
import time

from dbt.context.manifest import generate_query_header_context
from dbt.contracts.graph.semantic_manifest import SemanticManifest
from dbt.common.events.base_types import EventLevel
import json
Expand Down Expand Up @@ -72,7 +73,6 @@
)
from dbt.config import Project, RuntimeConfig
from dbt.context.docs import generate_runtime_docs_context
from dbt.context.manifest import generate_query_header_context
from dbt.context.macro_resolver import MacroResolver, TestMacroNamespace
from dbt.context.configured import generate_macro_context
from dbt.context.providers import ParseProvider
Expand Down Expand Up @@ -237,7 +237,7 @@
self,
root_project: RuntimeConfig,
all_projects: Mapping[str, Project],
macro_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
macro_hook: Optional[Callable[[Manifest], Any]] = None,
file_diff: Optional[FileDiff] = None,
) -> None:
self.root_project: RuntimeConfig = root_project
Expand All @@ -251,9 +251,9 @@
# This is a MacroQueryStringSetter callable, which is called
# later after we set the MacroManifest in the adapter. It sets
# up the query headers.
self.macro_hook: Callable[[Dict[str, Any]], Any]
self.macro_hook: Callable[[Manifest], Any]
if macro_hook is None:
self.macro_hook = lambda c: None
self.macro_hook = lambda m: None
Copy link
Contributor

@peterallenwebb peterallenwebb Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this change to the unused lambda parameter name.

else:
self.macro_hook = macro_hook

Expand Down Expand Up @@ -287,7 +287,7 @@
# the config and adapter may be persistent.
if reset:
config.clear_dependencies()
adapter.clear_macro_resolver()

Check warning on line 290 in core/dbt/parser/manifest.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/parser/manifest.py#L290

Added line #L290 was not covered by tests
macro_hook = adapter.connections.set_query_header

flags = get_flags()
Expand Down Expand Up @@ -1002,6 +1002,8 @@
def save_macros_to_adapter(self, adapter):
macro_manifest = MacroManifest(self.manifest.macros)
adapter.set_macro_resolver(macro_manifest)
# This executes the callable macro_hook and sets the
# query headers
# This executes the callable macro_hook and sets the query headers
query_header_context = generate_query_header_context(adapter.config, macro_manifest)
self.macro_hook(query_header_context)
Expand Down Expand Up @@ -1033,7 +1035,7 @@
def load_macros(
cls,
root_config: RuntimeConfig,
macro_hook: Callable[[Dict[str, Any]], Any],
macro_hook: Callable[[Manifest], Any],
base_macros_only=False,
) -> Manifest:
with PARSING_STATE:
Expand Down Expand Up @@ -1530,43 +1532,66 @@
node.depends_on.add_node(target_model_id)


def _process_metric_node(
def _process_metric_depends_on(
manifest: Manifest,
current_project: str,
metric: Metric,
) -> None:
"""Sets a metric's `input_measures` and `depends_on` properties"""

# This ensures that if this metrics input_measures have already been set
# we skip the work. This could happen either due to recursion or if multiple
# metrics derive from another given metric.
# NOTE: This does not protect against infinite loops
if len(metric.type_params.input_measures) > 0:
return
"""For a given metric, set the `depends_on` property"""

if metric.type is MetricType.SIMPLE or metric.type is MetricType.CUMULATIVE:
assert (
metric.type_params.measure is not None
), f"{metric} should have a measure defined, but it does not."
metric.type_params.input_measures.append(metric.type_params.measure)
assert len(metric.type_params.input_measures) > 0
for input_measure in metric.type_params.input_measures:
target_semantic_model = manifest.resolve_semantic_model_for_measure(
target_measure_name=metric.type_params.measure.name,
target_measure_name=input_measure.name,
current_project=current_project,
node_package=metric.package_name,
)
if target_semantic_model is None:
raise dbt.exceptions.ParsingError(
f"A semantic model having a measure `{metric.type_params.measure.name}` does not exist but was referenced.",
f"A semantic model having a measure `{input_measure.name}` does not exist but was referenced.",
node=metric,
)
if target_semantic_model.config.enabled is False:
raise dbt.exceptions.ParsingError(
f"The measure `{metric.type_params.measure.name}` is referenced on disabled semantic model `{target_semantic_model.name}`.",
f"The measure `{input_measure.name}` is referenced on disabled semantic model `{target_semantic_model.name}`.",
node=metric,
)

metric.depends_on.add_node(target_semantic_model.unique_id)


def _process_metric_node(
manifest: Manifest,
current_project: str,
metric: Metric,
) -> None:
"""Sets a metric's `input_measures` and `depends_on` properties"""

# This ensures that if this metrics input_measures have already been set
# we skip the work. This could happen either due to recursion or if multiple
# metrics derive from another given metric.
# NOTE: This does not protect against infinite loops
if len(metric.type_params.input_measures) > 0:
return

if metric.type is MetricType.SIMPLE or metric.type is MetricType.CUMULATIVE:
assert (
metric.type_params.measure is not None
), f"{metric} should have a measure defined, but it does not."
metric.type_params.input_measures.append(metric.type_params.measure)
_process_metric_depends_on(
manifest=manifest, current_project=current_project, metric=metric
)
elif metric.type is MetricType.CONVERSION:
conversion_type_params = metric.type_params.conversion_type_params
assert (
conversion_type_params
), f"{metric.name} is a conversion metric and must have conversion_type_params defined."
metric.type_params.input_measures.append(conversion_type_params.base_measure)
metric.type_params.input_measures.append(conversion_type_params.conversion_measure)
_process_metric_depends_on(
manifest=manifest, current_project=current_project, metric=metric
)
elif metric.type is MetricType.DERIVED or metric.type is MetricType.RATIO:
input_metrics = metric.input_metrics
if metric.type is MetricType.RATIO:
Expand Down
Loading
Loading