feat: PostgreSQL plugin reaches parity + safety with built-in driver (#614) - #577
feat: PostgreSQL plugin reaches parity + safety with built-in driver (#614)#577aesslinger wants to merge 76 commits into
Conversation
Extend the RpcDriver to forward save_blob_to_file and fetch_blob_as_data_url to plugin processes via JSON-RPC. Plugins that implement these methods can now handle binary data export/preview. Plugins that do not implement them receive a graceful fallback via is_method_not_found (same pattern as routines, triggers).
Extend the RpcDriver to forward get_materialized_views, get_materialized_view_columns, get_materialized_view_definition, and refresh_materialized_view to plugin processes via JSON-RPC. Plugins that declare materialized_views capability can now serve these queries. Plugins without support receive graceful fallbacks via is_method_not_found (empty vec or unsupported error).
Add an optional type_mappings field to PluginManifest and ConfigManifest that maps generic inferred types (e.g. DATETIME, JSON) to driver-specific types (e.g. TIMESTAMP, JSONB). The RpcDriver now overrides map_inferred_type to consult these static mappings at lookup time. This avoids the need for an async RPC call in a synchronous trait method. Built-in drivers continue to use their direct trait overrides and declare empty mappings. Existing plugins without type_mappings are unaffected (serde default is an empty map, passthrough behavior is preserved).
- Add separate CI workflow (pg-integration.yml) with PG 16 service
- Add seed script (postgres_seed.sql + seed_postgres.sh) for test schemas
- Add integration test harness (postgres_integration/) with 22 tests:
- schema_discovery: 4 tests (get_schemas, get_databases, get_tables)
- column_metadata: 6 tests (PK, nullable, types, max_length, enum)
- indexes: 4 tests (btree, unique, composite, primary key)
- foreign_keys: 4 tests (basic, composite table, cross-schema, empty)
- query_execution: 6 tests (basic SELECT, pagination, all types,
null handling, DML affected_rows, batch session state)
- All tests use #[ignore] and require PG on port 54320
- Seed creates test_schema + other_schema + secondary database
- CI job is separate from main test job (temporary for plugin migration)
…database tests (WIP) Adds remaining test modules for Phase 0 baseline. Some modules have compilation errors due to API signature mismatches that need fixing: - routines.rs: RoutineInfo has no specific_name field - crud.rs: update_record takes (pk_map, col_name, value) not (data, pk_map) - routines.rs: drop_routine and get_routine_definition signature differences These will be fixed in the next commit.
Fix compilation errors from incorrect function signature assumptions: - routines.rs: use RoutineInfo.routine_type (String, not Option) - routines.rs: get_routine_definition takes routine_type param - routines.rs: drop_routine takes routine_type not arg signature - crud.rs: update_record takes (&pk_map, col_name, value) per-column - crud.rs: delete_record takes &HashMap (borrow, not owned) - triggers.rs: get_trigger_definition requires table_name param All 56 integration tests now compile successfully.
All test modules compile and cover the full PostgreSQL driver API: - schema_discovery: 4 tests - column_metadata: 6 tests - indexes: 4 tests - foreign_keys: 4 tests - views: 6 tests - materialized_views: 4 tests - routines: 6 tests - triggers: 4 tests - crud: 8 tests - query_execution: 6 tests - multi_database: 7 tests - ddl_generation: 7 tests - explain: 3 tests - blob: 3 tests Total: 72 integration tests covering every public method of the PostgreSQL driver. All use #[ignore] and require PG on port 54320. CI workflow (pg-integration.yml) runs them with --include-ignored.
- Fix null handling test: exclude col_uuid (has DEFAULT gen_random_uuid())
- Fix character_max_length test: accept None (driver doesn't populate it)
- Fix enum type assertion: driver returns enum('val1','val2') format
- Fix MV definition test: handle pre-existing driver bug gracefully
- Fix blob insert test: use BLOB wire format instead of hex string
- Fix trigger test: cleanup at start (idempotent against prior failed runs)
- Fix alter_view test: cleanup at start (same reason)
All 72 tests pass with --test-threads=1 against PostgreSQL 16.
Two tests were weakened in the prior commit to 'accept either behavior' — this violates TDD principles. Tests must assert the EXACT behavior: - character_max_length: Assert None explicitly (known driver limitation). The plugin must return None too. If the driver is fixed later, this test will correctly fail — prompting both test and plugin updates. - MV definition: Assert the error explicitly (known driver bug on PG 16). The plugin must produce the same error. If the bug is fixed upstream, this test will correctly fail — signaling the spec has changed. Principle: Tests ARE the specification. A passing test means the behavior is correct. We never weaken a test to accommodate — we assert what IS.
Golden files record the exact output of driver methods against the seeded test database. They serve as the parity contract for Phase 1 — the plugin must produce output matching these files byte-for-byte. Adds: - golden_utils.rs: write_golden() and assert_golden() helpers - golden.rs: 17 capture/compare tests covering schemas, tables, columns, indexes, FKs, views, MVs, routines, triggers, queries, explain, multi-db - golden/ directory: 17 committed JSON snapshots To regenerate golden files after driver changes: REGENERATE_GOLDEN=1 cargo test --test postgres_integration golden -- --include-ignored --test-threads=1 Total test count: 89 (72 integration + 17 golden)
Remove #[ignore] from the 4 existing PG integration tests: - test_postgres_integration_flow - test_postgres_batch_preserves_temp_table_and_transaction - test_postgres_affected_rows_reported_correctly - test_postgres_foreign_keys_via_pg_catalog These tests soft-skip (eprintln + return) if PG isn't available, so they won't break the main CI that doesn't have a PG service. They WILL run in our pg-integration.yml workflow and in the standard cargo test flow when a local PG is available. MySQL tests remain #[ignore] (no MySQL in CI). Total tests now running against PG: 89 (new suite) + 4 (existing) = 93
Includes: - postgres-plugin-migration.md (original phased plan) - postgres-plugin-migration-alt.md (TDD approach with multi-db from day 1) - postgres-plugin/ directory (per-phase detailed docs) - sqlite-improvements.md (SQLite driver audit) - .markdownlint.json config for planning docs
Fixes critical and high issues from deep code review: Critical: - Use deterministic UUID in seed (fixed value, not gen_random_uuid()) so golden files produce identical output across environments - EXPLAIN golden test writes for documentation only (no exact assert) — plan costs/widths are volatile across PG versions and table stats High: - CRUD tests now clean up inserted rows (no state accumulation) - Restore #[ignore] on existing integration_tests.rs PG tests (avoids 20s timeout penalty on normal cargo test; CI uses --include-ignored) - CI workflow: add apt-get update before postgresql-client install - Remove unused TABULARIS_TEST_PG env var from CI Low: - Golden files now include trailing newline (POSIX compliance) - Remove unnecessary #[allow(dead_code)] on pg_params_secondary - Rename plan docs (alt plan is now primary)
Extend the RpcDriver to forward save_blob_to_file and fetch_blob_as_data_url to plugin processes via JSON-RPC. Plugins that implement these methods can now handle binary data export/preview. Plugins that do not implement them receive a graceful fallback via is_method_not_found (same pattern as routines, triggers).
Extend the RpcDriver to forward get_materialized_views, get_materialized_view_columns, get_materialized_view_definition, and refresh_materialized_view to plugin processes via JSON-RPC. Plugins that declare materialized_views capability can now serve these queries. Plugins without support receive graceful fallbacks via is_method_not_found (empty vec or unsupported error).
Add an optional type_mappings field to PluginManifest and ConfigManifest that maps generic inferred types (e.g. DATETIME, JSON) to driver-specific types (e.g. TIMESTAMP, JSONB). The RpcDriver now overrides map_inferred_type to consult these static mappings at lookup time. This avoids the need for an async RPC call in a synchronous trait method. Built-in drivers continue to use their direct trait overrides and declare empty mappings. Existing plugins without type_mappings are unaffected (serde default is an empty map, passthrough behavior is preserved).
…ration # Conflicts: # src-tauri/src/plugins/driver.rs
The test_pool_isolation_between_databases and test_alter_view tests failed intermittently in CI with 'connection closed' errors caused by pool exhaustion under parallel test execution (89 tests sharing a 10-connection pool). Add a retry_transient helper that retries up to 3 times with backoff when the error matches known transient pool/connection messages. Apply it to the multi-step queries in both affected tests.
Implements Phase 0 deliverable 0.3 — the parity test infrastructure that runs identical trait method calls against multiple driver implementations and asserts equivalent results. The harness compares outputs via JSON serialization (serde_json::Value), which works with any Serialize type without requiring PartialEq/Clone on model structs and catches subtle serialization differences. Phase 0: only DriverTarget::Builtin is registered — tests validate the harness works correctly against the built-in driver. Phase 1: with_plugin() adds a second target — tests then mechanically prove the plugin produces identical outputs to the built-in driver. 13 parity tests covering: schemas, databases, tables, columns, foreign keys, indexes, views, view definitions, materialized views, routines, triggers, multi-database, and map_inferred_type.
Set RUST_TEST_THREADS=4 in the pg-integration workflow. With 100+ tests sharing a 10-connection pool, unrestricted parallelism causes random 'connection closed' errors as tests race for connections. 4 threads gives reliable results: enough parallelism to keep wall-clock time short (~3s) while staying well within the pool's capacity.
With 100+ tests sharing global connection pools (keyed by params), even 4 threads caused intermittent 'connection closed' errors. The tests complete in ~8s sequentially — negligible CI impact — and the flakiness is eliminated entirely. Also add retry logic inside the parity harness for defense in depth.
ForeignKey struct uses 'column_name' not 'column'.
Add 10 new golden capture tests covering: - get_view_columns_active_users - get_mv_definition (materialized view definition) - get_mv_columns (materialized view columns) - get_routine_parameters_add_numbers - get_routine_definition_add_numbers - get_trigger_definition_audit - execute_query_with_pagination - explain_analyze - count_query CI now runs with REGENERATE_GOLDEN=1 and uploads the golden/ directory as an artifact. Once downloaded and committed, all golden assertions will be active.
The built-in driver's get_materialized_view_definition fails with 'error serializing parameter 0' on PG 16 (regclass cast bug). The golden test now captures whatever the driver returns (success or error) as the parity expectation.
9 new golden snapshots capturing the built-in PostgreSQL driver output: - get_view_columns_active_users.json - get_mv_columns.json - get_mv_definition.json (captures known regclass error) - get_routine_parameters_add_numbers.json - get_routine_definition_add_numbers.json - get_trigger_definition_audit.json - execute_query_with_pagination.json - explain_analyze.json - count_query.json Total golden files: 26 (17 existing + 9 new). These serve as the parity contract for Phase 1.
Phase 0 is done: - 102 integration tests passing in CI (target was 70+) - 26 golden files committed (covers all data-retrieval methods) - Parity harness with dual-target support ready for Phase 1 - 2 consecutive green CI runs DDL golden files removed from scope: DDL generation is validated structurally in ddl_generation.rs, not by byte-exact golden match. Exact-match goldens for SQL output create brittle tests that break on whitespace without catching real bugs.
…hunk 2/3) Adds get_routine_parameters, get_routine_definition, get_trigger_definition, create_trigger, drop_trigger — ports the builtin's exact catalog queries (pg_get_functiondef/pg_get_triggerdef for byte-identical definitions, information_schema.parameters + a synthetic OUT parameter for function return types). drop_trigger uses the plugin's quote_identifier/qualified helpers rather than the builtin's raw (unquoted) identifier interpolation — the two never need to match SQL text since the method returns (), only behavior. Wires create_trigger/drop_trigger into rpc.rs's dispatch table (previously hardcoded to not_implemented). Parity: 81/82 (was 76/82). Remaining 2 RED tests are BLOB (chunk 3).
Adds save_blob_to_file, fetch_blob_as_data_url — a single-column SELECT filtered by primary key, identical query shape to the builtin's save_blob_column_to_file/fetch_blob_column_as_data_url. Adds infer as a new plugin dependency for MIME-type sniffing (matches the builtin's encode_blob_full). Extracts build_pk_map_predicate into binding.rs (composite-PK WHERE clause, sorted keys, per-column bind_pk_value) — previously duplicated almost verbatim in exec_update and exec_delete. Both BLOB handlers and the two CRUD handlers now share it, removing ~30 lines of duplication. Adds query_typed to client.rs (query-side counterpart to execute_typed, same prepare_typed rationale) since blob lookups run a typed SELECT rather than a mutation. 3 new unit tests for encode_blob_full in a sibling blob_tests.rs (.rules/rust.md TabularisDB#4/TabularisDB#5): size/mime/base64 encoding, empty input, and magic-byte MIME sniffing. Parity: 82/82 — full CP-4 gate met (82/82 parity, 72/72 baseline, 26/26 golden, 72 plugin unit tests).
…ruth TabularisDB/tabularis-postgresql-plugin has been extracted and is ahead of this copy (v1.0.0-beta.2 includes the TabularisDB#618 keyless-table WHERE-binding fix that never landed here), so this crate no longer serves as a valid parity baseline — the plugin repo's own CI (build/clippy/fmt/security audit/live-db integration) already covers what the in-tree build step and 82 parity tests were doing. Drops the plugin-build step and parity job from pg-integration.yml; keeps the baseline + golden tests, which remain meaningful for as long as the builtin driver exists. Cross-repo parity re-verification stays a manual check (POSTGRES_PLUGIN_BIN against a real release binary), per the plugin repo's own CLAUDE.md. Also fixes two test call sites (ddl_generation.rs, parity_ddl.rs) left broken by the upstream/main merge, which added a `params` argument to get_create_foreign_key_sql (TabularisDB#576), and regenerates 4 golden fixtures for the `is_generated` column-metadata field added upstream after these fixtures were captured. Full postgres_integration suite verified 180/180 green against a live PostgreSQL 16 instance.
…isDB#614) The SSL mode dropdown branched on `driver === "postgres"` literally, so a postgres-compatible driver registered under a different id (e.g. the standalone PostgreSQL plugin, id "postgresql") fell into the MySQL-style branch. Since the plugin's own TLS check only recognizes the Postgres-style hyphenated ssl_mode values, this silently connects in cleartext when a user selects "Required"/"Verify CA". Switches the three branches (default value, options list, labels) to check `activeDriver.capabilities.sql_dialect === "postgres"` instead, requiring an explicit declaration (no fallback default) so plugins that omit the field — which is most of them today — aren't silently reclassified.
Fixing the SSL mode dropdown only stops new saves from getting the wrong
value. A plugin-driven Postgres connection saved before that fix, using the
MySQL-style underscored value the dropdown offered at the time ("required",
"verify_ca", etc.), stays silently cleartext forever — the plugin's TLS
check only matches the hyphenated spelling ("require", "verify-ca").
Adds a one-time, idempotent migration on connection load, following the
existing migrate_ssh_connections pattern: resolves each non-builtin
connection's driver dialect once, rewrites any stale MySQL-style ssl_mode
value to its Postgres-style equivalent, and re-saves. Builtin "postgres"
connections are excluded — their dropdown was always correct.
) Three MCP tool handlers defaulted the schema to "public" by checking `conn.params.driver == "postgres"` literally, so a postgres-compatible driver registered under a different id (e.g. the standalone PostgreSQL plugin) never got the default and behaved differently than the builtin driver on the same database. Extracts the shared decision into resolve_default_schema(), keyed off `driver.manifest().capabilities.sql_dialect == SqlDialect::Postgres` instead — deliberately not the broader `schemas` capability, which also covers non-Postgres multi-schema engines that would want a different default schema name.
) shouldQuoteIdentifiers() (and everything that funnels through it — quoteIdentifier, formatSqlIdentifier, quoteTableRef) checked `driver === "postgres"` literally, so a postgres-compatible driver registered under a different id (e.g. the standalone PostgreSQL plugin, id "postgresql") never got its identifiers quoted, producing broken SQL for mixed-case or reserved-word columns. Widens all four functions to accept the same `string | PluginManifest | DriverCapabilities` union getQuoteChar() already had. When given an object, checks `sql_dialect === "postgres"` (defaulting to "postgres" when the field is omitted, matching the manifest schema's own documented default and the existing sqlSplitter precedent) — not `identifier_quote === '"'`, which sqlite also uses and would otherwise get wrongly swept in. Bare-string callers keep the literal fallback unchanged, so nothing regresses for existing driver-string call sites before they're threaded onto the new object-based path.
Swaps activeDriver for activeCapabilities ?? activeDriver at call sites that already had capabilities in scope (Editor.tsx's handleSort, ExplorerSidebar.tsx's delete-table confirmation), and fixes two related bugs found during the TabularisDB#614 investigation that weren't in the original issue text: - tableToolbar.ts's formatSortClause had its own separate `driver !== "postgres"` check, never routed through shouldQuoteIdentifiers — now delegates to it, so it inherits the dialect-based fix instead of needing a second one. - connections.ts's getDefaultPort/getDriverLabel fell back to port 0 and an all-caps label for any driver id they didn't recognize by literal string match. Rather than adding a second literal case, widens both to accept a PluginManifest and read its own default_port/name fields directly — fields that already existed on the manifest but were never wired to a caller. TableToolbar.tsx's ~12 identifier-quoting call sites are consolidated through one derived `quotingDriver` (activeCapabilities ?? activeDriver) value instead of repeating the fallback at each site.
…DB#614) SidebarColumnItem only received a bare `driver` id string, so its identifier-quoting calls never saw a postgres-compatible driver's manifest — same for TriggerEditorModal's quoteIdentifier fallback. Threads a new `capabilities` prop through ExplorerSidebar -> SidebarTableItem/SidebarViewItem -> SidebarColumnItem, and into TriggerEditorModal, preferring it over the bare driver id wherever both are available. Leaves TriggerEditorModal's separate `isMysql` check untouched — that's a different capability axis (schema qualification in CREATE TRIGGER, not identifier quoting) and out of scope for this issue. Also adds `capabilities` to sidebarTableItem.ts's React.memo comparator (areTableItemPropsEqual) — without it, a capabilities-only update would have been silently skipped by memo, serving stale quoting behavior. This is the one change in the whole issue where a missed spot would have caused a real bug rather than a type error.
…risDB#614) Widens the driver parameter on visualQuery.ts (Visual Query Builder SQL generation), autocomplete.ts (Monaco SQL completion), foreignKeys.ts / useReferencedRecord.ts / RelatedRecordsPanel.tsx (foreign-key row preview), and databaseObjectActions.ts / objectPaletteItems.ts / quickNavigator.ts (command palette and sidebar context-menu object navigation) to accept a PluginManifest/DriverCapabilities object, not just a bare driver id string. Each caller already had activeCapabilities (or an equivalent connectionData.capabilities) in scope — this closes the last capability- driven gaps in the identifier-quoting chain: a postgres-compatible driver registered under a different id now quotes identically to the builtin "postgres" driver in autocomplete suggestions, visual query generation, FK row preview, and every object-navigation action (new console, count rows, show data).
Adversarial security review of the TabularisDB#614 SSL-mode fixes surfaced a real, currently-live gap (confirmed empirically, not just theoretically): DriverCapabilities.sql_dialect was typed as a plain SqlDialect with #[serde(default)] backed by impl Default -> Postgres. That default was introduced pre-TabularisDB#614 for the frontend statement splitter, where "unspecified means postgres" was the correct, harmless behavior at the time. This branch's new SSL-mode dropdown check and stale-value migration then read that same field expecting it to distinguish "explicitly declared postgres" from "said nothing" -- but by the time a plugin manifest reaches either of those checks, Serde has already collapsed both cases to the identical Postgres value, so there was nothing left to distinguish. The Oracle plugin is a live example: it sets supports_ssl: true and declares no sql_dialect. Before this commit, it would have been silently routed into Postgres-style SSL mode values by the dropdown, and any saved connection using it would have been rewritten by the migration -- both based on a default that was never actually declared. Changes sql_dialect to Option<SqlDialect> with skip_serializing_if, so "declared" and "absent" are genuinely distinguishable end to end: a manifest that omits the field now deserializes to None and is omitted from the JSON sent to the frontend, rather than arriving as the literal string "postgres". The three builtin drivers now wrap their explicit declarations in Some(...). The SSL dropdown check and migration were already written to treat only Some(Postgres) as true; the change is that None now actually means None. The frontend needs no changes -- sql_dialect?: Dialect was already optional there, and other consumers (the statement splitter) keep their own explicit `?? "postgres"` fallback at the point of use, unaffected by this change. Verified via a temporary test (added, run, and removed -- not part of this commit) that reproduced the Oracle scenario exactly: a manifest capabilities JSON with supports_ssl: true and sql_dialect omitted now deserializes to None and is dropped from re-serialized JSON, where it previously arrived as "postgres". Added a permanent regression test (migrate_connection_ssl_mode_leaves_a_resolved_driver_with_no_declared_dialect_alone) covering the same scenario for the migration path.
…abularisDB#614) The host/port grid rendered 4 columns for driver === "postgres" and 3 for everything else, so a non-builtin driver whose manifest declares the postgres SQL dialect (e.g. the standalone PostgreSQL plugin, id "postgresql") got a 3-column grid instead — the same two fields (host, port) laid out with a different column count than the builtin driver uses for them. Originally scoped out of this issue as purely cosmetic; on reconsideration it's a real, visible layout inconsistency between the builtin driver and the plugin, in the same category as everything else this issue is fixing, so it's in scope too. Reuses the isPostgresDialect check already added for the SSL mode dropdown instead of introducing a second capability check.
Manual smoke testing of the plugin found a real bug: execute_query returned null for a PostgreSQL enum column value, even though the database held a genuine non-null value. Filed as tabularis-postgresql-plugin#7 and fixed there (35a438a) — this adds the missing parity test so the class of bug can't silently reappear. The 82 existing parity tests only covered *writing* enum values (insert_record/update_record binding) and enum *metadata* (get_columns via pg_enum). Nothing exercised reading an enum value back through execute_query, so a plugin that silently nulled out enum SELECTs passed all 82 anyway. Verified the test is a real regression guard, not just a happy-path check: built the plugin at its pre-fix commit in an isolated git worktree and confirmed assert_parity fails with the exact left="happy" (builtin) vs right=null (broken plugin) mismatch. Rebuilt against the current, fixed commit and confirmed it passes. 83/83 parity tests green against the real plugin binary.
- Remove superseded/out-of-scope planning docs (postgres-plugin-migration-original.md, postgres-improvements.md, sqlite-improvements.md) and fix the phase-docs README's dangling link to the renamed master plan doc. - Fix pg-integration.yml: CI was setting REGENERATE_GOLDEN=1 unconditionally, which writes fresh golden output *before* comparing against it — the assertion could never catch drift. Regeneration is now workflow_dispatch-only, opt-in, and uploads as an artifact for manual review instead of validating against itself.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (focus: #614 capability-driven fixes)Rust backend
Frontend (#614 identifier-quoting + SSL + layout)
CI / tests
Notes: The capability-driven refactor is consistent end-to-end: security-relevant checks (SSL dropdown + stale-value migration, MCP Reviewed by glm-5.2 · Input: 117.4K · Output: 13.1K · Cached: 2.3M |
…ration Resolves the shouldQuoteIdentifiers/visualQuery.ts conflict with TabularisDB#588 per debba's instructions on that PR: keep the sql_dialect-based capability check as the primary path, but widen the literal-string fallback to also cover "postgresql" so TabularisDB#588's original driver-id fix isn't lost when no manifest is in scope. Also widens formatAggregateArgument/formatHavingColumnRef/ formatAlias/generateHavingClause (all new in TabularisDB#588) to the shared DriverArg type so they inherit the same capability-driven quoting, verified against a postgres-dialect plugin manifest producing byte-identical HAVING-clause output to the bare "postgres" string.
|
|
@debba — this is ready for your final review. Summary: PostgreSQL now ships as a standalone plugin with full parity to the built-in driver, plus the host-side fixes (#614) needed for the plugin to be a safe, correct, first-class alternative — not just parity in isolation. Status:
Deferred/out-of-scope items (Phase 2, Phase 3 deprecation decision, registry publication) are tracked separately — see the "Not in this release" section in the PR description. Let me know if you'd like anything else looked at before merging. |
|
@aesslinger Great work overall. I verified this PR locally against PostgreSQL 16 and the actual Before approval, I think these items should be addressed:
Verification performed:
The functional work looks solid; my request-changes recommendation is about making the new safety gates reliably fail when their prerequisites or fixtures are missing, plus the repository-rule cleanup above. |
|
@aesslinger One additional, non-blocking question: do you want to keep the |
Ref #16, closes #614
Summary
PostgreSQL now ships as a standalone plugin (
TabularisDB/tabularis-postgresql-plugin) with full parity to the built-in driver, plus the host-side fixes needed for the plugin to actually be a safe, correct, first-class alternative — not just parity in isolation. This closes out the migration's Phase 0/1 work as a shippable unit.What changed here:
driver === "postgres"and silently misbehaved for the plugin's"postgresql"driver id: broken identifier quoting, an SSL-mode dropdown that could silently connect in cleartext, wrong MCP schema defaults. Fixed across 9 commits, each independently verifiable — full breakdown and justifications on the issue thread, including a security-relevant gap found and closed during adversarial review of the fix itself before this was pushed.plugins/postgres-plugin/) — stale relative to the extracted repo's beta releases; the extracted repo is now the source of truth, with its own security audit, CI hardening, and 5-platform release builds.pnpm test/cargo testregression pass — done. Manual testing against the real plugin binary in the actual desktop UI surfaced one genuine bug outside Hardcoded driver === "postgres" checks in core break the standalone PostgreSQL plugin #614's scope:execute_queryreturnednullfor PostgreSQL enum values instead of the real label. Filed and fixed in the plugin repo (tabularis-postgresql-plugin#7), plus a new parity test here so the bug class can't silently reappear — parity suite is now 83/83 against the real plugin binary (v1.0.0-beta.3).upstream/main(three times — 106 commits, then 15 more, then PR #588's merge) with zero unresolved conflicts each time; full regression suite reverified green after every sync. See the comment below for how the Fix PostgreSQL visual query follow-ups #588 overlap was resolved.pg-integration.ymlwas settingREGENERATE_GOLDEN=1unconditionally, which writes fresh output before comparing against it, so the assertion could never catch drift. Regeneration is nowworkflow_dispatch-only and opt-in.Sign-off checklist
cargo audit, release-binary smoke test, live-db integration testrelease.yml— 5-platform matrix, smoke-testedpsql— not just the UI's own success indicatorspnpm test/cargo testregressionmain)Not in this release
tabularis-postgresql-plugin#9.postgres_integrationtest suite and this PR'spg-integration.ymlworkflow once the built-in driver they protect is actually gone.plugins/registry.jsonentry + a local-file install path forinstall_plugin) — the plugin isn't installable viaSettings > Pluginsyet. Out of scope here; not yet tracked by an issue.How to Validate Locally
To re-check cross-repo parity against a real plugin build (manual, not CI):
Depends On