1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-18 07:36:24 -07:00

Fully refactor all (or at least most) uses of ExecSQL, QuerySQL, and QueryRowSQL, QueryContextSQL, etc with their replacement functions

This commit is contained in:
Eggbertx 2025-02-05 23:32:12 -08:00
parent 10e0da4492
commit 7ceda2b218
17 changed files with 79 additions and 52 deletions

View file

@ -88,7 +88,7 @@ func RunSQLFile(path string, db *gcsql.GCDB) error {
for _, statement := range sqlArr {
statement = strings.TrimSpace(statement)
if len(statement) > 0 {
if _, err = db.ExecSQL(statement); err != nil {
if _, err = db.Exec(nil, statement); err != nil {
return err
}
}

View file

@ -36,7 +36,7 @@ func (dbu *GCDatabaseUpdater) Init(options *common.MigrationOptions) error {
func (dbu *GCDatabaseUpdater) IsMigrated() (bool, error) {
var currentDatabaseVersion int
err := dbu.db.QueryRowSQL(`SELECT version FROM DBPREFIXdatabase_version WHERE component = 'gochan'`, nil,
err := dbu.db.QueryRow(nil, "SELECT version FROM DBPREFIXdatabase_version WHERE component = 'gochan'", nil,
[]any{&currentDatabaseVersion})
if err != nil {
return false, err

View file

@ -16,7 +16,7 @@ func (m *Pre2021Migrator) MigrateAnnouncements() error {
errEv := common.LogError()
defer errEv.Discard()
rows, err := m.db.QuerySQL(announcementsQuery)
rows, err := m.db.Query(nil, announcementsQuery)
if err != nil {
errEv.Err(err).Caller().Msg("Failed to get announcements")
return err
@ -49,7 +49,7 @@ func (m *Pre2021Migrator) MigrateAnnouncements() error {
errEv.Err(err).Caller().Str("staff", announcement.oldPoster).Msg("Failed to get staff ID")
return err
}
if _, err = gcsql.ExecSQL(
if _, err = gcsql.Exec(nil,
"INSERT INTO DBPREFIXannouncements(staff_id,subject,message,timestamp) values(?,?,?,?)",
announcement.StaffID, announcement.Subject, announcement.Message, announcement.Timestamp,
); err != nil {

View file

@ -27,6 +27,6 @@ func TestMigrateAnnouncements(t *testing.T) {
}
var numAnnouncements int
assert.NoError(t, gcsql.QueryRowSQL("SELECT COUNT(*) FROM DBPREFIXannouncements WHERE staff_id > 0", nil, []any{&numAnnouncements}))
assert.NoError(t, gcsql.QueryRow(nil, "SELECT COUNT(*) FROM DBPREFIXannouncements WHERE staff_id > 0", nil, []any{&numAnnouncements}))
assert.Equal(t, 2, numAnnouncements, "Expected to have two announcement")
}

View file

@ -53,7 +53,7 @@ func (m *Pre2021Migrator) migrateBansInPlace() error {
for _, stmt := range statements {
stmt = strings.TrimSpace(stmt)
if strings.HasPrefix(stmt, "CREATE TABLE DBPREFIXip_ban") || strings.HasPrefix(stmt, "CREATE TABLE DBPREFIXfilter") {
_, err = gcsql.ExecSQL(stmt)
_, err = gcsql.Exec(nil, stmt)
if err != nil {
errEv.Err(err).Caller().
Str("statement", stmt).
@ -99,7 +99,7 @@ func (m *Pre2021Migrator) migrateBansToNewDB() error {
}
defer tx.Rollback()
rows, err := m.db.QuerySQL(bansQuery)
rows, err := m.db.Query(nil, bansQuery)
if err != nil {
errEv.Err(err).Caller().Msg("Failed to get bans")
return err

View file

@ -42,7 +42,7 @@ func validateBanMigration(t *testing.T) {
assert.NotZero(t, bans[0].StaffID, "Expected ban staff ID field to be set")
var numInvalidBans int
assert.NoError(t, gcsql.QueryRowSQL("SELECT COUNT(*) FROM DBPREFIXip_ban WHERE message = ?", []any{"Full ban on 8.8.0.0/16"}, []any{&numInvalidBans}))
assert.NoError(t, gcsql.QueryRow(nil, "SELECT COUNT(*) FROM DBPREFIXip_ban WHERE message = ?", []any{"Full ban on 8.8.0.0/16"}, []any{&numInvalidBans}))
assert.Equal(t, 0, numInvalidBans, "Expected the invalid test to not be migrated")
filters, err := gcsql.GetAllFilters(gcsql.TrueOrFalse)

View file

@ -19,7 +19,7 @@ func TestMigratePosts(t *testing.T) {
}
var numThreads int
if !assert.NoError(t, migrator.db.QueryRowSQL("SELECT COUNT(*) FROM DBPREFIXposts WHERE parentid = 0 AND deleted_timestamp IS NULL", nil, []any{&numThreads}), "Failed to get number of threads") {
if !assert.NoError(t, migrator.db.QueryRow(nil, "SELECT COUNT(*) FROM DBPREFIXposts WHERE parentid = 0 AND deleted_timestamp IS NULL", nil, []any{&numThreads}), "Failed to get number of threads") {
t.FailNow()
}
assert.Equal(t, 2, numThreads, "Expected to have two threads pre-migration")
@ -32,27 +32,27 @@ func TestMigratePosts(t *testing.T) {
func validatePostMigration(t *testing.T) {
var numThreads int
if !assert.NoError(t, gcsql.QueryRowSQL("SELECT COUNT(*) FROM DBPREFIXthreads", nil, []any{&numThreads}), "Failed to get number of threads") {
if !assert.NoError(t, gcsql.QueryRow(nil, "SELECT COUNT(*) FROM DBPREFIXthreads", nil, []any{&numThreads}), "Failed to get number of threads") {
t.FailNow()
}
assert.Equal(t, 2, numThreads, "Expected to have two threads pre-migration")
var numUploadPosts int
assert.NoError(t, gcsql.QueryRowSQL("SELECT COUNT(*) FROM DBPREFIXfiles", nil, []any{&numUploadPosts}))
assert.NoError(t, gcsql.QueryRow(nil, "SELECT COUNT(*) FROM DBPREFIXfiles", nil, []any{&numUploadPosts}))
assert.Equal(t, 1, numUploadPosts, "Expected to have 1 upload post")
var ip string
assert.NoError(t, gcsql.QueryRowSQL("SELECT IP_NTOA FROM DBPREFIXposts WHERE id = 1", nil, []any{&ip}))
assert.NoError(t, gcsql.QueryRow(nil, "SELECT IP_NTOA FROM DBPREFIXposts WHERE id = 1", nil, []any{&ip}))
assert.Equal(t, "192.168.56.1", ip, "Expected to have the correct IP address")
var numMigratedThreads int
if !assert.NoError(t, gcsql.QueryRowSQL("SELECT COUNT(*) FROM DBPREFIXthreads", nil, []any{&numMigratedThreads}), "Failed to get number of migrated threads") {
if !assert.NoError(t, gcsql.QueryRow(nil, "SELECT COUNT(*) FROM DBPREFIXthreads", nil, []any{&numMigratedThreads}), "Failed to get number of migrated threads") {
t.FailNow()
}
assert.Equal(t, 2, numMigratedThreads, "Expected to have three migrated threads")
var locked bool
if !assert.NoError(t, gcsql.QueryRowSQL("SELECT locked FROM DBPREFIXthreads WHERE id = 1", nil, []any{&locked})) {
if !assert.NoError(t, gcsql.QueryRow(nil, "SELECT locked FROM DBPREFIXthreads WHERE id = 1", nil, []any{&locked})) {
t.FailNow()
}
assert.True(t, locked, "Expected thread ID 1 to be locked")

View file

@ -72,12 +72,12 @@ func (m *Pre2021Migrator) renameTablesForInPlace() error {
var err error
errEv := common.LogError()
defer errEv.Discard()
if _, err = m.db.ExecSQL("DROP TABLE DBPREFIXinfo"); err != nil {
if _, err = m.db.Exec(nil, "DROP TABLE DBPREFIXinfo"); err != nil {
errEv.Err(err).Caller().Msg("Error dropping info table")
return err
}
for _, table := range renameTables {
if _, err = m.db.ExecSQL(fmt.Sprintf(renameTableStatementTemplate, table, table)); err != nil {
if _, err = m.db.Exec(nil, fmt.Sprintf(renameTableStatementTemplate, table, table)); err != nil {
errEv.Caller().Err(err).
Str("table", table).
Msg("Error renaming table")

View file

@ -26,13 +26,13 @@ func (m *Pre2021Migrator) getMigrationUser(errEv *zerolog.Event) (*gcsql.Staff,
Username: "pre2021-migration" + gcutil.RandomString(8),
AddedOn: time.Now(),
}
_, err := gcsql.ExecSQL("INSERT INTO DBPREFIXstaff(username,password_checksum,global_rank,is_active) values(?,'',0,0)", user.Username)
_, err := gcsql.Exec(nil, "INSERT INTO DBPREFIXstaff(username,password_checksum,global_rank,is_active) values(?,'',0,0)", user.Username)
if err != nil {
errEv.Err(err).Caller().Str("username", user.Username).Msg("Failed to create migration user")
return nil, err
}
if err = gcsql.QueryRowSQL("SELECT id FROM DBPREFIXstaff WHERE username = ?", []any{user.Username}, []any{&user.ID}); err != nil {
if err = gcsql.QueryRow(nil, "SELECT id FROM DBPREFIXstaff WHERE username = ?", []any{user.Username}, []any{&user.ID}); err != nil {
errEv.Err(err).Caller().Str("username", user.Username).Msg("Failed to get migration user ID")
return nil, err
}
@ -50,7 +50,7 @@ func (m *Pre2021Migrator) MigrateStaff() error {
return err
}
rows, err := m.db.QuerySQL(staffQuery)
rows, err := m.db.Query(nil, staffQuery)
if err != nil {
errEv.Err(err).Caller().Msg("Failed to get ban rows")
return err
@ -73,7 +73,7 @@ func (m *Pre2021Migrator) MigrateStaff() error {
staff.ID = newStaff.ID
} else if errors.Is(err, gcsql.ErrUnrecognizedUsername) {
// staff doesn't exist, create it (with invalid checksum to be updated by the admin)
if _, err := gcsql.ExecSQL(
if _, err := gcsql.Exec(nil,
"INSERT INTO DBPREFIXstaff(username,password_checksum,global_rank,added_on,last_login,is_active) values(?,'',?,?,?,1)",
staff.Username, staff.Rank, staff.AddedOn, staff.LastLogin,
); err != nil {
@ -102,7 +102,7 @@ func (m *Pre2021Migrator) MigrateStaff() error {
Msg("Failed to get board ID")
return err
}
if _, err = gcsql.ExecSQL("INSERT INTO DBPREFIXboard_staff(board_id,staff_id) VALUES(?,?)", boardID, staff.ID); err != nil {
if _, err = gcsql.Exec(nil, "INSERT INTO DBPREFIXboard_staff(board_id,staff_id) VALUES(?,?)", boardID, staff.ID); err != nil {
errEv.Err(err).Caller().
Str("username", staff.Username).
Str("board", board).

View file

@ -125,16 +125,19 @@ func getAllPostsToDelete(postIDs []any, fileOnly bool) ([]delPost, []any, error)
query = "SELECT post_id, thread_id, op_id, is_top_post, filename, dir FROM DBPREFIXv_posts_to_delete WHERE post_id IN " +
setPart + " OR thread_id IN (SELECT thread_id from DBPREFIXposts op WHERE op_id IN " + setPart + " AND is_top_post)"
}
rows, err := gcsql.QuerySQL(query, params...)
rows, cancel, err := gcsql.QueryTimeoutSQL(nil, query, params...)
if err != nil {
return nil, nil, err
}
defer func() {
rows.Close()
cancel()
}()
var posts []delPost
var postIDsAny []any
for rows.Next() {
var post delPost
if err = rows.Scan(&post.postID, &post.threadID, &post.opID, &post.isOP, &post.filename, &post.boardDir); err != nil {
rows.Close()
return nil, nil, err
}
posts = append(posts, post)
@ -258,7 +261,7 @@ func validatePostPasswords(posts []any, passwordMD5 string) (bool, error) {
params = append(params, posts[p])
}
err := gcsql.QueryRowSQL(queryPosts, params, []any{&count})
err := gcsql.QueryRow(nil, queryPosts, params, []any{&count})
return count == len(posts), err
}
@ -281,21 +284,24 @@ func markPostsAsDeleted(posts []any, request *http.Request, writer http.Response
defer cancel()
tx, err := gcsql.BeginContextTx(ctx)
opts := &gcsql.RequestOptions{Context: ctx, Tx: tx, Cancel: cancel}
wantsJSON := serverutil.IsRequestingJSON(request)
if err != nil {
errEv.Err(err).Caller().Msg("Unable to start deletion transaction")
serveError(writer, "Unable to delete posts", http.StatusInternalServerError, wantsJSON, errEv.Err(err).Caller())
return false
}
defer tx.Rollback()
const postsError = "Unable to mark post(s) as deleted"
const threadsError = "Unable to mark thread(s) as deleted"
if _, err = gcsql.ExecTxSQL(tx, deletePostsSQL, posts...); err != nil {
const postsError = "Unable to delete post(s)"
const threadsError = "Unable to delete thread(s)"
if _, err = gcsql.Exec(opts, deletePostsSQL, posts...); err != nil {
serveError(writer, postsError, http.StatusInternalServerError, wantsJSON, errEv.Err(err).Caller())
return false
}
if _, err = gcsql.ExecTxSQL(tx, deleteThreadSQL, posts...); err != nil {
if _, err = gcsql.Exec(opts, deleteThreadSQL, posts...); err != nil {
errEv.Err(err).Caller().Msg("Unable to mark thread(s) as deleted")
serveError(writer, threadsError, http.StatusInternalServerError, wantsJSON, errEv.Err(err).Caller())
return false
}
@ -351,7 +357,7 @@ func deletePostFiles(posts []delPost, deleteIDs []any, permDelete bool, request
http.StatusInternalServerError, wantsJSON, errEv.Array("errors", errArr))
return false
}
_, err = gcsql.ExecSQL(deleteFilesSQL, deleteIDs...)
_, err = gcsql.ExecTimeoutSQL(nil, deleteFilesSQL, deleteIDs...)
if err != nil {
serveError(writer, "Unable to delete file entries from database",
http.StatusInternalServerError, wantsJSON, errEv.Err(err).Caller())

View file

@ -36,18 +36,18 @@ var (
)
// DoesBoardExistByID returns a bool indicating whether a board with a given id exists
func DoesBoardExistByID(ID int) bool {
const query = `SELECT COUNT(id) FROM DBPREFIXboards WHERE id = ?`
func DoesBoardExistByID(ID int, requestOptions ...*RequestOptions) bool {
opts := setupOptionsWithTimeout(requestOptions...)
var count int
QueryRowSQL(query, []any{ID}, []any{&count})
QueryRow(opts, "SELECT COUNT(id) FROM DBPREFIXboards WHERE id = ?", []any{ID}, []any{&count})
return count > 0
}
// DoesBoardExistByDir returns a bool indicating whether a board with a given directory exists
func DoesBoardExistByDir(dir string) bool {
const query = `SELECT COUNT(dir) FROM DBPREFIXboards WHERE dir = ?`
func DoesBoardExistByDir(dir string, requestOpts ...*RequestOptions) bool {
opts := setupOptionsWithTimeout(requestOpts...)
var count int
QueryRowSQL(query, []any{dir}, []any{&count})
QueryRow(opts, "SELECT COUNT(dir) FROM DBPREFIXboards WHERE dir = ?", []any{dir}, []any{&count})
return count > 0
}
@ -108,10 +108,10 @@ func GetBoardDirFromPostID(postID int) (string, error) {
return boardURI, err
}
func getBoardBase(where string, whereParameters []interface{}) (*Board, error) {
func getBoardBase(requestOptions *RequestOptions, where string, whereParameters ...any) (*Board, error) {
query := selectBoardsBaseSQL + where
board := new(Board)
err := QueryRowTimeoutSQL(nil, query, whereParameters, []any{
err := QueryRow(requestOptions, query, whereParameters, []any{
&board.ID, &board.SectionID, &board.URI, &board.Dir, &board.NavbarPosition, &board.Title, &board.Subtitle,
&board.Description, &board.MaxFilesize, &board.MaxThreads, &board.DefaultStyle, &board.Locked,
&board.CreatedAt, &board.AnonymousName, &board.ForceAnonymous, &board.AutosageAfter, &board.NoImagesAfter,
@ -124,13 +124,15 @@ func getBoardBase(where string, whereParameters []interface{}) (*Board, error) {
}
// GetBoardFromID returns the board corresponding to a given id
func GetBoardFromID(id int) (*Board, error) {
return getBoardBase("WHERE DBPREFIXboards.id = ?", []any{id})
func GetBoardFromID(id int, requestOptions ...*RequestOptions) (*Board, error) {
opts := setupOptionsWithTimeout(requestOptions...)
return getBoardBase(opts, "WHERE DBPREFIXboards.id = ?", id)
}
// GetBoardFromDir returns the board corresponding to a given dir
func GetBoardFromDir(dir string) (*Board, error) {
return getBoardBase("WHERE DBPREFIXboards.dir = ?", []any{dir})
func GetBoardFromDir(dir string, requestOptions ...*RequestOptions) (*Board, error) {
opts := setupOptionsWithTimeout(requestOptions...)
return getBoardBase(opts, "WHERE DBPREFIXboards.dir = ?", dir)
}
// GetIDFromDir returns the id of the board with the given dir value

View file

@ -97,7 +97,7 @@ func getBoardDefaultStyleTmplFunc(dir string) string {
return boardCfg.DefaultStyle
}
var defaultStyle string
err := gcsql.QueryRowSQL(`SELECT default_style FROM DBPREFIXboards WHERE dir = ?`,
err := gcsql.QueryRowTimeoutSQL(nil, "SELECT default_style FROM DBPREFIXboards WHERE dir = ?",
[]any{dir}, []any{&defaultStyle})
if err != nil || defaultStyle == "" {
gcutil.LogError(err).Caller().

View file

@ -33,7 +33,10 @@ func CreateThread(requestOptions *RequestOptions, boardID int, locked bool, stic
if _, err = Exec(requestOptions, insertQuery, boardID, locked, stickied, anchored, cyclic); err != nil {
return 0, err
}
return threadID, QueryRow(requestOptions, "SELECT MAX(id) FROM DBPREFIXthreads", nil, []any{&threadID})
if err = QueryRow(requestOptions, "SELECT MAX(id) FROM DBPREFIXthreads", nil, []any{&threadID}); err != nil {
return 0, err
}
return threadID, nil
}
// GetThread returns a a thread object from the database, given its ID

View file

@ -69,17 +69,26 @@ func setupOptions(opts ...*RequestOptions) *RequestOptions {
return opts[0]
}
// Query is a wrapper for QueryContextSQL that uses the given options, or defaults to a background context if nil
func setupOptionsWithTimeout(opts ...*RequestOptions) *RequestOptions {
withoutContext := len(opts) == 0 || opts[0] == nil || opts[0].Context == nil
requestOptions := setupOptions(opts...)
if withoutContext {
requestOptions.Context, requestOptions.Cancel = context.WithTimeout(context.Background(), gcdb.defaultTimeout)
}
return requestOptions
}
// Query is a function for querying rows from the configured database, using the given options, or defaults to a background context if nil
func Query(opts *RequestOptions, query string, a ...any) (*sql.Rows, error) {
return gcdb.Query(opts, query, a...)
}
// QueryRow is a wrapper for QueryRowContextSQL that uses the given options, or defaults to a background context if nil
// QueryRow is a function for querying a single row from the configured database, using the given options, or defaults to a background context if nil
func QueryRow(opts *RequestOptions, query string, values, out []any) error {
return gcdb.QueryRow(opts, query, values, out)
}
// Exec is a wrapper for ExecContextSQL that uses the given options, or defaults to a background context if nil
// Exec is a function for executing a statement with the configured database, using the given options, or defaults to a background context if nil
func Exec(opts *RequestOptions, query string, values ...any) (sql.Result, error) {
return gcdb.Exec(opts, query, values...)
}
@ -269,7 +278,7 @@ func QueryRowContextSQL(ctx context.Context, tx *sql.Tx, query string, values, o
// QueryRowTimeoutSQL is a helper function for querying a single row with the configured default timeout.
// It creates a context with the default timeout to only be used for this query and then disposed.
// It should only be used by a function that does a single SQL query, otherwise use QueryRowContextSQL
// It should only be used by a function that does a single SQL query, otherwise use QueryRow with a context.
func QueryRowTimeoutSQL(tx *sql.Tx, query string, values, out []any) error {
ctx, cancel := context.WithTimeout(context.Background(), gcdb.defaultTimeout)
defer cancel()

View file

@ -358,7 +358,7 @@ func fixThumbnailsCallback(_ http.ResponseWriter, request *http.Request, _ *gcsq
if board != "" {
const query = `SELECT id, op, filename, is_spoilered, width, height, thumbnail_width, thumbnail_height
FROM DBPREFIXv_upload_info WHERE dir = ? ORDER BY created_on DESC`
rows, err := gcsql.QuerySQL(query, board)
rows, err := gcsql.Query(nil, query, board)
if err != nil {
return "", err
}

View file

@ -415,8 +415,7 @@ func reportsCallback(_ http.ResponseWriter, request *http.Request, staff *gcsql.
Bool("blocked", block != "").
Msg("Report cleared")
}
rows, err := gcsql.QuerySQL(`SELECT id,
handled_by_staff_id as staff_id,
rows, err := gcsql.Query(nil, `SELECT id, handled_by_staff_id as staff_id,
(SELECT username FROM DBPREFIXstaff WHERE id = DBPREFIXreports.handled_by_staff_id) as staff_user,
post_id, IP_NTOA, reason, is_cleared from DBPREFIXreports WHERE is_cleared = FALSE`)
if err != nil {

View file

@ -2,6 +2,7 @@ package manage
import (
"bytes"
"context"
"database/sql"
"errors"
"net/http"
@ -191,11 +192,15 @@ func getAllStaffNopass(activeOnly bool) ([]gcsql.Staff, error) {
if activeOnly {
query += " WHERE is_active"
}
rows, err := gcsql.QuerySQL(query)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.GetSQLConfig().DBTimeoutSeconds)*time.Second)
rows, err := gcsql.Query(&gcsql.RequestOptions{Context: ctx, Cancel: cancel}, query)
if err != nil {
return nil, err
}
defer rows.Close()
defer func() {
rows.Close()
cancel()
}()
var staff []gcsql.Staff
for rows.Next() {
var s gcsql.Staff
@ -205,6 +210,9 @@ func getAllStaffNopass(activeOnly bool) ([]gcsql.Staff, error) {
}
staff = append(staff, s)
}
if err = rows.Close(); err != nil {
return nil, err
}
return staff, nil
}