-
Notifications
You must be signed in to change notification settings - Fork 4
/
rm_cmd.go
137 lines (127 loc) · 3.31 KB
/
rm_cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package ddl
import (
"database/sql"
"flag"
"fmt"
"io"
"os"
"strconv"
"strings"
)
// RmCmd implements the `sqddl rm` subcommand.
type RmCmd struct {
// (Required) DB is the database.
DB *sql.DB
// (Required) Dialect is the database dialect.
Dialect string
// Filenames specifies the list of files to be removed from the
// history table.
Filenames []string
// Stderr specifies the command's standard error. If nil, the command
// writes to os.Stderr.
Stderr io.Writer
// HistoryTable is the name of the migration history table. If empty, the
// default history table name will be "sqddl_history".
HistoryTable string
db string // -db flag.
}
// RmCommand creates a new RmCmd with the given arguments.
//
// sqddl rm -db <DATABASE_URL> [FILENAMES...]
//
// RmCommand(
// "-db", "postgres://user:pass@localhost:5432/sakila",
// "-dir", "./migrations",
// "02_sakila.sql", 04_extras.sql",
// )
func RmCommand(args ...string) (*RmCmd, error) {
var cmd RmCmd
var dir string
flagset := flag.NewFlagSet("", flag.ContinueOnError)
flagset.StringVar(&cmd.db, "db", "", "(required) Database URL/DSN.")
flagset.StringVar(&dir, "dir", "", "Migration directory.")
flagset.StringVar(&cmd.HistoryTable, "history-table", "sqddl_history", "Name of migration history table.")
flagset.Usage = func() {
fmt.Fprint(flagset.Output(), `Usage:
sqddl rm -db <DATABASE_URL> [FILENAMES...]
sqddl rm -db 'postgres://user:pass@localhost:5432/sakila'
sqddl rm -db 'postgres://user:pass@localhost:5432/sakila' 01_init.sql 02_data.sql
Flags:
`)
flagset.PrintDefaults()
}
err := flagset.Parse(args)
if err != nil {
return nil, err
}
if cmd.db == "" {
return nil, fmt.Errorf("-db empty or not provided")
}
var driverName, dsn string
cmd.Dialect, driverName, dsn = NormalizeDSN(cmd.db)
if cmd.Dialect == "" {
return nil, fmt.Errorf("could not identity dialect for -db %q", cmd.db)
}
cmd.DB, err = sql.Open(driverName, dsn)
if err != nil {
return nil, err
}
cmd.Filenames = flagset.Args()
for i, filename := range cmd.Filenames {
cmd.Filenames[i] = normalizeFilename(filename, dir)
}
return &cmd, nil
}
// Run runs the RmCmd.
func (cmd *RmCmd) Run() error {
if cmd.DB == nil {
return fmt.Errorf("nil DB")
}
if cmd.Dialect == "" {
return fmt.Errorf("empty Dialect")
}
if cmd.Stderr == nil {
cmd.Stderr = os.Stderr
}
if cmd.HistoryTable == "" {
cmd.HistoryTable = "sqddl_history"
}
if cmd.db != "" {
defer cmd.DB.Close()
}
if len(cmd.Filenames) == 0 {
fmt.Fprintln(cmd.Stderr, "no filenames specified")
return nil
}
exists, err := historyTableExists(cmd.Dialect, cmd.DB, cmd.HistoryTable)
if err != nil {
return err
}
if !exists {
fmt.Fprintln(cmd.Stderr, "0 rows affected")
return nil
}
var b strings.Builder
b.WriteString("DELETE FROM " + QuoteIdentifier(cmd.Dialect, cmd.HistoryTable) + " WHERE filename IN (")
for i, filename := range cmd.Filenames {
if i > 0 {
b.WriteString(", ")
}
b.WriteString("'" + EscapeQuote(filename, '\'') + "'")
}
b.WriteString(")")
result, err := cmd.DB.Exec(b.String())
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 1 {
fmt.Fprintln(cmd.Stderr, "1 row affected")
} else {
fmt.Fprintln(cmd.Stderr, strconv.FormatInt(rowsAffected, 10)+" rows affected")
}
return nil
}