@@ -20,7 +20,9 @@ import (
2020 "encoding/json"
2121 "fmt"
2222 "log/slog"
23+ "regexp"
2324 "sort"
25+ "strconv"
2426 "strings"
2527
2628 "github.com/XSAM/otelsql"
@@ -392,26 +394,26 @@ func (c *PostgreSQLClient) UpdateDocumentStatusFields(
392394func (c * PostgreSQLClient ) UpdateDocument (
393395 ctx context.Context , filter interface {}, update interface {},
394396) (* UpdateResult , error ) {
395- // Build WHERE clause from filter
396- whereClause , args , err := c .buildWhereClause ( filter )
397+ // Build SET clause first so its placeholders own the initial parameter range.
398+ setClause , updateArgs , err := c .buildUpdateClause ( update )
397399 if err != nil {
398400 return nil , err
399401 }
400402
401- // Build SET clause from update
402- setClause , updateArgs , err := c .buildUpdateClause (update )
403+ whereClause , filterArgs , err := c .buildWhereClause (filter )
403404 if err != nil {
404405 return nil , err
405406 }
406407
407- // Combine args (WHERE args + SET args)
408- args = append (args , updateArgs ... )
408+ adjustedWhereClause := c .adjustParameterNumbers (whereClause , len (updateArgs ))
409+ args := updateArgs
410+ args = append (args , filterArgs ... )
409411
410412 // Build final query
411413 //nolint:gosec // G201: table name from config, clauses built with parameterized queries
412414 query := fmt .Sprintf (
413415 "UPDATE %s SET %s, updated_at = NOW() WHERE %s" ,
414- c .table , setClause , whereClause ,
416+ c .table , setClause , adjustedWhereClause ,
415417 )
416418
417419 result , err := c .db .ExecContext (ctx , query , args ... )
@@ -2622,17 +2624,16 @@ func (c *PostgreSQLClient) adjustParameterNumbers(clause string, offset int) str
26222624 return clause
26232625 }
26242626
2625- // Replace parameter placeholders: $1 → $N, $2 → $N+1, etc.
2626- // This is a simple implementation; for production, use a more robust parser
2627- result := clause
2627+ paramRe := regexp .MustCompile (`\$(\d+)` )
26282628
2629- for i := 20 ; i >= 1 ; i -- { // Process in reverse to avoid double replacement
2630- oldParam := fmt . Sprintf ( "$%d" , i )
2631- newParam := fmt . Sprintf ( "$%d" , i + offset )
2632- result = strings . ReplaceAll ( result , oldParam , newParam )
2633- }
2629+ return paramRe . ReplaceAllStringFunc ( clause , func ( match string ) string {
2630+ n , err := strconv . Atoi ( match [ 1 :] )
2631+ if err != nil {
2632+ return match
2633+ }
26342634
2635- return result
2635+ return fmt .Sprintf ("$%d" , n + offset )
2636+ })
26362637}
26372638
26382639// buildUpdateClause converts MongoDB-style update operators to PostgreSQL SET clause
0 commit comments