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