Skip to content

Multi-row INSERT emits one CommandComplete per row — drivers report rowcount 1; kv and timeseries DML answer a bare OK tag #297

Description

@emanzx

Version / build tested against

origin/main @ 2886155

Deployment mode

Origin — single node (local)

Engine(s) involved

Document (schemaless), Key-Value, Timeseries

Summary

A single multi-row INSERT ... VALUES (...), (...), (...) emits one CommandComplete per row (INSERT 0 1 three times) instead of one INSERT 0 3. Drivers that read the affected-row count get 1 regardless of how many rows were inserted — verified with psycopg2, where cursor.rowcount is 1 after a 3-row insert. ORMs and applications that verify write counts (optimistic-locking checks, batch-load accounting, "exactly N inserted" assertions) see phantom under-counts. Separately, the kv and timeseries engines answer DML with a bare OK command tag instead of INSERT 0 n, which drivers cannot parse for a row count at all.

Steps to reproduce

CREATE COLLECTION multi (id INT PRIMARY KEY, v TEXT);
INSERT INTO multi (id, v) VALUES (1,'a'), (2,'b'), (3,'c');
-- psql prints:
-- INSERT 0 1
-- INSERT 0 1
-- INSERT 0 1        <- three CommandComplete messages for ONE statement; expected one INSERT 0 3

SELECT count(*) FROM multi;   -- 3 (all rows did land)

-- kv / timeseries: no row count at all
CREATE COLLECTION kvp (k TEXT PRIMARY KEY, v TEXT) WITH (engine = 'kv');
INSERT INTO kvp (k, v) VALUES ('a', '1');
-- OK                <- expected INSERT 0 1

Driver-level confirmation (psycopg2):

cur.execute("INSERT INTO multi (id, v) VALUES (10,'x'), (11,'y'), (12,'z')")
cur.rowcount        -> 1     (expected 3)
cur.statusmessage   -> 'INSERT 0 1'

Expected behavior

One statement, one CommandComplete: INSERT 0 3 for a 3-row insert, per the PostgreSQL protocol contract. All engines answer DML with the standard INSERT/UPDATE/DELETE tags so drivers can extract the count.

Actual behavior

The simple-query path emits one INSERT 0 1 per value tuple; the extended-query path reports rowcount 1 for any multi-row insert. kv and timeseries DML answers OK, which is not a PostgreSQL command tag.

What actually happened? (check all that are true)

  • Acknowledged/committed data was lost, corrupted, or silently wrong
  • The server crashed, hung, or failed to start
  • A security or isolation boundary was crossed
  • Core functionality is broken with no acceptable workaround
  • A workaround exists (rewrite the query, avoid one path, etc.)

Proposed severity

SEV-3 — Medium: feature wrong, but operational and a workaround exists

Reproducibility

Always — every attempt

Last known-good version / commit (if a regression)

Unknown.

Environment & logs

Linux x86_64, release build from a fresh data directory, trust mode. Tested via psql 16 (simple query) and psycopg2 (extended query).

Before submitting

  • I searched existing issues and this is not a duplicate.
  • I reproduced this on a released tag or a current main build (not a stale local branch).
  • This is not a security vulnerability (those go to a private advisory).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:pgwirePostgreSQL wire protocol / client compatpriority:P2Scheduled, not urgentsev:3-mediumFeature wrong, but operational and a workaround existsstatus:needs-triageAwaiting maintainer triage (severity + priority)type:bugA defect — broken, incorrect, or lost data

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions