Skip to content

insertMany/upsertMany chunk by row count, silently overflowing Postgres's parameter limit on wide tables #16

Description

@smiggleworth

Summary

insertMany and upsertMany chunk only by row count. A 1,000-row batch on a 70-column table generates 70,000 bind parameters, overflowing PostgreSQL's 65,535-parameter protocol limit and returning the misleading bind message has 4464 parameter formats but 0 parameters error.

Triage

Confirmed, High. The documented/default 1,000-row chunk fails against a real wide PostgreSQL table; narrow-table tests do not exercise the protocol boundary.

Required implementation details

  • Compute the union of supplied row properties before batching.
  • Set the effective chunk size to min(configuredChunkSize, floor(65535 / propertyCount)).
  • Apply the same calculation to both insertMany and upsertMany.
  • Fail clearly before executing SQL if a single row shape could itself exceed the parameter limit.
  • Preserve the user-configured chunk size whenever it is already below the protocol-derived bound.

Acceptance criteria

  • Verify the issue author is smiggleworth.
  • insertMany derives an effective row chunk from both configured chunk size and row width, never producing more than 65,535 bind values.
  • upsertMany uses the identical protocol-aware bound.
  • Existing empty-batch, returning, configured-chunk, and no-implicit-transaction behavior remains intact.
  • A fast 70-column/1,000-row regression proves both operations split into 65,520- and 4,480-parameter statements.
  • A real PostgreSQL regression proves both the insert and upsert succeed for the same 70,000-parameter naive batch and affect all 1,000 rows.
  • Public method documentation states that configured row chunks are capped by PostgreSQL's bind-parameter limit.
  • Guardrail (prevent this class of bug): Keep both the deterministic generated-query bound and the real wide-table PostgreSQL test so future batch rewrites cannot regress to row-count-only chunking.
  • Full ORM release gate, real PostgreSQL 16/17/18 hosted matrix, and exact-head CI pass before merge.

Definition of done

  • All acceptance criteria above are checked with evidence and the release-prepared exact-head PR is ready for squash merge as @askrjs/orm@0.0.2.

Severity

High — the documented default can cross a hard wire-protocol limit on realistic wide tables and produces a corrupted, non-actionable error.


Found via a real-PostgreSQL scale-boundary pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions