dialect/sql/schema: add support for index prefixes in mysql

This commit is contained in:
Ariel Mashraki
2021-06-08 11:09:06 +03:00
committed by Ariel Mashraki
parent 110073c982
commit 66871c9806
55 changed files with 1500 additions and 315 deletions

View File

@@ -158,3 +158,100 @@ const (
SetNull ReferenceOption = "SET NULL"
SetDefault ReferenceOption = "SET DEFAULT"
)
// IndexAnnotation is a builtin schema annotation for attaching
// SQL metadata to schema indexes for both codegen and runtime.
type IndexAnnotation struct {
// Prefix defines a column prefix for a single string column index.
// In MySQL, the following annotation maps to:
//
// index.Fields("column").
// Annotation(entsql.Prefix(100))
//
// CREATE INDEX `table_column` ON `table`(`column`(100))
//
Prefix uint
// PrefixColumns defines column prefixes for a multi column index.
// In MySQL, the following annotation maps to:
//
// index.Fields("c1", "c2", "c3").
// Annotation(
// entsql.PrefixColumn("c1", 100),
// entsql.PrefixColumn("c2", 200),
// )
//
// CREATE INDEX `table_c1_c2_c3` ON `table`(`c1`(100), `c2`(200), `c3`)
//
PrefixColumns map[string]uint
}
// Prefix returns a new index annotation with a single string column index.
// In MySQL, the following annotation maps to:
//
// index.Fields("column").
// Annotation(entsql.Prefix(100))
//
// CREATE INDEX `table_column` ON `table`(`column`(100))
//
func Prefix(prefix uint) *IndexAnnotation {
return &IndexAnnotation{
Prefix: prefix,
}
}
// PrefixColumns returns a new index annotation with column prefix for
// multi-column indexes. In MySQL, the following annotation maps to:
//
// index.Fields("c1", "c2", "c3").
// Annotation(
// entsql.PrefixColumn("c1", 100),
// entsql.PrefixColumn("c2", 200),
// )
//
// CREATE INDEX `table_c1_c2_c3` ON `table`(`c1`(100), `c2`(200), `c3`)
//
func PrefixColumn(name string, prefix uint) *IndexAnnotation {
return &IndexAnnotation{
PrefixColumns: map[string]uint{
name: prefix,
},
}
}
// Name describes the annotation name.
func (IndexAnnotation) Name() string {
return "EntSQLIndexes"
}
// Merge implements the schema.Merger interface.
func (a IndexAnnotation) Merge(other schema.Annotation) schema.Annotation {
var ant IndexAnnotation
switch other := other.(type) {
case IndexAnnotation:
ant = other
case *IndexAnnotation:
if other != nil {
ant = *other
}
default:
return a
}
if ant.Prefix != 0 {
a.Prefix = ant.Prefix
}
if ant.PrefixColumns != nil {
if a.PrefixColumns == nil {
a.PrefixColumns = make(map[string]uint)
}
for column, prefix := range ant.PrefixColumns {
a.PrefixColumns[column] = prefix
}
}
return a
}
var (
_ schema.Annotation = (*IndexAnnotation)(nil)
_ schema.Merger = (*IndexAnnotation)(nil)
)

View File

@@ -67,37 +67,37 @@ func TestInspector_Tables(t *testing.T) {
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "").
AddRow("text", "longtext", "YES", "YES", "NULL", "", "", "").
AddRow("uuid", "char(36)", "YES", "YES", "NULL", "", "", "utf8mb4_bin"))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("public", "users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectQuery(escape("SELECT `column_name`, `column_type`, `is_nullable`, `column_key`, `column_default`, `extra`, `character_set_name`, `collation_name` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?")).
WithArgs("public", "pets").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "").
AddRow("user_pets", "bigint(20)", "YES", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("public", "pets").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectQuery(escape("SELECT `column_name`, `column_type`, `is_nullable`, `column_key`, `column_default`, `extra`, `character_set_name`, `collation_name` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?")).
WithArgs("public", "groups").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("public", "groups").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectQuery(escape("SELECT `column_name`, `column_type`, `is_nullable`, `column_key`, `column_default`, `extra`, `character_set_name`, `collation_name` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?")).
WithArgs("public", "user_groups").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("user_id", "bigint(20)", "NO", "YES", "NULL", "", "", "").
AddRow("group_id", "bigint(20)", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("public", "user_groups").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}))
},
dialect.SQLite: func(mock mysqlMock) {
mock.ExpectQuery(escape("SELECT `name` FROM `sqlite_schema` WHERE `type` = ?")).

View File

@@ -385,6 +385,13 @@ func (m *Migrate) changeSet(curr, new *Table) (*changes, error) {
case idx1.Unique != idx2.Unique:
change.index.drop.append(idx2)
change.index.add.append(idx1)
default:
im, ok := m.sqlDialect.(interface{ indexModified(old, new *Index) bool })
// If the dialect supports comparing indexes.
if ok && im.indexModified(idx2, idx1) {
change.index.drop.append(idx2)
change.index.add.append(idx1)
}
}
}

View File

@@ -12,6 +12,7 @@ import (
"strings"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/entsql"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/schema/field"
)
@@ -100,7 +101,7 @@ func (d *MySQL) table(ctx context.Context, tx dialect.Tx, name string) (*Table,
}
// Add and link indexes to table columns.
for _, idx := range indexes {
t.AddIndex(idx.Name, idx.Unique, idx.columns)
t.addIndex(idx)
}
if _, ok := d.mariadb(); ok {
if err := d.normalizeJSON(ctx, tx, t); err != nil {
@@ -113,7 +114,7 @@ func (d *MySQL) table(ctx context.Context, tx dialect.Tx, name string) (*Table,
// table loads the table indexes from the database.
func (d *MySQL) indexes(ctx context.Context, tx dialect.Tx, name string) ([]*Index, error) {
rows := &sql.Rows{}
query, args := sql.Select("index_name", "column_name", "non_unique", "seq_in_index").
query, args := sql.Select("index_name", "column_name", "sub_part", "non_unique", "seq_in_index").
From(sql.Table("STATISTICS").Schema("INFORMATION_SCHEMA")).
Where(sql.And(
d.matchSchema(),
@@ -303,7 +304,20 @@ func (d *MySQL) addColumn(c *Column) *sql.ColumnBuilder {
// addIndex returns the querying for adding an index to MySQL.
func (d *MySQL) addIndex(i *Index, table string) *sql.IndexBuilder {
return i.Builder(table)
idx := sql.CreateIndex(i.Name).Table(table)
if i.Unique {
idx.Unique()
}
parts := indexParts(i)
for _, c := range i.Columns {
part, ok := parts[c.Name]
if !ok || part == 0 {
idx.Column(c.Name)
} else {
idx.Column(fmt.Sprintf("%s(%d)", idx.Builder.Quote(c.Name), part))
}
}
return idx
}
// dropIndex drops a MySQL index.
@@ -460,8 +474,8 @@ func (d *MySQL) scanColumn(c *Column, rows *sql.Rows) error {
}
// scanIndexes scans sql.Rows into an Indexes list. The query for returning the rows,
// should return the following 4 columns: INDEX_NAME, COLUMN_NAME, NON_UNIQUE, SEQ_IN_INDEX.
// SEQ_IN_INDEX specifies the position of the column in the index columns.
// should return the following 5 columns: INDEX_NAME, COLUMN_NAME, SUB_PART, NON_UNIQUE,
// SEQ_IN_INDEX. SEQ_IN_INDEX specifies the position of the column in the index columns.
func (d *MySQL) scanIndexes(rows *sql.Rows) (Indexes, error) {
var (
i Indexes
@@ -473,8 +487,9 @@ func (d *MySQL) scanIndexes(rows *sql.Rows) (Indexes, error) {
column string
nonuniq bool
seqindex int
subpart sql.NullInt64
)
if err := rows.Scan(&name, &column, &nonuniq, &seqindex); err != nil {
if err := rows.Scan(&name, &column, &subpart, &nonuniq, &seqindex); err != nil {
return nil, fmt.Errorf("scanning index description: %w", err)
}
// Ignore primary keys.
@@ -483,11 +498,17 @@ func (d *MySQL) scanIndexes(rows *sql.Rows) (Indexes, error) {
}
idx, ok := names[name]
if !ok {
idx = &Index{Name: name, Unique: !nonuniq}
idx = &Index{Name: name, Unique: !nonuniq, Annotation: &entsql.IndexAnnotation{}}
i = append(i, idx)
names[name] = idx
}
idx.columns = append(idx.columns, column)
if subpart.Int64 > 0 {
if idx.Annotation.PrefixColumns == nil {
idx.Annotation.PrefixColumns = make(map[string]uint)
}
idx.Annotation.PrefixColumns[column] = uint(subpart.Int64)
}
}
if err := rows.Err(); err != nil {
return nil, err
@@ -689,3 +710,35 @@ func (d *MySQL) defaultSize(c *Column) int64 {
func (d *MySQL) needsConversion(old, new *Column) bool {
return d.cType(old) != d.cType(new)
}
// indexModified used by the migration differ to check if the index was modified.
func (d *MySQL) indexModified(old, new *Index) bool {
oldParts, newParts := indexParts(old), indexParts(new)
if len(oldParts) != len(newParts) {
return true
}
for column, oldPart := range oldParts {
newPart, ok := newParts[column]
if !ok || oldPart != newPart {
return true
}
}
return false
}
// indexParts returns a map holding the sub_part mapping if exist.
func indexParts(idx *Index) map[string]uint {
parts := make(map[string]uint)
if idx.Annotation == nil {
return parts
}
// If prefix (without a name) was defined on the
// annotation, map it to the single column index.
if idx.Annotation.Prefix > 0 && len(idx.Columns) == 1 {
parts[idx.Columns[0].Name] = idx.Annotation.Prefix
}
for column, part := range idx.Annotation.PrefixColumns {
parts[column] = part
}
return parts
}

View File

@@ -286,10 +286,10 @@ func TestMySQL_Create(t *testing.T) {
AddRow("decimal", "decimal(6,2)", "NO", "YES", "NULL", "", "", "").
AddRow("unsigned_decimal", "decimal(6,2) unsigned", "NO", "YES", "NULL", "", "", "").
AddRow("timestamp", "timestamp", "NO", "NO", "CURRENT_TIMESTAMP", "DEFAULT_GENERATED on update CURRENT_TIMESTAMP", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `age` bigint NOT NULL, ADD COLUMN `ts` timestamp NOT NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -321,10 +321,10 @@ func TestMySQL_Create(t *testing.T) {
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "").
AddRow("enums1", "enum('a')", "YES", "NO", "NULL", "", "", "").
AddRow("enums2", "enum('b', 'a')", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` MODIFY COLUMN `enums1` enum('a', 'b') NOT NULL, MODIFY COLUMN `enums2` enum('a') NOT NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -356,10 +356,10 @@ func TestMySQL_Create(t *testing.T) {
AddRow("created_at", "datetime", "NO", "YES", "NULL", "", "", "").
AddRow("updated_at", "timestamp", "NO", "YES", "NULL", "", "", "").
AddRow("deleted_at", "datetime", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` MODIFY COLUMN `updated_at` datetime NULL, MODIFY COLUMN `deleted_at` timestamp NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -390,10 +390,10 @@ func TestMySQL_Create(t *testing.T) {
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "").
AddRow("doc", "longblob", "YES", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `age` bigint NOT NULL DEFAULT 10")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -423,10 +423,10 @@ func TestMySQL_Create(t *testing.T) {
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `tiny` tinyblob NOT NULL, ADD COLUMN `blob` blob NOT NULL, ADD COLUMN `medium` mediumblob NOT NULL, ADD COLUMN `long` longblob NOT NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -453,10 +453,10 @@ func TestMySQL_Create(t *testing.T) {
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `binary` binary(20) NOT NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -486,10 +486,10 @@ func TestMySQL_Create(t *testing.T) {
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("tiny", "varbinary(255)", "NO", "YES", "NULL", "", "", "").
AddRow("medium", "varbinary(255)", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` MODIFY COLUMN `medium` longblob NOT NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -518,10 +518,10 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec("ALTER TABLE `users` ADD COLUMN `age` double NOT NULL DEFAULT 10.1").
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -550,10 +550,10 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec("ALTER TABLE `users` ADD COLUMN `age` boolean NOT NULL DEFAULT true").
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -582,10 +582,10 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `nick` varchar(255) NOT NULL DEFAULT 'unknown'")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -614,10 +614,10 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `nick` longtext NOT NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -645,10 +645,10 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` DROP COLUMN `name`")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -678,10 +678,10 @@ func TestMySQL_Create(t *testing.T) {
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", "").
AddRow("age", "bigint(20)", "NO", "NO", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` MODIFY COLUMN `name` varchar(255) NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
@@ -709,10 +709,10 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("age", "bigint(20)", "NO", "", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
// create the unique index.
mock.ExpectExec(escape("CREATE UNIQUE INDEX `age` ON `users`(`age`)")).
WillReturnResult(sqlmock.NewResult(0, 1))
@@ -741,11 +741,11 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("age", "bigint(20)", "NO", "UNI", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1").
AddRow("age", "age", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1").
AddRow("age", "age", nil, "0", "1"))
mock.ExpectCommit()
},
},
@@ -772,11 +772,11 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("age", "bigint(20)", "NO", "UNI", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1").
AddRow("age", "age", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1").
AddRow("age", "age", nil, "0", "1"))
// check if a foreign-key needs to be dropped.
mock.ExpectQuery(escape("SELECT `CONSTRAINT_NAME` FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` WHERE `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `POSITION_IN_UNIQUE_CONSTRAINT` IS NOT NULL AND `TABLE_SCHEMA` = (SELECT DATABASE())")).
WithArgs("users", "age").
@@ -787,6 +787,50 @@ func TestMySQL_Create(t *testing.T) {
mock.ExpectCommit()
},
},
{
name: "increase index sub_part",
tables: func() []*Table {
t := &Table{
Name: "users",
Columns: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "text", Type: field.TypeString, Size: math.MaxInt32, Nullable: true},
},
PrimaryKey: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
},
Indexes: []*Index{
{Name: "prefix_text", Annotation: &entsql.IndexAnnotation{Prefix: 100}},
},
}
t.Indexes[0].Columns = t.Columns[1:]
return []*Table{t}
}(),
options: []MigrateOption{WithDropIndex(true)},
before: func(mock mysqlMock) {
mock.start("5.7.23")
mock.tableExists("users", true)
mock.ExpectQuery(escape("SELECT `column_name`, `column_type`, `is_nullable`, `column_key`, `column_default`, `extra`, `character_set_name`, `collation_name` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("text", "longtext", "YES", "NO", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1").
AddRow("prefix_text", "text", "50", "0", "1"))
mock.ExpectQuery(escape("SELECT `CONSTRAINT_NAME` FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` WHERE `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `POSITION_IN_UNIQUE_CONSTRAINT` IS NOT NULL AND `TABLE_SCHEMA` = (SELECT DATABASE())")).
WithArgs("users", "text").
WillReturnRows(sqlmock.NewRows([]string{"CONSTRAINT_NAME"}))
// modify index by dropping and creating it.
mock.ExpectExec(escape("DROP INDEX `prefix_text` ON `users`")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec(escape("CREATE INDEX `prefix_text` ON `users`(`text`(100))")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
},
{
name: "ignore foreign keys on index dropping",
tables: []*Table{
@@ -818,12 +862,12 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("parent_id", "bigint(20)", "YES", "NULL", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1").
AddRow("old_index", "old", "0", "1").
AddRow("parent_id", "parent_id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1").
AddRow("old_index", "old", nil, "0", "1").
AddRow("parent_id", "parent_id", nil, "0", "1"))
// drop the unique index.
mock.ExpectExec(escape("DROP INDEX `old_index` ON `users`")).
WillReturnResult(sqlmock.NewResult(0, 1))
@@ -854,11 +898,11 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("parent_id", "bigint(20)", "YES", "NULL", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1").
AddRow("parent_id", "parent_id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1").
AddRow("parent_id", "parent_id", nil, "0", "1"))
// check if a foreign-key needs to be dropped.
mock.ExpectQuery(escape("SELECT `CONSTRAINT_NAME` FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` WHERE `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `POSITION_IN_UNIQUE_CONSTRAINT` IS NOT NULL AND `TABLE_SCHEMA` = (SELECT DATABASE())")).
WithArgs("users", "parent_id").
@@ -897,11 +941,11 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("parent_id", "bigint(20)", "YES", "NULL", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1").
AddRow("parent_id", "parent_id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1").
AddRow("parent_id", "parent_id", nil, "0", "1"))
// check if there's a foreign-key that is associated with this index.
mock.ExpectQuery(escape("SELECT `CONSTRAINT_NAME` FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` WHERE `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `POSITION_IN_UNIQUE_CONSTRAINT` IS NOT NULL AND `TABLE_SCHEMA` = (SELECT DATABASE())")).
WithArgs("users", "parent_id").
@@ -949,10 +993,10 @@ func TestMySQL_Create(t *testing.T) {
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "").
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `spouse_id` bigint NULL")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.fkExists("user_spouse_____________________390ed76f91d3c57cd3516e7690f621dc", false)
@@ -1016,10 +1060,10 @@ func TestMySQL_Create(t *testing.T) {
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
// query groups table.
mock.ExpectQuery(escape("SELECT COUNT(*) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")).
WithArgs("groups").
@@ -1086,10 +1130,10 @@ func TestMySQL_Create(t *testing.T) {
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}).
AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
// query the auto-increment value.
mock.ExpectQuery(escape("SELECT `AUTO_INCREMENT` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")).
WithArgs("users").
@@ -1194,10 +1238,10 @@ func TestMySQL_Create(t *testing.T) {
AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "").
AddRow("json", "longtext", "YES", "YES", "NULL", "", "utf8mb4", "utf8mb4_bin").
AddRow("longtext", "longtext", "YES", "YES", "NULL", "", "utf8mb4", "utf8mb4_bin"))
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `sub_part`, `non_unique`, `seq_in_index` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", "0", "1"))
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "sub_part", "non_unique", "seq_in_index"}).
AddRow("PRIMARY", "id", nil, "0", "1"))
mock.ExpectQuery(escape("SELECT `CONSTRAINT_NAME` FROM `INFORMATION_SCHEMA`.`CHECK_CONSTRAINTS` WHERE `CONSTRAINT_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? AND `CHECK_CLAUSE` LIKE ?")).
WithArgs("users", "json_valid(%)").
WillReturnRows(sqlmock.NewRows([]string{"CONSTRAINT_NAME"}).

View File

@@ -112,6 +112,15 @@ func (t *Table) column(name string) (*Column, bool) {
return nil, false
}
// Index returns a table index by its exact name.
func (t *Table) Index(name string) (*Index, bool) {
idx, ok := t.index(name)
if ok && idx.Name == name {
return idx, ok
}
return nil, false
}
// index returns a table index by its name.
func (t *Table) index(name string) (*Index, bool) {
for _, idx := range t.Indexes {
@@ -407,12 +416,13 @@ func (r ReferenceOption) ConstName() string {
// Index definition for table index.
type Index struct {
Name string // index name.
Unique bool // uniqueness.
Columns []*Column // actual table columns.
columns []string // columns loaded from query scan.
primary bool // primary key index.
realname string // real name in the database (Postgres only).
Name string // index name.
Unique bool // uniqueness.
Columns []*Column // actual table columns.
Annotation *entsql.IndexAnnotation // index annotation.
columns []string // columns loaded from query scan.
primary bool // primary key index.
realname string // real name in the database (Postgres only).
}
// Builder returns the query builder for index creation. The DSL is identical in all dialects.