Skip to content

[Pg]: Add PostgreSQL 18 OLD/NEW returning support - #6183

Open
fabric0de wants to merge 6 commits into
drizzle-team:rc5from
fabric0de:pg18-returning-old-new
Open

[Pg]: Add PostgreSQL 18 OLD/NEW returning support#6183
fabric0de wants to merge 6 commits into
drizzle-team:rc5from
fabric0de:pg18-returning-old-new

Conversation

@fabric0de

Copy link
Copy Markdown

Summary

This PR adds support for PostgreSQL 18's RETURNING WITH (OLD AS ..., NEW AS ...) syntax to the PostgreSQL dialect.

It introduces an opt-in API for returning the previous and resulting row values from mutation queries:

await db
  .update(users)
  .set({ name: 'Jane' })
  .returning({
    old: true,
    new: true,
  });

The same API is supported for PostgreSQL INSERT, UPDATE, and DELETE queries.

Behavior

The returned values follow PostgreSQL's OLD and NEW semantics:

  • INSERT: old is null, and new contains the inserted row.
  • UPDATE: old contains the row before the update, and new contains the updated row.
  • DELETE: old contains the deleted row, and new is null.
  • INSERT ... ON CONFLICT DO UPDATE: the values reflect the outcome of each returned row.

Either value can be requested independently:

await db
  .delete(users)
  .where(eq(users.id, 1))
  .returning({ old: true });

The existing .returning() and .returning({ id: users.id }) APIs remain unchanged.

Implementation

The PostgreSQL dialect emits aliased OLD and NEW rows using:

RETURNING WITH (
  OLD AS "__drizzle_old",
  NEW AS "__drizzle_new"
)

The result mapper reconstructs the nested old and new objects and preserves null when a row version does not exist.

OLD/NEW projections cannot be mixed with regular returning fields. Invalid mixed projections are rejected at both the type and runtime levels.

This initial implementation intentionally supports whole-row old and new projections only. Partial OLD/NEW projections and other advanced projection forms are outside the scope of this PR.

Tests

The implementation includes:

  • Type tests for INSERT, UPDATE, and DELETE
  • Type-level rejection of mixed OLD/NEW and regular projections
  • SQL generation tests
  • PostgreSQL 18 integration tests executed in a dedicated CI shard
  • INSERT ... ON CONFLICT DO UPDATE coverage
  • Prepared-query coverage
  • Driver-side result-mapping coverage
  • Rows containing only nullable values

Related to #5109

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.

1 participant