Documentation
¶
Overview ¶
Package migrate checks a schema-change draft, .aicoded/migration.sql, and applies an app's migrations to its database as the database's owner.
A draft holds at most 64 KiB of UTF-8 and 100 statements. Check splits it into statements and allows only those that change the schema of the app's own tables (Classify): Expand ones, which add to it, CREATE TABLE, CREATE INDEX, and ALTER TABLE with only ADD clauses, where a new NOT NULL column has a DEFAULT; and Destructive ones, in a fixed set of DROP, RENAME, TRUNCATE and ALTER TABLE forms, which Notes names with E-MIG-003. Everything else is refused with E-MIG-002. Each publish adds its draft's statements to the app's list of steps, and Apply runs the steps that the database's journal, the table aicoded_migrations, lacks, a destructive one only when its caller allows it.
The aicoded CLI and the platform import it. App code may not: E-LINT-001 refuses the import.
Index ¶
- Constants
- func Check(draft []byte) ([]string, error)
- func Destructives(stmts []string) ([]int, error)
- func Extends(ran, steps []Step) error
- func IsJournal(name string) bool
- func Normalize(stmt string) string
- func Notes(draft []byte) []error
- func Split(draft []byte) ([]string, error)
- type Kind
- type Step
Examples ¶
Constants ¶
const CodeRollback = "E-MIG-007"
CodeRollback is the code that refuses a rollback whose release would leave behind steps that ran in production and hold a destructive one.
Variables ¶
This section is empty.
Functions ¶
func Check ¶
Check splits a draft into its statements and classifies each. It returns the statements and one error that joins every problem, each at the line of the draft its statement starts on. A destructive statement is not a problem; Notes names it.
Example ¶
package main
import (
"fmt"
"aicoded.dev/framework/migrate"
)
func main() {
statements, err := migrate.Check([]byte("CREATE TABLE notes (id BIGINT AUTO_INCREMENT PRIMARY KEY, body TEXT);\n" +
"ALTER TABLE notes ADD COLUMN author VARCHAR(100) NULL;\n"))
fmt.Println(len(statements), err)
}
Output: 2 <nil>
func Destructives ¶ added in v0.7.0
Destructives returns the indexes in stmts of the destructive statements, or the first error of Classify.
func Extends ¶
Extends returns E-MIG-004 unless ran, the steps a database's journal holds, starts steps, or steps starts ran (a rollback). Steps match by Seq, Publish, Index and SHA256.
func IsJournal ¶
IsJournal reports whether name, a table name without quotes, names the journal of migrations, aicoded_migrations, under any case folding MySQL may apply to table names: Unicode simple folding, and İ and ı to i. Lint uses it to keep app code away from the journal.
func Normalize ¶
Normalize returns the form of stmt, a statement as Split returns it, in which two statements that MySQL reads alike are the same: its tokens with one space between each two, without comments or a final ;, and each SQL keyword, type and function of a fixed list in upper case. Every other word, a name, keeps its case, since MySQL tells tables apart by the case of their names. Strings and names in backquotes are kept as they are. Text the checks cannot read is returned without surrounding space.
func Notes ¶ added in v0.7.0
Notes returns E-MIG-003, at the line of the draft it starts on, for each destructive statement of a draft, in order. It returns nil for a draft that Split refuses, and leaves out the statements that Check refuses.
Types ¶
type Kind ¶
type Kind int
Kind is the kind of a statement that Classify allows.
const ( // Expand is a statement that adds to the schema and changes or removes nothing in it. Expand Kind = 1 // Destructive is a statement that removes, renames or changes what the schema of the app's own // tables has. A release applies it only with an approval, while the app is stopped. Destructive Kind = 2 )
func Classify ¶
Classify returns Expand for a statement that only adds to the schema of the app's own database: CREATE TABLE, CREATE INDEX, and ALTER TABLE with only ADD clauses. It returns Destructive for one that removes, renames or changes what is there, on the app's own tables only: DROP TABLE [IF EXISTS], DROP INDEX … ON, RENAME TABLE, TRUNCATE [TABLE], and ALTER TABLE whose clauses add or are DROP [COLUMN], DROP INDEX or KEY, DROP FOREIGN KEY, DROP PRIMARY KEY, DROP CHECK or CONSTRAINT, MODIFY, CHANGE, RENAME TO, AS, COLUMN, INDEX or KEY, or ALTER [COLUMN] … SET DEFAULT, DROP DEFAULT, SET VISIBLE or SET INVISIBLE. It refuses every other statement, and any it cannot read, with E-MIG-002.
Example ¶
package main
import (
"fmt"
"aicoded.dev/framework/internal/errs"
"aicoded.dev/framework/migrate"
)
func main() {
kind, err := migrate.Classify("ALTER TABLE notes DROP COLUMN author")
fmt.Println(kind == migrate.Destructive, err)
_, err = migrate.Classify("DROP DATABASE notes")
fmt.Println(errs.Code(err))
}
Output: true <nil> E-MIG-002
type Step ¶
type Step struct {
// Seq is the step's place in the app's list, from 1.
Seq int `json:"seq"`
// Publish is the id of the publish that added the step, or "draft" locally.
Publish string `json:"publish"`
// Index is the step's place within its publish, from 1.
Index int `json:"index"`
// SHA256 is the lower-case hex SHA-256 of Statement.
SHA256 string `json:"sha256"`
// Statement is the SQL statement.
Statement string `json:"statement"`
}
Step is one statement of an app's migrations.
func Apply ¶
func Apply(ctx context.Context, db *sql.DB, steps []Step, destructive bool) (applied []Step, err error)
Apply runs on db the steps that its journal, the table aicoded_migrations, lacks, in order, and returns them. It holds the database's migration lock meanwhile, a named lock of aicoded_migrations: and the hex SHA-256 of the database's name cut to 64 characters, on one connection with lock_wait_timeout 30, and runs each step under a 5-minute context, then adds its row to the journal.
It refuses, before running anything, a list whose steps are not numbered from 1 or whose hash does not match its statement (E-MIG-004), a list the journal does not agree with (E-MIG-004, see Extends), a step to run that Classify refuses, and, unless destructive is true, a step to run that Classify finds Destructive (E-MIG-003). A caller passes true only for an approved release while the app is stopped, or for a database of test data. A step that fails stops Apply with E-MIG-005; the journal keeps the steps that ran. So does a step that ran but whose row could not be added to the journal, even on a new connection; its message says it ran but was not recorded.