Skip to content

fix: support parsing extension type arrays like CITEXT#28941

Open
przpl wants to merge 2 commits intoprisma:mainfrom
przpl:fix/pg-extension-array-parsing
Open

fix: support parsing extension type arrays like CITEXT#28941
przpl wants to merge 2 commits intoprisma:mainfrom
przpl:fix/pg-extension-array-parsing

Conversation

@przpl
Copy link
Copy Markdown

@przpl przpl commented Dec 18, 2025

Fixes #28349

Problem Fixed
PostgreSQL extension types (like CITEXT arrays) couldn't be parsed correctly because they have dynamic OIDs that aren't known upfront. When querying extension type arrays, they were returned as unparsed strings instead of proper JavaScript arrays.

Solution:

  • Query PostgreSQL's pg_type catalog to discover metadata about unknown extension types
  • Cache this metadata per adapter to avoid repeated lookups
  • Parse extension arrays correctly based on the cached metadata
  • Use two parsing paths: immediate parsing via getTypeParser hook (for cached OIDs) and post-processing (for first-encounter OIDs)
  • Result: Extension arrays like CITEXT[] now work correctly and are properly returned as JavaScript arrays.

Summary by CodeRabbit

  • New Features

    • Improved PostgreSQL extension-type support: automatic discovery and parsing of extension array columns and a shared, adapter-wide metadata cache to speed and stabilize array type handling.
  • Bug Fixes

    • Enforced that list/array fields must be actual arrays with clearer error messages on invalid input, preventing silent incorrect mappings.

✏️ Tip: You can customize this high-level summary in your review settings.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Dec 18, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 18, 2025

Walkthrough

Adds OID metadata caching and extension-array parsing to the PostgreSQL adapter; threads a shared OidMetadataCache through PgQueryable/PgTransaction/PrismaPgAdapter; and enforces that list-arity fields receive actual arrays in the data mapper (throws on non-array input).

Changes

Cohort / File(s) Summary
PostgreSQL Adapter — OID metadata & extension-array parsing
packages/adapter-pg/src/pg.ts
Added OidMetadata, OidMetadataCache, parseExtensionArray, ensureOidMetadata; threaded oidMetadataCache through PgQueryable and PgTransaction; updated query flow to collect unknown extension OIDs, fetch metadata from pg_type, cache it, and post-process extension array columns into native arrays; wired adapter-wide adapterOidMetadataCache in PrismaPgAdapter.
Data mapper — list validation
packages/client-engine-runtime/src/interpreter/data-mapper.ts
Enforced that fields with arity list must be actual arrays; now throws DataMapperError with descriptive message when receiving non-array values.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant PrismaPgAdapter
    participant PgQueryable
    participant Postgres
    participant OidMetadataCache

    Client->>PrismaPgAdapter: request query
    PrismaPgAdapter->>PgQueryable: execute query (shared OidMetadataCache)
    PgQueryable->>Postgres: send SQL, receive rows (may include extension array text)
    Postgres-->>PgQueryable: rows (text arrays, extension type OIDs)
    PgQueryable->>OidMetadataCache: check for unknown extension OIDs
    alt unknown OIDs present
        PgQueryable->>Postgres: query pg_type for OIDs
        Postgres-->>PgQueryable: pg_type rows (oid, typelem)
        PgQueryable->>OidMetadataCache: update cache
    end
    PgQueryable->>PgQueryable: post-process rows (parseExtensionArray using cache)
    PgQueryable-->>PrismaPgAdapter: return processed rows
    PrismaPgAdapter-->>Client: return results
Loading

Possibly related PRs

  • fix: fix citext test #28906 — Main PR that adds OidMetadata handling, caching, and parsing for extension types in adapter-pg (directly related to citext extension array parsing fix).

Suggested labels

lgtm

Suggested reviewers

  • jacek-prisma
  • aqrln

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: support parsing extension type arrays like CITEXT' directly describes the main objective: enabling correct parsing of PostgreSQL extension array types, particularly CITEXT arrays.
Linked Issues check ✅ Passed The PR implementation directly addresses issue #28349: it queries pg_type to discover extension type metadata, caches it per adapter, and implements parsing for extension arrays to return JavaScript arrays instead of raw PostgreSQL strings.
Out of Scope Changes check ✅ Passed All changes are directly related to supporting extension array parsing: OidMetadata and caching in adapter-pg, strict type validation in data-mapper for array fields, and cache propagation through queries/transactions.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1101d6 and e05d0f6.

📒 Files selected for processing (1)
  • packages/adapter-pg/src/pg.ts (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file (e.g., import { foo } from './utils/query-utils' not import { foo } from './utils'), unless ./utils/index.ts file already exists

Files:

  • packages/adapter-pg/src/pg.ts
🧬 Code graph analysis (1)
packages/adapter-pg/src/pg.ts (2)
packages/adapter-pg/src/constants.ts (1)
  • FIRST_NORMAL_OBJECT_ID (6-6)
packages/adapter-pg/src/conversion.ts (1)
  • customParsers (384-407)
🔇 Additional comments (8)
packages/adapter-pg/src/pg.ts (8)

18-18: LGTM!

Correct import for parsing PostgreSQL array string representations.


32-48: Well-structured metadata caching for extension types.

The OID metadata cache and parsing logic are well-designed. The identity transform in parseExtensionArray (line 47) assumes all extension array elements are returned as strings from PostgreSQL, which is correct for text-format results.


54-58: LGTM!

Cache parameter correctly threaded through the constructor to enable shared metadata across queries.


69-100: Excellent fix addressing the previous review feedback.

The use of a Set (line 70) now deduplicates unknown OIDs, avoiding redundant entries in the catalog query. The two-phase parsing strategy is sound:

  • Unknown extension array OIDs on first encounter return strings and are post-processed (lines 92-100).
  • Cached OIDs on subsequent queries are parsed during execution via getTypeParser (lines 177-191).

The typeof value === 'string' guard (line 96) correctly prevents re-parsing values that were already handled by the parser hook.


177-191: LGTM!

The enhanced getTypeParser correctly detects cached extension array types and returns parseExtensionArray for text-format columns. The logic properly falls back to the default parser for non-extension types or when metadata indicates a non-array type (typelem is 0).


213-228: Correct implementation with acceptable race condition trade-off.

The batch query against pg_type is efficient and correct. The comment (lines 210-212) appropriately acknowledges that concurrent queries may trigger redundant metadata fetches, but the final cache state remains consistent. This is a reasonable trade-off that avoids added complexity of promise deduplication.


237-245: LGTM!

PgTransaction correctly accepts and propagates the shared oidMetadataCache, ensuring transactions use the adapter-wide cache populated during non-transactional queries.


273-305: LGTM!

The adapter-wide OID metadata cache is correctly initialized in the constructor (lines 280-282) and shared with all transactions (line 305). This ensures consistent type handling across the adapter's lifetime and avoids redundant catalog queries.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@przpl
Copy link
Copy Markdown
Author

przpl commented Dec 18, 2025

Let me know if this is the right direction. If so, I'll check if the adapter-neon and adapter-pgg need it too.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fb55a9 and e1101d6.

📒 Files selected for processing (2)
  • packages/adapter-pg/src/pg.ts (8 hunks)
  • packages/client-engine-runtime/src/interpreter/data-mapper.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file (e.g., import { foo } from './utils/query-utils' not import { foo } from './utils'), unless ./utils/index.ts file already exists

Files:

  • packages/client-engine-runtime/src/interpreter/data-mapper.ts
  • packages/adapter-pg/src/pg.ts
🧬 Code graph analysis (1)
packages/client-engine-runtime/src/interpreter/data-mapper.ts (1)
packages/client-engine-runtime/src/index.ts (1)
  • DataMapperError (3-3)
🔇 Additional comments (6)
packages/client-engine-runtime/src/interpreter/data-mapper.ts (1)

131-136: LGTM! Good defensive validation.

This strict array check ensures that list fields always receive actual arrays, surfacing a clear DataMapperError early rather than allowing silent failures or confusing runtime errors downstream. The error message is appropriately descriptive for debugging.

packages/adapter-pg/src/pg.ts (5)

177-191: Design clarification: Two-pass parsing approach is correct.

The getTypeParser hook handles subsequent queries where OID metadata is already cached, while the post-processing loop (lines 93-100) handles first-encounter cases where the cache wasn't populated yet. This two-pass approach is intentional and correctly addresses the timing constraint.

Minor inconsistency: Line 86 uses metadata.typelem > 0 while line 187 uses metadata?.typelem (truthy). Both work correctly since typelem is 0 for non-arrays, but consistent style would improve readability.


213-228: LGTM! Safe recursive performIO call.

The catalog query correctly uses parameterized SQL and built-in types, so it won't trigger recursive OID lookups. The race condition documentation is helpful and the behavior (idempotent cache updates) is safe.


272-282: LGTM! Proper cache initialization and sharing.

The cache is correctly instantiated once per adapter and shared across all queries and transactions. The memory growth concern is already documented with a note about potential LRU eviction if needed.


304-305: LGTM!

Correctly passes the shared cache to transactions, ensuring consistent OID metadata handling across the adapter lifecycle.


46-48: No action needed on parseExtensionArray implementation.

The function correctly handles CITEXT arrays, which use text-based elements. PostgreSQL extensions that support array syntax are limited to text-based types like CITEXT, where string[] is the appropriate return type. The implementation is correct for current use cases.

@mklinke
Copy link
Copy Markdown

mklinke commented Jan 14, 2026

Since the underlying issue is blocking us from migrating to Prisma 7, just checking in here to see if anyone's looking into this from the Prisma side. Thx in advance for any update!

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.

PostgreSQL adapter fails to parse String[] @db.Citext arrays (Error P2023)

3 participants