feat(pgsql): add support for concurrent index creation
* Implemented `Concurrent` field in index model * Updated index creation template to support `CREATE INDEX CONCURRENTLY` * Added tests for concurrent index creation in migration writer
This commit is contained in:
@@ -169,6 +169,7 @@ When `include_audit` is enabled, adds:
|
||||
- Constraint actions (CASCADE, RESTRICT, SET NULL)
|
||||
- Partial indexes
|
||||
- Function-based indexes
|
||||
- Concurrent index creation (`CREATE INDEX CONCURRENTLY`) via `Index.Concurrent`
|
||||
- Check constraints with expressions
|
||||
|
||||
## Data Types
|
||||
|
||||
@@ -652,6 +652,7 @@ func (w *MigrationWriter) generateIndexScripts(model *models.Schema, current *mo
|
||||
IndexType: indexType,
|
||||
Columns: strings.Join(columnExprs, ", "),
|
||||
Unique: modelIndex.Unique,
|
||||
Concurrent: modelIndex.Concurrent,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -334,6 +334,46 @@ func TestWriteMigration_DoesNotAlterEquivalentNormalizedColumnType(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteMigration_ConcurrentIndex(t *testing.T) {
|
||||
current := models.InitDatabase("testdb")
|
||||
currentSchema := models.InitSchema("public")
|
||||
current.Schemas = append(current.Schemas, currentSchema)
|
||||
|
||||
model := models.InitDatabase("testdb")
|
||||
modelSchema := models.InitSchema("public")
|
||||
|
||||
table := models.InitTable("articles", "public")
|
||||
titleCol := models.InitColumn("title", "articles", "public")
|
||||
titleCol.Type = "text"
|
||||
table.Columns["title"] = titleCol
|
||||
|
||||
index := &models.Index{
|
||||
Name: "idx_articles_title",
|
||||
Columns: []string{"title"},
|
||||
Concurrent: true,
|
||||
}
|
||||
table.Indexes[index.Name] = index
|
||||
|
||||
modelSchema.Tables = append(modelSchema.Tables, table)
|
||||
model.Schemas = append(model.Schemas, modelSchema)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer, err := NewMigrationWriter(&writers.WriterOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create writer: %v", err)
|
||||
}
|
||||
writer.writer = &buf
|
||||
|
||||
if err := writer.WriteMigration(model, current); err != nil {
|
||||
t.Fatalf("WriteMigration failed: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "CREATE INDEX CONCURRENTLY IF NOT EXISTS") {
|
||||
t.Fatalf("expected CONCURRENTLY create index statement, got:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteMigration_GinIndexOnTextUsesTrigramOperatorClass(t *testing.T) {
|
||||
current := models.InitDatabase("testdb")
|
||||
currentSchema := models.InitSchema("public")
|
||||
|
||||
@@ -139,6 +139,7 @@ type CreateIndexData struct {
|
||||
IndexType string
|
||||
Columns string
|
||||
Unique bool
|
||||
Concurrent bool
|
||||
}
|
||||
|
||||
// CreateForeignKeyData contains data for create foreign key template
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CREATE {{if .Unique}}UNIQUE {{end}}INDEX IF NOT EXISTS {{quote_ident .IndexName}}
|
||||
CREATE {{if .Unique}}UNIQUE {{end}}INDEX {{if .Concurrent}}CONCURRENTLY {{end}}IF NOT EXISTS {{quote_ident .IndexName}}
|
||||
ON {{qual_table .SchemaName .TableName}} USING {{.IndexType}} ({{.Columns}});
|
||||
@@ -1097,8 +1097,13 @@ func (w *Writer) writeIndexes(schema *models.Schema) error {
|
||||
whereClause = fmt.Sprintf(" WHERE %s", index.Where)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w.writer, "CREATE %sINDEX IF NOT EXISTS %s\n",
|
||||
unique, indexName)
|
||||
concurrently := ""
|
||||
if index.Concurrent {
|
||||
concurrently = "CONCURRENTLY "
|
||||
}
|
||||
|
||||
fmt.Fprintf(w.writer, "CREATE %sINDEX %sIF NOT EXISTS %s\n",
|
||||
unique, concurrently, indexName)
|
||||
fmt.Fprintf(w.writer, " ON %s USING %s (%s)%s;\n\n",
|
||||
w.qualTable(schema.SQLName(), table.SQLName()), indexType, strings.Join(columnExprs, ", "), whereClause)
|
||||
}
|
||||
|
||||
@@ -87,6 +87,41 @@ func TestWriteDatabase(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteDatabase_ConcurrentIndex(t *testing.T) {
|
||||
db := models.InitDatabase("testdb")
|
||||
schema := models.InitSchema("public")
|
||||
|
||||
table := models.InitTable("users", "public")
|
||||
|
||||
emailCol := models.InitColumn("email", "users", "public")
|
||||
emailCol.Type = "text"
|
||||
table.Columns["email"] = emailCol
|
||||
|
||||
concurrentIndex := &models.Index{
|
||||
Name: "idx_users_email",
|
||||
Columns: []string{"email"},
|
||||
Concurrent: true,
|
||||
}
|
||||
table.Indexes["idx_users_email"] = concurrentIndex
|
||||
|
||||
schema.Tables = append(schema.Tables, table)
|
||||
db.Schemas = append(db.Schemas, schema)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer := NewWriter(&writers.WriterOptions{})
|
||||
writer.writer = &buf
|
||||
|
||||
if err := writer.WriteDatabase(db); err != nil {
|
||||
t.Fatalf("WriteDatabase failed: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
|
||||
if !strings.Contains(output, "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email") {
|
||||
t.Errorf("Output missing CONCURRENTLY index creation:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteDatabase_GinIndexOnTextArrayDoesNotUseTrigramOperatorClass(t *testing.T) {
|
||||
db := models.InitDatabase("testdb")
|
||||
schema := models.InitSchema("public")
|
||||
|
||||
Reference in New Issue
Block a user