Skip to content

postgres source: update/delete mutations return affected_rows: 0 and hardcoded success even though rows are changed #116

Description

@asmgit

Environment

  • hugr v0.3.37, ghcr.io/hugr-lab/automigrate:latest
  • Data source: type: "postgres", self_defined: true, writable

Reproduce

CREATE TABLE customers (id text PRIMARY KEY, company_name text);
INSERT INTO customers VALUES ('ALFKI', 'Alfreds Futterkiste'), ('BONAP', 'Bon app');
mutation {
  scratch {
    update_customers(
      filter: {id: {eq: "ALFKI"}}
      data: {company_name: "Alfreds Futterkiste 2"}
    ) { affected_rows last_id success message }
  }
}

Actual

{"update_customers": {"affected_rows": 0, "last_id": 0, "success": true, "message": "success"}}

The row IS updated in PostgreSQL (verified directly). delete_customers behaves the same: row deleted, affected_rows: 0.

A mutation whose filter matches nothing returns exactly the same OperationResult, so callers cannot distinguish "updated 1 row" from "updated nothing".

Root cause (from reading the code)

pkg/planner/node_update.go (same in node_delete.go): the result is built from the driver's exec result —

res, err := db.Exec(ctx, node.plan.CompiledQuery, ...)
...
case "affected_rows":
    r, _ := res.RowsAffected()

For engines implementing EngineQueryScanner (postgres, mssql) the statement is wrapped in CALL postgres_execute('db', 'UPDATE ...'). A CALL returns no changed-rows count through the DuckDB driver, so RowsAffected() is always 0. success and message are hardcoded ("true" / "success"), and LastInsertId() is not supported by the driver, so the whole OperationResult carries no real information for pushdown-executed mutations.

A possible fix: have postgres_execute (or a wrapper query) select the affected-row count — e.g. execute WITH r AS (UPDATE ... RETURNING 1) SELECT COUNT(*) FROM r via postgres_query — and populate OperationResult from that result instead of the driver metadata.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions