go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/db/migration/group.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package migration 9 10 import ( 11 "context" 12 "database/sql" 13 14 "go.charczuk.com/sdk/db" 15 ) 16 17 // NewGroup creates a new Group from a given list of actionable. 18 func NewGroup(options ...GroupOption) *Group { 19 g := Group{} 20 for _, o := range options { 21 o(&g) 22 } 23 return &g 24 } 25 26 // NewGroupWithAction returns a new group with a single action. 27 func NewGroupWithAction(action Action, options ...GroupOption) *Group { 28 return NewGroup( 29 append([]GroupOption{OptGroupActions(action)}, options...)..., 30 ) 31 } 32 33 // NewGroupWithStep returns a new group with a single step. 34 func NewGroupWithStep(guard GuardFunc, action Action, options ...GroupOption) *Group { 35 return NewGroup( 36 append([]GroupOption{OptGroupActions(NewStep(guard, action))}, options...)..., 37 ) 38 } 39 40 // GroupOption is an option for migration Groups (Group) 41 type GroupOption func(g *Group) 42 43 // OptGroupActions allows you to add actions to the NewGroup. If you want, multiple OptActions can be applied to the same NewGroup. 44 // They are additive. 45 func OptGroupActions(actions ...Action) GroupOption { 46 return func(g *Group) { 47 if len(g.Actions) == 0 { 48 g.Actions = actions 49 } else { 50 g.Actions = append(g.Actions, actions...) 51 } 52 } 53 } 54 55 // OptGroupSkipTransaction will allow this group to be run outside of a transaction. Use this to concurrently create indices 56 // and perform other actions that cannot be executed in a Tx 57 func OptGroupSkipTransaction() GroupOption { 58 return func(g *Group) { 59 g.SkipTransaction = true 60 } 61 } 62 63 // OptGroupTx sets a transaction on the group. 64 func OptGroupTx(tx *sql.Tx) GroupOption { 65 return func(g *Group) { 66 g.Tx = tx 67 } 68 } 69 70 // Group is an series of migration actions. 71 // It uses normally transactions to apply these actions as an atomic unit, but this transaction can be bypassed by 72 // setting the SkipTransaction flag to true. This allows the use of CONCURRENT index creation and other operations that 73 // postgres will not allow within a transaction. 74 type Group struct { 75 Actions []Action 76 Tx *sql.Tx 77 SkipTransaction bool 78 } 79 80 // Action runs the groups actions within a transaction. 81 func (ga *Group) Action(ctx context.Context, c *db.Connection) (err error) { 82 var tx *sql.Tx 83 if ga.Tx != nil { // if we have a transaction provided to us 84 tx = ga.Tx 85 } else if !ga.SkipTransaction { // if we aren't told to skip transactions 86 tx, err = c.BeginTx(ctx) 87 if err != nil { 88 return 89 } 90 defer func() { 91 if err != nil { 92 _ = tx.Rollback() 93 } else { 94 _ = tx.Commit() 95 } 96 }() 97 } 98 99 for _, a := range ga.Actions { 100 err = a.Action(ctx, c, tx) 101 if err != nil { 102 return 103 } 104 } 105 106 return 107 }