diff --git a/server/migrations/38_migrate_run_sequences.up.sql b/server/migrations/38_migrate_run_sequences.up.sql new file mode 100644 index 0000000000..c9f04ebcde --- /dev/null +++ b/server/migrations/38_migrate_run_sequences.up.sql @@ -0,0 +1,22 @@ +DO $$ +DECLARE + temprow record; +BEGIN + FOR temprow IN + SELECT max(id)+1 AS next_value, test_id, tenant_id + FROM test_runs + GROUP BY test_id, tenant_id + LOOP + EXECUTE format('CREATE SEQUENCE IF NOT EXISTS runs_test_%s_seq START WITH %s', + MD5(FORMAT('%s%s', temprow.test_id, temprow.tenant_id)), temprow.next_value); + END LOOP; + + FOR temprow IN + SELECT max(id::int)+1 AS next_value, test_suite_id, tenant_id + FROM test_suite_runs + GROUP BY test_suite_id, tenant_id + LOOP + EXECUTE format('CREATE SEQUENCE IF NOT EXISTS runs_test_suite_%s_seq START WITH %s', + MD5(FORMAT('%s%s', temprow.test_suite_id, temprow.tenant_id)), temprow.next_value); + END LOOP; +END $$; diff --git a/server/pkg/sqlutil/tenant.go b/server/pkg/sqlutil/tenant.go index 2ac8dd290c..14b091c5b0 100644 --- a/server/pkg/sqlutil/tenant.go +++ b/server/pkg/sqlutil/tenant.go @@ -44,6 +44,15 @@ func TenantInsert(ctx context.Context, params ...any) []any { return append(params, *tenantID) } +func TenantIDString(ctx context.Context) string { + tenantID := TenantID(ctx) + if tenantID == nil { + return "" + } + + return *tenantID +} + func TenantID(ctx context.Context) *string { tenantID := ctx.Value(middleware.TenantIDKey) diff --git a/server/test/run_repository.go b/server/test/run_repository.go index 04f8f0e358..d8f47808c9 100644 --- a/server/test/run_repository.go +++ b/server/test/run_repository.go @@ -169,7 +169,9 @@ func (r *runRepository) CreateRun(ctx context.Context, test Test, run Run) (Run, return Run{}, fmt.Errorf("sql beginTx: %w", err) } - _, err = tx.ExecContext(ctx, replaceRunSequenceName(createSequeceQuery, test.ID)) + tenantID := sqlutil.TenantIDString(ctx) + + _, err = tx.ExecContext(ctx, replaceRunSequenceName(createSequeceQuery, test.ID, tenantID)) if err != nil { tx.Rollback() return Run{}, fmt.Errorf("sql exec: %w", err) @@ -196,7 +198,7 @@ func (r *runRepository) CreateRun(ctx context.Context, test Test, run Run) (Run, ) var runID int - err = tx.QueryRowContext(ctx, replaceRunSequenceName(createRunQuery, test.ID), params...).Scan(&runID) + err = tx.QueryRowContext(ctx, replaceRunSequenceName(createRunQuery, test.ID, tenantID), params...).Scan(&runID) if err != nil { tx.Rollback() return Run{}, fmt.Errorf("sql exec: %w", err) diff --git a/server/test/test_repository.go b/server/test/test_repository.go index f5b5c6e707..d767d2fbf3 100644 --- a/server/test/test_repository.go +++ b/server/test/test_repository.go @@ -517,7 +517,7 @@ func (r *repository) Delete(ctx context.Context, id id.ID) error { } } - dropSequence(ctx, tx, id) + dropSequence(ctx, tx, id, sqlutil.TenantIDString(ctx)) err = tx.Commit() if err != nil { @@ -533,10 +533,10 @@ const ( runSequenceName = "%sequence_name%" ) -func dropSequence(ctx context.Context, tx *sql.Tx, testID id.ID) error { +func dropSequence(ctx context.Context, tx *sql.Tx, testID id.ID, tenantID string) error { _, err := tx.ExecContext( ctx, - replaceRunSequenceName(dropSequenceQuery, testID), + replaceRunSequenceName(dropSequenceQuery, testID, tenantID), ) return err @@ -547,12 +547,12 @@ func md5Hash(text string) string { return hex.EncodeToString(hash[:]) } -func replaceRunSequenceName(sql string, testID id.ID) string { +func replaceRunSequenceName(sql string, testID id.ID, tenantID string) string { // postgres doesn't like uppercase chars in sequence names. // testID might contain uppercase chars, and we cannot lowercase them // because they might lose their uniqueness. // md5 creates a unique, lowercase hash. - seqName := "runs_test_" + md5Hash(testID.String()) + "_seq" + seqName := "runs_test_" + md5Hash(testID.String()+tenantID) + "_seq" return strings.ReplaceAll(sql, runSequenceName, seqName) } diff --git a/server/testsuite/testsuite_run_repository.go b/server/testsuite/testsuite_run_repository.go index 05f0195512..26dd205d89 100644 --- a/server/testsuite/testsuite_run_repository.go +++ b/server/testsuite/testsuite_run_repository.go @@ -93,12 +93,12 @@ func md5Hash(text string) string { return hex.EncodeToString(hash[:]) } -func replaceTestSuiteRunSequenceName(sql string, ID id.ID) string { +func replaceTestSuiteRunSequenceName(sql string, ID id.ID, tenantID string) string { // postgres doesn't like uppercase chars in sequence names. // transactionID might contain uppercase chars, and we cannot lowercase them // because they might lose their uniqueness. // md5 creates a unique, lowercase hash. - seqName := "runs_test_suite_" + md5Hash(ID.String()) + "_seq" + seqName := "runs_test_suite_" + md5Hash(ID.String()+tenantID) + "_seq" return strings.ReplaceAll(sql, runSequenceName, seqName) } @@ -118,12 +118,6 @@ func (td *RunRepository) CreateRun(ctx context.Context, tr TestSuiteRun) (TestSu return TestSuiteRun{}, fmt.Errorf("sql beginTx: %w", err) } - _, err = tx.ExecContext(ctx, replaceTestSuiteRunSequenceName(createSequenceQuery, tr.TestSuiteID)) - if err != nil { - tx.Rollback() - return TestSuiteRun{}, fmt.Errorf("sql exec: %w", err) - } - params := sqlutil.TenantInsert(ctx, tr.TestSuiteID, tr.TestSuiteVersion, @@ -134,10 +128,18 @@ func (td *RunRepository) CreateRun(ctx context.Context, tr TestSuiteRun) (TestSu jsonVariableSet, ) + tenantID := sqlutil.TenantIDString(ctx) + + _, err = tx.ExecContext(ctx, replaceTestSuiteRunSequenceName(createSequenceQuery, tr.TestSuiteID, tenantID)) + if err != nil { + tx.Rollback() + return TestSuiteRun{}, fmt.Errorf("sql exec: %w", err) + } + var runID int err = tx.QueryRowContext( ctx, - replaceTestSuiteRunSequenceName(createTestSuiteRunQuery, tr.TestSuiteID), + replaceTestSuiteRunSequenceName(createTestSuiteRunQuery, tr.TestSuiteID, tenantID), params..., ).Scan(&runID) if err != nil {