storj/storagenode/storagenodedb/utcdb.go
Jeff Wendling b9d8ddaad1
storagenode: remove datetime calls in favor of UTC (#2557)
* storagenode: remove datetime calls in favor of UTC

datetime only has second level granularity whereas string
comparisons don't. Since we're wiping everything anyway, it's
easier to just use UTC everywhere rather than migrate to
datetime calls.

* add utcdb to check that arguments are utc

* storagenodedb: add trivial tests to ensure calls work

This at least tests that all of the timestamps passed in are
in the UTC timezone.

* fix truncated comment and change migrations to be UTC
2019-07-15 13:38:08 -04:00

106 lines
2.8 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package storagenodedb
import (
"context"
"database/sql"
"time"
"github.com/zeebo/errs"
)
// utcChecks controls if the time zone checks are enabled.
var utcChecks = false
// 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
}