mirror of
https://github.com/ent/ent.git
synced 2026-05-22 09:31:45 +03:00
dialect/postgres: fix postgres naming prefix and detection for indexes (#543)
This commit is contained in:
@@ -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).
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user