mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-15 13:38:46 +00:00
128 lines
3.5 KiB
Go
128 lines
3.5 KiB
Go
![]() |
package migration
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"embed"
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
"text/template"
|
||
|
|
||
|
"github.com/mitchellh/mapstructure"
|
||
|
|
||
|
"github.com/zitadel/zitadel/internal/database"
|
||
|
"github.com/zitadel/zitadel/internal/domain"
|
||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
countTriggerTmpl = "count_trigger"
|
||
|
deleteParentCountsTmpl = "delete_parent_counts_trigger"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
//go:embed *.sql
|
||
|
templateFS embed.FS
|
||
|
templates = template.Must(template.ParseFS(templateFS, "*.sql"))
|
||
|
)
|
||
|
|
||
|
// CountTrigger registers the existing projections.count_trigger function.
|
||
|
// The trigger than takes care of keeping count of existing
|
||
|
// rows in the source table.
|
||
|
// It also pre-populates the projections.resource_counts table with
|
||
|
// the counts for the given table.
|
||
|
//
|
||
|
// During the population of the resource_counts table,
|
||
|
// the source table is share-locked to prevent concurrent modifications.
|
||
|
// Projection handlers will be halted until the lock is released.
|
||
|
// SELECT statements are not blocked by the lock.
|
||
|
//
|
||
|
// This migration repeats when any of the arguments are changed,
|
||
|
// such as renaming of a projection table.
|
||
|
func CountTrigger(
|
||
|
db *database.DB,
|
||
|
table string,
|
||
|
parentType domain.CountParentType,
|
||
|
instanceIDColumn string,
|
||
|
parentIDColumn string,
|
||
|
resource string,
|
||
|
) RepeatableMigration {
|
||
|
return &triggerMigration{
|
||
|
triggerConfig: triggerConfig{
|
||
|
Table: table,
|
||
|
ParentType: parentType.String(),
|
||
|
InstanceIDColumn: instanceIDColumn,
|
||
|
ParentIDColumn: parentIDColumn,
|
||
|
Resource: resource,
|
||
|
},
|
||
|
db: db,
|
||
|
templateName: countTriggerTmpl,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DeleteParentCountsTrigger
|
||
|
//
|
||
|
// This migration repeats when any of the arguments are changed,
|
||
|
// such as renaming of a projection table.
|
||
|
func DeleteParentCountsTrigger(
|
||
|
db *database.DB,
|
||
|
table string,
|
||
|
parentType domain.CountParentType,
|
||
|
instanceIDColumn string,
|
||
|
parentIDColumn string,
|
||
|
resource string,
|
||
|
) RepeatableMigration {
|
||
|
return &triggerMigration{
|
||
|
triggerConfig: triggerConfig{
|
||
|
Table: table,
|
||
|
ParentType: parentType.String(),
|
||
|
InstanceIDColumn: instanceIDColumn,
|
||
|
ParentIDColumn: parentIDColumn,
|
||
|
Resource: resource,
|
||
|
},
|
||
|
db: db,
|
||
|
templateName: deleteParentCountsTmpl,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type triggerMigration struct {
|
||
|
triggerConfig
|
||
|
db *database.DB
|
||
|
templateName string
|
||
|
}
|
||
|
|
||
|
// String implements [Migration] and [fmt.Stringer].
|
||
|
func (m *triggerMigration) String() string {
|
||
|
return fmt.Sprintf("repeatable_%s_%s", m.Resource, m.templateName)
|
||
|
}
|
||
|
|
||
|
// Execute implements [Migration]
|
||
|
func (m *triggerMigration) Execute(ctx context.Context, _ eventstore.Event) error {
|
||
|
var query strings.Builder
|
||
|
err := templates.ExecuteTemplate(&query, m.templateName, m.triggerConfig)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("%s: execute trigger template: %w", m, err)
|
||
|
}
|
||
|
_, err = m.db.ExecContext(ctx, query.String())
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("%s: exec trigger query: %w", m, err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type triggerConfig struct {
|
||
|
Table string `json:"table,omitempty" mapstructure:"table"`
|
||
|
ParentType string `json:"parent_type,omitempty" mapstructure:"parent_type"`
|
||
|
InstanceIDColumn string `json:"instance_id_column,omitempty" mapstructure:"instance_id_column"`
|
||
|
ParentIDColumn string `json:"parent_id_column,omitempty" mapstructure:"parent_id_column"`
|
||
|
Resource string `json:"resource,omitempty" mapstructure:"resource"`
|
||
|
}
|
||
|
|
||
|
// Check implements [RepeatableMigration].
|
||
|
func (c *triggerConfig) Check(lastRun map[string]any) bool {
|
||
|
var dst triggerConfig
|
||
|
if err := mapstructure.Decode(lastRun, &dst); err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return dst != *c
|
||
|
}
|