Files

108 lines
2.7 KiB
Go
Raw Permalink Normal View History

2025-04-29 06:03:47 +02:00
package postgres
import (
"context"
"errors"
2025-04-29 06:03:47 +02:00
"github.com/jackc/pgx/v5"
"github.com/zitadel/zitadel/backend/v3/storage/database"
)
type pgxTx struct{ pgx.Tx }
var _ database.Transaction = (*pgxTx)(nil)
// Commit implements [database.Transaction].
func (tx *pgxTx) Commit(ctx context.Context) error {
2025-07-17 00:54:21 +02:00
err := tx.Tx.Commit(ctx)
return wrapError(err)
2025-04-29 06:03:47 +02:00
}
// Rollback implements [database.Transaction].
func (tx *pgxTx) Rollback(ctx context.Context) error {
2025-07-17 00:54:21 +02:00
err := tx.Tx.Rollback(ctx)
return wrapError(err)
2025-04-29 06:03:47 +02:00
}
// End implements [database.Transaction].
func (tx *pgxTx) End(ctx context.Context, err error) error {
if err != nil {
rollbackErr := tx.Rollback(ctx)
if rollbackErr != nil {
err = errors.Join(err, rollbackErr)
}
2025-04-29 06:03:47 +02:00
return err
}
return tx.Commit(ctx)
}
// Query implements [database.Transaction].
// Subtle: this method shadows the method (Tx).Query of pgxTx.Tx.
func (tx *pgxTx) Query(ctx context.Context, sql string, args ...any) (database.Rows, error) {
rows, err := tx.Tx.Query(ctx, sql, args...)
2025-07-17 00:54:21 +02:00
if err != nil {
return nil, wrapError(err)
}
return &Rows{rows}, nil
2025-04-29 06:03:47 +02:00
}
// QueryRow implements [database.Transaction].
// Subtle: this method shadows the method (Tx).QueryRow of pgxTx.Tx.
func (tx *pgxTx) QueryRow(ctx context.Context, sql string, args ...any) database.Row {
2025-07-17 00:54:21 +02:00
return &Row{tx.Tx.QueryRow(ctx, sql, args...)}
2025-04-29 06:03:47 +02:00
}
// Exec implements [database.Transaction].
// Subtle: this method shadows the method (Pool).Exec of pgxPool.Pool.
feat(db): adding relational instance table (#10007) <!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved Implementing Instance table to new relational database schema # How the Problems Are Solved The following fields must be managed in this table: - `id` - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - `created_at` - `updated_at` - `deleted_at` The repository must provide the following functions: Manipulations: - create - `name` - `default_org_id` - `zitadel_project_id` - `console_client_id` - `console_app_id` - `default_language` - update - `name` - `default_language` - delete Queries: - get returns single instance matching the criteria and pagination, should return error if multiple instances were found - list returns list of instances matching the criteria, pagination Criteria are the following: - by id pagination: - by created_at - by updated_at - by name ### instance events The following events must be applied on the table using a projection (`internal/query/projection`) - `instance.added` results in create - `instance.changed` changes the `name` field - `instance.removed` sets the the `deleted_at` field - `instance.default.org.set` sets the `default_org_id` field - `instance.iam.project.set` sets the `zitadel_project_id` field - `instance.iam.console.set` sets the `console_client_id` and `console_app_id` fields - `instance.default.language.set` sets the `default_language` field - if answer is yes to discussion: `instance.domain.primary.set` sets the `primary_domain` field ### acceptance criteria - [x] migration is implemented and gets executed - [x] domain interfaces are implemented and documented for service layer - [x] repository is implemented and implements domain interface - [x] testing - [x] the repository methods - [x] events get reduced correctly - [x] unique constraints # Additional Context - Closes https://github.com/zitadel/zitadel/issues/9935
2025-06-17 09:46:01 +02:00
func (tx *pgxTx) Exec(ctx context.Context, sql string, args ...any) (int64, error) {
res, err := tx.Tx.Exec(ctx, sql, args...)
2025-07-17 00:54:21 +02:00
if err != nil {
return 0, wrapError(err)
}
return res.RowsAffected(), nil
2025-04-29 06:03:47 +02:00
}
// Begin implements [database.Transaction].
// As postgres does not support nested transactions we use savepoints to emulate them.
func (tx *pgxTx) Begin(ctx context.Context) (database.Transaction, error) {
savepoint, err := tx.Tx.Begin(ctx)
if err != nil {
2025-07-17 00:54:21 +02:00
return nil, wrapError(err)
2025-04-29 06:03:47 +02:00
}
return &pgxTx{savepoint}, nil
}
func transactionOptionsToPgx(opts *database.TransactionOptions) pgx.TxOptions {
if opts == nil {
return pgx.TxOptions{}
}
return pgx.TxOptions{
IsoLevel: isolationToPgx(opts.IsolationLevel),
AccessMode: accessModeToPgx(opts.AccessMode),
}
}
func isolationToPgx(isolation database.IsolationLevel) pgx.TxIsoLevel {
switch isolation {
case database.IsolationLevelSerializable:
return pgx.Serializable
case database.IsolationLevelReadCommitted:
return pgx.ReadCommitted
default:
return pgx.Serializable
}
}
func accessModeToPgx(accessMode database.AccessMode) pgx.TxAccessMode {
switch accessMode {
case database.AccessModeReadWrite:
return pgx.ReadWrite
case database.AccessModeReadOnly:
return pgx.ReadOnly
default:
return pgx.ReadWrite
}
}