Skip to content

PostgreSQL UPDATE FROM double-counts preceding arguments when numbering placeholders #772

Description

@dmakushin

With Bob v0.50.0, an UPDATE containing bound SET values and a parameterized FROM source generates incorrect PostgreSQL placeholder numbers.
bob.Build returns no error, but the generated SQL references arguments beyond the returned argument slice.

Reproduction

No database, generated models, or explicit scan calls are required.

mkdir bob-update-from-repro
cd bob-update-from-repro
go mod init example.com/bob-update-from-repro
go get github.com/stephenafamo/bob@v0.50.0

Save as main.go:

package main

import (
	"context"
	"fmt"

	"github.com/stephenafamo/bob"
	"github.com/stephenafamo/bob/dialect/psql"
	"github.com/stephenafamo/bob/dialect/psql/um"
	"github.com/stephenafamo/bob/dialect/psql/vm"
)

func main() {
	q := psql.Update(
		um.Table("widgets"),
		um.SetCol("changed_by").ToArg("tester"),
		um.SetCol("changed_by_id").ToArg(int64(42)),
		um.From(psql.Values(
			vm.RowValue(psql.Arg(int64(17), "first")),
			vm.RowValue(psql.Arg(int64(29), "second")),
		)).As("requested", "id", "value"),
		um.Where(
			psql.Quote("widgets", "id").
				EQ(psql.Quote("requested", "id")),
		),
	)

	query, args, err := bob.Build(context.Background(), q)
	fmt.Printf("SQL:\n%s\nARGS(%d): %#v\nERROR: %v\n",
		query, len(args), args, err)
}

Run:

go run .

Actual result
SQL:

UPDATE widgets SET
"changed_by" = $1,
"changed_by_id" = $2
FROM (VALUES ($5, $6), ($7, $8)
) AS "requested"("id", "value")
WHERE ("widgets"."id" = "requested"."id")
ARGS(6): []interface {}{"tester", 42, 17, "first", 29, "second"}
ERROR: <nil>

The SQL skips $3 and $4, then references $7 and $8, although only six arguments exist.

Expected result
The FROM source should continue numbering immediately after the two SET arguments:

UPDATE widgets SET
"changed_by" = $1,
"changed_by_id" = $2
FROM (VALUES ($3, $4), ($5, $6))
    AS "requested"("id", "value")
WHERE ("widgets"."id" = "requested"."id")

The returned argument slice should remain unchanged.

Suspected cause

UpdateQuery.WriteSQL passes both start+len(args) and the accumulated args into writeFromItemList.
writeFromItemList then adds len(args) again when rendering each item. Consequently, the preceding arguments are counted twice.
A likely correction is to pass the original start into the helper, which already accounts for accumulated arguments. Other callers should be checked before changing the shared convention.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions