go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/db/migration/guard_predicates.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 "fmt" 14 15 "go.charczuk.com/sdk/db" 16 ) 17 18 // Always always runs a step. 19 func Always() GuardFunc { 20 return Guard("always run", func(_ context.Context, _ *db.Connection, _ *sql.Tx) (bool, error) { return true, nil }) 21 } 22 23 // TableExists returns a guard that ensures a table exists 24 func TableExists(tableName string) GuardFunc { 25 return guardPredicate(fmt.Sprintf("Check table exists: %s", tableName), PredicateTableExists, tableName) 26 } 27 28 // TableNotExists returns a guard that ensures a table does not exist 29 func TableNotExists(tableName string) GuardFunc { 30 return guardNotPredicate(fmt.Sprintf("Check table does not exist: %s", tableName), PredicateTableExists, tableName) 31 } 32 33 // ColumnExists returns a guard that ensures a column exists 34 func ColumnExists(tableName, columnName string) GuardFunc { 35 return guardPredicate2(fmt.Sprintf("Check column exists: %s.%s", tableName, columnName), 36 PredicateColumnExists, tableName, columnName) 37 } 38 39 // ColumnNotExists returns a guard that ensures a column does not exist 40 func ColumnNotExists(tableName, columnName string) GuardFunc { 41 return guardNotPredicate2(fmt.Sprintf("Check column does not exist: %s.%s", tableName, columnName), 42 PredicateColumnExists, tableName, columnName) 43 } 44 45 // ConstraintExists returns a guard that ensures a constraint exists 46 func ConstraintExists(tableName, constraintName string) GuardFunc { 47 return guardPredicate2(fmt.Sprintf("Check constraint %s exists on table %s", constraintName, tableName), 48 PredicateConstraintExists, tableName, constraintName) 49 } 50 51 // ConstraintNotExists returns a guard that ensures a constraint does not exist 52 func ConstraintNotExists(tableName, constraintName string) GuardFunc { 53 return guardNotPredicate2(fmt.Sprintf("Check constraint %s does not exist on table %s", constraintName, tableName), 54 PredicateConstraintExists, tableName, constraintName) 55 } 56 57 // IndexExists returns a guard that ensures an index exists 58 func IndexExists(tableName, indexName string) GuardFunc { 59 return guardPredicate2(fmt.Sprintf("Check index %s exists on table %s", indexName, tableName), 60 PredicateIndexExists, tableName, indexName) 61 } 62 63 // IndexNotExists returns a guard that ensures an index does not exist 64 func IndexNotExists(tableName, indexName string) GuardFunc { 65 return guardNotPredicate2(fmt.Sprintf("Check index %s does not exist on table %s", indexName, tableName), 66 PredicateIndexExists, tableName, indexName) 67 } 68 69 // RoleExists returns a guard that ensures a role (user) exists 70 func RoleExists(roleName string) GuardFunc { 71 return guardPredicate(fmt.Sprintf("Check Role Exists: %s", roleName), PredicateRoleExists, roleName) 72 } 73 74 // RoleNotExists returns a guard that ensures a role (user) does not exist 75 func RoleNotExists(roleName string) GuardFunc { 76 return guardNotPredicate(fmt.Sprintf("Check Role Not Exists: %s", roleName), PredicateRoleExists, roleName) 77 } 78 79 // IfExists only runs the statement if the given item exists. 80 func IfExists(statement string, args ...interface{}) GuardFunc { 81 return Guard("if exists run", func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 82 return PredicateAny(ctx, c, tx, statement, args...) 83 }) 84 } 85 86 // IfNotExists only runs the statement if the given item doesn't exist. 87 func IfNotExists(statement string, args ...interface{}) GuardFunc { 88 return Guard("if not exists run", func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 89 return PredicateNone(ctx, c, tx, statement, args...) 90 }) 91 } 92 93 // GuardFunc is a control for migration steps. 94 // It should internally evaluate if the action should be called. 95 // The action is typically given separately so these two components can be composed. 96 type GuardFunc func(context.Context, *db.Connection, *sql.Tx, Action) error 97 98 // GuardPredicateFunc is a function that can act as a guard 99 type GuardPredicateFunc func(context.Context, *db.Connection, *sql.Tx) (bool, error) 100 101 // -------------------------------------------------------------------------------- 102 // Guards 103 // -------------------------------------------------------------------------------- 104 105 // Guard returns a function that determines if a step in a group should run. 106 func Guard(description string, predicate GuardPredicateFunc) GuardFunc { 107 return func(ctx context.Context, c *db.Connection, tx *sql.Tx, step Action) error { 108 proceed, err := predicate(ctx, c, tx) 109 if err != nil { 110 if suite := GetContextSuite(ctx); suite != nil { 111 return suite.WriteError(WithLabel(ctx, description), err) 112 } 113 return err 114 } 115 116 if !proceed { 117 if suite := GetContextSuite(ctx); suite != nil { 118 suite.WriteSkipf(ctx, description) 119 } 120 return nil 121 } 122 123 err = step.Action(ctx, c, tx) 124 if err != nil { 125 if suite := GetContextSuite(ctx); suite != nil { 126 return suite.WriteError(WithLabel(ctx, description), err) 127 } 128 return err 129 } 130 if suite := GetContextSuite(ctx); suite != nil { 131 suite.WriteApplyf(ctx, description) 132 } 133 return nil 134 } 135 } 136 137 // guardPredicate wraps a predicate in a GuardFunc 138 func guardPredicate(description string, p predicate, arg1 string) GuardFunc { 139 return Guard(description, func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 140 return p(ctx, c, tx, arg1) 141 }) 142 } 143 144 // guardNotPredicate inverts a predicate, and wraps that in a GuardFunc 145 func guardNotPredicate(description string, p predicate, arg1 string) GuardFunc { 146 return Guard(description, func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 147 return Not(p(ctx, c, tx, arg1)) 148 }) 149 } 150 151 // guardPredicate2 wraps a predicate2 in a GuardFunc 152 func guardPredicate2(description string, p predicate2, arg1, arg2 string) GuardFunc { 153 return Guard(description, func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 154 return p(ctx, c, tx, arg1, arg2) 155 }) 156 } 157 158 // guardNotPredicate2 inverts a predicate2, and wraps that in a GuardFunc 159 func guardNotPredicate2(description string, p predicate2, arg1, arg2 string) GuardFunc { 160 return Guard(description, func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 161 return Not(p(ctx, c, tx, arg1, arg2)) 162 }) 163 } 164 165 // guardPredicate3 wraps a predicate3 in a GuardFunc 166 func guardPredicate3(description string, p predicate3, arg1, arg2, arg3 string) GuardFunc { 167 return Guard(description, func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 168 return p(ctx, c, tx, arg1, arg2, arg3) 169 }) 170 } 171 172 // guardNotPredicate3 inverts a predicate3, and wraps that in a GuardFunc 173 func guardNotPredicate3(description string, p predicate3, arg1, arg2, arg3 string) GuardFunc { 174 return Guard(description, func(ctx context.Context, c *db.Connection, tx *sql.Tx) (bool, error) { 175 return Not(p(ctx, c, tx, arg1, arg2, arg3)) 176 }) 177 } 178 179 // predicate is a function that evaluates based on a string param. 180 type predicate func(context.Context, *db.Connection, *sql.Tx, string) (bool, error) 181 182 // predicate2 is a function that evaluates based on two string params. 183 type predicate2 func(context.Context, *db.Connection, *sql.Tx, string, string) (bool, error) 184 185 // predicate3 is a function that evaluates based on three string params. 186 type predicate3 func(context.Context, *db.Connection, *sql.Tx, string, string, string) (bool, error)