Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions page.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package pgkit
import (
"cmp"
"context"
"fmt"
"regexp"
"slices"
"strings"
Expand Down Expand Up @@ -92,7 +91,7 @@ func (s Sort) String() string {
if s.Column == "" {
return ""
}
return fmt.Sprintf("%s %s", s.Column, s.Order)
return s.Column + " " + string(s.Order)
}

var _MatcherOrderBy = regexp.MustCompile(`-?([a-zA-Z0-9]+)`)
Expand Down Expand Up @@ -266,7 +265,7 @@ func (p Paginator[T]) getOrder(page *Page) []string {
sort := page.GetOrder(p.settings.ColumnFunc, p.settings.Sort...)
list := make([]string, len(sort))
for i := range sort {
list[i] = fmt.Sprintf("%s %s", sort[i].Column, sort[i].Order)
list[i] = sort[i].Column + " " + string(sort[i].Order)
}
return list
}
Expand Down
7 changes: 4 additions & 3 deletions querier.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package pgkit

import (
"bytes"
"context"
"fmt"
"strconv"
"strings"

sq "github.com/Masterminds/squirrel"
Expand Down Expand Up @@ -260,14 +260,15 @@ func (r RawSQL) Prepare(query string) (string, int, error) {

parts := strings.Split(query, "?")

q := bytes.Buffer{}
q := strings.Builder{}
for i, p := range parts {
if p == "" {
continue
}
q.WriteString(p)
if i < n {
q.WriteString(fmt.Sprintf("$%d", i+1))
q.WriteByte('$')
q.WriteString(strconv.Itoa(i + 1))
}
}

Expand Down
47 changes: 47 additions & 0 deletions stringconcat_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package pgkit_test

import (
"testing"

"github.com/goware/pgkit/v2"
"github.com/stretchr/testify/require"
)

// TestStringConcatOutputUnchanged pins the exact strings produced by the hot
// query-building paths, so the fmt.Sprintf -> concatenation change is proven
// to be output-preserving (must pass before and after the fix).
func TestStringConcatOutputUnchanged(t *testing.T) {
// Columns are quoted by pgx.Identifier.Sanitize().
require.Equal(t, `"created_at" DESC`, pgkit.Sort{Column: "created_at", Order: pgkit.Desc}.String())
require.Equal(t, `"name" ASC`, pgkit.Sort{Column: "name", Order: pgkit.Asc}.String())
// Unset order defaults to ASC via sanitize.
require.Equal(t, `"id" ASC`, pgkit.Sort{Column: "id"}.String())

stmt := pgkit.RawQuery("SELECT * FROM t WHERE a = ? AND b = ? AND c = ?")
require.NoError(t, stmt.Err())
require.Equal(t, "SELECT * FROM t WHERE a = $1 AND b = $2 AND c = $3", stmt.GetQuery())
require.Equal(t, 3, stmt.NumArgs())
}

// BenchmarkSortString measures Sort.String(), called once per ORDER BY column
// on every paginated and keyset query build.
func BenchmarkSortString(b *testing.B) {
s := pgkit.Sort{Column: "created_at", Order: pgkit.Desc}
b.ReportAllocs()
for b.Loop() {
_ = s.String()
}
}

// BenchmarkRawQueryPrepare measures RawQuery, which runs the placeholder
// rewrite (?-> $N) in RawSQL.Prepare once per call.
func BenchmarkRawQueryPrepare(b *testing.B) {
const q = "SELECT * FROM t WHERE a = ? AND b = ? AND c = ? AND d = ? AND e = ?"
b.ReportAllocs()
for b.Loop() {
stmt := pgkit.RawQuery(q)
if stmt.Err() != nil {
b.Fatal(stmt.Err())
}
}
}
Loading