this is the proposal i mentioned in the review

This commit is contained in:
adlerhurst
2025-06-13 11:59:25 +02:00
parent abae73ef1e
commit a77f88c8fb
4 changed files with 41 additions and 42 deletions

View File

@@ -2,8 +2,6 @@ package database
import ( import (
"context" "context"
"github.com/jackc/pgx/v5"
) )
// Pool is a connection pool. e.g. pgxpool // Pool is a connection pool. e.g. pgxpool
@@ -59,13 +57,25 @@ type Row interface {
// Rows is an abstraction of sql.Rows. // Rows is an abstraction of sql.Rows.
type Rows interface { type Rows interface {
pgx.Rows Scanner
Next() bool
Close() error
Err() error
} }
// Collector is an interface for collecting rows into a specific type. type CollectableRows interface {
type Collector[T any] interface { // Collect collects all rows and scans them into dest.
// Collect collects a single row into the specified type. // dest must be a pointer to a slice of pointer to structs
Collect(Row) (T, error) // e.g. *[]*MyStruct
// CollectRows collects multiple rows into a slice of the specified type. // Rows are closed after this call.
CollectRows(Rows) ([]T, error) Collect(dest any) error
// CollectFirst collects the first row and scans it into dest.
// dest must be a pointer to a struct
// e.g. *MyStruct{}
// Rows are closed after this call.
CollectFirst(dest any) error
// CollectExactlyOneRow collects exactly one row and scans it into dest.
// e.g. *MyStruct{}
// Rows are closed after this call.
CollectExactlyOneRow(dest any) error
} }

View File

@@ -5,7 +5,6 @@ import (
"errors" "errors"
"time" "time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgconn"
"github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database"
@@ -43,17 +42,11 @@ func (i *instance) Get(ctx context.Context, opts ...database.Condition) (*domain
andCondition := database.And(opts...) andCondition := database.And(opts...)
i.writeCondition(&builder, andCondition) i.writeCondition(&builder, andCondition)
rows, err := i.client.Query(ctx, builder.String(), builder.Args()...) return scanInstance(ctx, i.client, &builder)
if err != nil {
return nil, err
}
defer rows.Close()
return scanInstance(rows)
} }
// List implements [domain.InstanceRepository]. // List implements [domain.InstanceRepository].
func (i *instance) List(ctx context.Context, opts ...database.Condition) ([]domain.Instance, error) { func (i *instance) List(ctx context.Context, opts ...database.Condition) ([]*domain.Instance, error) {
builder := database.StatementBuilder{} builder := database.StatementBuilder{}
builder.WriteString(queryInstanceStmt) builder.WriteString(queryInstanceStmt)
@@ -63,13 +56,7 @@ func (i *instance) List(ctx context.Context, opts ...database.Condition) ([]doma
andCondition := database.And(opts...) andCondition := database.And(opts...)
i.writeCondition(&builder, andCondition) i.writeCondition(&builder, andCondition)
rows, err := i.client.Query(ctx, builder.String(), builder.Args()...) return scanInstances(ctx, i.client, &builder)
if err != nil {
return nil, err
}
defer rows.Close()
return scanInstances(rows)
} }
const createInstanceStmt = `INSERT INTO zitadel.instances (id, name, default_org_id, iam_project_id, console_client_id, console_app_id, default_language)` + const createInstanceStmt = `INSERT INTO zitadel.instances (id, name, default_org_id, iam_project_id, console_client_id, console_app_id, default_language)` +
@@ -221,31 +208,29 @@ func (i *instance) writeCondition(
condition.Write(builder) condition.Write(builder)
} }
func scanInstance(rows database.Rows) (*domain.Instance, error) { func scanInstance(ctx context.Context, querier database.Querier, builder *database.StatementBuilder) (*domain.Instance, error) {
instance, err := pgx.CollectOneRow[domain.Instance](rows, pgx.RowToStructByNameLax[domain.Instance]) rows, err := querier.Query(ctx, builder.String(), builder.Args()...)
if err != nil { if err != nil {
// if no results returned, this is not a error
// it just means the instance was not found
// the caller should check if the returned instance is nil
if err.Error() == "no rows in result set" {
return nil, nil
}
return nil, err return nil, err
} }
return &instance, nil instance := new(domain.Instance)
if err := rows.(database.CollectableRows).CollectExactlyOneRow(instance); err != nil {
return nil, err
}
return instance, nil
} }
func scanInstances(rows database.Rows) ([]domain.Instance, error) { func scanInstances(ctx context.Context, querier database.Querier, builder *database.StatementBuilder) (instances []*domain.Instance, err error) {
instances, err := pgx.CollectRows[domain.Instance](rows, pgx.RowToStructByNameLax[domain.Instance]) rows, err := querier.Query(ctx, builder.String(), builder.Args()...)
if err != nil { if err != nil {
// if no results returned, this is not a error
// it just means the instance was not found
// the caller should check if the returned instance is nil
if err.Error() == "no rows in result set" {
return nil, nil
}
return nil, err return nil, err
} }
if err := rows.(database.CollectableRows).Collect(&instances); err != nil {
return nil, err
}
return instances, nil return instances, nil
} }

1
go.mod
View File

@@ -119,6 +119,7 @@ require (
github.com/crewjam/httperr v0.2.0 // indirect github.com/crewjam/httperr v0.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/georgysavva/scany/v2 v2.1.4 // indirect
github.com/go-ini/ini v1.67.0 // indirect github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect

3
go.sum
View File

@@ -233,6 +233,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/georgysavva/scany/v2 v2.1.4 h1:nrzHEJ4oQVRoiKmocRqA1IyGOmM/GQOEsg9UjMR5Ip4=
github.com/georgysavva/scany/v2 v2.1.4/go.mod h1:fqp9yHZzM/PFVa3/rYEC57VmDx+KDch0LoqrJzkvtos=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
@@ -311,6 +313,7 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=