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

Fix setting of global behavior flags #348

Merged
merged 6 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20241112-143740.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Move require_batched_execution_for_custom_microbatch_strategy flag to global
time: 2024-11-12T14:37:40.681284-06:00
custom:
Author: QMalcolm
Issue: 327
19 changes: 11 additions & 8 deletions dbt/adapters/base/impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,13 @@ class BaseAdapter(metaclass=AdapterMeta):
}

MAX_SCHEMA_METADATA_RELATIONS = 100
GLOBAL_BEHAVIOR_FLAGS = [
{
"name": "require_batched_execution_for_custom_microbatch_strategy",
"default": False,
"docs_url": "https://docs.getdbt.com/docs/build/incremental-microbatch",
}
]

# This static member variable can be overridden in concrete adapter
# implementations to indicate adapter support for optional capabilities.
Expand All @@ -274,7 +281,7 @@ def __init__(self, config, mp_context: SpawnContext) -> None:
self._macro_resolver: Optional[MacroResolverProtocol] = None
self._macro_context_generator: Optional[MacroContextGeneratorCallable] = None
# this will be updated to include global behavior flags once they exist
self.behavior = [] # type: ignore
self.behavior = self.GLOBAL_BEHAVIOR_FLAGS # type: ignore
QMalcolm marked this conversation as resolved.
Show resolved Hide resolved

###
# Methods to set / access a macro resolver
Expand Down Expand Up @@ -314,14 +321,10 @@ def behavior(self, flags: List[BehaviorFlag]) -> None:
def _behavior_flags(self) -> List[BehaviorFlag]:
"""
This method should be overwritten by adapter maintainers to provide platform-specific flags

The BaseAdapter should NOT include any global flags here as should be defined in BaseAdapter.GLOBAL_BEHAVIOR_FLAGS
QMalcolm marked this conversation as resolved.
Show resolved Hide resolved
"""
return [
{
"name": "require_batched_execution_for_custom_microbatch_strategy",
"default": False,
"docs_url": "https://docs.getdbt.com/docs/build/incremental-microbatch",
}
]
return []

###
# Methods that pass through to the connection manager
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/behavior_flag_tests/test_behavior_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ def test_register_behavior_flags(adapter):
assert not adapter.behavior.default_true_user_false_flag
assert adapter.behavior.default_true_user_true_flag
assert adapter.behavior.default_true_user_skip_flag


def test_behaviour_flags_property_empty(adapter_default_behaviour_flags):
assert adapter_default_behaviour_flags._behavior_flags == []
8 changes: 7 additions & 1 deletion tests/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
from tests.unit.fixtures import adapter, behavior_flags, config, flags
from tests.unit.fixtures import (
adapter,
adapter_default_behaviour_flags,
behavior_flags,
config,
flags,
)
8 changes: 7 additions & 1 deletion tests/unit/fixtures/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
from tests.unit.fixtures.adapter import adapter, behavior_flags, config, flags
from tests.unit.fixtures.adapter import (
adapter,
adapter_default_behaviour_flags,
behavior_flags,
config,
flags,
)
187 changes: 96 additions & 91 deletions tests/unit/fixtures/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,105 +15,110 @@
from tests.unit.fixtures.credentials import CredentialsStub


@pytest.fixture
def adapter(config, behavior_flags) -> BaseAdapter:
class BaseAdapterStub(BaseAdapter):
"""
A stub for an adapter that uses the cache as the database
"""

ConnectionManager = ConnectionManagerStub

###
# Abstract methods for database-specific values, attributes, and types
###
@classmethod
def date_function(cls) -> str:
return "date_function"

@classmethod
def is_cancelable(cls) -> bool:
return False

def list_schemas(self, database: str) -> List[str]:
return list(self.cache.schemas)

###
# Abstract methods about relations
###
def drop_relation(self, relation: BaseRelation) -> None:
self.cache_dropped(relation)

def truncate_relation(self, relation: BaseRelation) -> None:
self.cache_dropped(relation)

def rename_relation(self, from_relation: BaseRelation, to_relation: BaseRelation) -> None:
self.cache_renamed(from_relation, to_relation)

def get_columns_in_relation(self, relation: BaseRelation) -> List[Column]:
# there's no database, so these need to be added as kwargs in the existing_relations fixture
return relation.columns

def expand_column_types(self, goal: BaseRelation, current: BaseRelation) -> None:
# there's no database, so these need to be added as kwargs in the existing_relations fixture
object.__setattr__(current, "columns", goal.columns)

def list_relations_without_caching(self, schema_relation: BaseRelation) -> List[BaseRelation]:
# there's no database, so use the cache as the database
return self.cache.get_relations(schema_relation.database, schema_relation.schema)

###
# ODBC FUNCTIONS -- these should not need to change for every adapter,
# although some adapters may override them
###
def create_schema(self, relation: BaseRelation):
# there's no database, this happens implicitly by adding a relation to the cache
pass

def drop_schema(self, relation: BaseRelation):
for each_relation in self.cache.get_relations(relation.database, relation.schema):
self.cache_dropped(each_relation)

@classmethod
def quote(cls, identifier: str) -> str:
quote_char = ""
return f"{quote_char}{identifier}{quote_char}"

###
# Conversions: These must be implemented by concrete implementations, for
# converting agate types into their sql equivalents.
###
@classmethod
def convert_text_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "str"

@classmethod
def convert_number_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "float"

@classmethod
def convert_boolean_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "bool"

@classmethod
def convert_datetime_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "datetime"

@classmethod
def convert_date_type(cls, *args, **kwargs):
return "date"

@classmethod
def convert_time_type(cls, *args, **kwargs):
return "time"

class BaseAdapterStub(BaseAdapter):
"""
A stub for an adapter that uses the cache as the database
"""

ConnectionManager = ConnectionManagerStub
@pytest.fixture
def adapter(config, behavior_flags) -> BaseAdapter:

class BaseAdapterBehaviourFlagStub(BaseAdapterStub):
@property
def _behavior_flags(self) -> List[BehaviorFlag]:
return behavior_flags

###
# Abstract methods for database-specific values, attributes, and types
###
@classmethod
def date_function(cls) -> str:
return "date_function"

@classmethod
def is_cancelable(cls) -> bool:
return False

def list_schemas(self, database: str) -> List[str]:
return list(self.cache.schemas)

###
# Abstract methods about relations
###
def drop_relation(self, relation: BaseRelation) -> None:
self.cache_dropped(relation)

def truncate_relation(self, relation: BaseRelation) -> None:
self.cache_dropped(relation)

def rename_relation(self, from_relation: BaseRelation, to_relation: BaseRelation) -> None:
self.cache_renamed(from_relation, to_relation)

def get_columns_in_relation(self, relation: BaseRelation) -> List[Column]:
# there's no database, so these need to be added as kwargs in the existing_relations fixture
return relation.columns

def expand_column_types(self, goal: BaseRelation, current: BaseRelation) -> None:
# there's no database, so these need to be added as kwargs in the existing_relations fixture
object.__setattr__(current, "columns", goal.columns)

def list_relations_without_caching(
self, schema_relation: BaseRelation
) -> List[BaseRelation]:
# there's no database, so use the cache as the database
return self.cache.get_relations(schema_relation.database, schema_relation.schema)

###
# ODBC FUNCTIONS -- these should not need to change for every adapter,
# although some adapters may override them
###
def create_schema(self, relation: BaseRelation):
# there's no database, this happens implicitly by adding a relation to the cache
pass

def drop_schema(self, relation: BaseRelation):
for each_relation in self.cache.get_relations(relation.database, relation.schema):
self.cache_dropped(each_relation)

@classmethod
def quote(cls, identifier: str) -> str:
quote_char = ""
return f"{quote_char}{identifier}{quote_char}"

###
# Conversions: These must be implemented by concrete implementations, for
# converting agate types into their sql equivalents.
###
@classmethod
def convert_text_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "str"

@classmethod
def convert_number_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "float"

@classmethod
def convert_boolean_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "bool"

@classmethod
def convert_datetime_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "datetime"

@classmethod
def convert_date_type(cls, *args, **kwargs):
return "date"

@classmethod
def convert_time_type(cls, *args, **kwargs):
return "time"
return BaseAdapterBehaviourFlagStub(config, get_context("spawn"))


@pytest.fixture
def adapter_default_behaviour_flags(config) -> BaseAdapter:
return BaseAdapterStub(config, get_context("spawn"))


Expand Down
Loading