pase/store/dialect.go
2026-05-04 18:39:25 +00:00

70 lines
1.6 KiB
Go

package store
import (
"strconv"
"strings"
)
// Dialect captures the small syntactic differences between SQL backends.
// All queries are written with ? placeholders; Rebind rewrites them as
// needed for the target driver. Semantic differences (e.g., RETURNING
// support) are not the Dialect's concern — they belong in separate Store
// implementations.
type Dialect interface {
Rebind(query string) string
}
// SQLiteDialect leaves queries unchanged; database/sql + the SQLite driver
// already accept ? placeholders.
type SQLiteDialect struct{}
func (SQLiteDialect) Rebind(query string) string { return query }
// PostgresDialect rewrites ? placeholders into $1, $2, ... while leaving
// any ? characters that appear inside single-quoted string literals or
// double-quoted identifiers untouched.
type PostgresDialect struct{}
func (PostgresDialect) Rebind(query string) string {
var b strings.Builder
b.Grow(len(query) + 8)
n := 0
i := 0
for i < len(query) {
c := query[i]
switch c {
case '\'', '"':
// Copy the entire quoted span verbatim, handling doubled-quote escapes.
quote := c
b.WriteByte(c)
i++
for i < len(query) {
if query[i] == quote {
if i+1 < len(query) && query[i+1] == quote {
// Escaped quote: '' or "". Copy both bytes and continue.
b.WriteByte(quote)
b.WriteByte(quote)
i += 2
continue
}
b.WriteByte(quote)
i++
break
}
b.WriteByte(query[i])
i++
}
case '?':
n++
b.WriteByte('$')
b.WriteString(strconv.Itoa(n))
i++
default:
b.WriteByte(c)
i++
}
}
return b.String()
}