666 lines
16 KiB
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"`
|
|
}
|