feat: init migrations for transactional tables (#9946)

# Which Problems Are Solved

To start with transactional tables we need to setup the new `zitadel`
schema in a way that does not rely on the event store later.

# How the Problems Are Solved

Setup step added which calls the function which executes the migrations.

# Additional Changes

none

# Additional Context

- closes #9933
This commit is contained in:
Silvan
2025-05-26 08:20:14 +02:00
committed by GitHub
parent 3ddecca600
commit 362420f62b
16 changed files with 197 additions and 36 deletions

View File

@@ -21,6 +21,7 @@ import (
// These tests give an overview of how to use the domain package.
func TestExample(t *testing.T) {
t.Skip("skip example test because it is not a real test")
ctx := context.Background()
ctrl := gomock.NewController(t)

View File

@@ -19,6 +19,7 @@ var (
type Config struct {
*pgxpool.Config
*pgxpool.Pool
// Host string
// Port int32
@@ -37,7 +38,7 @@ type Config struct {
// Connect implements [database.Connector].
func (c *Config) Connect(ctx context.Context) (database.Pool, error) {
pool, err := pgxpool.NewWithConfig(ctx, c.Config)
pool, err := c.getPool(ctx)
if err != nil {
return nil, err
}
@@ -47,6 +48,13 @@ func (c *Config) Connect(ctx context.Context) (database.Pool, error) {
return &pgxPool{pool}, nil
}
func (c *Config) getPool(ctx context.Context) (*pgxpool.Pool, error) {
if c.Pool != nil {
return c.Pool, nil
}
return pgxpool.NewWithConfig(ctx, c.Config)
}
func NameMatcher(name string) bool {
return slices.Contains([]string{"postgres", "pg"}, strings.ToLower(name))
}

View File

@@ -6,11 +6,15 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
"github.com/zitadel/zitadel/backend/v3/storage/database"
"github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres/migration"
)
type pgxConn struct{ *pgxpool.Conn }
var _ database.Client = (*pgxConn)(nil)
var (
_ database.Client = (*pgxConn)(nil)
_ database.Migrator = (*pgxConn)(nil)
)
// Release implements [database.Client].
func (c *pgxConn) Release(_ context.Context) error {
@@ -46,3 +50,8 @@ func (c *pgxConn) Exec(ctx context.Context, sql string, args ...any) error {
_, err := c.Conn.Exec(ctx, sql, args...)
return err
}
// Migrate implements [database.Migrator].
func (c *pgxConn) Migrate(ctx context.Context) error {
return migration.Migrate(ctx, c.Conn.Conn())
}

View File

@@ -0,0 +1,16 @@
package migration
import (
_ "embed"
)
var (
//go:embed 001_instance_table/up.sql
up001InstanceTable string
//go:embed 001_instance_table/down.sql
down001InstanceTable string
)
func init() {
registerSQLMigration(1, up001InstanceTable, down001InstanceTable)
}

View File

@@ -0,0 +1 @@
DROP TABLE zitadel.instances;

View File

@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS zitadel.instances(
id VARCHAR(100) NOT NULL
, PRIMARY KEY (id)
, name VARCHAR(100) NOT NULL
);

View File

@@ -0,0 +1,13 @@
// This package contains the migration logic for the PostgreSQL dialect.
// It uses the [github.com/jackc/tern/v2/migrate] package to handle the migration process.
//
// **Developer Note**:
//
// Each migration MUST be registered in an init function.
// Create a go file for each migration with the sequence of the migration as prefix and some descriptive name.
// The file name MUST be in the format <sequence>_<name>.go.
// Each migration SHOULD provide an up and down migration.
// Prefer to write SQL statements instead of funcs if it is reasonable.
// To keep the folder clean create a folder to store the sql files with the following format: <sequence>_<name>/{up/down}.sql.
// And use the go embed directive to embed the sql files.
package migration

View File

@@ -0,0 +1,33 @@
package migration
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/tern/v2/migrate"
)
var migrations []*migrate.Migration
func Migrate(ctx context.Context, conn *pgx.Conn) error {
// we need to ensure that the schema exists before we can run the migration
// because creating the migrations table already required the schema
_, err := conn.Exec(ctx, "CREATE SCHEMA IF NOT EXISTS zitadel")
if err != nil {
return err
}
migrator, err := migrate.NewMigrator(ctx, conn, "zitadel.migrations")
if err != nil {
return err
}
migrator.Migrations = migrations
return migrator.Migrate(ctx)
}
func registerSQLMigration(sequence int32, up, down string) {
migrations = append(migrations, &migrate.Migration{
Sequence: sequence,
UpSQL: up,
DownSQL: down,
})
}

View File

@@ -6,11 +6,15 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
"github.com/zitadel/zitadel/backend/v3/storage/database"
"github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres/migration"
)
type pgxPool struct{ *pgxpool.Pool }
var _ database.Pool = (*pgxPool)(nil)
var (
_ database.Pool = (*pgxPool)(nil)
_ database.Migrator = (*pgxPool)(nil)
)
// Acquire implements [database.Pool].
func (c *pgxPool) Acquire(ctx context.Context) (database.Client, error) {
@@ -55,3 +59,12 @@ func (c *pgxPool) Close(_ context.Context) error {
c.Pool.Close()
return nil
}
// Migrate implements [database.Migrator].
func (c *pgxPool) Migrate(ctx context.Context) error {
client, err := c.Pool.Acquire(ctx)
if err != nil {
return err
}
return migration.Migrate(ctx, client.Conn())
}

View File

@@ -0,0 +1,8 @@
package database
import "context"
type Migrator interface {
// Migrate executes migrations to setup the database.
Migrate(ctx context.Context) error
}

View File

@@ -12,6 +12,7 @@ import (
)
func TestQueryUser(t *testing.T) {
t.Skip("tests are meant as examples and are not real tests")
t.Run("User filters", func(t *testing.T) {
client := dbmock.NewMockClient(gomock.NewController(t))
@@ -67,6 +68,7 @@ func TestArg(t *testing.T) {
}
func TestWriteUser(t *testing.T) {
t.Skip("tests are meant as examples and are not real tests")
t.Run("update user", func(t *testing.T) {
user := repository.UserRepository(nil)
user.Human().Update(context.Background(), user.IDCondition("test"), user.SetUsername("test"))