Skip to content

Commit

Permalink
feat: In SQL targets, users can now disable column type alterations w…
Browse files Browse the repository at this point in the history
…ith the `allow_column_alter` built-in setting
  • Loading branch information
edgarrmondragon committed Jun 25, 2024
1 parent 9e051aa commit 70be244
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 1 deletion.
6 changes: 5 additions & 1 deletion singer_sdk/connectors/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from singer_sdk import typing as th
from singer_sdk._singerlib import CatalogEntry, MetadataMapping, Schema
from singer_sdk.exceptions import ConfigValidationError
from singer_sdk.helpers._builtin_setting import BuiltinSetting
from singer_sdk.helpers.capabilities import TargetLoadMethods

if t.TYPE_CHECKING:
Expand All @@ -39,7 +40,10 @@ class SQLConnector: # noqa: PLR0904

allow_column_add: bool = True # Whether ADD COLUMN is supported.
allow_column_rename: bool = True # Whether RENAME COLUMN is supported.
allow_column_alter: bool = False # Whether altering column types is supported.

#: Whether altering column types is supported.
allow_column_alter = BuiltinSetting(default=True)

allow_merge_upsert: bool = False # Whether MERGE UPSERT is supported.
allow_overwrite: bool = False # Whether overwrite load method is supported.
allow_temp_tables: bool = True # Whether temp tables are supported.
Expand Down
51 changes: 51 additions & 0 deletions singer_sdk/helpers/_builtin_setting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from __future__ import annotations

import typing as t

T = t.TypeVar("T")


class ConfiguredProtocol(t.Protocol):
"""Protocol for objects with a config attribute."""

config: dict[str, t.Any]


class BuiltinSetting(t.Generic[T]):
"""A descriptor that gets a value from a named key of the config attribute."""

def __init__(self, custom_key: str | None = None, *, default: T | None = None):
"""Initialize the descriptor.
Args:
custom_key: The key to get from the config attribute instead of the
attribute name.
default: The default value if the key is not found.
"""
self.key = custom_key
self.default = default

def __set_name__(self, owner: type[ConfiguredProtocol], name: str) -> None:
"""Set the name of the attribute.
Args:
owner: The class of the object.
name: The name of the attribute.
"""
self.key = self.key or name

def __get__(
self,
instance: ConfiguredProtocol,
owner: type[ConfiguredProtocol],
) -> T | None:
"""Get the value from the instance's config attribute.
Args:
instance: The instance of the object.
owner: The class of the object.
Returns:
The value from the config attribute.
"""
return instance.config.get(self.key, self.default) # type: ignore[no-any-return]
7 changes: 7 additions & 0 deletions singer_sdk/helpers/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@
description="Maximum number of rows in each batch.",
),
).to_dict()
TARGET_ALLOW_COLUMN_ALTER_CONFIG = PropertiesList(
Property(
"allow_column_alter",
BooleanType,
description="Allow altering columns in the target database.",
),
).to_dict()


class TargetLoadMethods(str, Enum):
Expand Down
3 changes: 3 additions & 0 deletions singer_sdk/target_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from singer_sdk.helpers.capabilities import (
ADD_RECORD_METADATA_CONFIG,
BATCH_CONFIG,
TARGET_ALLOW_COLUMN_ALTER_CONFIG,
TARGET_BATCH_SIZE_ROWS_CONFIG,
TARGET_HARD_DELETE_CONFIG,
TARGET_LOAD_METHOD_CONFIG,
Expand Down Expand Up @@ -685,6 +686,8 @@ def _merge_missing(source_jsonschema: dict, target_jsonschema: dict) -> None:
if k not in target_jsonschema["properties"]:
target_jsonschema["properties"][k] = v

_merge_missing(TARGET_ALLOW_COLUMN_ALTER_CONFIG, config_jsonschema)

capabilities = cls.capabilities

if TargetCapabilities.TARGET_SCHEMA in capabilities:
Expand Down
Empty file added tests/core/helpers/__init__.py
Empty file.
33 changes: 33 additions & 0 deletions tests/core/helpers/test_builtin_setting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Test the BuiltinSetting descriptor."""

from __future__ import annotations

from singer_sdk.helpers._builtin_setting import BuiltinSetting


def test_builtin_setting_descriptor():
class ObjWithConfig:
example = BuiltinSetting(default=1)

def __init__(self):
self.config = {"example": 1}

obj = ObjWithConfig()
assert obj.example == 1

obj.config["example"] = 2
assert obj.example == 2


def test_builtin_setting_descriptor_custom_key():
class ObjWithConfig:
my_attr = BuiltinSetting("example", default=1)

def __init__(self):
self.config = {"example": 1}

obj = ObjWithConfig()
assert obj.my_attr == 1

obj.config["example"] = 2
assert obj.my_attr == 2
1 change: 1 addition & 0 deletions tests/core/targets/test_target_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class MyTarget(SQLTargetMock, capabilities=capabilities):
about = MyTarget._get_about_info()
default_settings = {
"add_record_metadata",
"allow_column_alter",
"load_method",
"batch_size_rows",
}
Expand Down

0 comments on commit 70be244

Please sign in to comment.