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
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ go 1.26.3
require (
github.com/getsentry/sentry-go v0.46.1
github.com/golang-migrate/migrate/v4 v4.19.1
github.com/jackc/pgx/v5 v5.5.4
github.com/jellydator/ttlcache/v3 v3.4.0
github.com/lib/pq v1.12.3
github.com/stretchr/testify v1.11.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0
go.opentelemetry.io/otel v1.43.0
Expand Down Expand Up @@ -121,6 +121,10 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jgautheron/goconst v1.8.2 // indirect
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
github.com/jjti/go-spancheck v0.6.5 // indirect
Expand All @@ -138,6 +142,7 @@ require (
github.com/ldez/tagliatelle v0.7.2 // indirect
github.com/ldez/usetesting v0.5.0 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect
github.com/lib/pq v1.12.3 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/macabu/inamedparam v0.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
Expand Down Expand Up @@ -219,6 +224,7 @@ require (
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect
golang.org/x/mod v0.34.0 // indirect
golang.org/x/net v0.53.0 // indirect
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,16 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4=
Expand Down Expand Up @@ -632,6 +642,7 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
Expand Down Expand Up @@ -731,6 +742,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -1079,6 +1092,7 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
10 changes: 5 additions & 5 deletions internal/adapters/accountrepository/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"fmt"
"time"

"github.com/jackc/pgx/v5"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"

Expand Down Expand Up @@ -67,7 +67,7 @@ func (p *Postgres) StoreAccount(ctx context.Context, account domain.Account) err
}
defer txx.Rollback()

_, err = txx.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pq.QuoteIdentifier(p.schema)))
_, err = txx.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pgx.Identifier{p.schema}.Sanitize()))
if err != nil {
err := fmt.Errorf("failed to set search path: %w", err)
reporting.Report(ctx, err, map[string]string{
Expand Down Expand Up @@ -168,7 +168,7 @@ func (p *Postgres) RemoveUsername(ctx context.Context, username string) error {
_, err := p.db.ExecContext(ctx, fmt.Sprintf(`
DELETE FROM %s.usernames
WHERE lower(username) = lower($1)`,
pq.QuoteIdentifier(p.schema),
pgx.Identifier{p.schema}.Sanitize(),
),
username,
)
Expand Down Expand Up @@ -200,7 +200,7 @@ func (p *Postgres) GetAccountByUUID(ctx context.Context, uuid string) (domain.Ac
player_uuid, username, queried_at
FROM %s.usernames
WHERE player_uuid = $1`,
pq.QuoteIdentifier(p.schema),
pgx.Identifier{p.schema}.Sanitize(),
),
uuid,
)
Expand Down Expand Up @@ -232,7 +232,7 @@ func (p *Postgres) GetAccountByUsername(ctx context.Context, username string) (d
player_uuid, username, queried_at
FROM %s.usernames
WHERE lower(username) = lower($1)`,
pq.QuoteIdentifier(p.schema),
pgx.Identifier{p.schema}.Sanitize(),
),
username,
)
Expand Down
10 changes: 5 additions & 5 deletions internal/adapters/accountrepository/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"testing"
"time"

"github.com/jackc/pgx/v5"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
"github.com/stretchr/testify/require"

"github.com/Amund211/flashlight/internal/adapters/database"
Expand All @@ -22,9 +22,9 @@ func newPostgres(t *testing.T, db *sqlx.DB, schemaSuffix string) *Postgres {

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

db.MustExec(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pq.QuoteIdentifier(schema)))
db.MustExec(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pgx.Identifier{schema}.Sanitize()))

migrator := database.NewDatabaseMigrator(db, logger)
migrator := database.NewDatabaseMigrator(database.LocalConnectionString, logger)

err := migrator.Migrate(t.Context(), schema)
require.NoError(t, err)
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestPostgres(t *testing.T) {
require.NoError(t, err)
defer txx.Rollback()

_, err = txx.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pq.QuoteIdentifier(p.schema)))
_, err = txx.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pgx.Identifier{p.schema}.Sanitize()))
require.NoError(t, err)

var entries []dbUsernamesEntry
Expand Down Expand Up @@ -102,7 +102,7 @@ func TestPostgres(t *testing.T) {
require.NoError(t, err)
defer txx.Rollback()

_, err = txx.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pq.QuoteIdentifier(p.schema)))
_, err = txx.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pgx.Identifier{p.schema}.Sanitize()))
require.NoError(t, err)

var entries []dbUsernameQueriesEntry
Expand Down
2 changes: 1 addition & 1 deletion internal/adapters/database/cloudsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "fmt"
// https://cloud.google.com/sql/docs/mysql/connect-functions
func GetCloudSQLConnectionString(dbUsername, dbPassword, unixSocketPath string) string {
return fmt.Sprintf(
"user=%s password=%s database=%s host=%s",
"user=%s password=%s dbname=%s host=%s",
dbUsername,
dbPassword,
DBName,
Expand Down
32 changes: 13 additions & 19 deletions internal/adapters/database/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,24 @@ import (
"log/slog"

"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"
pgxmigrate "github.com/golang-migrate/migrate/v4/database/pgx/v5"
"github.com/golang-migrate/migrate/v4/source/iofs"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
"github.com/jackc/pgx/v5"
)

//go:embed migrations/*.sql
var embeddedMigrations embed.FS

type migrator struct {
db *sqlx.DB
connectionString string

logger *slog.Logger
}

func NewDatabaseMigrator(db *sqlx.DB, logger *slog.Logger) *migrator {
func NewDatabaseMigrator(connectionString string, logger *slog.Logger) *migrator {
return &migrator{
db: db,
logger: logger,
connectionString: connectionString,
logger: logger,
}
}

Expand All @@ -35,37 +34,32 @@ func (m *migrator) Migrate(ctx context.Context, schemaName string) error {
}

func (m *migrator) migrate(ctx context.Context, schemaName string) error {
conn, err := m.db.Conn(ctx)
db, err := openSchemaScopedDB(m.connectionString, schemaName)
if err != nil {
return fmt.Errorf("migrate: failed to connect to db: %w", err)
return fmt.Errorf("migrate: %w", err)
}
defer conn.Close()
defer db.Close()

_, err = conn.ExecContext(ctx, fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s", pq.QuoteIdentifier(schemaName)))
_, err = db.ExecContext(ctx, fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s", pgx.Identifier{schemaName}.Sanitize()))
if err != nil {
return fmt.Errorf("migrate: failed to create schema: %w", err)
}

_, err = conn.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pq.QuoteIdentifier(schemaName)))
if err != nil {
return fmt.Errorf("migrate: failed to set search path: %w", err)
}

migrationSource, err := iofs.New(embeddedMigrations, "migrations")
if err != nil {
return fmt.Errorf("migrate: failed to create driver from embedded migrations: %w", err)
}
defer migrationSource.Close()

dbDriver, err := postgres.WithConnection(ctx, conn, &postgres.Config{
dbDriver, err := pgxmigrate.WithInstance(db, &pgxmigrate.Config{
DatabaseName: DBName,
SchemaName: schemaName,
})
if err != nil {
return fmt.Errorf("migrate: failed to create postgres driver: %w", err)
return fmt.Errorf("migrate: failed to create pgx driver: %w", err)
}

migratorInstance, err := migrate.NewWithInstance("iofs", migrationSource, "postgres", dbDriver)
migratorInstance, err := migrate.NewWithInstance("iofs", migrationSource, "pgx5", dbDriver)
if err != nil {
return fmt.Errorf("migrate: failed to create migration instance: %w", err)
}
Expand Down
18 changes: 8 additions & 10 deletions internal/adapters/database/migrator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"testing"

"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"
pgxmigrate "github.com/golang-migrate/migrate/v4/database/pgx/v5"
"github.com/golang-migrate/migrate/v4/source/iofs"
"github.com/lib/pq"
"github.com/jackc/pgx/v5"
"github.com/stretchr/testify/require"
)

Expand All @@ -28,32 +28,30 @@ func TestMigrator(t *testing.T) {
db, err := NewPostgresDatabase(LocalConnectionString)
require.NoError(t, err)

db.MustExec(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pq.QuoteIdentifier(schemaName)))
db.MustExec(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pgx.Identifier{schemaName}.Sanitize()))

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
migrator := NewDatabaseMigrator(db, logger)
migrator := NewDatabaseMigrator(LocalConnectionString, logger)

err = migrator.migrate(ctx, schemaName)
require.NoError(t, err, "error migrating up")

// Migrate down manually
conn, err := db.Conn(ctx)
require.NoError(t, err)
defer conn.Close()
_, err = conn.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s", pq.QuoteIdentifier(schemaName)))
schemaDB, err := openSchemaScopedDB(LocalConnectionString, schemaName)
require.NoError(t, err)
defer schemaDB.Close()

migrationSource, err := iofs.New(embeddedMigrations, "migrations")
require.NoError(t, err)
defer migrationSource.Close()

dbDriver, err := postgres.WithConnection(ctx, conn, &postgres.Config{
dbDriver, err := pgxmigrate.WithInstance(schemaDB, &pgxmigrate.Config{
DatabaseName: DBName,
SchemaName: schemaName,
})
require.NoError(t, err)

migratorInstance, err := migrate.NewWithInstance("iofs", migrationSource, "postgres", dbDriver)
migratorInstance, err := migrate.NewWithInstance("iofs", migrationSource, "pgx5", dbDriver)
require.NoError(t, err)
defer migratorInstance.Close()

Expand Down
38 changes: 27 additions & 11 deletions internal/adapters/database/postgres.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package database

import (
"database/sql"
"fmt"

"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/stdlib"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"

"github.com/Amund211/flashlight/internal/config"
)
Expand All @@ -24,7 +26,7 @@
}

func NewPostgresDatabase(connectionString string) (*sqlx.DB, error) {
db, err := sqlx.Connect("postgres", connectionString)
db, err := sqlx.Connect("pgx", connectionString)
if err != nil {
return nil, fmt.Errorf("failed to connect to db: %w", err)
}
Expand All @@ -37,20 +39,34 @@
return db, nil
}

func NewCloudsqlPostgresDatabase(conf config.Config) (*sqlx.DB, error) {
var connectionString string
func ConnectionString(conf config.Config) string {
if conf.IsDevelopment() {
connectionString = LocalConnectionString
} else {
connectionString = GetCloudSQLConnectionString(conf.DBUsername(), conf.DBPassword(), conf.CloudSQLUnixSocketPath())
return LocalConnectionString
}
return GetCloudSQLConnectionString(conf.DBUsername(), conf.DBPassword(), conf.CloudSQLUnixSocketPath())
}

db, err := NewPostgresDatabase(connectionString)
// openSchemaScopedDB returns a one-off *sql.DB whose every connection has its
// search_path set to schemaName. Used by the migrator (and its tests) so
// migrate's WithInstance driver runs unqualified SQL in the right schema
// regardless of which conn from the pool it borrows.
//
// The schema name is double-quoted before being sent as a startup parameter:
// Postgres parses each comma-separated entry of search_path as a SQL
// identifier, so unquoted entries get lower-cased and reject hyphens. Without
// the quoting, mixed-case or hyphenated schema names silently resolve to a
// non-existent schema and unqualified DDL fails with SQLSTATE 3F000.
func openSchemaScopedDB(connectionString, schemaName string) (*sql.DB, error) {
connConfig, err := pgx.ParseConfig(connectionString)
if err != nil {
return nil, fmt.Errorf("failed to create postgres database: %w", err)
return nil, fmt.Errorf("failed to parse connection config: %w", err)
}
if connConfig.RuntimeParams == nil {
connConfig.RuntimeParams = map[string]string{}
}
connConfig.RuntimeParams["search_path"] = pgx.Identifier{schemaName}.Sanitize()

return db, nil
return stdlib.OpenDB(*connConfig), nil
}

func createDatabaseIfNotExists(db *sqlx.DB, dbName string) error {
Expand All @@ -68,7 +84,7 @@
return nil
}

_, err := db.Exec(fmt.Sprintf("CREATE DATABASE %s", pq.QuoteIdentifier(dbName)))
_, err := db.Exec(fmt.Sprintf("CREATE DATABASE %s", pgx.Identifier{dbName}.Sanitize()))

Check failure on line 87 in internal/adapters/database/postgres.go

View workflow job for this annotation

GitHub Actions / Format, vet, test

(*database/sql.DB).Exec must not be called. use (*database/sql.DB).ExecContext (noctx)
if err != nil {
return fmt.Errorf("createDB: failed to create database: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/adapters/database/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestDB(t *testing.T) {
t.Run("createDatabaseIfNotExists", func(t *testing.T) {
t.Parallel()

db, err := sqlx.Connect("postgres", LocalConnectionString)
db, err := sqlx.Connect("pgx", LocalConnectionString)
require.NoError(t, err)
t.Run("already existing", func(t *testing.T) {
t.Parallel()
Expand Down
Loading
Loading