dialect/postgres: fix postgres naming prefix and detection for indexes (#543)

This commit is contained in:
Ariel Mashraki
2020-06-10 22:24:16 +03:00
committed by GitHub
parent e03e387988
commit d26fbaeb54
3 changed files with 83 additions and 4 deletions

View File

@@ -329,11 +329,30 @@ func (d *Postgres) alterColumn(c *Column) (ops []*sql.ColumnBuilder) {
return ops
}
// hasUniqueName reports if the index has a unique name in the schema.
func hasUniqueName(i *Index) bool {
name := i.Name
// The "_key" suffix is added by Postgres for implicit indexes.
if strings.HasSuffix(name, "_key") {
name = strings.TrimSuffix(name, "_key")
}
suffix := strings.Join(i.columnNames(), "_")
if !strings.HasSuffix(name, suffix) {
return true // Assume it has a custom storage-key.
}
// The codegen prefixes by default indexes with the type name.
// For example, an index "users"("name"), will named as "user_name".
return name != suffix
}
// addIndex returns the querying for adding an index to PostgreSQL.
func (d *Postgres) addIndex(i *Index, table string) *sql.IndexBuilder {
// Since index name should be unique in pg_class for schema,
// we prefix it with the table name and remove on read.
name := fmt.Sprintf("%s_%s", table, i.Name)
name := i.Name
if !hasUniqueName(i) {
// Since index name should be unique in pg_class for schema,
// we prefix it with the table name and remove on read.
name = fmt.Sprintf("%s_%s", table, i.Name)
}
idx := sql.Dialect(dialect.Postgres).
CreateIndex(name).Table(table)
if i.Unique {
@@ -349,7 +368,7 @@ func (d *Postgres) addIndex(i *Index, table string) *sql.IndexBuilder {
func (d *Postgres) dropIndex(ctx context.Context, tx dialect.Tx, idx *Index, table string) error {
name := idx.Name
build := sql.Dialect(dialect.Postgres)
if prefix := table + "_"; !strings.HasPrefix(name, prefix) {
if prefix := table + "_"; !strings.HasPrefix(name, prefix) && !hasUniqueName(idx) {
name = prefix + name
}
query, args := sql.Dialect(dialect.Postgres).

View File

@@ -480,6 +480,54 @@ func TestPostgres_Create(t *testing.T) {
mock.ExpectCommit()
},
},
{
name: "add and remove indexes",
tables: func() []*Table {
c := []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
// Add implicit index.
{Name: "age", Type: field.TypeInt, Unique: true},
{Name: "score", Type: field.TypeInt},
}
return []*Table{
{
Name: "users",
Columns: c,
PrimaryKey: c[0:1],
Indexes: Indexes{
// Change non-unique index to unique.
{Name: "user_score", Columns: c[2:3], Unique: true},
},
},
}
}(),
options: []MigrateOption{WithDropIndex(true)},
before: func(mock pgMock) {
mock.start("120000")
mock.tableExists("users", true)
mock.ExpectQuery(escape(`SELECT "column_name", "data_type", "is_nullable", "column_default" FROM INFORMATION_SCHEMA.COLUMNS WHERE "table_schema" = CURRENT_SCHEMA() AND "table_name" = $1`)).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"column_name", "data_type", "is_nullable", "column_default"}).
AddRow("id", "bigint", "NO", "NULL").
AddRow("age", "bigint", "NO", "NULL").
AddRow("score", "bigint", "NO", "NULL"))
mock.ExpectQuery(escape(fmt.Sprintf(indexesQuery, "users"))).
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "primary", "unique", "seq_in_index"}).
AddRow("users_pkey", "id", "t", "t", 0).
AddRow("user_score", "score", "f", "f", 0))
mock.ExpectQuery(escape(`SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE "table_schema" = CURRENT_SCHEMA() AND "constraint_type" = $1 AND "constraint_name" = $2`)).
WithArgs("UNIQUE", "user_score").
WillReturnRows(sqlmock.NewRows([]string{"count"}).
AddRow(0))
mock.ExpectExec(escape(`DROP INDEX "user_score"`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec(escape(`CREATE UNIQUE INDEX "users_age" ON "users"("age")`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec(escape(`CREATE UNIQUE INDEX "user_score" ON "users"("score")`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
},
{
name: "add edge to table",
tables: func() []*Table {

View File

@@ -399,6 +399,18 @@ func (i *Index) sameAs(idx *Index) bool {
return true
}
// columnNames returns the names of the columns of the index.
func (i *Index) columnNames() []string {
if len(i.columns) > 0 {
return i.columns
}
columns := make([]string, 0, len(i.Columns))
for _, c := range i.Columns {
columns = append(columns, c.Name)
}
return columns
}
// Indexes used for scanning all sql.Rows into a list of indexes, because
// multiple sql rows can represent the same index (multi-columns indexes).
type Indexes []*Index