Skip to content

fix(targets): honor 'schema' setting as alias for default_target_schema#3689

Open
Rasaec5 wants to merge 15 commits into
meltano:mainfrom
Rasaec5:main
Open

fix(targets): honor 'schema' setting as alias for default_target_schema#3689
Rasaec5 wants to merge 15 commits into
meltano:mainfrom
Rasaec5:main

Conversation

@Rasaec5

@Rasaec5 Rasaec5 commented Jun 30, 2026

Copy link
Copy Markdown

What

When a SQL target sets a schema setting but does not set default_target_schema,
the SDK now uses the configured schema as the destination schema instead of deriving
one from the incoming stream ID.

Why

Closes #2766.

Many SQL targets expose a schema connection setting (for example the MeltanoLabs
variant of target-snowflake). Today, if default_target_schema is unset,
SQLSink.schema_name derives the schema from the stream ID — e.g. a record on stream
TAP_SCHEMA-users is written to a schema named tap_schema. When the connecting
account has no privileges on that derived schema, schema/table/file-format creation
fails. Users reasonably expect the schema they configured to be honored.

How

SQLSink.schema_name now resolves in this order:

default_target_schema (unchanged — keeps precedence so existing setups are unaffected)
schema (new fallback)
stream-derived (-

/ --
)
None

Implementation:

Added a SQLSink.schema_config cached property that reads the schema setting and
returns it verbatim (no name conforming), mirroring the existing
default_target_schema property.
schema_name falls back to schema_config before the stream-derived branch.

This is intentionally non-breaking: default_target_schema still wins when both are set,
and behavior is unchanged when neither is set.

Files changed

singer_sdk/sql/sink.py — add schema_config; update schema_name.
tests/sql/test_sink.py — add test_schema_name_schema_setting parametrized cases.
tests/sql/test_schema_alias.py — new dedicated test module for the alias behavior.

Testing

Unit tests run without external credentials using a dummy SQL connector/sink/target.
Resolution-order matrix: schema overrides stream-derived for 1-, 2-, and 3-part
stream names; default_target_schema takes precedence when both are set; only
default_target_schema set; neither set (stream-derived fallback, including None
for 1- and 4-part stream names).
Edge cases: empty-string schema falls through; schema returned verbatim;
table_name derivation unaffected.
Regression: existing default_target_schema / stream-derived cases still pass.

python -m pytest tests/sql/test_schema_alias.py tests/sql/test_sink.py -v

tests/sql/test_sink.py -> 13 passed

tests/sql/test_schema_alias.py -> 13 passed

Open question for reviewers

Should schema also be registered as a documented builtin setting on SQLTarget
(an explicit alias of default_target_schema), or is reading it from config enough?
This PR keeps it read-from-config to avoid colliding with targets that already define
their own schema setting.

Notes / checklist

X I have read the Contributing Guide.
X pre-commit run --all, nox -s tests, and nox -t typing pass locally.
X This contribution was assisted by an AI coding agent (per Meltano's Agentic Coding guidelines).

Summary by Sourcery

Honor the schema configuration as a fallback destination schema for SQL sinks when default_target_schema is not set, while preserving existing precedence and stream-derived behavior.

New Features:

  • Support the schema config setting as an alias/fallback for default_target_schema in SQLSink schema resolution.

Enhancements:

  • Clarify and expand SQLSink docstrings for connector, connection, schema, table, and key properties accessors.

Documentation:

  • Add a contributor README documenting the chosen issue, reproduction steps, implementation plan, and testing strategy for the schema alias change.

Tests:

  • Add dedicated tests validating schema resolution precedence between default_target_schema, schema, and stream-derived names, including edge cases and regression coverage.

Rasaec5 and others added 14 commits June 7, 2026 11:57
Added a section to CW_README explaining the choice of issue meltano#2766 and personal qualifications for tackling it.
Expanded the CW_README to include steps to reproduce the issue, reproduction evidence, an implementation plan, and details on supporting changes and tests.
Update CW_README to clarify the solution plan and schema handling for SDK SQL targets.
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Added schema_config property to retrieve schema from config.
Add parameterized test for schema name settings in DummySQLSink.
@Rasaec5 Rasaec5 requested a review from edgarrmondragon as a code owner June 30, 2026 16:44
@sourcery-ai

sourcery-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Honor the schema config key as a fallback alias for default_target_schema in SQL sinks, add minimal connector/target test scaffolding to validate resolution order, and improve docstrings for several SQLSink properties. Also add a contributor-facing README describing the implementation approach.

Sequence diagram for SQLSink schema_name resolution order

sequenceDiagram
    participant SQLTarget
    participant SQLSink
    participant Config
    participant Connector

    SQLTarget->>SQLSink: full_table_name()
    SQLSink->>SQLSink: table_name
    SQLSink->>SQLSink: schema_name

    SQLSink->>Config: get(default_target_schema)
    alt default_target_schema set
        SQLSink-->>SQLSink: use default_target_schema
    else default_target_schema not set
        SQLSink->>Config: get(schema)
        alt schema set
            SQLSink-->>SQLSink: use schema_config
        else schema not set
            SQLSink-->>SQLSink: derive schema from stream_name
        end
    end

    SQLSink->>Connector: get_fully_qualified_name(table_name,schema_name,database_name)
    Connector-->>SQLTarget: FullyQualifiedName
Loading

File-Level Changes

Change Details Files
Add support for schema config as a fallback alias to default_target_schema in SQLSink schema resolution.
  • Introduce a cached schema_config property that reads the raw schema setting from sink config without name conforming.
  • Update schema_name resolution order to prefer default_target_schema, then schema_config, then stream-derived schema, otherwise return None.
  • Keep table_name derivation unchanged and ensure database/full table/full schema name accessors use the updated schema resolution.
singer_sdk/sql/sink.py
Extend and add unit tests to cover the new schema resolution behavior and alias semantics.
  • Add parametrized tests ensuring schema overrides stream-derived schema, default_target_schema takes precedence over schema, and behavior with no schema settings remains stream-derived.
  • Create a dedicated test module with minimal SQL connector/target implementations to exercise resolution-order matrix, edge cases for empty schema, verbatim schema behavior, and table name independence from schema settings.
tests/sql/test_sink.py
tests/sql/test_schema_alias.py
Add contributor-facing documentation describing why and how issue #2766 was implemented.
  • Document rationale for choosing the issue, reproduction steps, implementation plan, code changes, and testing strategy.
  • Clarify the intended precedence order among default_target_schema, schema, stream-derived schema, and None, and note backward compatibility considerations.
Contribution_README.md

Assessment against linked issues

Issue Objective Addressed Explanation
#2766 Update SQL sink schema resolution so that the schema config setting is honored as a fallback alias for default_target_schema, with default_target_schema retaining precedence over schema, and stream-derived schema used only when neither is set.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@read-the-docs-community

read-the-docs-community Bot commented Jun 30, 2026

Copy link
Copy Markdown

Documentation build overview

📚 Meltano SDK | 🛠️ Build #33378051 | 📁 Comparing dda5ed2 against latest (c507d33)

  🔍 Preview build  

3 files changed
± genindex.html
± stream_maps.html
± classes/singer_sdk.sql.SQLSink.html

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The new Contribution_README.md file appears to be contributor notes rather than project code; consider removing it from the PR or moving this content to an appropriate project documentation location if desired.
  • The new schema alias behavior is tested both in tests/sql/test_sink.py and in the dedicated tests/sql/test_schema_alias.py; consider consolidating these into a single suite to avoid duplication and keep the behavior in one place.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `Contribution_README.md` file appears to be contributor notes rather than project code; consider removing it from the PR or moving this content to an appropriate project documentation location if desired.
- The new `schema` alias behavior is tested both in `tests/sql/test_sink.py` and in the dedicated `tests/sql/test_schema_alias.py`; consider consolidating these into a single suite to avoid duplication and keep the behavior in one place.

## Individual Comments

### Comment 1
<location path="tests/sql/test_schema_alias.py" line_range="84-66" />
<code_context>
+                "target_schema",
+                id="schema-used-for-single-part-stream",
+            ),
+            # default_target_schema always wins when both are set.
+            pytest.param(
+                "TAP_SCHEMA-users",
+                {"schema": "ignored", "default_target_schema": "default_schema"},
</code_context>
<issue_to_address>
**suggestion (testing):** Add a case for an empty-string `default_target_schema` to confirm it does not override `schema`.

Because `default_target_schema` is treated as truthy/falsy, an empty string currently behaves like “not set” and falls back to `schema_config` and then stream-derived logic. Please add a parametrized test with `default_target_schema: ""` and `schema: "target_schema"` asserting that `schema` still wins, to protect against future changes that might treat empty strings as a valid override.
</issue_to_address>

### Comment 2
<location path="Contribution_README.md" line_range="57" />
<code_context>
+
+1. **Decide on `schema` precedence vs stream-derived.** As above, an explicit `schema` setting
+   should win over the implicit stream prefix — that is the user's intent in the bug report.
+1. **Builtin setting (optional).** Consider registering `schema` (and/or treating it as an alias
+   of `default_target_schema`) in `SQLTarget`'s base config schema so all SQL targets get it
+   consistently, rather than relying on each target to define it.
</code_context>
<issue_to_address>
**issue (typo):** Use the standard hyphenated form "Built-in" instead of "Builtin".

Here, "built-in" is used as an adjective, which is conventionally hyphenated. Updating "Builtin" to "Built-in" will match standard usage.

```suggestion
1. **Built-in setting (optional).** Consider registering `schema` (and/or treating it as an alias
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

"stream_name, config, expected_schema_name",
[
# `schema` is honored when default_target_schema is absent.
pytest.param(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Add a case for an empty-string default_target_schema to confirm it does not override schema.

Because default_target_schema is treated as truthy/falsy, an empty string currently behaves like “not set” and falls back to schema_config and then stream-derived logic. Please add a parametrized test with default_target_schema: "" and schema: "target_schema" asserting that schema still wins, to protect against future changes that might treat empty strings as a valid override.

Comment thread Contribution_README.md Outdated
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.22%. Comparing base (c507d33) to head (dda5ed2).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3689   +/-   ##
=======================================
  Coverage   94.21%   94.22%           
=======================================
  Files          73       73           
  Lines        6208     6213    +5     
  Branches      763      764    +1     
=======================================
+ Hits         5849     5854    +5     
  Misses        267      267           
  Partials       92       92           
Flag Coverage Δ
core 83.06% <100.00%> (+0.01%) ⬆️
end-to-end 76.01% <60.00%> (-0.02%) ⬇️
optional-components 44.85% <40.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codspeed-hq

codspeed-hq Bot commented Jun 30, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 14 untouched benchmarks


Comparing Rasaec5:main (dda5ed2) with main (a1a8fe5)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (c507d33) during the generation of this report, so a1a8fe5 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: New setting alias schema for builtin default_target_schema

1 participant