Skip to content

Commit 535abb3

Browse files
authored
New DBAction for creating index concurrently (#939)
Relates to #742
1 parent 063a244 commit 535abb3

File tree

2 files changed

+88
-47
lines changed

2 files changed

+88
-47
lines changed

pkg/migrations/dbactions.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,80 @@ func (a *dropFunctionAction) Execute(ctx context.Context) error {
220220
return err
221221
}
222222

223+
type createIndexConcurrentlyAction struct {
224+
conn db.DB
225+
table string
226+
name string
227+
method string
228+
unique bool
229+
columns map[string]IndexField
230+
storageParameters string
231+
predicate string
232+
}
233+
234+
func NewCreateIndexConcurrentlyAction(conn db.DB, table, name, method string, unique bool, columns map[string]IndexField, storageParameters, predicate string) *createIndexConcurrentlyAction {
235+
return &createIndexConcurrentlyAction{
236+
conn: conn,
237+
table: table,
238+
name: name,
239+
method: method,
240+
unique: unique,
241+
columns: columns,
242+
storageParameters: storageParameters,
243+
predicate: predicate,
244+
}
245+
}
246+
247+
func (a *createIndexConcurrentlyAction) Execute(ctx context.Context) error {
248+
stmtFmt := "CREATE INDEX CONCURRENTLY %s ON %s"
249+
if a.unique {
250+
stmtFmt = "CREATE UNIQUE INDEX CONCURRENTLY %s ON %s"
251+
}
252+
stmt := fmt.Sprintf(stmtFmt,
253+
pq.QuoteIdentifier(a.name),
254+
pq.QuoteIdentifier(a.table))
255+
256+
if a.method != "" {
257+
stmt += fmt.Sprintf(" USING %s", a.method)
258+
}
259+
260+
colSQLs := make([]string, 0, len(a.columns))
261+
for columnName, settings := range a.columns {
262+
colSQL := pq.QuoteIdentifier(columnName)
263+
// deparse collations
264+
if settings.Collate != "" {
265+
colSQL += " COLLATE " + settings.Collate
266+
}
267+
// deparse operator classes and their parameters
268+
if settings.Opclass != nil {
269+
colSQL += " " + settings.Opclass.Name
270+
if len(settings.Opclass.Params) > 0 {
271+
colSQL += " " + strings.Join(settings.Opclass.Params, ", ")
272+
}
273+
}
274+
// deparse sort order of the index column
275+
if settings.Sort != "" {
276+
colSQL += " " + string(settings.Sort)
277+
}
278+
// deparse nulls order of the index column
279+
if settings.Nulls != nil {
280+
colSQL += " " + string(*settings.Nulls)
281+
}
282+
colSQLs = append(colSQLs, colSQL)
283+
}
284+
stmt += fmt.Sprintf(" (%s)", strings.Join(colSQLs, ", "))
285+
286+
if a.storageParameters != "" {
287+
stmt += fmt.Sprintf(" WITH (%s)", a.storageParameters)
288+
}
289+
290+
if a.predicate != "" {
291+
stmt += fmt.Sprintf(" WHERE %s", a.predicate)
292+
}
293+
_, err := a.conn.ExecContext(ctx, stmt)
294+
return err
295+
}
296+
223297
// commentColumnAction is a DBAction that adds a comment to a column in a table.
224298
type commentColumnAction struct {
225299
conn db.DB

pkg/migrations/op_create_index.go

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package migrations
55
import (
66
"context"
77
"fmt"
8-
"strings"
98

109
"github.com/lib/pq"
1110

@@ -27,55 +26,23 @@ func (o *OpCreateIndex) Start(ctx context.Context, l Logger, conn db.DB, latestS
2726
return nil, TableDoesNotExistError{Name: o.Table}
2827
}
2928

30-
// create index concurrently
31-
stmtFmt := "CREATE INDEX CONCURRENTLY %s ON %s"
32-
if o.Unique {
33-
stmtFmt = "CREATE UNIQUE INDEX CONCURRENTLY %s ON %s"
29+
cols := make(map[string]IndexField, len(o.Columns))
30+
for name, settings := range map[string]IndexField(o.Columns) {
31+
physicalName := table.PhysicalColumnNamesFor(name)
32+
cols[physicalName[0]] = settings
3433
}
35-
stmt := fmt.Sprintf(stmtFmt,
36-
pq.QuoteIdentifier(o.Name),
37-
pq.QuoteIdentifier(table.Name))
3834

39-
if o.Method != "" {
40-
stmt += fmt.Sprintf(" USING %s", string(o.Method))
41-
}
42-
43-
colSQLs := make([]string, 0, len(o.Columns))
44-
for columnName, settings := range map[string]IndexField(o.Columns) {
45-
physicalName := table.PhysicalColumnNamesFor(columnName)
46-
colSQL := pq.QuoteIdentifier(physicalName[0])
47-
// deparse collations
48-
if settings.Collate != "" {
49-
colSQL += " COLLATE " + settings.Collate
50-
}
51-
// deparse operator classes and their parameters
52-
if settings.Opclass != nil {
53-
colSQL += " " + settings.Opclass.Name
54-
if len(settings.Opclass.Params) > 0 {
55-
colSQL += " " + strings.Join(settings.Opclass.Params, ", ")
56-
}
57-
}
58-
// deparse sort order of the index column
59-
if settings.Sort != "" {
60-
colSQL += " " + string(settings.Sort)
61-
}
62-
// deparse nulls order of the index column
63-
if settings.Nulls != nil {
64-
colSQL += " " + string(*settings.Nulls)
65-
}
66-
colSQLs = append(colSQLs, colSQL)
67-
}
68-
stmt += fmt.Sprintf(" (%s)", strings.Join(colSQLs, ", "))
69-
70-
if o.StorageParameters != "" {
71-
stmt += fmt.Sprintf(" WITH (%s)", o.StorageParameters)
72-
}
73-
74-
if o.Predicate != "" {
75-
stmt += fmt.Sprintf(" WHERE %s", o.Predicate)
76-
}
35+
err := NewCreateIndexConcurrentlyAction(
36+
conn,
37+
table.Name,
38+
o.Name,
39+
string(o.Method),
40+
o.Unique,
41+
cols,
42+
o.StorageParameters,
43+
o.Predicate,
44+
).Execute(ctx)
7745

78-
_, err := conn.ExecContext(ctx, stmt)
7946
return nil, err
8047
}
8148

0 commit comments

Comments
 (0)