From 3a0f7ef19371925fb00edd6bda616e6809d87e10 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 22 Nov 2023 14:05:14 +0200 Subject: [PATCH] fix: ensure uniqueness (#6956) * fix: ensure uniqueness * only update wrong ones * Update cmd/setup/16.go Co-authored-by: Silvan --------- Co-authored-by: Silvan (cherry picked from commit b5630411035acf521643a10527cf1abc89e7b9d3) --- cmd/setup/16.go | 33 ++++++++++++++++++++ cmd/setup/16.sql | 13 ++++++++ cmd/setup/config.go | 25 ++++++++------- cmd/setup/setup.go | 3 ++ internal/eventstore/v3/unique_constraints.go | 1 + 5 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 cmd/setup/16.go create mode 100644 cmd/setup/16.sql diff --git a/cmd/setup/16.go b/cmd/setup/16.go new file mode 100644 index 0000000000..6204dcd989 --- /dev/null +++ b/cmd/setup/16.go @@ -0,0 +1,33 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/database" +) + +var ( + //go:embed 16.sql + uniqueConstraintLower string +) + +type UniqueConstraintToLower struct { + dbClient *database.DB +} + +func (mig *UniqueConstraintToLower) Execute(ctx context.Context) error { + res, err := mig.dbClient.ExecContext(ctx, uniqueConstraintLower) + if err != nil { + return err + } + count, err := res.RowsAffected() + logging.WithFields("count", count).Info("unique constraints updated") + return err +} + +func (mig *UniqueConstraintToLower) String() string { + return "16_unique_constraint_lower" +} diff --git a/cmd/setup/16.sql b/cmd/setup/16.sql new file mode 100644 index 0000000000..baafaff640 --- /dev/null +++ b/cmd/setup/16.sql @@ -0,0 +1,13 @@ +WITH casesensitive as ( + SELECT instance_id, unique_type, lower(unique_field) + FROM eventstore.unique_constraints + GROUP BY instance_id, unique_type, lower(unique_field) + HAVING count(unique_field) < 2 +) +UPDATE eventstore.unique_constraints c + SET unique_field = casesensitive.lower + FROM casesensitive + WHERE c.instance_id = casesensitive.instance_id + AND c.unique_type = casesensitive.unique_type + AND lower(c.unique_field) = casesensitive.lower + AND c.unique_field <> casesensitive.lower; \ No newline at end of file diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 61930baecf..c2a2e9eda2 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -61,18 +61,19 @@ func MustNewConfig(v *viper.Viper) *Config { } type Steps struct { - s1ProjectionTable *ProjectionTable - s2AssetsTable *AssetTable - FirstInstance *FirstInstance - s5LastFailed *LastFailed - s6OwnerRemoveColumns *OwnerRemoveColumns - s7LogstoreTables *LogstoreTables - s8AuthTokens *AuthTokenIndexes - CorrectCreationDate *CorrectCreationDate - s12AddOTPColumns *AddOTPColumns - s13FixQuotaProjection *FixQuotaConstraints - s14NewEventsTable *NewEventsTable - s15CurrentStates *CurrentProjectionState + s1ProjectionTable *ProjectionTable + s2AssetsTable *AssetTable + FirstInstance *FirstInstance + s5LastFailed *LastFailed + s6OwnerRemoveColumns *OwnerRemoveColumns + s7LogstoreTables *LogstoreTables + s8AuthTokens *AuthTokenIndexes + CorrectCreationDate *CorrectCreationDate + s12AddOTPColumns *AddOTPColumns + s13FixQuotaProjection *FixQuotaConstraints + s14NewEventsTable *NewEventsTable + s15CurrentStates *CurrentProjectionState + s16UniqueConstraintsLower *UniqueConstraintToLower } type encryptionKeyConfig struct { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 8ef9373f7b..7b30340c54 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -100,6 +100,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s13FixQuotaProjection = &FixQuotaConstraints{dbClient: zitadelDBClient} steps.s14NewEventsTable = &NewEventsTable{dbClient: esPusherDBClient} steps.s15CurrentStates = &CurrentProjectionState{dbClient: zitadelDBClient} + steps.s16UniqueConstraintsLower = &UniqueConstraintToLower{dbClient: zitadelDBClient} err = projection.Create(ctx, zitadelDBClient, eventstoreClient, config.Projections, nil, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -140,6 +141,8 @@ func Setup(config *Config, steps *Steps, masterKey string) { logging.WithFields("name", steps.s13FixQuotaProjection.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s15CurrentStates) logging.WithFields("name", steps.s15CurrentStates.String()).OnError(err).Fatal("migration failed") + err = migration.Migrate(ctx, eventstoreClient, steps.s16UniqueConstraintsLower) + logging.WithFields("name", steps.s16UniqueConstraintsLower.String()).OnError(err).Fatal("migration failed") for _, repeatableStep := range repeatableSteps { err = migration.Migrate(ctx, eventstoreClient, repeatableStep) diff --git a/internal/eventstore/v3/unique_constraints.go b/internal/eventstore/v3/unique_constraints.go index f721e77385..953aea61c0 100644 --- a/internal/eventstore/v3/unique_constraints.go +++ b/internal/eventstore/v3/unique_constraints.go @@ -33,6 +33,7 @@ func handleUniqueConstraints(ctx context.Context, tx *sql.Tx, commands []eventst for _, command := range commands { for _, constraint := range command.UniqueConstraints() { + constraint.UniqueField = strings.ToLower(constraint.UniqueField) switch constraint.Action { case eventstore.UniqueConstraintAdd: addPlaceholders = append(addPlaceholders, fmt.Sprintf("($%d, $%d, $%d)", len(addArgs)+1, len(addArgs)+2, len(addArgs)+3))