Fix SQLite migration

This commit is contained in:
Neil Alexander 2022-11-11 13:02:32 +00:00
parent 7e7edb4885
commit e4e42797ee
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944

View file

@ -4,9 +4,11 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"strings"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/sirupsen/logrus"
) )
var serverNamesTables = []string{ var serverNamesTables = []string{
@ -28,6 +30,13 @@ var serverNamesDropPK = []string{
"userapi_profiles", "userapi_profiles",
} }
// These indices are out of date so let's drop them. They will get recreated
// automatically.
var serverNamesDropIndex = []string{
"userapi_pusher_localpart_idx",
"userapi_pusher_app_id_pushkey_localpart_idx",
}
// I know what you're thinking: you're wondering "why doesn't this use $1 // I know what you're thinking: you're wondering "why doesn't this use $1
// and pass variadic parameters to ExecContext?" — the answer is because // and pass variadic parameters to ExecContext?" — the answer is because
// PostgreSQL doesn't expect the table name to be specified as a substituted // PostgreSQL doesn't expect the table name to be specified as a substituted
@ -36,37 +45,58 @@ var serverNamesDropPK = []string{
func UpServerNames(ctx context.Context, tx *sql.Tx, serverName gomatrixserverlib.ServerName) error { func UpServerNames(ctx context.Context, tx *sql.Tx, serverName gomatrixserverlib.ServerName) error {
for _, table := range serverNamesTables { for _, table := range serverNamesTables {
q := fmt.Sprintf( q := fmt.Sprintf(
"SELECT COUNT(name) FROM sqlite_schema WHERE type='table' AND name=%s;", "SELECT COUNT(*) FROM pragma_table_info(%s) WHERE name='server_name'",
pq.QuoteIdentifier(table), pq.QuoteIdentifier(table),
) )
var c int var c int
if err := tx.QueryRowContext(ctx, q).Scan(&c); err != nil || c == 0 { if err := tx.QueryRowContext(ctx, q).Scan(&c); err != nil || c == 1 {
logrus.Infof("Table %s already has column, skipping", table)
continue continue
} }
q = fmt.Sprintf( logrus.Infof("Table %s add column", table)
"ALTER TABLE %s ADD COLUMN IF NOT EXISTS server_name TEXT NOT NULL DEFAULT '';", if c == 0 {
pq.QuoteIdentifier(table), q = fmt.Sprintf(
) "ALTER TABLE %s ADD COLUMN server_name TEXT NOT NULL DEFAULT '';",
if _, err := tx.ExecContext(ctx, q); err != nil { pq.QuoteIdentifier(table),
return fmt.Errorf("add server name to %q error: %w", table, err) )
if _, err := tx.ExecContext(ctx, q); err != nil {
return fmt.Errorf("add server name to %q error: %w", table, err)
}
} }
} }
for _, table := range serverNamesDropPK { for _, table := range serverNamesDropPK {
q := fmt.Sprintf( q := fmt.Sprintf(
"SELECT COUNT(name) FROM sqlite_schema WHERE type='table' AND name=%s;", "SELECT COUNT(name), sql FROM sqlite_schema WHERE type='table' AND name=%s;",
pq.QuoteIdentifier(table), pq.QuoteIdentifier(table),
) )
var c int var c int
if err := tx.QueryRowContext(ctx, q).Scan(&c); err != nil || c == 0 { var sql string
if err := tx.QueryRowContext(ctx, q).Scan(&c, &sql); err != nil || c == 0 {
continue continue
} }
q = fmt.Sprintf( q = fmt.Sprintf(`
"ALTER TABLE %s DROP PRIMARY KEY;", %s; -- create temporary table
pq.QuoteIdentifier(table), INSERT INTO %s SELECT * FROM %s; -- copy data
DROP TABLE %s; -- drop original table
ALTER TABLE %s RENAME TO %s; -- rename new table
`,
strings.Replace(sql, table, table+"_tmp", 1), // create temporary table
table+"_tmp", table, // copy data
table, // drop original table
table+"_tmp", table, // rename new table
) )
if _, err := tx.ExecContext(ctx, q); err != nil { if _, err := tx.ExecContext(ctx, q); err != nil {
return fmt.Errorf("drop PK from %q error: %w", table, err) return fmt.Errorf("drop PK from %q error: %w", table, err)
} }
} }
for _, index := range serverNamesDropIndex {
q := fmt.Sprintf(
"DROP INDEX IF EXISTS %s;",
pq.QuoteIdentifier(index),
)
if _, err := tx.ExecContext(ctx, q); err != nil {
return fmt.Errorf("drop index %q error: %w", index, err)
}
}
return nil return nil
} }