github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/ddl/ddl.go (about) 1 /* 2 Easily generate common SQL DDL (data definition language) text or migrations 3 using a simple builder. This supports common DDL operations, not everything, 4 and is an attempt to make the common operations easy to do (since the uncommon 5 operations are usually database-specific and you can still just write them out 6 by hand as SQL). But most applications just want to create some tables and indexes 7 add a column from time to time, and maybe some foriegn keys. This package allows 8 you to do that painlessly for SQLite3 and MySQL (Postgres not implemented, yet), with other outputs 9 possible by implementing the Formatter interface. 10 11 */ 12 package ddl 13 14 // Notes on things to support: 15 // CREATE TABLE 16 // DROP TABLE 17 // ALTER TABLE RENAME TO (rename table) 18 // ALTER TABLE ADD COLUMN 19 // CREATE INDEX 20 // DROP INDEX 21 // ALTER TABLE MODIFY COLUMN (change type) 22 // ALTER TABLE DROP COLUMN 23 // ALTER TABLE RENAME COLUMN 24 25 // NOTE: MSSQL seems to support a ALTER INDEX statement but whatever, supporting create index 26 // and drop index is fine for now; and we're looking for common functionality across DBs, 27 // people can always just write their own db-specific stuff without this package to address 28 // special cases. 29 30 // Design note: using structs is wrong - because adding fields to the struct will produce different output - this is bad 31 // ddl.CreateTableFrom(structHere).PrimaryKey("some_id") 32 33 // New returns a new empty Builder. 34 func New() *Builder { 35 return &Builder{} 36 } 37 38 // Builder is a set of up an down migration statements and is used to generate 39 // SQL via a formatter and optionally produce migrations compatible with migrate.Migration. 40 // A builder has a set of "up" statements (usually create table, create index, etc.) and 41 // a separate set of "down" statements (should be the reverse - drop table, drop index, etc.). 42 type Builder struct { 43 Category string 44 Version string 45 46 UpStmtList StmtList 47 DownStmtList StmtList 48 49 down bool // set to true when new statments are down, default is up 50 } 51 52 // Reset will clear all builder fields 53 func (b *Builder) Reset() *Builder { 54 *b = Builder{} 55 return b 56 } 57 58 func (b *Builder) pushStmt(stmt Stmt) { 59 if !b.down { 60 b.UpStmtList = append(b.UpStmtList, stmt) 61 } else { 62 b.DownStmtList = append(b.DownStmtList, stmt) 63 } 64 } 65 66 // MakeSQL will generate and return the up and down SQL statments using 67 // the formatter provided. 68 func (b *Builder) MakeSQL(f Formatter) (up []string, down []string, err error) { 69 70 for _, s := range b.UpStmtList { 71 sql, ferr := f.Format(s) 72 if ferr != nil { 73 err = ferr 74 return 75 } 76 up = append(up, sql...) 77 } 78 79 for _, s := range b.DownStmtList { 80 sql, ferr := f.Format(s) 81 if ferr != nil { 82 err = ferr 83 return 84 } 85 down = append(down, sql...) 86 } 87 88 return 89 } 90 91 // MustMigrations is like Migrations but will panic on error. 92 func (b *Builder) MustMigrations(formatters ...Formatter) DDLTmplMigrationList { 93 ml, err := b.Migrations(formatters...) 94 if err != nil { 95 panic(err) 96 } 97 return ml 98 } 99 100 // Migrations will generate and return the migrations (compatible with migrate.Migration) 101 // that correspond to each of the formatters provided. 102 // This call will also clear the Builder of everything except for the category, 103 // so subsequent calls can start fresh on the next migration. 104 func (b *Builder) Migrations(formatters ...Formatter) (ml DDLTmplMigrationList, err error) { 105 106 for _, f := range formatters { 107 108 var m DDLTmplMigration 109 m.DriverNameValue = f.DriverName() 110 m.CategoryValue = b.Category 111 m.VersionValue = b.Version 112 113 for _, s := range b.UpStmtList { 114 sql, ferr := f.Format(s) 115 if ferr != nil { 116 err = ferr 117 return 118 } 119 m.UpSQL = append(m.UpSQL, sql...) 120 } 121 122 for _, s := range b.DownStmtList { 123 sql, ferr := f.Format(s) 124 if ferr != nil { 125 err = ferr 126 return 127 } 128 m.DownSQL = append(m.DownSQL, sql...) 129 } 130 131 ml = append(ml, m) 132 133 } 134 135 // if successful, reset everything except category 136 *b = Builder{Category: b.Category} 137 138 return 139 } 140 141 // Down makes it so the next statement created is added to the down list. 142 func (b *Builder) Down() *Builder { 143 b.down = true 144 return b 145 } 146 147 // Up makes it so the next statement created id assed to the up list. 148 // This is the same as a Builder in it's initial state, i.e. 149 // New().Up() and just New() are functionally equivalent. 150 func (b *Builder) Up() *Builder { 151 b.down = false 152 return b 153 } 154 155 // SetCategory sets the category string to be included in the migration. 156 // Usually this is just set once when the Builder is created. It is 157 // common for this to be the name of your application or package. 158 // This method has no effect if you are using MakeSQL() instead of Migrations(). 159 func (b *Builder) SetCategory(category string) *Builder { 160 b.Category = category 161 return b 162 } 163 164 // SetVersion sets the version string to be included in the migration. 165 // After Migrations() is called, this field is cleared and needs to be set again, 166 // since each migration should have it's own version string. Version strings 167 // must sort correctly, and the recommened form is as follows: "0001_create_tables", 168 // and then "0002_add_something_else" for the next migration, and so on. The number 169 // serves to make it sort correctly and the rest of the text is purely descriptive. 170 // This method has no effect if you are using MakeSQL() instead of Migrations(). 171 func (b *Builder) SetVersion(version string) *Builder { 172 b.Version = version 173 return b 174 } 175 176 // CreateTable will start a CREATE TABLE definition. 177 func (b *Builder) CreateTable(name string) *CreateTableStmt { 178 stmt := &CreateTableStmt{ 179 Builder: b, 180 NameValue: name, 181 } 182 b.pushStmt(stmt) 183 return stmt 184 } 185 186 // DropTable will start a DROP TABLE definition. 187 func (b *Builder) DropTable(name string) *DropTableStmt { 188 stmt := &DropTableStmt{ 189 Builder: b, 190 NameValue: name, 191 } 192 b.pushStmt(stmt) 193 return stmt 194 } 195 196 // AlterTableRename will start a ALTER TABLE RENAME TO definition. 197 func (b *Builder) AlterTableRename(oldName, newName string) *AlterTableRenameStmt { 198 stmt := &AlterTableRenameStmt{ 199 Builder: b, 200 OldNameValue: oldName, 201 NewNameValue: newName, 202 } 203 b.pushStmt(stmt) 204 return stmt 205 } 206 207 // AlterTableAdd will start a ALTER TABLE ADD COLUMN definition. 208 func (b *Builder) AlterTableAdd(tableName string) *AlterTableAddStmt { 209 stmt := &AlterTableAddStmt{ 210 Builder: b, 211 NameValue: tableName, 212 } 213 b.pushStmt(stmt) 214 return stmt 215 } 216 217 // CreateIndex will start a CREATE INDEX definition. 218 func (b *Builder) CreateIndex(indexName, tableName string) *CreateIndexStmt { 219 stmt := &CreateIndexStmt{ 220 Builder: b, 221 NameValue: indexName, 222 TableNameValue: tableName, 223 } 224 b.pushStmt(stmt) 225 return stmt 226 } 227 228 // DropIndex will start a DROP INDEX definition. 229 func (b *Builder) DropIndex(indexName, tableName string) *DropIndexStmt { 230 stmt := &DropIndexStmt{ 231 Builder: b, 232 NameValue: indexName, 233 TableNameValue: tableName, 234 } 235 b.pushStmt(stmt) 236 return stmt 237 } 238 239 // Stmt marker interface used to ensure statements are of a valid type. 240 // (Different statements otherwise have just completely different data 241 // and no common methods.) 242 type Stmt interface { 243 IsStmt() 244 } 245 246 type StmtList []Stmt