feat(lagoon): per-query collation for OrderBy, drop the database locale check

- lagoon.OrderBy takes variadic lagoon.OrderOption values; lagoon.Collate(name)
  emits a validated, double-quoted COLLATE clause (e.g. "pl-x-icu")
- remove the exported CheckLocale and the ICU pl-PL check from Open and Use
- framework test containers and per-test databases are plain PostgreSQL
- lagoon README, root README and docs pages drop the locale requirement;
  queries-and-pagination gains a "Sorting with a collation" section
  backed by ExampleCollate
This commit is contained in:
Jakub Zych
2026-10-01 09:51:03 +02:00
parent 565ce982d9
commit 037dc53030
22 changed files with 344 additions and 168 deletions

View File

@@ -13,13 +13,8 @@ import (
"gorm.io/gorm"
)
const (
requiredLocaleProvider = "i"
requiredICULocale = "pl-PL"
)
// Open pings dsn through pgx stdlib, requires Postgres 16 ICU pl-PL, and
// returns that exact *sql.DB plus a GORM handle opened on it.
// Open pings dsn through pgx stdlib and returns that exact *sql.DB plus a
// GORM handle opened on it.
//
// The job worker in the conga module opens its own single-connection
// pgxpool.Pool for River LISTEN/NOTIFY. Do not create that listener pool
@@ -37,10 +32,6 @@ func Open(ctx context.Context, dsn string) (*sql.DB, *gorm.DB, error) {
_ = sqlDB.Close()
return nil, nil, fmt.Errorf("lagoon: ping postgres: %w", err)
}
if err := CheckLocale(ctx, sqlDB); err != nil {
_ = sqlDB.Close()
return nil, nil, err
}
gdb, err := gormFromSQL(sqlDB)
if err != nil {
_ = sqlDB.Close()
@@ -49,9 +40,9 @@ func Open(ctx context.Context, dsn string) (*sql.DB, *gorm.DB, error) {
return sqlDB, gdb, nil
}
// Use pings an existing pool, requires ICU pl-PL, and returns a GORM handle
// opened on that exact *sql.DB. Callers that already hold a pool (tests,
// the app boot seam) must not open a second connection.
// Use pings an existing pool and returns a GORM handle opened on that exact
// *sql.DB. Callers that already hold a pool (tests, the app boot seam) must
// not open a second connection.
func Use(ctx context.Context, sqlDB *sql.DB) (*gorm.DB, error) {
if sqlDB == nil {
return nil, fmt.Errorf("lagoon: sql db is nil")
@@ -59,9 +50,6 @@ func Use(ctx context.Context, sqlDB *sql.DB) (*gorm.DB, error) {
if err := sqlDB.PingContext(ctx); err != nil {
return nil, fmt.Errorf("lagoon: ping postgres: %w", err)
}
if err := CheckLocale(ctx, sqlDB); err != nil {
return nil, err
}
return gormFromSQL(sqlDB)
}
@@ -127,28 +115,3 @@ func Publish(app *backpack.App, sqlDB *sql.DB, gdb *gorm.DB) error {
}
return runDatabaseHooks(app, sqlDB, gdb)
}
// CheckLocale fails unless the connected database uses ICU locale pl-PL.
func CheckLocale(ctx context.Context, db *sql.DB) error {
if db == nil {
return fmt.Errorf("lagoon: sql db is nil")
}
var provider, icu string
err := db.QueryRowContext(ctx, `
SELECT datlocprovider::text, COALESCE(daticulocale, '')
FROM pg_database
WHERE datname = current_database()`).Scan(&provider, &icu)
if err != nil {
return fmt.Errorf("lagoon: read database locale: %w", err)
}
return checkLocale(provider, icu)
}
func checkLocale(provider, icu string) error {
provider = strings.TrimSpace(provider)
icu = strings.TrimSpace(icu)
if provider == requiredLocaleProvider && icu == requiredICULocale {
return nil
}
return fmt.Errorf("lagoon: database locale must be ICU pl-PL (datlocprovider=%q, daticulocale=%q); got provider %q locale %q. Create the database with: CREATE DATABASE ... TEMPLATE template0 ENCODING 'UTF8' LOCALE_PROVIDER icu ICU_LOCALE 'pl-PL'", requiredLocaleProvider, requiredICULocale, provider, icu)
}