mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-18 11:46:23 -07:00
Move filter migration out of common
This commit is contained in:
parent
eb06047055
commit
8d98bc39df
11 changed files with 478 additions and 569 deletions
|
@ -1,326 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
)
|
||||
|
||||
// AddFilterTables is used for the db version 4 upgrade to create the filter tables from the respective SQL init file
|
||||
func AddFilterTables(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *config.SQLConfig) error {
|
||||
filePath, err := getInitFilePath("initdb_" + sqlConfig.DBtype + ".sql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ba, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqlStr := commentRemover.ReplaceAllString(string(ba), " ")
|
||||
sqlArr := strings.Split(sqlStr, ";")
|
||||
|
||||
for _, stmtStr := range sqlArr {
|
||||
stmtStr = strings.TrimSpace(stmtStr)
|
||||
if !strings.HasPrefix(stmtStr, "CREATE TABLE DBPREFIXfilter") {
|
||||
continue
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx, stmtStr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MigrateFileBans migrates file checksum and image fingerprint bans to the filter table
|
||||
func MigrateFileBans(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, cfg *config.SQLConfig) error {
|
||||
fileBanTableExists, err := TableExists(ctx, db, tx, "DBPREFIXfilename_ban", cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !fileBanTableExists {
|
||||
// no filename bans to migrate (database partially migrated?)
|
||||
return nil
|
||||
}
|
||||
rows, err := db.QueryContextSQL(ctx, tx, `SELECT board_id,staff_id,staff_note,issued_at,checksum,fingerprinter,ban_ip,ban_ip_message FROM DBPREFIXfile_ban`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var fBanBoardID *int
|
||||
var fBanStaffID int
|
||||
var fBanStaffNote string
|
||||
var fBanIssuedAt time.Time
|
||||
var fBanChecksum string
|
||||
var fBanFingerprinter *string
|
||||
var fBanBanIP bool
|
||||
var fBanBanIPMessage *string
|
||||
|
||||
var matchAction string
|
||||
var detail string
|
||||
var filterID int
|
||||
var field string
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(
|
||||
&fBanBoardID, &fBanStaffID, &fBanStaffNote, &fBanIssuedAt, &fBanChecksum, &fBanFingerprinter, &fBanBanIP, &fBanBanIPMessage,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if fBanBanIP {
|
||||
matchAction = "ban"
|
||||
} else {
|
||||
matchAction = "reject"
|
||||
}
|
||||
if fBanBanIPMessage == nil {
|
||||
detail = ""
|
||||
} else {
|
||||
detail = *fBanBanIPMessage
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilters(staff_id, staff_note, issued_at, match_action, match_detail, is_active) VALUES(?,?,?,?,?,?)`,
|
||||
fBanStaffID, fBanStaffNote, fBanIssuedAt, matchAction, detail, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = db.QueryRowContextSQL(ctx, tx, `SELECT MAX(id) FROM DBPREFIXfilters`, nil, []any{&filterID}); err != nil {
|
||||
return err
|
||||
}
|
||||
if fBanBoardID != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_boards(filter_id, board_id) VALUES(?,?)`, filterID, *fBanBoardID,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fBanFingerprinter != nil {
|
||||
field = *fBanFingerprinter
|
||||
}
|
||||
if field == "" {
|
||||
field = "checksum"
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_conditions(filter_id,match_mode,search,field) VALUES(?,?,?,?)`, filterID, gcsql.ExactMatch, fBanChecksum, field,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rows.Close()
|
||||
}
|
||||
|
||||
// MigrateFilenameBans migrates filename bans to the filter table
|
||||
func MigrateFilenameBans(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, cfg *config.SQLConfig) error {
|
||||
filenameBanTableExists, err := TableExists(ctx, db, tx, "DBPREFIXfilename_ban", cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !filenameBanTableExists {
|
||||
// no filename bans to migrate (database partially migrated?)
|
||||
return nil
|
||||
}
|
||||
rows, err := db.QueryContextSQL(ctx, tx, `SELECT board_id,staff_id,staff_note,issued_at,filename,is_regex FROM DBPREFIXfilename_ban`)
|
||||
if err != nil {
|
||||
fmt.Println("query error")
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var fnBanBoardID *int
|
||||
var fnBanStaffID int
|
||||
var fnBanStaffNote string
|
||||
var fnBanIssuedAt time.Time
|
||||
var fnBanFilename string
|
||||
var fnBanIsRegex bool
|
||||
var filterID int
|
||||
var matchMode gcsql.StringMatchMode
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(
|
||||
&fnBanBoardID, &fnBanStaffID, &fnBanStaffNote, &fnBanIssuedAt, &fnBanFilename, &fnBanIsRegex,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilters(staff_id, staff_note, issued_at, match_action, match_detail, is_active) VALUES(?,?,?,?,?,?)`,
|
||||
fnBanStaffID, fnBanStaffNote, fnBanIssuedAt, "reject", "", true,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = db.QueryRowContextSQL(ctx, tx, `SELECT MAX(id) FROM DBPREFIXfilters`, nil, []any{&filterID}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fnBanBoardID != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_boards(filter_id, board_id) VALUES(?,?)`, filterID, *fnBanBoardID,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if fnBanIsRegex {
|
||||
matchMode = gcsql.RegexMatch
|
||||
} else {
|
||||
matchMode = gcsql.SubstrMatch
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_conditions(filter_id,match_mode,search,field) VALUES(?,?,?,?)`,
|
||||
filterID, matchMode, fnBanFilename, "filename",
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rows.Close()
|
||||
}
|
||||
|
||||
// MigrateUsernameBans migrates poster name bans to the filter table
|
||||
func MigrateUsernameBans(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, cfg *config.SQLConfig) error {
|
||||
usernameBanTableExists, err := TableExists(ctx, db, tx, "DBPREFIXusername_ban", cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !usernameBanTableExists {
|
||||
// no name bans to migrate (database partially migrated?)
|
||||
return nil
|
||||
}
|
||||
rows, err := db.QueryContextSQL(ctx, tx, `SELECT board_id,staff_id,staff_note,issued_at,username,is_regex FROM DBPREFIXusername_ban`)
|
||||
if err != nil {
|
||||
fmt.Println("MigrateUsernameBans rows error")
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var unBanBoardID *int
|
||||
var unBanStaffID int
|
||||
var unBanStaffNote string
|
||||
var unBanIssuedAt time.Time
|
||||
var unBanUsername string
|
||||
var unBanIsRegex bool
|
||||
var filterID int
|
||||
var matchMode gcsql.StringMatchMode
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(
|
||||
&unBanBoardID, &unBanStaffID, &unBanStaffNote, &unBanIssuedAt, &unBanUsername, &unBanIsRegex,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilters(staff_id, staff_note, issued_at, match_action, match_detail, is_active) VALUES(?,?,?,?,?,?)`,
|
||||
unBanStaffID, unBanStaffNote, unBanIssuedAt, "reject", "", true,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = db.QueryRowContextSQL(ctx, tx, `SELECT MAX(id) FROM DBPREFIXfilters`, nil, []any{&filterID}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unBanBoardID != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_boards(filter_id, board_id) VALUES(?,?)`, filterID, *unBanBoardID,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if unBanIsRegex {
|
||||
matchMode = gcsql.RegexMatch
|
||||
} else {
|
||||
matchMode = gcsql.SubstrMatch
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_conditions(filter_id,match_mode,search,field) VALUES(?,?,?,?)`,
|
||||
filterID, matchMode, unBanUsername, "name",
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rows.Close()
|
||||
}
|
||||
|
||||
// MigrateWordfilters migrates pre-filter wordfilters to the filter table
|
||||
func MigrateWordfilters(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *config.SQLConfig) error {
|
||||
wordfiltersTableExists, err := TableExists(ctx, db, tx, "DBPREFIXwordfilters", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !wordfiltersTableExists {
|
||||
// no wordfilters to migrate (database partially migrated?)
|
||||
return nil
|
||||
}
|
||||
rows, err := db.QueryContextSQL(ctx, tx, `SELECT board_dirs, staff_id, staff_note, issued_at, search, is_regex, change_to FROM DBPREFIXwordfilters`)
|
||||
if err != nil {
|
||||
fmt.Println("MigrateWordfilters rows error")
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var boardDirsPtr *string
|
||||
var boardDirs []string
|
||||
var boardID int
|
||||
var staffID int
|
||||
var staffNote string
|
||||
var issuedAt time.Time
|
||||
var search string
|
||||
var isRegex bool
|
||||
var changeTo string
|
||||
var filterID int
|
||||
var matchMode gcsql.StringMatchMode
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(&boardDirsPtr, &staffID, &staffNote, &issuedAt, &search, &isRegex, &changeTo); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilters(staff_id, staff_note, issued_at, match_action, match_detail, is_active) VALUES(?,?,?,'replace',?,TRUE)`,
|
||||
staffID, staffNote, issuedAt, changeTo,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = db.QueryRowContextSQL(ctx, tx, `SELECT MAX(id) FROM DBPREFIXfilters`, nil, []any{&filterID}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if boardDirsPtr != nil {
|
||||
boardDirs = strings.Split(*boardDirsPtr, ",")
|
||||
for _, dir := range boardDirs {
|
||||
if dir == "" || dir == "*" {
|
||||
// treated as "all boards", but handle this in the loop just in case there's something like "a,*,b"
|
||||
// if the only value in the string is *, there will be no single board associated with the filter
|
||||
continue
|
||||
}
|
||||
err = db.QueryRowContextSQL(ctx, tx, `SELECT id FROM DBPREFIXboards WHERE dir = ?`, []any{dir}, []any{&boardID})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// board may have been deleted, skip it and don't return an error
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_boards(filter_id,board_id) VALUES(?,?)`, filterID, boardID,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if isRegex {
|
||||
matchMode = gcsql.RegexMatch
|
||||
} else {
|
||||
matchMode = gcsql.SubstrMatch
|
||||
}
|
||||
|
||||
if _, err = db.ExecContextSQL(ctx, tx,
|
||||
`INSERT INTO DBPREFIXfilter_conditions(filter_id, match_mode, search, field) VALUES(?,?,?,'body')`,
|
||||
filterID, matchMode, search,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rows.Close()
|
||||
}
|
|
@ -11,8 +11,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrInvalidSchema = errors.New("invalid database schema for old database")
|
||||
ErrUnsupportedDBType = errors.New("unsupported SQL driver, currently only MySQL and Postgres are supported")
|
||||
ErrInvalidSchema = errors.New("invalid database schema for old database")
|
||||
)
|
||||
|
||||
type MigrationError struct {
|
||||
|
@ -57,8 +56,9 @@ type DBMigrator interface {
|
|||
// will exit
|
||||
IsMigrated() (bool, error)
|
||||
|
||||
// MigrateDB migrates the imageboard data (posts, boards, etc) to the new database. It is
|
||||
// assumed that MigrateDB will handle logging any errors that occur during the migration
|
||||
// MigrateDB alters the database schema to match the new schema, then migrates the imageboard
|
||||
// data (posts, boards, etc) to the new database. It is assumed that MigrateDB will handle
|
||||
// logging any errors that occur during the migration
|
||||
MigrateDB() (bool, error)
|
||||
|
||||
// MigrateBoards gets info about the old boards in the board table and inserts each one
|
||||
|
@ -83,6 +83,6 @@ type DBMigrator interface {
|
|||
// and inserts them into the new database,
|
||||
MigrateAnnouncements() error
|
||||
|
||||
// Close closes the database if initialized
|
||||
// Close closes the database if initialized and deltes the temporary columns created
|
||||
Close() error
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
commentRemover = regexp.MustCompile("--.*\n?")
|
||||
CommentRemover = regexp.MustCompile("--.*\n?")
|
||||
)
|
||||
|
||||
// ColumnType returns a string representation of the column's data type. It does not return an error
|
||||
|
@ -82,7 +82,7 @@ func RunSQLFile(path string, db *gcsql.GCDB) error {
|
|||
return err
|
||||
}
|
||||
|
||||
sqlStr := commentRemover.ReplaceAllString(string(sqlBytes), " ")
|
||||
sqlStr := CommentRemover.ReplaceAllString(string(sqlBytes), " ")
|
||||
sqlArr := strings.Split(sqlStr, ";")
|
||||
|
||||
for _, statement := range sqlArr {
|
||||
|
@ -96,7 +96,8 @@ func RunSQLFile(path string, db *gcsql.GCDB) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func getInitFilePath(initFile string) (string, error) {
|
||||
// GetInitFilePath returns the path to the given SQL initialization file and returns an error if it is not found
|
||||
func GetInitFilePath(initFile string) (string, error) {
|
||||
filePath := gcutil.FindResource(initFile,
|
||||
path.Join("./sql", initFile),
|
||||
path.Join("/usr/local/share/gochan", initFile),
|
||||
|
@ -108,7 +109,7 @@ func getInitFilePath(initFile string) (string, error) {
|
|||
}
|
||||
|
||||
func InitDB(initFile string, db *gcsql.GCDB) error {
|
||||
filePath, err := getInitFilePath(initFile)
|
||||
filePath, err := GetInitFilePath(initFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
93
cmd/gochan-migration/internal/gcupdate/filters.go
Normal file
93
cmd/gochan-migration/internal/gcupdate/filters.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package gcupdate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/cmd/gochan-migration/internal/common"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// WordFilter represents data in the deprecated wordfilters table
|
||||
type Wordfilter struct {
|
||||
ID int // sql: `id`
|
||||
BoardDirs *string // sql: `board_dirs`
|
||||
BoardID *int // sql: `board_id`, replaced with board_dirs
|
||||
StaffID int // sql: `staff_id`
|
||||
StaffNote string // sql: `staff_note`
|
||||
IssuedAt time.Time // sql: `issued_at`
|
||||
Search string // sql: `search`
|
||||
IsRegex bool // sql: `is_regex`
|
||||
ChangeTo string // sql: `change_to`
|
||||
}
|
||||
|
||||
type filenameOrUsernameBanBase struct {
|
||||
ID int // sql: id
|
||||
BoardID *int // sql: board_id
|
||||
StaffID int // sql: staff_id
|
||||
StaffNote string // sql: staff_note
|
||||
IssuedAt time.Time // sql: issued_at
|
||||
check string // replaced with username or filename
|
||||
IsRegex bool // sql: is_regex
|
||||
}
|
||||
|
||||
// UsernameBan represents data in the deprecated username_ban table
|
||||
type UsernameBan struct {
|
||||
filenameOrUsernameBanBase
|
||||
Username string // sql: `username`
|
||||
}
|
||||
|
||||
// FilenameBan represents data in the deprecated filename_ban table
|
||||
type FilenameBan struct {
|
||||
filenameOrUsernameBanBase
|
||||
Filename string // sql: `filename`
|
||||
IsRegex bool // sql: `is_regex`
|
||||
}
|
||||
|
||||
// FileBan represents data in the deprecated file_ban table
|
||||
type FileBan struct {
|
||||
ID int // sql: `id`
|
||||
BoardID *int // sql: `board_id`
|
||||
StaffID int // sql: `staff_id`
|
||||
StaffNote string // sql: `staff_note`
|
||||
IssuedAt time.Time // sql: `issued_at`
|
||||
Checksum string // sql: `checksum`
|
||||
Fingerprinter *string // sql: `fingerprinter`
|
||||
BanIP bool // sql: `ban_ip`
|
||||
BanIPMessage *string // sql: `ban_ip_message`
|
||||
}
|
||||
|
||||
// addFilterTables is used for the db version 4 upgrade to create the filter tables from the respective SQL init file
|
||||
func addFilterTables(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *config.SQLConfig, errEv *zerolog.Event) error {
|
||||
filePath, err := common.GetInitFilePath("initdb_" + sqlConfig.DBtype + ".sql")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
errEv.Err(err).Caller(1).Send()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ba, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqlStr := common.CommentRemover.ReplaceAllString(string(ba), " ")
|
||||
sqlArr := strings.Split(sqlStr, ";")
|
||||
|
||||
for _, stmtStr := range sqlArr {
|
||||
stmtStr = strings.TrimSpace(stmtStr)
|
||||
if !strings.HasPrefix(stmtStr, "CREATE TABLE DBPREFIXfilter") {
|
||||
continue
|
||||
}
|
||||
if _, err = db.ExecContextSQL(ctx, tx, stmtStr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -3,13 +3,16 @@ package gcupdate
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/cmd/gochan-migration/internal/common"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type GCDatabaseUpdater struct {
|
||||
|
@ -46,77 +49,321 @@ func (dbu *GCDatabaseUpdater) IsMigrated() (bool, error) {
|
|||
}
|
||||
|
||||
func (dbu *GCDatabaseUpdater) MigrateDB() (bool, error) {
|
||||
errEv := common.LogError()
|
||||
|
||||
gcsql.SetDB(dbu.db)
|
||||
migrated, err := dbu.IsMigrated()
|
||||
if migrated || err != nil {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
errEv.Err(err).Caller(1).Send()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return migrated, err
|
||||
}
|
||||
if migrated {
|
||||
return migrated, nil
|
||||
}
|
||||
|
||||
sqlConfig := config.GetSQLConfig()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
tx, err := dbu.db.BeginTx(ctx, &sql.TxOptions{
|
||||
Isolation: 0,
|
||||
ReadOnly: false,
|
||||
})
|
||||
|
||||
var filterTableExists bool
|
||||
filterTableExists, err = common.TableExists(ctx, dbu.db, nil, "DBPREFIXfilters", &sqlConfig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
if !filterTableExists {
|
||||
// DBPREFIXfilters not found, create it and migrate data from DBPREFIXfile_ban, DBPREFIXfilename_ban, and DBPREFIXusername_ban,
|
||||
if err = addFilterTables(ctx, dbu.db, nil, &sqlConfig, errEv); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
switch sqlConfig.DBtype {
|
||||
case "mysql":
|
||||
err = updateMysqlDB(ctx, dbu.db, tx, &sqlConfig)
|
||||
err = updateMysqlDB(ctx, dbu, &sqlConfig, errEv)
|
||||
case "postgres":
|
||||
err = updatePostgresDB(ctx, dbu.db, tx, &sqlConfig)
|
||||
err = updatePostgresDB(ctx, dbu, &sqlConfig, errEv)
|
||||
case "sqlite3":
|
||||
err = updateSqliteDB(ctx, dbu.db, tx, &sqlConfig)
|
||||
err = updateSqliteDB(ctx, dbu, &sqlConfig, errEv)
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// commit the transaction and start a new one (to avoid deadlocks)
|
||||
if err = tx.Commit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err = ctx.Err(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
filterTableExists, err := common.TableExists(ctx, dbu.db, nil, "DBPREFIXfilters", &sqlConfig)
|
||||
if err != nil {
|
||||
if err = dbu.migrateFilters(ctx, &sqlConfig, errEv); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !filterTableExists {
|
||||
// DBPREFIXfilters not found, create it and migrate data from DBPREFIXfile_bans, DBPREFIXfilename_bans, and DBPREFIXusername_bans,
|
||||
if err = common.AddFilterTables(ctx, dbu.db, nil, &sqlConfig); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err = common.MigrateFileBans(ctx, dbu.db, nil, &sqlConfig); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err = common.MigrateFilenameBans(ctx, dbu.db, nil, &sqlConfig); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err = common.MigrateUsernameBans(ctx, dbu.db, nil, &sqlConfig); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err = common.MigrateWordfilters(ctx, dbu.db, nil, &sqlConfig); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
query := `UPDATE DBPREFIXdatabase_version SET version = ? WHERE component = 'gochan'`
|
||||
_, err = dbu.db.ExecContextSQL(ctx, nil, query, dbu.TargetDBVer)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// return false, tx.Commit()
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (dbu *GCDatabaseUpdater) migrateFilters(ctx context.Context, sqlConfig *config.SQLConfig, errEv *zerolog.Event) (err error) {
|
||||
var fileBansExist, filenameBansExist, usernameBansExist, wordfiltersExist bool
|
||||
|
||||
fileBansExist, err = common.TableExists(ctx, dbu.db, nil, "DBPREFIXfile_ban", sqlConfig)
|
||||
defer func() {
|
||||
if a := recover(); a != nil {
|
||||
err = errors.New(fmt.Sprintf("recovered: %v", a))
|
||||
errEv.Caller(4).Err(err).Send()
|
||||
errEv.Discard()
|
||||
} else if err != nil {
|
||||
errEv.Err(err).Caller(1).Send()
|
||||
errEv.Discard()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filenameBansExist, err = common.TableExists(ctx, dbu.db, nil, "DBPREFIXfilename_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
usernameBansExist, err = common.TableExists(ctx, dbu.db, nil, "DBPREFIXusername_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wordfiltersExist, err = common.TableExists(ctx, dbu.db, nil, "DBPREFIXwordfilters", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rows *sql.Rows
|
||||
if fileBansExist {
|
||||
query := "SELECT board_id, staff_id, staff_note, issued_at, checksum, fingerprinter, ban_ip, ban_ip_message FROM DBPREFIXfile_ban"
|
||||
var fingerprinterCol string
|
||||
fingerprinterCol, err = common.ColumnType(ctx, dbu.db, nil, "fingerprinter", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fingerprinterCol == "" {
|
||||
query = strings.ReplaceAll(query, "fingerprinter", "'checksum' AS fingerprinter")
|
||||
}
|
||||
|
||||
var banIPCol string
|
||||
banIPCol, err = common.ColumnType(ctx, dbu.db, nil, "ban_ip", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if banIPCol == "" {
|
||||
query = strings.ReplaceAll(query, "ban_ip", "FALSE AS ban_ip")
|
||||
}
|
||||
|
||||
rows, err = dbu.db.QueryContextSQL(ctx, nil, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var ban FileBan
|
||||
if err = rows.Scan(
|
||||
&ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Checksum,
|
||||
&ban.Fingerprinter, &ban.BanIP, &ban.BanIPMessage,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filter := &gcsql.Filter{
|
||||
StaffID: &ban.StaffID,
|
||||
StaffNote: ban.StaffNote,
|
||||
IssuedAt: ban.IssuedAt,
|
||||
MatchAction: "reject",
|
||||
IsActive: true,
|
||||
}
|
||||
if ban.BanIP {
|
||||
filter.MatchAction = "ban"
|
||||
if ban.BanIPMessage != nil {
|
||||
filter.MatchDetail = *ban.BanIPMessage
|
||||
}
|
||||
}
|
||||
var boards []int
|
||||
if ban.BoardID != nil {
|
||||
boards = append(boards, *ban.BoardID)
|
||||
}
|
||||
|
||||
condition := gcsql.FilterCondition{MatchMode: gcsql.ExactMatch, Search: ban.Checksum, Field: "checksum"}
|
||||
if ban.Fingerprinter != nil {
|
||||
condition.Field = *ban.Fingerprinter
|
||||
}
|
||||
|
||||
if err = gcsql.ApplyFilter(filter, []gcsql.FilterCondition{condition}, boards); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = rows.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if filenameBansExist {
|
||||
query := "SELECT board_id, staff_id, staff_note, issued_at, filename, is_regex FROM DBPREFIXfilename_ban"
|
||||
rows, err = dbu.db.QueryContextSQL(ctx, nil, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var ban FilenameBan
|
||||
if err = rows.Scan(
|
||||
&ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Filename, &ban.IsRegex,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filter := &gcsql.Filter{
|
||||
StaffID: &ban.StaffID,
|
||||
StaffNote: ban.StaffNote,
|
||||
IssuedAt: ban.IssuedAt,
|
||||
MatchAction: "reject",
|
||||
IsActive: true,
|
||||
MatchDetail: "File rejected",
|
||||
}
|
||||
|
||||
condition := gcsql.FilterCondition{MatchMode: gcsql.ExactMatch, Search: ban.Filename, Field: "filename"}
|
||||
if ban.IsRegex {
|
||||
condition.MatchMode = gcsql.RegexMatch
|
||||
}
|
||||
var boards []int
|
||||
if ban.BoardID != nil {
|
||||
boards = append(boards, *ban.BoardID)
|
||||
}
|
||||
if err = gcsql.ApplyFilter(filter, []gcsql.FilterCondition{condition}, boards); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = rows.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if usernameBansExist {
|
||||
query := "SELECT board_id, staff_id, staff_note, issued_at, username FROM DBPREFIXusername_ban"
|
||||
rows, err = dbu.db.QueryContextSQL(ctx, nil, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var ban UsernameBan
|
||||
if err = rows.Scan(
|
||||
&ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Username,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filter := &gcsql.Filter{
|
||||
StaffID: &ban.StaffID,
|
||||
StaffNote: ban.StaffNote,
|
||||
IssuedAt: ban.IssuedAt,
|
||||
MatchAction: "reject",
|
||||
IsActive: true,
|
||||
MatchDetail: "Username rejected",
|
||||
}
|
||||
|
||||
condition := gcsql.FilterCondition{MatchMode: gcsql.ExactMatch, Search: ban.Username, Field: "username"}
|
||||
if ban.IsRegex {
|
||||
condition.MatchMode = gcsql.RegexMatch
|
||||
}
|
||||
var boards []int
|
||||
if ban.BoardID != nil {
|
||||
boards = append(boards, *ban.BoardID)
|
||||
}
|
||||
if err = gcsql.ApplyFilter(filter, []gcsql.FilterCondition{condition}, boards); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = rows.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if wordfiltersExist {
|
||||
query := "SELECT board_dirs, staff_id, staff_note, issued_at, search, is_regex, change_to FROM DBPREFIXwordfilters"
|
||||
var boardIDCol string
|
||||
boardIDCol, err = common.ColumnType(ctx, dbu.db, nil, "board_id", "DBPREFIXwordfilters", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boardIDCol != "" {
|
||||
query = strings.ReplaceAll(query, "board_dirs", "board_id")
|
||||
}
|
||||
|
||||
rows, err = dbu.db.QueryContextSQL(ctx, nil, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var wf Wordfilter
|
||||
var boards []int
|
||||
if boardIDCol != "" {
|
||||
if err = rows.Scan(
|
||||
&wf.BoardID, &wf.StaffID, &wf.StaffNote, &wf.IssuedAt, &wf.Search, &wf.IsRegex, &wf.ChangeTo,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if wf.BoardID != nil {
|
||||
boards = append(boards, *wf.BoardID)
|
||||
}
|
||||
} else {
|
||||
if err = rows.Scan(
|
||||
&wf.BoardDirs, &wf.StaffID, &wf.StaffNote, &wf.IssuedAt, &wf.Search, &wf.IsRegex, &wf.ChangeTo,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if wf.BoardDirs != nil {
|
||||
boardDirs := strings.Split(*wf.BoardDirs, ",")
|
||||
for _, boardDir := range boardDirs {
|
||||
boardID, err := gcsql.GetBoardIDFromDir(strings.TrimSpace(boardDir))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
boards = append(boards, boardID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filter := &gcsql.Filter{
|
||||
StaffID: &wf.StaffID,
|
||||
StaffNote: wf.StaffNote,
|
||||
IssuedAt: wf.IssuedAt,
|
||||
MatchAction: "replace",
|
||||
IsActive: true,
|
||||
MatchDetail: wf.ChangeTo,
|
||||
}
|
||||
condition := gcsql.FilterCondition{MatchMode: gcsql.ExactMatch, Search: wf.Search, Field: "body"}
|
||||
if wf.IsRegex {
|
||||
condition.MatchMode = gcsql.RegexMatch
|
||||
}
|
||||
if err = gcsql.ApplyFilter(filter, []gcsql.FilterCondition{condition}, boards); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = rows.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*GCDatabaseUpdater) MigrateBoards() error {
|
||||
return gcutil.ErrNotImplemented
|
||||
}
|
||||
|
|
|
@ -6,56 +6,31 @@ import (
|
|||
|
||||
"github.com/gochan-org/gochan/cmd/gochan-migration/internal/common"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func updateMysqlDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *config.SQLConfig) error {
|
||||
func updateMysqlDB(ctx context.Context, dbu *GCDatabaseUpdater, sqlConfig *config.SQLConfig, errEv *zerolog.Event) error {
|
||||
var query string
|
||||
var dataType string
|
||||
dbName := sqlConfig.DBname
|
||||
|
||||
wordfiltersTableExists, err := common.TableExists(ctx, db, tx, "DBPREFIXwordfilters", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if wordfiltersTableExists {
|
||||
// wordfilters table is going to be migrated by the end of the update, but we want to make sure its legacy data is migrated first
|
||||
// so it can be properly merged into the filter table
|
||||
|
||||
var numConstraints int
|
||||
query = `SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE CONSTRAINT_NAME = 'wordfilters_board_id_fk'
|
||||
AND TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'DBPREFIXwordfilters'`
|
||||
|
||||
if err = db.QueryRowContextSQL(ctx, tx, query, nil, []any{&numConstraints}); err != nil {
|
||||
return err
|
||||
}
|
||||
if numConstraints > 0 {
|
||||
query = `ALTER TABLE DBPREFIXwordfilters DROP FOREIGN KEY wordfilters_board_id_fk`
|
||||
} else {
|
||||
query = ""
|
||||
}
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "board_dirs", "DBPREFIXwordfilters", sqlConfig)
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
return err
|
||||
errEv.Err(err).Caller(1).Send()
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXwordfilters ADD COLUMN board_dirs varchar(255) DEFAULT '*'`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
dbName := sqlConfig.DBname
|
||||
db := dbu.db
|
||||
|
||||
// Yay, collation! Everybody loves MySQL's default collation!
|
||||
// fix default collation
|
||||
query = `ALTER DATABASE ` + dbName + ` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.GetBaseDB().ExecContext(ctx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rows *sql.Rows
|
||||
query = `SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ?`
|
||||
rows, err := db.QueryContextSQL(ctx, tx, query, dbName)
|
||||
rows, err = db.QueryContextSQL(ctx, nil, query, dbName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -69,15 +44,15 @@ func updateMysqlDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *c
|
|||
return err
|
||||
}
|
||||
query = `ALTER TABLE ` + tableName + ` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`
|
||||
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = rows.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ip", "DBPREFIXip_ban", sqlConfig)
|
||||
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "ip", "DBPREFIXip_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -86,11 +61,12 @@ func updateMysqlDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *c
|
|||
query = `ALTER TABLE DBPREFIXip_ban
|
||||
ADD COLUMN IF NOT EXISTS range_start VARBINARY(16) NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS range_end VARBINARY(16) NOT NULL`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert ban IP string to IP range
|
||||
if rows, err = db.QueryContextSQL(ctx, tx, `SELECT id, ip FROM DBPREFIXip_ban`); err != nil {
|
||||
if rows, err = db.QueryContextSQL(ctx, nil, "SELECT id, ip FROM DBPREFIXip_ban"); err != nil {
|
||||
return err
|
||||
}
|
||||
var rangeStart, rangeEnd string
|
||||
|
@ -105,11 +81,11 @@ func updateMysqlDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *c
|
|||
}
|
||||
query = `UPDATE DBPREFIXip_ban
|
||||
SET range_start = INET6_ATON(?), range_end = INET6_ATON(?) WHERE id = ?`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query, rangeStart, rangeEnd, id); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query, rangeStart, rangeEnd, id); err != nil {
|
||||
return err
|
||||
}
|
||||
query = `ALTER TABLE DBPREFIXip_ban DROP COLUMN IF EXISTS ip`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -119,132 +95,88 @@ func updateMysqlDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *c
|
|||
}
|
||||
|
||||
// Convert DBPREFIXposts.ip to from varchar to varbinary
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ip", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "ip", "DBPREFIXposts", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if common.IsStringType(dataType) {
|
||||
// rename `ip` to a temporary column to then be removed
|
||||
query = `ALTER TABLE DBPREFIXposts CHANGE ip ip_str varchar(45)`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
query = "ALTER TABLE DBPREFIXposts CHANGE ip ip_str varchar(45)"
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query = `ALTER TABLE DBPREFIXposts
|
||||
ADD COLUMN IF NOT EXISTS ip VARBINARY(16) NOT NULL`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert post IP VARCHAR(45) to VARBINARY(16)
|
||||
query = `UPDATE DBPREFIXposts SET ip = INET6_ATON(ip_str)`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query = `ALTER TABLE DBPREFIXposts DROP COLUMN IF EXISTS ip_str`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Convert DBPREFIXreports.ip to from varchar to varbinary
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ip", "DBPREFIXreports", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "ip", "DBPREFIXreports", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if common.IsStringType(dataType) {
|
||||
// rename `ip` to a temporary column to then be removed
|
||||
query = `ALTER TABLE DBPREFIXreports CHANGE ip ip_str varchar(45)`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
query = "ALTER TABLE DBPREFIXreports CHANGE ip ip_str varchar(45)"
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query = `ALTER TABLE DBPREFIXreports
|
||||
ADD COLUMN IF NOT EXISTS ip VARBINARY(16) NOT NULL`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert report IP VARCHAR(45) to VARBINARY(16)
|
||||
query = `UPDATE DBPREFIXreports SET ip = INET6_ATON(ip_str)`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query = `ALTER TABLE DBPREFIXreports DROP COLUMN IF EXISTS ip_str`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add flag column to DBPREFIXposts
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "flag", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "flag", "DBPREFIXposts", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXposts ADD COLUMN flag VARCHAR(45) NOT NULL DEFAULT ''`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add country column to DBPREFIXposts
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "country", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "country", "DBPREFIXposts", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXposts ADD COLUMN country VARCHAR(80) NOT NULL DEFAULT ''`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fileBanTableExists, err := common.TableExists(ctx, db, tx, "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fileBanTableExists {
|
||||
// file ban table is going to be migrated by the end of the update, but we want to make sure its legacy data is migrated first
|
||||
// so it can be properly merged into the filter table
|
||||
|
||||
// add fingerprinter column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "fingerprinter", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN fingerprinter VARCHAR(64)`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add ban_ip column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ban_ip", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN ban_ip BOOL NOT NULL`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// add ban_ip_message column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ban_ip_message", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN ban_ip_message TEXT`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,35 +2,29 @@ package gcupdate
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/gochan-org/gochan/cmd/gochan-migration/internal/common"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func updatePostgresDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *config.SQLConfig) error {
|
||||
func updatePostgresDB(ctx context.Context, dbu *GCDatabaseUpdater, sqlConfig *config.SQLConfig, errEv *zerolog.Event) error {
|
||||
db := dbu.db
|
||||
var query string
|
||||
|
||||
query := `ALTER TABLE DBPREFIXwordfilters
|
||||
DROP CONSTRAINT IF EXISTS board_id_fk`
|
||||
_, err := db.ExecContextSQL(ctx, tx, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query = `ALTER TABLE DBPREFIXwordfilters
|
||||
ADD COLUMN IF NOT EXISTS board_dirs varchar(255) DEFAULT '*'`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataType, err := common.ColumnType(ctx, db, tx, "ip", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err := common.ColumnType(ctx, db, nil, "ip", "DBPREFIXposts", sqlConfig)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
errEv.Err(err).Caller(1).Send()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if common.IsStringType(dataType) {
|
||||
// change ip column to temporary ip_str
|
||||
query = `ALTER TABLE DBPREFIXposts RENAME COLUMN ip TO ip_str,`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -38,22 +32,22 @@ func updatePostgresDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig
|
|||
// because it is non-nil
|
||||
query = `ALTER TABLE DBPREFIXposts
|
||||
ADD COLUMN IF NOT EXISTS ip INET NOT NULL DEFAULT '127.0.0.1'`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query = `UPDATE TABLE DBPREFIXposts SET ip = ip_str`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query = `ALTER TABLE DBPREFIXposts DROP COLUMN ip_str`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ip", "DBPREFIXip_ban", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "ip", "DBPREFIXip_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -61,72 +55,36 @@ func updatePostgresDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig
|
|||
query = `ALTER TABLE DBPREFIXip_ban
|
||||
ADD COLUMN IF NOT EXISTS range_start INET NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS range_end INET NOT NULL`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query = `UPDATE DBPREFIXip_ban SET range_start = ip::INET, SET range_end = ip::INET`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add flag column to DBPREFIXposts
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "flag", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "flag", "DBPREFIXposts", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXposts ADD COLUMN flag VARCHAR(45) NOT NULL DEFAULT ''`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add country column to DBPREFIXposts
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "country", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "country", "DBPREFIXposts", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXposts ADD COLUMN country VARCHAR(80) NOT NULL DEFAULT ''`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add fingerprinter column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "fingerprinter", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN fingerprinter VARCHAR(64)`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add ban_ip column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ban_ip", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN ban_ip BOOL NOT NULL`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add ban_ip_message column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ban_ip_message", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN ban_ip_message TEXT`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,112 +2,107 @@ package gcupdate
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/gochan-org/gochan/cmd/gochan-migration/internal/common"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func updateSqliteDB(ctx context.Context, db *gcsql.GCDB, tx *sql.Tx, sqlConfig *config.SQLConfig) error {
|
||||
func updateSqliteDB(ctx context.Context, dbu *GCDatabaseUpdater, sqlConfig *config.SQLConfig, errEv *zerolog.Event) error {
|
||||
db := dbu.db
|
||||
var query string
|
||||
|
||||
_, err := db.ExecContextSQL(ctx, tx, `PRAGMA foreign_keys = ON`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataType, err := common.ColumnType(ctx, db, tx, "DBPREFIXwordfilters", "board_dirs", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXwordfilters ADD COLUMN board_dirs varchar(255) DEFAULT '*'`
|
||||
if _, err = db.ExecTxSQL(tx, query); err != nil {
|
||||
return err
|
||||
_, err := db.ExecContextSQL(ctx, nil, `PRAGMA foreign_keys = ON`)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
errEv.Err(err).Caller(1).Send()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dataType string
|
||||
// Add range_start column to DBPREFIXIp_ban if it doesn't exist
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "DBPREFIXip_ban", "range_start", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "DBPREFIXip_ban", "range_start", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXip_ban ADD COLUMN range_start VARCHAR(45) NOT NULL`
|
||||
if _, err = db.ExecTxSQL(tx, query); err != nil {
|
||||
if _, err = db.ExecTxSQL(nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add range_start column if it doesn't exist
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "DBPREFIXip_ban", "range_end", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "DBPREFIXip_ban", "range_end", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXip_ban ADD COLUMN range_end VARCHAR(45) NOT NULL`
|
||||
if _, err = db.ExecTxSQL(tx, query); err != nil {
|
||||
if _, err = db.ExecTxSQL(nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add flag column to DBPREFIXposts
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "flag", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "flag", "DBPREFIXposts", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXposts ADD COLUMN flag VARCHAR(45) NOT NULL DEFAULT ''`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add country column to DBPREFIXposts
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "country", "DBPREFIXposts", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "country", "DBPREFIXposts", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXposts ADD COLUMN country VARCHAR(80) NOT NULL DEFAULT ''`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add fingerprinter column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "fingerprinter", "DBPREFIXfile_ban", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "fingerprinter", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN fingerprinter VARCHAR(64)`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add ban_ip column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ban_ip", "DBPREFIXfile_ban", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "ban_ip", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN ban_ip BOOL NOT NULL`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add ban_ip_message column to DBPREFIXfile_ban
|
||||
dataType, err = common.ColumnType(ctx, db, tx, "ban_ip_message", "DBPREFIXfile_ban", sqlConfig)
|
||||
dataType, err = common.ColumnType(ctx, db, nil, "ban_ip_message", "DBPREFIXfile_ban", sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dataType == "" {
|
||||
query = `ALTER TABLE DBPREFIXfile_ban ADD COLUMN ban_ip_message TEXT`
|
||||
if _, err = db.ExecContextSQL(ctx, tx, query); err != nil {
|
||||
if _, err = db.ExecContextSQL(ctx, nil, query); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ func ConnectToDB(cfg *config.SQLConfig) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// SetDB sets the global database connection (mainly used by gochan-migration)
|
||||
func SetDB(db *GCDB) {
|
||||
gcdb = db
|
||||
}
|
||||
|
||||
func SetTestingDB(dbDriver string, dbName string, dbPrefix string, db *sql.DB) (err error) {
|
||||
testutil.PanicIfNotTest()
|
||||
sqlConfig := config.GetSQLConfig()
|
||||
|
|
|
@ -86,6 +86,10 @@ func (db *GCDB) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (db *GCDB) GetBaseDB() *sql.DB {
|
||||
return db.db
|
||||
}
|
||||
|
||||
func (db *GCDB) PrepareSQL(query string, tx *sql.Tx) (*sql.Stmt, error) {
|
||||
return db.PrepareContextSQL(context.Background(), query, tx)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ var (
|
|||
"2006-01-02 15:04:05",
|
||||
"2006-01-02T15:04:05Z",
|
||||
}
|
||||
ErrUnsupportedDB = errors.New("unsupported SQL driver")
|
||||
ErrUnsupportedDB = errors.New("unsupported SQL driver, supported drivers: " + strings.Join(sql.Drivers(), ", "))
|
||||
ErrNotConnected = errors.New("error connecting to database")
|
||||
)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue