Skip to content

Commit fd9072d

Browse files
committed
feat: multiple database support
1 parent 2c3ed81 commit fd9072d

File tree

7 files changed

+1513
-119
lines changed

7 files changed

+1513
-119
lines changed

internal/cli/cli.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,13 @@ func Parse() {
168168
printErrorAndExit(fmt.Errorf("unable to generate javascript bundle: %v", err))
169169
}
170170

171+
kind, err := database.DetectKind(config.Opts.DatabaseURL())
172+
if err != nil {
173+
printErrorAndExit(fmt.Errorf("unable to parse database kind: %v", err))
174+
}
175+
171176
db, err := database.NewConnectionPool(
177+
kind,
172178
config.Opts.DatabaseURL(),
173179
config.Opts.DatabaseMinConns(),
174180
config.Opts.DatabaseMaxConns(),
@@ -179,14 +185,14 @@ func Parse() {
179185
}
180186
defer db.Close()
181187

182-
store := storage.NewStorage(db)
188+
store := storage.NewStorage(kind, db)
183189

184190
if err := store.Ping(); err != nil {
185191
printErrorAndExit(err)
186192
}
187193

188194
if flagMigrate {
189-
if err := database.Migrate(db); err != nil {
195+
if err := database.Migrate(kind, db); err != nil {
190196
printErrorAndExit(err)
191197
}
192198
return
@@ -228,12 +234,12 @@ func Parse() {
228234

229235
// Run migrations and start the daemon.
230236
if config.Opts.RunMigrations() {
231-
if err := database.Migrate(db); err != nil {
237+
if err := database.Migrate(kind, db); err != nil {
232238
printErrorAndExit(err)
233239
}
234240
}
235241

236-
if err := database.IsSchemaUpToDate(db); err != nil {
242+
if err := database.IsSchemaUpToDate(kind, db); err != nil {
237243
printErrorAndExit(err)
238244
}
239245

internal/database/migrations.go renamed to internal/database/cockroach.go

Lines changed: 70 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99
"miniflux.app/v2/internal/crypto"
1010
)
1111

12-
var schemaVersion = len(migrations)
12+
var cockroachSchemaVersion = len(cockroachMigrations)
1313

1414
// Order is important. Add new migrations at the end of the list.
15-
var migrations = [...]func(tx *sql.Tx) error{
15+
var cockroachMigrations = []Migration{
1616
func(tx *sql.Tx) (err error) {
1717
sql := `
1818
CREATE TABLE schema_version (
@@ -123,13 +123,7 @@ var migrations = [...]func(tx *sql.Tx) error{
123123
return err
124124
},
125125
func(tx *sql.Tx) (err error) {
126-
sql := `
127-
CREATE EXTENSION IF NOT EXISTS hstore;
128-
ALTER TABLE users ADD COLUMN extra hstore;
129-
CREATE INDEX users_extra_idx ON users using gin(extra);
130-
`
131-
_, err = tx.Exec(sql)
132-
return err
126+
return nil
133127
},
134128
func(tx *sql.Tx) (err error) {
135129
sql := `
@@ -263,11 +257,7 @@ var migrations = [...]func(tx *sql.Tx) error{
263257
return err
264258
},
265259
func(tx *sql.Tx) (err error) {
266-
sql := `
267-
ALTER TABLE user_sessions ALTER COLUMN ip SET DATA TYPE inet using ip::inet;
268-
`
269-
_, err = tx.Exec(sql)
270-
return err
260+
return nil
271261
},
272262
func(tx *sql.Tx) (err error) {
273263
sql := `
@@ -281,24 +271,20 @@ var migrations = [...]func(tx *sql.Tx) error{
281271
func(tx *sql.Tx) (err error) {
282272
sql := `
283273
ALTER TABLE entries ADD COLUMN document_vectors tsvector;
284-
UPDATE entries SET document_vectors = to_tsvector(substring(title || ' ' || coalesce(content, '') for 1000000));
285-
CREATE INDEX document_vectors_idx ON entries USING gin(document_vectors);
286274
`
287275
_, err = tx.Exec(sql)
288276
return err
289277
},
290278
func(tx *sql.Tx) (err error) {
291-
sql := `ALTER TABLE feeds ADD COLUMN user_agent text default ''`
279+
sql := `
280+
UPDATE entries SET document_vectors = to_tsvector(substring(title || ' ' || title || ' ' || coalesce(content, '') for 1000000));
281+
CREATE INDEX document_vectors_idx ON entries USING gin(document_vectors);
282+
`
292283
_, err = tx.Exec(sql)
293284
return err
294285
},
295286
func(tx *sql.Tx) (err error) {
296-
sql := `
297-
UPDATE
298-
entries
299-
SET
300-
document_vectors = setweight(to_tsvector(substring(coalesce(title, '') for 1000000)), 'A') || setweight(to_tsvector(substring(coalesce(content, '') for 1000000)), 'B')
301-
`
287+
sql := `ALTER TABLE feeds ADD COLUMN user_agent text default ''`
302288
_, err = tx.Exec(sql)
303289
return err
304290
},
@@ -325,6 +311,12 @@ var migrations = [...]func(tx *sql.Tx) error{
325311
func(tx *sql.Tx) (err error) {
326312
sql := `
327313
ALTER TABLE entries ADD COLUMN changed_at timestamp with time zone;
314+
`
315+
_, err = tx.Exec(sql)
316+
return err
317+
},
318+
func(tx *sql.Tx) (err error) {
319+
sql := `
328320
UPDATE entries SET changed_at = published_at;
329321
ALTER TABLE entries ALTER COLUMN changed_at SET not null;
330322
`
@@ -350,19 +342,31 @@ var migrations = [...]func(tx *sql.Tx) error{
350342
func(tx *sql.Tx) (err error) {
351343
sql := `
352344
ALTER TABLE entries ADD COLUMN share_code text not null default '';
345+
`
346+
_, err = tx.Exec(sql)
347+
return err
348+
},
349+
func(tx *sql.Tx) (err error) {
350+
sql := `
353351
CREATE UNIQUE INDEX entries_share_code_idx ON entries USING btree(share_code) WHERE share_code <> '';
354352
`
355353
_, err = tx.Exec(sql)
356354
return err
357355
},
358356
func(tx *sql.Tx) (err error) {
359-
sql := `CREATE INDEX enclosures_user_entry_url_idx ON enclosures(user_id, entry_id, md5(url))`
357+
sql := `CREATE INDEX enclosures_user_entry_url_idx ON enclosures(user_id, entry_id, url);`
360358
_, err = tx.Exec(sql)
361359
return err
362360
},
363361
func(tx *sql.Tx) (err error) {
364362
sql := `
365363
ALTER TABLE feeds ADD COLUMN next_check_at timestamp with time zone default now();
364+
`
365+
_, err = tx.Exec(sql)
366+
return err
367+
},
368+
func(tx *sql.Tx) (err error) {
369+
sql := `
366370
CREATE INDEX entries_user_feed_idx ON entries (user_id, feed_id);
367371
`
368372
_, err = tx.Exec(sql)
@@ -430,6 +434,12 @@ var migrations = [...]func(tx *sql.Tx) error{
430434
func(tx *sql.Tx) (err error) {
431435
sql := `
432436
ALTER TABLE entries ADD COLUMN created_at timestamp with time zone not null default now();
437+
`
438+
_, err = tx.Exec(sql)
439+
return err
440+
},
441+
func(tx *sql.Tx) (err error) {
442+
sql := `
433443
UPDATE entries SET created_at = published_at;
434444
`
435445
_, err = tx.Exec(sql)
@@ -442,62 +452,9 @@ var migrations = [...]func(tx *sql.Tx) error{
442452
ADD column google_id text not null default '',
443453
ADD column openid_connect_id text not null default ''
444454
`)
445-
if err != nil {
446-
return err
447-
}
448-
449-
_, err = tx.Exec(`
450-
DECLARE my_cursor CURSOR FOR
451-
SELECT
452-
id,
453-
COALESCE(extra->'custom_css', '') as custom_css,
454-
COALESCE(extra->'google_id', '') as google_id,
455-
COALESCE(extra->'oidc_id', '') as oidc_id
456-
FROM users
457-
FOR UPDATE
458-
`)
459-
if err != nil {
460-
return err
461-
}
462-
defer tx.Exec("CLOSE my_cursor")
463-
464-
for {
465-
var (
466-
userID int64
467-
customStylesheet string
468-
googleID string
469-
oidcID string
470-
)
471-
472-
if err := tx.QueryRow(`FETCH NEXT FROM my_cursor`).Scan(&userID, &customStylesheet, &googleID, &oidcID); err != nil {
473-
if err == sql.ErrNoRows {
474-
break
475-
}
476-
return err
477-
}
478-
479-
_, err := tx.Exec(
480-
`UPDATE
481-
users
482-
SET
483-
stylesheet=$2,
484-
google_id=$3,
485-
openid_connect_id=$4
486-
WHERE
487-
id=$1
488-
`,
489-
userID, customStylesheet, googleID, oidcID)
490-
if err != nil {
491-
return err
492-
}
493-
}
494-
495455
return err
496456
},
497457
func(tx *sql.Tx) (err error) {
498-
if _, err = tx.Exec(`ALTER TABLE users DROP COLUMN extra;`); err != nil {
499-
return err
500-
}
501458
_, err = tx.Exec(`
502459
CREATE UNIQUE INDEX users_google_id_idx ON users(google_id) WHERE google_id <> '';
503460
CREATE UNIQUE INDEX users_openid_connect_id_idx ON users(openid_connect_id) WHERE openid_connect_id <> '';
@@ -506,7 +463,7 @@ var migrations = [...]func(tx *sql.Tx) error{
506463
},
507464
func(tx *sql.Tx) (err error) {
508465
_, err = tx.Exec(`
509-
CREATE INDEX entries_feed_url_idx ON entries(feed_id, url) WHERE length(url) < 2000;
466+
CREATE INDEX entries_feed_url_idx ON entries(feed_id, url);
510467
CREATE INDEX entries_user_status_feed_idx ON entries(user_id, status, feed_id);
511468
CREATE INDEX entries_user_status_changed_idx ON entries(user_id, status, changed_at);
512469
`)
@@ -531,6 +488,12 @@ var migrations = [...]func(tx *sql.Tx) error{
531488
func(tx *sql.Tx) (err error) {
532489
sql := `
533490
CREATE TYPE webapp_display_mode AS enum('fullscreen', 'standalone', 'minimal-ui', 'browser');
491+
`
492+
_, err = tx.Exec(sql)
493+
return err
494+
},
495+
func(tx *sql.Tx) (err error) {
496+
sql := `
534497
ALTER TABLE users ADD COLUMN display_mode webapp_display_mode default 'standalone';
535498
`
536499
_, err = tx.Exec(sql)
@@ -566,6 +529,12 @@ var migrations = [...]func(tx *sql.Tx) error{
566529
func(tx *sql.Tx) (err error) {
567530
sql := `
568531
CREATE TYPE entry_sorting_order AS enum('published_at', 'created_at');
532+
`
533+
_, err = tx.Exec(sql)
534+
return err
535+
},
536+
func(tx *sql.Tx) (err error) {
537+
sql := `
569538
ALTER TABLE users ADD COLUMN entry_order entry_sorting_order default 'published_at';
570539
`
571540
_, err = tx.Exec(sql)
@@ -659,10 +628,21 @@ var migrations = [...]func(tx *sql.Tx) error{
659628
},
660629
func(tx *sql.Tx) (err error) {
661630
sql := `
662-
ALTER TABLE users RENAME double_tap TO gesture_nav;
663-
ALTER TABLE users
664-
ALTER COLUMN gesture_nav SET DATA TYPE text using case when gesture_nav = true then 'tap' when gesture_nav = false then 'none' end,
665-
ALTER COLUMN gesture_nav SET default 'tap';
631+
ALTER TABLE users ADD COLUMN gesture_nav text DEFAULT 'tap' NOT NULL;
632+
`
633+
_, err = tx.Exec(sql)
634+
return err
635+
},
636+
func(tx *sql.Tx) (err error) {
637+
sql := `
638+
UPDATE users SET gesture_nav = CASE WHEN double_tap THEN 'tap' ELSE 'none' END;
639+
`
640+
_, err = tx.Exec(sql)
641+
return err
642+
},
643+
func(tx *sql.Tx) (err error) {
644+
sql := `
645+
ALTER TABLE users DROP COLUMN double_tap;
666646
`
667647
_, err = tx.Exec(sql)
668648
return err
@@ -710,7 +690,7 @@ var migrations = [...]func(tx *sql.Tx) error{
710690
}
711691

712692
// Create unique index
713-
_, err = tx.Exec(`CREATE UNIQUE INDEX enclosures_user_entry_url_unique_idx ON enclosures(user_id, entry_id, md5(url))`)
693+
_, err = tx.Exec(`CREATE UNIQUE INDEX enclosures_user_entry_url_unique_idx ON enclosures(user_id, entry_id, url);`)
714694
if err != nil {
715695
return err
716696
}
@@ -1045,6 +1025,12 @@ var migrations = [...]func(tx *sql.Tx) error{
10451025
func(tx *sql.Tx) (err error) {
10461026
sql := `
10471027
ALTER TABLE icons ADD COLUMN external_id text default '';
1028+
`
1029+
_, err = tx.Exec(sql)
1030+
return err
1031+
},
1032+
func(tx *sql.Tx) (err error) {
1033+
sql := `
10481034
CREATE UNIQUE INDEX icons_external_id_idx ON icons USING btree(external_id) WHERE external_id <> '';
10491035
`
10501036
_, err = tx.Exec(sql)
@@ -1079,7 +1065,6 @@ var migrations = [...]func(tx *sql.Tx) error{
10791065
UPDATE icons SET external_id = $1 WHERE id = $2
10801066
`,
10811067
crypto.GenerateRandomStringHex(20), id)
1082-
10831068
if err != nil {
10841069
return err
10851070
}
@@ -1160,7 +1145,7 @@ var migrations = [...]func(tx *sql.Tx) error{
11601145
},
11611146
// This migration replaces deprecated timezones by their equivalent on Debian Trixie.
11621147
func(tx *sql.Tx) (err error) {
1163-
var deprecatedTimeZoneMap = map[string]string{
1148+
deprecatedTimeZoneMap := map[string]string{
11641149
// Africa
11651150
"Africa/Asmera": "Africa/Asmara",
11661151

0 commit comments

Comments
 (0)