fix: avoid sort_order collisions when inserting content at a specific position
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4f873fe9de
commit
a704c3b29f
|
|
@ -39,10 +39,7 @@ func (s *Store) CreateExamPrepUnit(ctx context.Context, catalogCourseID int64, i
|
|||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
target := int32(*input.SortOrder)
|
||||
if _, err := tx.Exec(ctx,
|
||||
`UPDATE exam_prep.units SET sort_order = sort_order + 1 WHERE catalog_course_id = $1 AND sort_order >= $2`,
|
||||
catalogCourseID, target,
|
||||
); err != nil {
|
||||
if err := shiftExamPrepUnitsSortOrderForInsert(ctx, tx, catalogCourseID, target); err != nil {
|
||||
return domain.ExamPrepUnit{}, err
|
||||
}
|
||||
u, err := q.ExamPrepCreateUnit(ctx, dbgen.ExamPrepCreateUnitParams{
|
||||
|
|
|
|||
|
|
@ -39,10 +39,7 @@ func (s *Store) CreateCourse(ctx context.Context, programID int64, input domain.
|
|||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
target := int32(*input.SortOrder)
|
||||
if _, err := tx.Exec(ctx,
|
||||
`UPDATE courses SET sort_order = sort_order + 1 WHERE program_id = $1 AND sort_order >= $2`,
|
||||
programID, target,
|
||||
); err != nil {
|
||||
if err := shiftCoursesSortOrderForInsert(ctx, tx, programID, target); err != nil {
|
||||
return domain.Course{}, err
|
||||
}
|
||||
c, err := q.CreateCourse(ctx, dbgen.CreateCourseParams{
|
||||
|
|
|
|||
|
|
@ -41,10 +41,7 @@ func (s *Store) CreateLesson(ctx context.Context, moduleID int64, input domain.C
|
|||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
target := int32(*input.SortOrder)
|
||||
if _, err := tx.Exec(ctx,
|
||||
`UPDATE lessons SET sort_order = sort_order + 1 WHERE module_id = $1 AND sort_order >= $2`,
|
||||
moduleID, target,
|
||||
); err != nil {
|
||||
if err := shiftLessonsSortOrderForInsert(ctx, tx, moduleID, target); err != nil {
|
||||
return domain.Lesson{}, err
|
||||
}
|
||||
l, err := q.CreateLesson(ctx, dbgen.CreateLessonParams{
|
||||
|
|
|
|||
|
|
@ -40,10 +40,7 @@ func (s *Store) CreateModule(ctx context.Context, programID, courseID int64, inp
|
|||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
target := int32(*input.SortOrder)
|
||||
if _, err := tx.Exec(ctx,
|
||||
`UPDATE modules SET sort_order = sort_order + 1 WHERE course_id = $1 AND sort_order >= $2`,
|
||||
courseID, target,
|
||||
); err != nil {
|
||||
if err := shiftModulesSortOrderForInsert(ctx, tx, courseID, target); err != nil {
|
||||
return domain.Module{}, err
|
||||
}
|
||||
m, err := q.CreateModule(ctx, dbgen.CreateModuleParams{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,64 @@ import (
|
|||
// Sequential siblings (programs global, courses per program, modules per course, lessons per module) use unique sort_order.
|
||||
// These helpers move id from oldPos to newPos without collisions by temporarily assigning sort_order = -id then shifting intermediates.
|
||||
|
||||
// shift*SortOrderForInsert makes room at fromPos by incrementing existing rows at/after that position.
|
||||
// Rows are updated highest-first so the unique (parent, sort_order) index is never violated mid-shift.
|
||||
|
||||
func shiftProgramsSortOrderForInsert(ctx context.Context, tx pgx.Tx, fromPos int32) error {
|
||||
_, err := tx.Exec(ctx, `
|
||||
UPDATE programs AS t
|
||||
SET sort_order = t.sort_order + 1
|
||||
FROM (
|
||||
SELECT id FROM programs WHERE sort_order >= $1 ORDER BY sort_order DESC
|
||||
) AS s
|
||||
WHERE t.id = s.id`, fromPos)
|
||||
return err
|
||||
}
|
||||
|
||||
func shiftCoursesSortOrderForInsert(ctx context.Context, tx pgx.Tx, programID int64, fromPos int32) error {
|
||||
_, err := tx.Exec(ctx, `
|
||||
UPDATE courses AS t
|
||||
SET sort_order = t.sort_order + 1
|
||||
FROM (
|
||||
SELECT id FROM courses WHERE program_id = $1 AND sort_order >= $2 ORDER BY sort_order DESC
|
||||
) AS s
|
||||
WHERE t.id = s.id`, programID, fromPos)
|
||||
return err
|
||||
}
|
||||
|
||||
func shiftModulesSortOrderForInsert(ctx context.Context, tx pgx.Tx, courseID int64, fromPos int32) error {
|
||||
_, err := tx.Exec(ctx, `
|
||||
UPDATE modules AS t
|
||||
SET sort_order = t.sort_order + 1
|
||||
FROM (
|
||||
SELECT id FROM modules WHERE course_id = $1 AND sort_order >= $2 ORDER BY sort_order DESC
|
||||
) AS s
|
||||
WHERE t.id = s.id`, courseID, fromPos)
|
||||
return err
|
||||
}
|
||||
|
||||
func shiftLessonsSortOrderForInsert(ctx context.Context, tx pgx.Tx, moduleID int64, fromPos int32) error {
|
||||
_, err := tx.Exec(ctx, `
|
||||
UPDATE lessons AS t
|
||||
SET sort_order = t.sort_order + 1
|
||||
FROM (
|
||||
SELECT id FROM lessons WHERE module_id = $1 AND sort_order >= $2 ORDER BY sort_order DESC
|
||||
) AS s
|
||||
WHERE t.id = s.id`, moduleID, fromPos)
|
||||
return err
|
||||
}
|
||||
|
||||
func shiftExamPrepUnitsSortOrderForInsert(ctx context.Context, tx pgx.Tx, catalogCourseID int64, fromPos int32) error {
|
||||
_, err := tx.Exec(ctx, `
|
||||
UPDATE exam_prep.units AS t
|
||||
SET sort_order = t.sort_order + 1
|
||||
FROM (
|
||||
SELECT id FROM exam_prep.units WHERE catalog_course_id = $1 AND sort_order >= $2 ORDER BY sort_order DESC
|
||||
) AS s
|
||||
WHERE t.id = s.id`, catalogCourseID, fromPos)
|
||||
return err
|
||||
}
|
||||
|
||||
func repositionProgramSortOrder(ctx context.Context, tx pgx.Tx, id int64, oldPos, newPos int32) error {
|
||||
if oldPos == newPos {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func (s *Store) CreateProgram(ctx context.Context, input domain.CreateProgramInp
|
|||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
target := int32(*input.SortOrder)
|
||||
if _, err := tx.Exec(ctx, `UPDATE programs SET sort_order = sort_order + 1 WHERE sort_order >= $1`, target); err != nil {
|
||||
if err := shiftProgramsSortOrderForInsert(ctx, tx, target); err != nil {
|
||||
return domain.Program{}, err
|
||||
}
|
||||
p, err := q.CreateProgram(ctx, dbgen.CreateProgramParams{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user