Skip to content

Commit 06bc285

Browse files
committed
Add migrations to use uast nullable cols in FilePairs
1 parent fa2f839 commit 06bc285

File tree

5 files changed

+325
-0
lines changed

5 files changed

+325
-0
lines changed

cli/migrations/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
### Disclaimer:
2+
3+
These scripts are <u>**not real migrations**</u>. There is no up/down rules. These scripts are not production ready. The idempotence of these scripts is not guaranteed at all..
4+
5+
These scripts were needed to migrate the Database between different states.
6+
7+
- [Add UAST nullable columns](command-UAST-add-columns.go)
8+
```shell
9+
# prepares the current "internal.db". Adds "uast_a" and "uast_b" nulable cols
10+
go run cli/migrations/*.go uast-add-cols internal.db
11+
```
12+
13+
- [Add UAST to a database, reading from other Database](command-UAST-import-from-source-db.go)
14+
```shell
15+
# import UASTs into "internal.db" reading from "source.db"
16+
go run cli/migrations/*.go uast-import internal.db source.db
17+
```
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/src-d/code-annotation/server/dbutil"
8+
)
9+
10+
type uastColsCmd struct {
11+
commandDesc
12+
Args struct {
13+
InternalDBPath string `description:"filepath to the internal SQLite database"`
14+
} `positional-args:"yes" required:"yes"`
15+
}
16+
17+
var uastColsOpts = uastColsCmd{
18+
commandDesc: commandDesc{
19+
name: "uast-add-cols",
20+
shortDesc: "Add uast BLOB columns",
21+
longDesc: "Adds 'uast_a' and 'uast_b' BLOB columns to the sqlite://InternalDBPath database",
22+
},
23+
}
24+
25+
const (
26+
leftFile = "a"
27+
rightFile = "b"
28+
29+
addUastQuery = "ALTER TABLE file_pairs ADD COLUMN uast_%s BLOB"
30+
)
31+
32+
func (c *uastColsCmd) Execute(args []string) error {
33+
internalDb, err := dbutil.Open(fmt.Sprintf(sqliteDSN, c.Args.InternalDBPath), false)
34+
if err != nil {
35+
log.Fatal(err)
36+
}
37+
38+
defer internalDb.Close()
39+
40+
if err := addColumn(internalDb, leftFile); err != nil {
41+
return err
42+
}
43+
44+
if err := addColumn(internalDb, rightFile); err != nil {
45+
return err
46+
}
47+
48+
fmt.Println("New BLOB columns 'uast_a' and 'uast_b' were added")
49+
return nil
50+
}
51+
52+
func addColumn(db dbutil.DB, side string) error {
53+
if _, err := db.Exec(fmt.Sprintf(addUastQuery, side)); err != nil {
54+
return err
55+
}
56+
57+
return nil
58+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/src-d/code-annotation/server/dbutil"
8+
)
9+
10+
type uastImportCmd struct {
11+
commandDesc
12+
Args struct {
13+
InternalDBPath string `description:"filepath to the internal SQLite database"`
14+
SourceDBPath string `description:"filepath to the SQLite database containing the UAST to import"`
15+
} `positional-args:"yes" required:"yes"`
16+
}
17+
18+
var uastImportOpts = uastImportCmd{
19+
commandDesc: commandDesc{
20+
name: "uast-import",
21+
shortDesc: "Import UASTs",
22+
longDesc: "Adds UASTs to the sqlite://InternalDBPath database reading from sqlite://SourceDBPath database",
23+
},
24+
}
25+
26+
const (
27+
filePairsWithoutUastQuery = `SELECT
28+
blob_id_a, uast_a IS NOT NULL as hasUastA, blob_id_b, uast_b IS NOT NULL as hasUastB
29+
FROM file_pairs
30+
WHERE hasUastA = 0 or hasUastB = 0`
31+
uastByBlobIDQuery = `SELECT uast_%s
32+
FROM files
33+
WHERE blob_id_%s = CAST($1 AS BLOB)
34+
LIMIT 1`
35+
updateByBlobIDQuery = `UPDATE file_pairs
36+
SET uast_%s = $1
37+
WHERE blob_id_%s = $2 and uast_%s IS NULL`
38+
indexAddBlobID = `CREATE INDEX blob_id_%s ON file_pairs (blob_id_%s);`
39+
)
40+
41+
type file struct {
42+
side string
43+
blobID string
44+
}
45+
46+
func (c *uastImportCmd) Execute(args []string) error {
47+
internalDb, err := dbutil.Open(fmt.Sprintf(sqliteDSN, c.Args.InternalDBPath), false)
48+
if err != nil {
49+
log.Fatal(err)
50+
}
51+
52+
defer internalDb.Close()
53+
54+
sourceDb, err := dbutil.Open(fmt.Sprintf(sqliteDSN, c.Args.SourceDBPath), false)
55+
if err != nil {
56+
log.Fatal(err)
57+
}
58+
59+
defer sourceDb.Close()
60+
61+
fixSourceDb(sourceDb)
62+
63+
files, fileReadfailures := getFilesToUpdate(internalDb)
64+
65+
var rowsEdited, uastFails, uastsImported int64
66+
for _, file := range files {
67+
affectedRows, err := importUastForBlobID(internalDb, sourceDb, file.side, file.blobID)
68+
if err != nil {
69+
fmt.Println(err)
70+
uastFails++
71+
continue
72+
}
73+
74+
rowsEdited += affectedRows
75+
uastsImported++
76+
}
77+
78+
fmt.Printf(`
79+
UASTs added into the internal DB:
80+
- processed files: %d
81+
- succeeded:
82+
- imported UASTs: %d
83+
- edited rows: %d
84+
- failed: %d
85+
- FilePair read errors: %d
86+
`, len(files), uastsImported, rowsEdited, uastFails, fileReadfailures)
87+
88+
if fileReadfailures+uastFails > 0 {
89+
log.Fatal("Some rows could not be properly updated")
90+
}
91+
92+
return nil
93+
}
94+
95+
type files map[string]file
96+
97+
func (f *files) add(blobID string, side string, ignore bool) {
98+
if ignore {
99+
return
100+
}
101+
102+
if _, ok := (*f)[blobID+"_"+side]; !ok {
103+
(*f)[blobID+"_"+side] = file{side: side, blobID: blobID}
104+
}
105+
}
106+
107+
func getFilesToUpdate(internalDb dbutil.DB) (map[string]file, int64) {
108+
rows, err := internalDb.Query(filePairsWithoutUastQuery)
109+
if err != nil {
110+
log.Fatal(err)
111+
}
112+
113+
defer rows.Close()
114+
115+
filesToImport := files{}
116+
var failures int64
117+
for rows.Next() {
118+
var blobIDA, blobIDB string
119+
var hasUastA, hasUastB int
120+
err := rows.Scan(&blobIDA, &hasUastA, &blobIDB, &hasUastB)
121+
if err != nil {
122+
fmt.Printf("Failed to read row from internal DB\nerror: %v\n", err)
123+
failures++
124+
continue
125+
}
126+
127+
filesToImport.add(blobIDA, leftFile, hasUastA == 1)
128+
filesToImport.add(blobIDB, rightFile, hasUastB == 1)
129+
}
130+
131+
return filesToImport, failures
132+
}
133+
134+
func importUastForBlobID(internalDb dbutil.DB, sourceDb dbutil.DB, side string, blobID string) (int64, error) {
135+
uast, err := getUastByBlobID(sourceDb, side, blobID)
136+
if err != nil {
137+
return 0, fmt.Errorf("uast_%s could not be retrieved for blobID#%s; %s", side, blobID, err)
138+
}
139+
140+
return setUastToBlobID(internalDb, side, blobID, uast)
141+
}
142+
143+
func getUastByBlobID(sourceDb dbutil.DB, side string, blobID string) ([]byte, error) {
144+
var uast []byte
145+
if err := sourceDb.QueryRow(fmt.Sprintf(uastByBlobIDQuery, side, side), blobID).Scan(&uast); err != nil {
146+
return nil, err
147+
}
148+
149+
return uast, nil
150+
}
151+
152+
func setUastToBlobID(internalDb dbutil.DB, side string, blobID string, uast []byte) (int64, error) {
153+
res, err := internalDb.Exec(fmt.Sprintf(updateByBlobIDQuery, side, side, side), uast, blobID)
154+
if err != nil {
155+
return 0, fmt.Errorf("uast_%s could not be saved for blobID#%s; %s", side, blobID, err)
156+
}
157+
158+
rows, _ := res.RowsAffected()
159+
if rows == 0 {
160+
return 0, fmt.Errorf("no uast_%s to be imported for blobID#%s", side, blobID)
161+
}
162+
163+
return rows, nil
164+
}
165+
166+
func fixSourceDb(sourceDb dbutil.DB) error {
167+
if _, err := sourceDb.Exec(fmt.Sprintf(indexAddBlobID, leftFile, leftFile)); err != nil {
168+
return fmt.Errorf("can not create index over blob_id_%s; %s", leftFile, err)
169+
}
170+
171+
if _, err := sourceDb.Exec(fmt.Sprintf(indexAddBlobID, rightFile, rightFile)); err != nil {
172+
return fmt.Errorf("can not create index over blob_id_%s; %s", rightFile, err)
173+
}
174+
175+
return nil
176+
}

cli/migrations/command.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
_ "net/http/pprof"
5+
)
6+
7+
// Command is a runnable command
8+
type Command interface {
9+
Name() string
10+
ShortDesc() string
11+
LongDesc() string
12+
Execute(args []string) error
13+
}
14+
15+
type commandDesc struct {
16+
name string
17+
shortDesc string
18+
longDesc string
19+
}
20+
21+
func (c *commandDesc) Name() string {
22+
return c.name
23+
}
24+
25+
func (c *commandDesc) ShortDesc() string {
26+
return c.shortDesc
27+
}
28+
29+
func (c *commandDesc) LongDesc() string {
30+
return c.longDesc
31+
}

cli/migrations/main.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
_ "net/http/pprof"
6+
"os"
7+
8+
"github.com/jessevdk/go-flags"
9+
)
10+
11+
const (
12+
description string = "Migrate internal database"
13+
sqliteDSN = "sqlite://%s"
14+
)
15+
16+
func main() {
17+
parser := flags.NewParser(nil, flags.Default)
18+
addCommand(parser, &uastColsOpts)
19+
addCommand(parser, &uastImportOpts)
20+
parse(parser, description)
21+
}
22+
23+
func addCommand(parser *flags.Parser, command Command) {
24+
if _, err := parser.AddCommand(command.Name(), command.ShortDesc(), command.LongDesc(), command); err != nil {
25+
panic(err)
26+
}
27+
}
28+
29+
func parse(parser *flags.Parser, description string) {
30+
parser.LongDescription = description
31+
if _, err := parser.Parse(); err != nil {
32+
if err, ok := err.(*flags.Error); ok {
33+
if err.Type == flags.ErrHelp {
34+
os.Exit(0)
35+
}
36+
37+
fmt.Println()
38+
parser.WriteHelp(os.Stdout)
39+
}
40+
41+
os.Exit(1)
42+
}
43+
}

0 commit comments

Comments
 (0)