Skip to content

fix(destination-pgvector, destination-snowflake-cortex): adopt SQLAlchemy 2.x - #84401

Open
devin-ai-integration[bot] wants to merge 3 commits into
masterfrom
devin/1786690640-fix-pgvector-cortex-sqlalchemy
Open

fix(destination-pgvector, destination-snowflake-cortex): adopt SQLAlchemy 2.x#84401
devin-ai-integration[bot] wants to merge 3 commits into
masterfrom
devin/1786690640-fix-pgvector-cortex-sqlalchemy

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

What

Resolves https://github.com/airbytehq/oncall/issues/13306:

Every sync on destination-pgvector 0.1.12 fails at the write stage with:

ImportError: Using URI string without sqlalchemy installed.

raised from pandas/io/sql.py::pandasSQL_builder.

Both connectors' lockfiles resolve pandas==2.2.3, while their pyproject.toml still declared a stale sqlalchemy = "<2.0" pin, so the images shipped sqlalchemy==1.4.54. pandas 2.2.x requires SQLAlchemy >=2.0.0 in its optional-dependency check (2.1.4 required >=1.4.36), so import_optional_dependency("sqlalchemy", errors="ignore") returns None and pandas rejects the URI-string form of DataFrame.to_sql() — which is exactly the form used in common/sql/sql_processor.py::_write_files_to_new_table.

destination-snowflake-cortex 0.2.30 has the identical dependency shape and the same call site, so it is fixed here too.

Moving to SQLAlchemy 2.x also requires fixing the connectors' remaining SQLAlchemy 1.4-only call sites: 2.0 no longer accepts raw SQL strings in Connection.execute(). A /ai-prove-fix run on the first revision of this PR caught this — append-only writes passed, but dedup writes failed with sqlalchemy.exc.ObjectNotExecutableError: Not an executable object: '\nDELETE FROM ...'.

How

Two parts:

  1. Dependencies: replaced the stale pin with sqlalchemy = ">=2.0.0,<3.0" in both connectors and relocked. SQLAlchemy 2.x is the version this code targets — the vendored common/sql/sql_processor.py is a copy of PyAirbyte's shared SQL processor, and upstream PyAirbyte pins sqlalchemy==2.0.43. snowflake-sqlalchemy 1.11.0 requires only sqlalchemy>=1.4.19 and works under 2.0.52; pgvector 0.3.2 supports 2.x. The relock is contained: the only package version change in either lockfile is sqlalchemy 1.4.54 → 2.0.52.

  2. SQLAlchemy 2.0 call sites, in the connector-specific overrides that bypass _execute_sql (which already wraps strings):

    • pgvector_processor.py::_emulated_merge_temp_table_to_final_table and cortex_processor.py::_emulated_merge_temp_table_to_final_table — the DELETE/INSERT statements are now wrapped in sqlalchemy.text(), still inside one get_sql_connection() transaction.
    • cortex_processor.py::_init_connection_settings — the ALTER SESSION SET ... statement likewise. This runs on every connection, so unfixed it would break check and every Cortex sync regardless of sync mode.
    • Vendored common/sql/sql_processor.py in both connectors: select([temp_table])select(temp_table) (the 1.4 list form is rejected by 2.0; matches upstream PyAirbyte), and dropped the 1.4-only execution_options(autocommit=True) block — get_sql_connection() uses engine.begin(), so statements are committed regardless.

Review guide

  1. */pyproject.toml — the constraint change
  2. */poetry.lock — only sqlalchemy changes version
  3. pgvector_processor.py, cortex_processor.py — the text() wraps
  4. */common/sql/sql_processor.py — the two 1.4-only patterns
  5. */unit_tests/dependency_test.py — regression guards

Test Coverage

unit_tests/dependency_test.py in both connectors now covers both failure modes:

  • DataFrame.to_sql(..., "sqlite://", ...) — the pandas/SQLAlchemy pair. In a scratch venv with pandas==2.2.3 + sqlalchemy==1.4.54 it fails with the exact production ImportError; passes in both fixed environments.
  • _emulated_merge_temp_table_to_final_table executed for real against temporary SQLite — reverting the text() wraps makes both connectors' tests fail with ObjectNotExecutableError.
  • Cortex _init_connection_settings — asserts the statement handed to Connection.execute is a TextClause, not a str (ALTER SESSION has no SQLite equivalent to execute).

Local runs (unit test suites are currently disabled in both connectors' metadata.yaml, so these are the signal):

  • poetry run pytest unit_tests/ -x — destination-pgvector: 7 passed; destination-snowflake-cortex: 8 passed

Live write-path validation for pgvector against a local pgvector/pgvector:pg16 container (CDK fake embedder):

  • check returned SUCCEEDED
  • append write created the destination table and loaded the expected rows
  • dedup write landed 2 rows / 2 distinct documents, and a second identical dedup write still yielded 2 rows — merge replaces instead of duplicating

No Snowflake credentials were available, so destination-snowflake-cortex was validated by unit tests plus dependency/dialect resolution under SQLAlchemy 2.0.52, not a live write. A Cortex prerelease or regression run before release would be worthwhile.

User Impact

Syncs on both destinations start working again, in both append and dedup modes. No spec, schema, stream, state, or behavior changes, so this is a patch bump for each connector (0.1.13 and 0.2.31) and is not a breaking change.

Can this PR be safely reverted and rolled back?

  • YES 💚
  • NO ❌

Requested by Devin Bot via the /ai-fix workflow on https://github.com/airbytehq/oncall/issues/13306.

Link to Devin session: https://app.devin.ai/sessions/fba3851ccb9e45e18322f5db3cada037

…lchemy 2.x for pandas 2.2

pandas 2.2 requires SQLAlchemy >=2.0 for its optional-dependency check, so the stale sqlalchemy <2.0 pin made every write fail with 'Using URI string without sqlalchemy installed.'

Co-Authored-By: bot_apk <apk@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Co-Authored-By: bot_apk <apk@cognition.ai>
@github-actions

Copy link
Copy Markdown
Contributor

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

PR Slash Commands

Airbyte Maintainers (that's you!) can execute the following slash commands on your PR:

  • 🛠️ Quick Fixes
    • /format-fix - Fixes most formatting issues.
    • /bump-version - Bumps connector versions, scraping changelog description from the PR title.
      • Bump types: patch (default), minor, major, major_rc, rc, promote.
      • The rc type is a smart default: applies minor_rc if stable, or bumps the RC number if already RC.
      • The promote type strips the RC suffix to finalize a release.
      • Example: /bump-version type=rc or /bump-version type=minor
    • /bump-progressive-rollout-version - Alias for /bump-version type=rc. Bumps with an RC suffix and enables progressive rollout.
  • ❇️ AI Testing and Review (internal link: AI-SDLC Docs):
    • /ai-prove-fix - Runs prerelease readiness checks, including testing against customer connections.
    • /ai-canary-prerelease - Rolls out prerelease to 5-10 connections for canary testing.
    • /ai-review - AI-powered PR review for connector safety and quality gates.
  • 📝 AI Documentation:
    • /ai-docs-review - AI-powered documentation review for PRs with connector changes.
    • /ai-create-docs-pr - Creates a documentation PR for connector changes, stacked on the current PR.
  • 🚀 Connector Releases:
    • /publish-connectors-prerelease - Publishes pre-release connector builds (tagged as {version}-preview.{git-sha}) for all modified connectors in the PR.
    • /enable-autopilot-rollouts - Enables autopilot progressive rollouts for the modified connector(s) in the PR, remediating "autopilot rollouts not enabled for {connector-name}" auto-merge blockers. Sets defaultRolloutMode: autopilot and enableProgressiveRollout: true, preserving any existing autopilotConfig.
      • Optional args: connector=<CONNECTOR_NAME> (defaults to the modified connectors in the PR), strategy=fast|slow|default (defaults to fast).
      • Example: /enable-autopilot-rollouts or /enable-autopilot-rollouts connector=source-faker strategy=slow
  • ☕️ JVM connectors:
    • /update-connector-cdk-version connector=<CONNECTOR_NAME> - Updates the specified connector to the latest CDK version.
      Example: /update-connector-cdk-version connector=destination-bigquery
  • 🐍 Python connectors:
    • /poe connector source-example lock - Run the Poe lock task on the source-example connector, committing the results back to the branch.
    • /poe source example lock - Alias for /poe connector source-example lock.
    • /poe source example use-cdk-branch my/branch - Pin the source-example CDK reference to the branch name specified.
    • /poe source example use-cdk-latest - Update the source-example CDK dependency to the latest available version.
  • ⚙️ Admin commands:
    • /force-merge reason="<REASON>" - Force merges the PR using admin privileges, bypassing CI checks. Requires a reason.
      Example: /force-merge reason="CI is flaky, tests pass locally"
📚 Show Repo Guidance

Helpful Resources

📝 Edit this welcome message.

@github-actions

Copy link
Copy Markdown
Contributor

Note

Autopilot progressive rollouts are not enabled for the following modified connector(s):

  • destination-pgvector
  • destination-snowflake-cortex

This is a courtesy heads-up only — it does not block merge or fail any check.
To enable automatic progressive rollouts for the connector(s) above, comment
/enable-autopilot-rollouts on this PR. This sets defaultRolloutMode: autopilot
and enableProgressiveRollout: true in each connector's metadata.yaml,
preserving any existing autopilotConfig.

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Deploy preview for airbyte-docs ready!

Project:airbyte-docs
Status: ✅  Deploy successful!
Preview URL:https://airbyte-docs-gjte50clv-airbyte-growth.vercel.app
Latest Commit:dbfb10d

Deployed with vercel-action

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

destination-snowflake-cortex Connector Test Results

11 tests   9 ✅  22s ⏱️
 2 suites  2 💤
 2 files    0 ❌

Results for commit dbfb10d.

♻️ This comment has been updated with latest results.

Copilot AI 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.

Pull request overview

This PR fixes production sync failures in destination-pgvector and destination-snowflake-cortex caused by an incompatible pandas/SQLAlchemy pairing by updating both connectors to require SQLAlchemy 2.x and adding regression tests to prevent reintroducing the issue.

Changes:

  • Update both connectors’ pyproject.toml to require sqlalchemy >=2.0.0,<3.0 and relock to SQLAlchemy 2.0.52.
  • Add unit tests that exercise pandas.DataFrame.to_sql(..., "<sqlalchemy-uri>", ...) with a URI string to guard against the original failure mode.
  • Bump connector versions in metadata.yaml and update destination docs changelogs.

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
docs/integrations/destinations/snowflake-cortex.md Adds changelog entry for the dependency compatibility fix.
docs/integrations/destinations/pgvector.md Adds changelog entry for the dependency compatibility fix.
airbyte-integrations/connectors/destination-snowflake-cortex/unit_tests/dependency_test.py Adds regression test covering pandas.to_sql() with a URI string.
airbyte-integrations/connectors/destination-snowflake-cortex/pyproject.toml Pins SQLAlchemy to 2.x-compatible range and bumps connector version.
airbyte-integrations/connectors/destination-snowflake-cortex/poetry.lock Relocks dependencies to SQLAlchemy 2.0.52.
airbyte-integrations/connectors/destination-snowflake-cortex/metadata.yaml Updates docker image tag to 0.2.31.
airbyte-integrations/connectors/destination-pgvector/unit_tests/dependency_test.py Adds regression test covering pandas.to_sql() with a URI string.
airbyte-integrations/connectors/destination-pgvector/pyproject.toml Pins SQLAlchemy to 2.x-compatible range and bumps connector version.
airbyte-integrations/connectors/destination-pgvector/poetry.lock Relocks dependencies to SQLAlchemy 2.0.52.
airbyte-integrations/connectors/destination-pgvector/metadata.yaml Updates docker image tag to 0.1.13.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +22 to +23
def test_pandas_to_sql_accepts_sqlite_uri():
pd.DataFrame({"value": [1]}).to_sql("table", "sqlite://", if_exists="append", index=False)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚫 Not fixing. Same as the pgvector thread: the test only asserts that to_sql() with a URI string doesn't raise and never reads rows back, so in-memory persistence across reconnects isn't relied on. The bare sqlite:// form keeps the guard free of filesystem state, and the failure mode being guarded (pandas rejecting the URI-string form when its SQLAlchemy optional-dependency check fails) fires before any SQL is issued.


Devin session

Comment on lines +30 to +31
def test_pandas_to_sql_accepts_sqlite_uri():
pd.DataFrame({"value": [1]}).to_sql("table", "sqlite://", if_exists="append", index=False)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚫 Not fixing. The reconnect concern doesn't apply here: this test only asserts that to_sql() with a URI string doesn't raise, it never reads the rows back, so in-memory table persistence isn't something it depends on. The bare sqlite:// form is also deliberate — it keeps the guard free of filesystem state, and pandas creates exactly one engine/connection for the call.

The failure mode being guarded is pandas' optional-dependency check rejecting the URI-string form, which fires before any SQL is issued, so the URL flavor doesn't affect what this catches.


Devin session

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

destination-pgvector Connector Test Results

10 tests   8 ✅  18s ⏱️
 2 suites  2 💤
 2 files    0 ❌

Results for commit dbfb10d.

♻️ This comment has been updated with latest results.

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

↪️ Triggering /ai-prove-fix per Hands-Free AI Triage Project triage next step.

Reason: Draft PR, CI checks passing, and no /ai-prove-fix has run yet on this branch — prove-fix is the next pipeline stage before review.

Devin session

@octavia-bot

octavia-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🔍 AI Prove Fix session starting... Running readiness checks and testing against customer connections. View playbook

Devin AI session created successfully!

@airbyte-support-bot

Airbyte Support Bot (airbyte-support-bot) commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🔴 Fix Disproven — incomplete

Requiring SQLAlchemy 2.x does clear the reported ImportError: Using URI string without sqlalchemy installed., but SQLAlchemy 2.0 then breaks a second spot in the same write path, so dedup-mode streams still fail — with a different error. Recommend not merging as-is.

Before/after comparison (isolated, synthetic data only)

Local pgvector/Postgres 16 container + PyAirbyte's source-smoke-test, published known-bad image vs. the pre-release built from this PR (0.1.13-preview.9ec5fc3):

  • 0.1.12, append-only stream — FAIL: Using URI string without sqlalchemy installed. (reproduces the reported error)
  • 0.1.12, dedup stream — FAIL: same ImportError
  • 0.1.13-preview, append-only stream — PASS: 3/3 records written
  • 0.1.13-preview, dedup stream — FAIL: sqlalchemy.exc.ObjectNotExecutableError: Not an executable object: '\nDELETE FROM ...'

Why

SQLAlchemy 2.0 rejects raw SQL strings passed to Connection.execute() — they must be wrapped in sqlalchemy.text(). Confirmed inside the two images:

0.1.12                 -> sqlalchemy 1.4.54, raw-string execute: OK
0.1.13-preview.9ec5fc3 -> sqlalchemy 2.0.52, raw-string execute: ObjectNotExecutableError

common/sql/sql_processor.py::_execute_sql already wraps strings in text(), but the connector-specific merge overrides bypass it:

  • destination_pgvector/pgvector_processor.py:166-167 in _emulated_merge_temp_table_to_final_table — confirmed failing at runtime
  • destination_snowflake_cortex/cortex_processor.py:301-302 — same override
  • destination_snowflake_cortex/cortex_processor.py:254_init_connection_settings (ALTER SESSION SET ...)

destination-snowflake-cortex is likely hit harder: _init_connection_settings runs on every connection, so 0.2.31 would probably fail check and every sync regardless of sync mode. Not executed — no Snowflake test credentials in this environment — so that part is inference from the identical call pattern, not a measured result.

Suggested next step: wrap those statements in sqlalchemy.text() (or call self._execute_sql), bump again, and re-run the same before/after comparison. Note the new dependency_test.py cases only cover pandas.to_sql with a URI string, so they pass while the merge path is still broken — worth extending to a dedup write.

Pre-flight checks
  • Viability: plausible and correctly diagnosed as far as it goes — pandas 2.2.3 rejects SQLAlchemy 1.4 for DataFrame.to_sql(..., "postgresql://..."), the write path both connectors share.
  • Safety: no suspicious code — dependency constraints, lockfiles, version bumps, changelog rows, two regression tests. Lockfile delta in both connectors is limited to sqlalchemy 1.4.54 → 2.0.52.
  • Breaking change: no spec, catalog, stream, schema, or state changes; patch bumps only.
  • Reversibility: yes — dependency-only, no persisted state or config format change.
  • Design intent: the old sqlalchemy = "<2.0" pin conflicts with the URI-string to_sql() call, so lifting it is consistent with intent; it just needs the SQLAlchemy 2.0 call-site updates to go with it.

Detailed results including the affected connections: see airbytehq/oncall issue number 13306.

Devin session

@airbyte-support-bot Airbyte Support Bot (airbyte-support-bot) added the hyd-prove Hydra: ai-prove-fix stage has run label Aug 14, 2026
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Pre-release Connector Publish Started

Publishing pre-release build for connector destination-pgvector.
PR: #84401

Pre-release versions will be tagged as {version}-preview.9ec5fc3
and are available for version pinning via the scoped_configuration API.

View workflow run
Pre-release Publish: SUCCESS

Docker image (pre-release):
airbyte/destination-pgvector:0.1.13-preview.9ec5fc3

Docker Hub: https://hub.docker.com/layers/airbyte/destination-pgvector/0.1.13-preview.9ec5fc3

Registry JSON:

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Pre-release Connector Publish Started

Publishing pre-release build for connector destination-snowflake-cortex.
PR: #84401

Pre-release versions will be tagged as {version}-preview.9ec5fc3
and are available for version pinning via the scoped_configuration API.

View workflow run
Pre-release Publish: SUCCESS

Docker image (pre-release):
airbyte/destination-snowflake-cortex:0.2.31-preview.9ec5fc3

Docker Hub: https://hub.docker.com/layers/airbyte/destination-snowflake-cortex/0.2.31-preview.9ec5fc3

Registry JSON:

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

🙋 Escalated per Hands-Free AI Triage Project — this PR is remediating a live production failure.

A post-merge health check on #84364 shows destination-pgvector 0.1.12 failing every sync with ImportError: Using URI string without sqlalchemy installed. from sql_processor._write_files_to_new_tabledataframe.to_sql(...), versus 7/7 successful syncs on 0.1.11. destination-snowflake-cortex 0.2.30 shares the same code path and CVE bump.

That makes this draft the fix for an active breakage rather than routine hardening. /ai-prove-fix has been triggered; flagging to the owning oncall so a human can expedite review and merge (or pin affected connections back) rather than waiting on the normal queue.

Devin session

Co-Authored-By: bot_apk <apk@cognition.ai>
@devin-ai-integration devin-ai-integration Bot changed the title fix(destination-pgvector, destination-snowflake-cortex): require SQLAlchemy 2.x fix(destination-pgvector, destination-snowflake-cortex): adopt SQLAlchemy 2.x Aug 14, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

☑️ Resolved in dbfb10d — thanks, the prove-fix disproof was correct: the dependency change alone left the SQLAlchemy 1.4-only call sites broken under 2.0.

What changed:

  • pgvector_processor.py / cortex_processor.py _emulated_merge_temp_table_to_final_table — DELETE/INSERT statements wrapped in sqlalchemy.text(), still inside the single get_sql_connection() transaction (kept, rather than routing through _execute_sql, which would open separate connections and lose the transactional intent).
  • cortex_processor.py::_init_connection_settingsALTER SESSION SET ... likewise wrapped; agreed this would otherwise have failed check and every Cortex sync.
  • Vendored common/sql/sql_processor.py in both connectors — select([temp_table])select(temp_table) (1.4 list form, rejected by 2.0; matches upstream PyAirbyte), and removed the 1.4-only execution_options(autocommit=True) block since get_sql_connection() uses engine.begin().

Test coverage now includes the dedup path, per your note that the to_sql guards alone couldn't catch this: both connectors' _emulated_merge_temp_table_to_final_table is executed for real against temporary SQLite (reverting the text() wraps reproduces ObjectNotExecutableError), and Cortex _init_connection_settings is asserted to hand Connection.execute a TextClause rather than a str. Local: pgvector 7 passed, cortex 8 passed.

Live re-validation against a local pgvector/pgvector:pg16 container: dedup write landed 2 rows / 2 distinct documents, and a second identical dedup write still yielded 2 rows — merge replaces rather than duplicates. Snowflake Cortex still has no live write validation (no credentials); verified only that snowflake-sqlalchemy 1.11.0 resolves and compiles under sqlalchemy 2.0.52.

Versions were intentionally not bumped again — 0.1.13 / 0.2.31 were never published, so the existing changelog rows were extended instead. Ready for another prove-fix pass whenever convenient.

Link to Devin session: https://app.devin.ai/sessions/fba3851ccb9e45e18322f5db3cada037

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

↪️ Triggering /ai-prove-fix per Hands-Free AI Triage Project triage next step.

Reason: the earlier prove-fix on this branch returned 🔴 Fix Disproven (SQLAlchemy 2.0 broke a second spot in the same write path, so dedup-mode streams still failed), and the author has since pushed dbfb10d3 wrapping the affected statements in sqlalchemy.text(), fixing the 1.4-only select([...]) form and adding dedup-path test coverage. Re-running prove-fix against the new head is the next stage — this PR remediates a live production failure on destination-pgvector 0.1.12.


Devin session

@octavia-bot

octavia-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🔍 AI Prove Fix session starting... Running readiness checks and testing against customer connections. View playbook

Devin AI session created successfully!

@airbyte-support-bot

Airbyte Support Bot (airbyte-support-bot) commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🟢 Fix Proven

The reported failure reproduces on the currently published destination-pgvector image and no longer occurs on this PR's pre-release, in both append and dedup write modes, with correct dedup output. destination-snowflake-cortex receives the identical code change and is verified at unit-test and image level, but its Snowflake write path could not be exercised live from the agent environment — see the caveat below.

Next steps: review and merge, then run /ai-canary-prerelease for broader validation (particularly for destination-snowflake-cortex) and monitor connector health post-merge.

Detailed results, including the full evidence log: airbytehq/oncall#13306 (internal). Devin session

Connector, versions and pre-releases tested
  • destination-pgvector 0.1.12 → 0.1.13 · pre-release tested: airbyte/destination-pgvector:0.1.13-preview.dbfb10d
  • destination-snowflake-cortex 0.2.30 → 0.2.31 · pre-release tested: airbyte/destination-snowflake-cortex:0.2.31-preview.dbfb10d
  • PR head: dbfb10d3f4dbfaa7a5ff4dc2a40a96f4e2425a43
  • Comparison baselines: the published 0.1.12 image (reproduces the original ImportError), and the previous revision of this PR (preview.9ec5fc3), which was disproven because dedup writes then failed with sqlalchemy.exc.ObjectNotExecutableError.
  • Both fixed images resolve SQLAlchemy 2.0.52 with pandas 2.2.3; the published images resolve SQLAlchemy 1.4.54.
Evidence plan
  • Proving criteria: on the pre-release, check succeeds and writes in append and dedup modes both succeed and land the expected records; a repeated dedup write replaces the prior chunks for the same primary keys without duplicating rows; no new SQLAlchemy exceptions in the write path.
  • Disproving criteria: the original ImportError persists, any new SQLAlchemy-2 incompatibility appears, or dedup output is wrong (duplicated or missing rows).
  • Strategy: isolated destination smoke testing (destinations are not covered by source regression tests), with live Cloud connection testing held in reserve if isolated evidence proved insufficient. It was sufficient, so no customer connection was pinned or synced for this validation.
Cases attempted
  • Internal-A-US-1 — isolated destination-pgvector against a local pgvector Postgres, Fake embedding mode, two records with an integer primary key: baseline image vs pre-release image, covering check, append write, dedup write, and a repeated dedup write with changed content. Completed, both directions.
  • Internal-B-US-1destination-snowflake-cortex with internal integration-test credentials. Incomplete: the invalid-config check passed and the valid-config check reached the Snowflake driver, then stalled with no response and was terminated. This is a network-egress limitation of the test environment, not a connector error. Covered instead by the connector's unit tests and by image-level spec / invalid-config check.
  • No live Cloud connection case was needed, so no human approval for version pinning was requested.
Pre-flight checks
  • Viability: the reported ImportError: Using URI string without sqlalchemy installed. originates in pandas 2.2.3 rejecting SQLAlchemy < 2 on the DataFrame.to_sql(uri_string) path in _write_files_to_new_table. The PR moves both connectors to sqlalchemy >=2.0.0,<3.0 and fixes the SQLAlchemy-2 call sites in the same write path.
  • Safety: no credential handling, no new network destinations, no data-flow changes; the lockfile delta is the SQLAlchemy version only.
  • Breaking change: no — no spec, schema, stream, primary-key, cursor, or state changes; patch bump on both connectors, with metadata.yaml and changelog updated.
  • Reversibility: yes — revert by pinning affected actors back to the currently published versions.
  • The raw statements now wrapped in text() contain no :-prefixed tokens, so the wrapping introduces no bind-parameter ambiguity.
Evidence log

Baseline reproduction — published airbyte/destination-pgvector:0.1.12

check succeeded; the append write, the dedup write, and the repeated dedup write all failed with the production error:

"internal_message":"Using URI string without sqlalchemy installed."
...
File "destination_pgvector/common/sql/sql_processor.py", line 625, in _write_files_to_new_table
    dataframe.to_sql(...)
File "pandas/io/sql.py", line 903, in pandasSQL_builder
    raise ImportError("Using URI string without sqlalchemy installed.")

This matches the reported failure, including the frame it is raised from.

Fix verification — airbyte/destination-pgvector:0.1.13-preview.dbfb10d, same scenario and same database

  • check: {"type":"CONNECTION_STATUS","connectionStatus":{"status":"SUCCEEDED"}}
  • append write: exit 0, 2 rows landed
  • dedup write (initial): exit 0
  • dedup write repeated with changed content for the same primary keys: exit 0
  • Post-run verification: total_rows = 2, distinct_primary_keys = 2, and both rows contain the updated content — the old chunks were replaced, not duplicated.

The dedup path is the one that failed both in production and on the previous revision of this PR, so this is the discriminating result.

Unit tests on the PR branch

  • destination-pgvector: 7 passed, including test_pandas_to_sql_accepts_sqlite_uri and test_pgvector_emulated_merge_replaces_rows
  • destination-snowflake-cortex: 8 passed, including test_pandas_to_sql_accepts_sqlite_uri, test_snowflake_cortex_emulated_merge_replaces_rows, and test_snowflake_cortex_connection_settings_use_executable_sql

The Cortex merge test runs the real _emulated_merge_temp_table_to_final_table against a temporary SQLite engine under SQLAlchemy 2, and the settings test asserts _init_connection_settings passes a TextClause rather than a raw string — the two SQLAlchemy-2 call sites Cortex needed.

Cortex pre-release image — airbyte/destination-snowflake-cortex:0.2.31-preview.dbfb10d

  • spec: emits a SPEC message, exit 0
  • check with a deliberately invalid Snowflake host: the connector initializes and returns CONNECTION_STATUS FAILED with a Snowflake driver connectivity error (250001: Could not connect to Snowflake backend) rather than an import or SQLAlchemy error — i.e. the import-time and initialization paths are healthy under SQLAlchemy 2.
  • Installed in image: SQLAlchemy 2.0.52, pandas 2.2.3

Caveat

A Snowflake-backed write was never executed, so Snowflake-dialect-specific behavior of the wrapped statements (the ALTER SESSION SET ... MULTI_STATEMENT_COUNT = 0 statement and the wrapped DELETE/INSERT) rests on code parity with pgvector plus SQLite-level coverage. A canary pre-release or a CI integration-test run for destination-snowflake-cortex would close that gap.

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Pre-release Connector Publish Started

Publishing pre-release build for connector destination-pgvector.
PR: #84401

Pre-release versions will be tagged as {version}-preview.dbfb10d
and are available for version pinning via the scoped_configuration API.

View workflow run
Pre-release Publish: SUCCESS

Docker image (pre-release):
airbyte/destination-pgvector:0.1.13-preview.dbfb10d

Docker Hub: https://hub.docker.com/layers/airbyte/destination-pgvector/0.1.13-preview.dbfb10d

Registry JSON:

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Pre-release Connector Publish Started

Publishing pre-release build for connector destination-snowflake-cortex.
PR: #84401

Pre-release versions will be tagged as {version}-preview.dbfb10d
and are available for version pinning via the scoped_configuration API.

View workflow run
Pre-release Publish: SUCCESS

Docker image (pre-release):
airbyte/destination-snowflake-cortex:0.2.31-preview.dbfb10d

Docker Hub: https://hub.docker.com/layers/airbyte/destination-snowflake-cortex/0.2.31-preview.dbfb10d

Registry JSON:

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

↪️ Triggering /ai-review per Hands-Free AI Triage Project triage next step.

Reason: the re-run /ai-prove-fix on head dbfb10d3 returned 🟢 Fix Proven — the production ImportError reproduces on published 0.1.12 and no longer occurs on 0.1.13-preview.dbfb10d in both append and dedup write modes, with dedup output verified correct (2 rows, 2 distinct PKs, replaced not duplicated). There is no AI review marker on this PR yet, so review is the next stage. The Cortex live-write caveat is carried into the review.


Devin session

@octavia-bot
octavia-bot Bot marked this pull request as ready for review August 14, 2026 14:25
@octavia-bot

octavia-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

AI PR Review starting...

Reviewing PR for connector safety and quality.
View playbook

Devin AI session created successfully!

@airbyte-support-bot

Airbyte Support Bot (airbyte-support-bot) commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🛡️ AI PR Review Report

Review Action

APPROVE — all enforced gates pass. One non-blocking WARNING on Breaking Dependencies (major SQLAlchemy upgrade).

Risk Level

3 / 5 (medium) — typical functional connector change. The PR upgrades SQLAlchemy from 1.4.54 to 2.0.52 and rewrites raw SQL execution in two production destination connectors (text() wrapping of DELETE/INSERT/ALTER SESSION, select([temp_table])select(temp_table), removal of the 1.4-only execution_options(autocommit=True)). This touches append and dedup write paths, but is confined to two connectors, carries new regression tests, and has been validated end-to-end by /ai-prove-fix.

Gates Needing Attention

Gate Status Note
Breaking Dependencies WARNING SQLAlchemy major upgrade 1.4.542.0.52 in both connectors (constraint <2.0>=2.0.0,<3.0). All identified 1.4-only call sites were updated and covered by tests, but a major DB-library transition warrants reviewer attention.

All other 11 gates: PASS.

PR Details
  • Title: fix(destination-pgvector, destination-snowflake-cortex): adopt SQLAlchemy 2.x
  • Author: devin-ai-integration[bot]
  • Branch: devin/1786690640-fix-pgvector-cortex-sqlalchemymaster
  • Head SHA: dbfb10d3f4dbfaa7a5ff4dc2a40a96f4e2425a43
  • Draft: no
  • Connectors: destination-pgvector (0.1.12 → 0.1.13), destination-snowflake-cortex (0.2.30 → 0.2.31)
  • Linked incident: airbytehq/oncall#13306
  • Changed files: 14 (connector source ×3, unit tests ×2, pyproject.toml ×2, poetry.lock ×2, metadata.yaml ×2, docs changelogs ×2)
  • PR body: raw 5190 chars; 5151 after HTML-comment stripping + whitespace normalization; 5025 visible content chars. Preview: ## What Resolves https://github.com/airbytehq/oncall/issues/13306: - https://github.com/airbytehq/on
Gate Evaluation Details

1. PR Hygiene — PASS
Substantive body (5025 visible chars) with What/How/Testing sections and a linked incident. Docs changelog rows added for both connectors (docs/integrations/destinations/pgvector.md 0.1.13, docs/integrations/destinations/snowflake-cortex.md 0.2.31); Check Changelog Updated check is green. Two unresolved review threads exist but both are Copilot (bot) comments, which are not counted; the author replied to both with a reasoned disposition.

2. Code Hygiene — PASS
Source changes in both connector implementations (sql_processor.py, pgvector_processor.py, cortex_processor.py) plus manifests/metadata, and test files were modified alongside them in both connectors.

3. Test Coverage — PASS
Title is a fix, so behavioral-change coverage applies. Both unit_tests/dependency_test.py files gained new test functions with assertions: a pandas to_sql(uri_string) compatibility test and a SQLite dedup/merge-replacement test; the Cortex test additionally asserts TextClause usage for connection settings.

4. Code Security — PASS
No changed path matches security-sensitive patterns (no auth/credential/secret/spec files). metadata.yaml diffs only change dockerImageTag. Dependency changes are library-version changes, not credential or authentication handling.

5. Per-Record Performance — PASS
No per-record work added. Changed statements execute once per batch/merge operation (text()-wrapped DELETE/INSERT inside a single transaction, one ALTER SESSION per connection); no new per-record loops, logging, or round trips.

6. Breaking Dependencies — WARNING
sqlalchemy moves from 1.4.54 to 2.0.52 in both lockfiles, with direct constraints changed from <2.0 to >=2.0.0,<3.0. Lockfile diffs show SQLAlchemy as the only third-party package version change (other version lines are the connectors' own bumps). Mitigations present: raw SQL strings wrapped in sqlalchemy.text(), select([temp_table])select(temp_table), removal of 1.4-only autocommit execution options while retaining engine.begin() transaction semantics, plus new regression tests. Flagged as a WARNING because a major DB-library transition can surface untested call paths; it does not block approval.

7. Backwards Compatibility — PASS
No spec, schema, stream, primary-key, cursor, state, or configuration changes. Version bumps are patch-level, consistent with a non-breaking fix, and no releases.breakingChanges entry is required. The change restores previously failing writes rather than removing functionality.

8. Forwards Compatibility — PASS
No state/cursor/checkpoint format changes. Transactional semantics are preserved via engine.begin() after dropping the 1.4 autocommit option, and the dedup merge path is covered by a new unit test. The change is reversible by rolling back the connector image.

9. Behavioral Changes — PASS
Diff hunks of non-lockfile files contain no changes to rate limits, retry/backoff policy, timeouts, sleeps, page sizes, or error handlers. Functional changes are limited to SQL statement construction/execution and dependency constraints.

10. Out-of-Scope Changes — PASS
All changes are under airbyte-integrations/connectors/destination-pgvector/**, airbyte-integrations/connectors/destination-snowflake-cortex/**, and the two corresponding docs/integrations/destinations/*.md files — all in scope for this fix.

11. CI Checks — PASS
All core checks for dbfb10d3 are green on the latest run: Test destination-pgvector Connector, Test destination-snowflake-cortex Connector, Lint for both connectors, Build and Verify Artifacts for both connectors, Connector CI Checks Summary, Check Changelog Updated, Format Check, Docs / MarkDownLint, Docs / Vale, Build Airbyte Docs, Enforce PR structure. Pre-release, progressive-rollout, and PR-title checks are excluded from this gate; CodeQL reports neutral (not a failure).

12. Live / E2E Tests — PASS
Deferred entirely to /ai-prove-fix, whose current conclusion on this head SHA is 🟢 Fix Proven: published pgvector 0.1.12 reproduces the ImportError, and the PR prerelease 0.1.13-preview.dbfb10d fixes both append and dedup writes (dedup output verified as 2 rows / 2 distinct documents with no duplicate growth). Caveat carried forward for reviewers: destination-snowflake-cortex was validated at unit-test and image level only, not against live Snowflake credentials.

Evidence Consulted
  • PR metadata, raw body, labels, reviews, and review-comment threads for fix(destination-pgvector, destination-snowflake-cortex): adopt SQLAlchemy 2.x #84401
  • Full PR diff (14 files), including both poetry.lock and pyproject.toml diffs
  • Check-runs snapshot for head SHA dbfb10d3f4dbfaa7a5ff4dc2a40a96f4e2425a43 (latest re-run: all core checks success)
  • /ai-prove-fix conclusion comment on this PR (🟢 Fix Proven) and the earlier disproven run
  • Linked incident airbytehq/oncall#13306
  • Docs changelog entries for both connectors

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

↪️ Triggering /ai-ready per Hands-Free AI Triage Project triage next step.

Reason: the /ai-review re-run returned APPROVE at the current head dbfb10d3 (11 gates PASS, one non-blocking WARNING for the SQLAlchemy 1.4 → 2.0 major upgrade), the PR is no longer a draft, it is MERGEABLE and not DIRTY, no core check is failing or pending, and no /ai-ready has run for this head. Note this is the remediation for the live destination-pgvector 0.1.12 production failure, so readiness is being evaluated now rather than waiting.


Devin session

@octavia-bot octavia-bot Bot added the hyd-ready Hydra: ready for human review or merge label Aug 14, 2026
@octavia-bot-hoard

octavia-bot-hoard Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Auto-merge evaluation: FAIL

Eligibility (all must pass)

Pass Check Reasoning
Airbyte Support Bot Assigned Airbyte Support Bot assignment confirms automated merge eligibility.
No Destination Changes Destination-related changes require human approval and merge.

Scope analysis was skipped because deterministic auto-merge eligibility did not pass.

Workflow run

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

💬 Readiness outcome for the record, per Hands-Free AI Triage Project: /ai-ready returned FAIL on the deterministic No Destination Changes gate — destination changes always require human approval and merge, so auto-merge was not enabled and scope analysis was skipped. Airbyte Support Bot Assigned passed, AI review is APPROVE at the current head dbfb10d3, the PR is MERGEABLE, and the only non-green checks are progressive-rollout gates. So every automated stage on this PR is complete and there is no automated path to merge by design. This is the remediation for destination-pgvector 0.1.12 failing production syncs (ImportError: Using URI string without sqlalchemy installed.), which was already escalated to the DB/DW oncall earlier in this session — no repeat escalation from here, but the merge is the last remaining step.


Devin session

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

🙋 Escalated to #human-in-the-loop per Hands-Free AI Triage Project triage next step.

Reason: CI is green, /ai-prove-fix returned 🟢 Fix Proven, and /ai-review is APPROVE for the current HEAD dbfb10d3. /ai-ready returned FAIL only on the deterministic No Destination Changes gate, which a destination change can never satisfy — so the remaining step is a human review and a manual merge. There is no further automated stage I can run here.

Devin session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

connectors/destination/pgvector connectors/destination/snowflake-cortex hyd-fix Hydra: ai-fix stage has run hyd-prove Hydra: ai-prove-fix stage has run hyd-ready Hydra: ready for human review or merge hyd-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants