storj/storagenode/storagenodedb/utcdb.go
Jeff Wendling b51d3a69da enable utccheck in all tests by default (#2565)
* enable utccheck in all tests by default
2019-07-16 09:42:19 +09:00

114 lines
3.1 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package storagenodedb
import (
"context"
"database/sql"
"os"
"strings"
"time"
"github.com/zeebo/errs"
)
// utcChecks controls if the time zone checks are enabled. They are by default
// enabled only during tests.
var utcChecks = len(os.Args) > 0 && strings.HasSuffix(os.Args[0], ".test")
// EnableUTCChecks turns on tracking for timestamps being passed to the database being in
// the UTC location. They cannot be turned off once turned on, and should be turned on
// before any database calls are made.
func EnableUTCChecks() { utcChecks = true }
// utcDB wraps a sql.DB and checks all of the arguments to queries to ensure they are in UTC.
type utcDB struct {
db *sql.DB
}
// Close closes the database.
func (u utcDB) Close() error { return u.db.Close() }
// Query executes Query after checking all of the arguments.
func (u utcDB) Query(sql string, args ...interface{}) (*sql.Rows, error) {
if err := utcCheckArgs(args); err != nil {
return nil, err
}
return u.db.Query(sql, args...)
}
// QueryRow executes QueryRow after checking all of the arguments.
func (u utcDB) QueryRow(sql string, args ...interface{}) *sql.Row {
// TODO(jeff): figure out a way to return an errored *sql.Row so we can consider
// enabling all of these checks in production.
if err := utcCheckArgs(args); err != nil {
panic(err)
}
return u.db.QueryRow(sql, args...)
}
// QueryContext executes QueryContext after checking all of the arguments.
func (u utcDB) QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error) {
if err := utcCheckArgs(args); err != nil {
return nil, err
}
return u.db.QueryContext(ctx, sql, args...)
}
// QueryRowContext executes QueryRowContext after checking all of the arguments.
func (u utcDB) QueryRowContext(ctx context.Context, sql string, args ...interface{}) *sql.Row {
// TODO(jeff): figure out a way to return an errored *sql.Row so we can consider
// enabling all of these checks in production.
if err := utcCheckArgs(args); err != nil {
panic(err)
}
return u.db.QueryRowContext(ctx, sql, args...)
}
// Exec executes Exec after checking all of the arguments.
func (u utcDB) Exec(sql string, args ...interface{}) (sql.Result, error) {
if err := utcCheckArgs(args); err != nil {
return nil, err
}
return u.db.Exec(sql, args...)
}
// ExecContext executes ExecContext after checking all of the arguments.
func (u utcDB) ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error) {
if err := utcCheckArgs(args); err != nil {
return nil, err
}
return u.db.ExecContext(ctx, sql, args...)
}
// utcCheckArgs checks the arguments for time.Time values that are not in the UTC location.
func utcCheckArgs(args []interface{}) error {
if !utcChecks {
return nil
}
for n, arg := range args {
var t time.Time
var ok bool
switch a := arg.(type) {
case time.Time:
t, ok = a, true
case *time.Time:
if a != nil {
t, ok = *a, true
}
}
if !ok {
continue
}
if loc := t.Location(); loc != time.UTC {
return errs.New("invalid timezone on argument %d: %v", n, loc)
}
}
return nil
}