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  }