statping-ng/database/database.go

666 lines
16 KiB
Go

package database
import (
"database/sql"
"fmt"
"github.com/jinzhu/gorm"
"github.com/statping-ng/statping-ng/types/metrics"
"github.com/statping-ng/statping-ng/utils"
"strings"
"time"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/mattn/go-sqlite3"
)
var database Database
// Database is an interface which DB implements
type Database interface {
Close() error
DB() *sql.DB
New() Database
NewScope(value interface{}) *gorm.Scope
CommonDB() gorm.SQLCommon
Callback() *gorm.Callback
SetLogger(l gorm.Logger)
LogMode(enable bool) Database
SingularTable(enable bool)
Where(query interface{}, args ...interface{}) Database
Or(query interface{}, args ...interface{}) Database
Not(query interface{}, args ...interface{}) Database
Limit(value int) Database
Offset(value int) Database
Order(value string, reorder ...bool) Database
Select(query interface{}, args ...interface{}) Database
Omit(columns ...string) Database
Group(query string) Database
Having(query string, values ...interface{}) Database
Joins(query string, args ...interface{}) Database
Scopes(funcs ...func(*gorm.DB) *gorm.DB) Database
Unscoped() Database
Attrs(attrs ...interface{}) Database
Assign(attrs ...interface{}) Database
First(out interface{}, where ...interface{}) Database
Last(out interface{}, where ...interface{}) Database
Find(out interface{}, where ...interface{}) Database
Scan(dest interface{}) Database
Row() *sql.Row
Rows() (*sql.Rows, error)
ScanRows(rows *sql.Rows, result interface{}) error
Pluck(column string, value interface{}) Database
Count(value interface{}) Database
Related(value interface{}, foreignKeys ...string) Database
FirstOrInit(out interface{}, where ...interface{}) Database
FirstOrCreate(out interface{}, where ...interface{}) Database
Update(attrs ...interface{}) Database
Updates(values interface{}, ignoreProtectedAttrs ...bool) Database
UpdateColumn(attrs ...interface{}) Database
UpdateColumns(values interface{}) Database
Save(value interface{}) Database
Create(value interface{}) Database
Delete(value interface{}, where ...interface{}) Database
Raw(sql string, values ...interface{}) Database
Exec(sql string, values ...interface{}) Database
Model(value interface{}) Database
Table(name string) Database
Debug() Database
Begin() Database
Commit() Database
Rollback() Database
NewRecord(value interface{}) bool
RecordNotFound() bool
CreateTable(values ...interface{}) Database
DropTable(values ...interface{}) Database
DropTableIfExists(values ...interface{}) Database
HasTable(value interface{}) bool
AutoMigrate(values ...interface{}) Database
ModifyColumn(column string, typ string) Database
DropColumn(column string) Database
AddIndex(indexName string, column ...string) Database
AddUniqueIndex(indexName string, column ...string) Database
RemoveIndex(indexName string) Database
AddForeignKey(field string, dest string, onDelete string, onUpdate string) Database
Association(column string) *gorm.Association
Preload(column string, conditions ...interface{}) Database
Set(name string, value interface{}) Database
InstantSet(name string, value interface{}) Database
Get(name string) (value interface{}, ok bool)
SetJoinTableHandler(source interface{}, column string, handler gorm.JoinTableHandlerInterface)
AddError(err error) error
GetErrors() (errors []error)
// extra
Error() error
Status() int
RowsAffected() int64
Since(time.Time) Database
Between(time.Time, time.Time) Database
SelectByTime(time.Duration) string
MultipleSelects(args ...string) Database
FormatTime(t time.Time) string
ParseTime(t string) (time.Time, error)
DbType() string
GormDB() *gorm.DB
ChunkSize() int
}
func (it *Db) ChunkSize() int {
switch it.Database.Dialect().GetName() {
case "mysql":
return 3000
case "postgres":
return 3000
default:
return 100
}
}
func Routine() {
for {
if database.DB() == nil {
time.Sleep(5 * time.Second)
continue
}
metrics.CollectDatabase(database.DB().Stats())
time.Sleep(5 * time.Second)
}
}
func (it *Db) GormDB() *gorm.DB {
return it.Database
}
func (it *Db) DbType() string {
return it.Database.Dialect().GetName()
}
func Close(db Database) error {
if db == nil {
return nil
}
return db.Close()
}
func LogMode(db Database, b bool) Database {
return db.LogMode(b)
}
func Begin(db Database, model interface{}) Database {
if all, ok := model.(string); ok {
if all == "migration" {
return db.Begin()
}
}
return db.Model(model).Begin()
}
func Available(db Database) bool {
if db == nil {
return false
}
if err := db.DB().Ping(); err != nil {
return false
}
return true
}
func (it *Db) MultipleSelects(args ...string) Database {
joined := strings.Join(args, ", ")
return it.Select(joined)
}
type Db struct {
Database *gorm.DB
Type string
ReadOnly bool
}
// Openw is a drop-in replacement for Open()
func Openw(dialect string, args ...interface{}) (db Database, err error) {
gorm.NowFunc = func() time.Time {
return utils.Now()
}
if dialect == "sqlite" {
dialect = "sqlite3"
}
gormdb, err := gorm.Open(dialect, args...)
if err != nil {
return nil, err
}
database = Wrap(gormdb)
go Routine()
return database, err
}
func OpenTester() (Database, error) {
testDB := utils.Params.GetString("DB_CONN")
var dbString string
switch testDB {
case "mysql":
dbString = fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27",
utils.Params.GetString("DB_HOST"),
utils.Params.GetString("DB_PASS"),
utils.Params.GetString("DB_HOST"),
utils.Params.GetInt("DB_PORT"),
utils.Params.GetString("DB_DATABASE"),
)
case "postgres":
dbString = fmt.Sprintf("host=%s port=%v user=%s dbname=%s password=%s sslmode=disable timezone=UTC",
utils.Params.GetString("DB_HOST"),
utils.Params.GetInt("DB_PORT"),
utils.Params.GetString("DB_USER"),
utils.Params.GetString("DB_DATABASE"),
utils.Params.GetString("DB_PASS"))
default:
dbString = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12))
}
if utils.Params.IsSet("DB_DSN") {
dbString = utils.Params.GetString("DB_DSN")
}
newDb, err := Openw(testDB, dbString)
if err != nil {
return nil, err
}
newDb.DB().SetMaxOpenConns(1)
if testDB != "sqlite3" {
newDb.DB().SetMaxOpenConns(25)
}
return newDb, err
}
// Wrap wraps gorm.DB in an interface
func Wrap(db *gorm.DB) Database {
return &Db{
Database: db,
Type: db.Dialect().GetName(),
ReadOnly: utils.Params.GetBool("READ_ONLY"),
}
}
func (it *Db) Close() error {
return it.Database.Close()
}
func (it *Db) DB() *sql.DB {
return it.Database.DB()
}
func (it *Db) New() Database {
return Wrap(it.Database.New())
}
func (it *Db) NewScope(value interface{}) *gorm.Scope {
return it.Database.NewScope(value)
}
func (it *Db) CommonDB() gorm.SQLCommon {
return it.Database.CommonDB()
}
func (it *Db) Callback() *gorm.Callback {
return it.Database.Callback()
}
func (it *Db) SetLogger(log gorm.Logger) {
it.Database.SetLogger(log)
}
func (it *Db) LogMode(enable bool) Database {
return Wrap(it.Database.LogMode(enable))
}
func (it *Db) SingularTable(enable bool) {
it.Database.SingularTable(enable)
}
func (it *Db) Where(query interface{}, args ...interface{}) Database {
return Wrap(it.Database.Where(query, args...))
}
func (it *Db) Or(query interface{}, args ...interface{}) Database {
return Wrap(it.Database.Or(query, args...))
}
func (it *Db) Not(query interface{}, args ...interface{}) Database {
return Wrap(it.Database.Not(query, args...))
}
func (it *Db) Limit(value int) Database {
return Wrap(it.Database.Limit(value))
}
func (it *Db) Offset(value int) Database {
return Wrap(it.Database.Offset(value))
}
func (it *Db) Order(value string, reorder ...bool) Database {
return Wrap(it.Database.Order(value, reorder...))
}
func (it *Db) Select(query interface{}, args ...interface{}) Database {
return Wrap(it.Database.Select(query, args...))
}
func (it *Db) Omit(columns ...string) Database {
return Wrap(it.Database.Omit(columns...))
}
func (it *Db) Group(query string) Database {
return Wrap(it.Database.Group(query))
}
func (it *Db) Having(query string, values ...interface{}) Database {
return Wrap(it.Database.Having(query, values...))
}
func (it *Db) Joins(query string, args ...interface{}) Database {
return Wrap(it.Database.Joins(query, args...))
}
func (it *Db) Scopes(funcs ...func(*gorm.DB) *gorm.DB) Database {
return Wrap(it.Database.Scopes(funcs...))
}
func (it *Db) Unscoped() Database {
return Wrap(it.Database.Unscoped())
}
func (it *Db) Attrs(attrs ...interface{}) Database {
return Wrap(it.Database.Attrs(attrs...))
}
func (it *Db) Assign(attrs ...interface{}) Database {
return Wrap(it.Database.Assign(attrs...))
}
func (it *Db) First(out interface{}, where ...interface{}) Database {
return Wrap(it.Database.First(out, where...))
}
func (it *Db) Last(out interface{}, where ...interface{}) Database {
return Wrap(it.Database.Last(out, where...))
}
func (it *Db) Find(out interface{}, where ...interface{}) Database {
return Wrap(it.Database.Find(out, where...))
}
func (it *Db) Scan(dest interface{}) Database {
return Wrap(it.Database.Scan(dest))
}
func (it *Db) Row() *sql.Row {
return it.Database.Row()
}
func (it *Db) Rows() (*sql.Rows, error) {
return it.Database.Rows()
}
func (it *Db) ScanRows(rows *sql.Rows, result interface{}) error {
return it.Database.ScanRows(rows, result)
}
func (it *Db) Pluck(column string, value interface{}) Database {
return Wrap(it.Database.Pluck(column, value))
}
func (it *Db) Count(value interface{}) Database {
return Wrap(it.Database.Count(value))
}
func (it *Db) Related(value interface{}, foreignKeys ...string) Database {
return Wrap(it.Database.Related(value, foreignKeys...))
}
func (it *Db) FirstOrInit(out interface{}, where ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.FirstOrInit(out, where...))
}
func (it *Db) FirstOrCreate(out interface{}, where ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.FirstOrCreate(out, where...))
}
func (it *Db) Update(attrs ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Update(attrs...))
}
func (it *Db) Updates(values interface{}, ignoreProtectedAttrs ...bool) Database {
return Wrap(it.Database.Updates(values, ignoreProtectedAttrs...))
}
func (it *Db) UpdateColumn(attrs ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.UpdateColumn(attrs...))
}
func (it *Db) UpdateColumns(values interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.UpdateColumns(values))
}
func (it *Db) Save(value interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Save(value))
}
func (it *Db) Create(value interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Create(value))
}
func (it *Db) Delete(value interface{}, where ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Delete(value, where...))
}
func (it *Db) Raw(sql string, values ...interface{}) Database {
return Wrap(it.Database.Raw(sql, values...))
}
func (it *Db) Exec(sql string, values ...interface{}) Database {
return Wrap(it.Database.Exec(sql, values...))
}
func (it *Db) Model(value interface{}) Database {
return Wrap(it.Database.Model(value))
}
func (it *Db) Table(name string) Database {
return Wrap(it.Database.Table(name))
}
func (it *Db) Debug() Database {
return Wrap(it.Database.Debug())
}
func (it *Db) Begin() Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Begin())
}
func (it *Db) Commit() Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Commit())
}
func (it *Db) Rollback() Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Rollback())
}
func (it *Db) NewRecord(value interface{}) bool {
return it.Database.NewRecord(value)
}
func (it *Db) RecordNotFound() bool {
return it.Database.RecordNotFound()
}
func (it *Db) CreateTable(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.CreateTable(values...))
}
func (it *Db) DropTable(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.DropTable(values...))
}
func (it *Db) DropTableIfExists(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.DropTableIfExists(values...))
}
func (it *Db) HasTable(value interface{}) bool {
return it.Database.HasTable(value)
}
func (it *Db) AutoMigrate(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.AutoMigrate(values...))
}
func (it *Db) ModifyColumn(column string, typ string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.ModifyColumn(column, typ))
}
func (it *Db) DropColumn(column string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.DropColumn(column))
}
func (it *Db) AddIndex(indexName string, columns ...string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.AddIndex(indexName, columns...))
}
func (it *Db) AddUniqueIndex(indexName string, columns ...string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.AddUniqueIndex(indexName, columns...))
}
func (it *Db) RemoveIndex(indexName string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.RemoveIndex(indexName))
}
func (it *Db) Association(column string) *gorm.Association {
return it.Database.Association(column)
}
func (it *Db) Preload(column string, conditions ...interface{}) Database {
return Wrap(it.Database.Preload(column, conditions...))
}
func (it *Db) Set(name string, value interface{}) Database {
return Wrap(it.Database.Set(name, value))
}
func (it *Db) InstantSet(name string, value interface{}) Database {
return Wrap(it.Database.InstantSet(name, value))
}
func (it *Db) Get(name string) (interface{}, bool) {
return it.Database.Get(name)
}
func (it *Db) SetJoinTableHandler(source interface{}, column string, handler gorm.JoinTableHandlerInterface) {
it.Database.SetJoinTableHandler(source, column, handler)
}
func (it *Db) AddForeignKey(field string, dest string, onDelete string, onUpdate string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.AddForeignKey(field, dest, onDelete, onUpdate))
}
func (it *Db) AddError(err error) error {
return it.Database.AddError(err)
}
func (it *Db) GetErrors() (errors []error) {
return it.Database.GetErrors()
}
func (it *Db) RowsAffected() int64 {
return it.Database.RowsAffected
}
func (it *Db) Error() error {
return it.Database.Error
}
func (it *Db) Status() int {
switch it.Database.Error {
case gorm.ErrRecordNotFound:
return 404
case gorm.ErrCantStartTransaction:
return 422
case gorm.ErrInvalidSQL:
return 500
case gorm.ErrUnaddressable:
return 500
default:
return 500
}
}
func (it *Db) Loggable() bool {
switch it.Database.Error {
case gorm.ErrCantStartTransaction:
return true
case gorm.ErrInvalidSQL:
return true
case gorm.ErrUnaddressable:
return true
default:
return false
}
}
func (it *Db) Since(ago time.Time) Database {
return it.Where("created_at > ?", it.FormatTime(ago))
}
func (it *Db) Between(t1 time.Time, t2 time.Time) Database {
return it.Where("created_at BETWEEN ? AND ?", it.FormatTime(t1), it.FormatTime(t2))
}
type TimeValue struct {
Timeframe string `json:"timeframe"`
Amount int64 `json:"amount"`
}