package db import ( "context" "database/sql" "errors" "fmt" // PostgreSQL _ "github.com/lib/pq" // MySQL //_ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" //_ "github.com/mattn/go-sqlite3" ) // DB define type DB struct { Driver string conn *sqlx.DB tx *sqlx.Tx } // Tx is an in-progress database transaction. type Tx struct { tx *sqlx.Tx } // New new DB object func New() *DB { return &DB{Driver: config.Driver} } // Release free db connect func Release() { if db != nil { db.Close() } } // NewConfig new DB dynamic object func NewConfig(config Config) (dbx *DB, err error) { dbx = &DB{} dbx.Driver = config.Driver dbx.conn, err = sqlx.Connect(config.Driver, config.DNS) if err != nil { return } dbx.conn.SetMaxOpenConns(config.MaxOpenConns) dbx.conn.SetMaxIdleConns(config.MaxIdle) dbx.conn.SetConnMaxIdleTime(config.MaxIdleTime) dbx.conn.SetConnMaxLifetime(config.MaxLifeTime) //err = dbx.conn.Ping() return } // ReleaseConfig free db connect func ReleaseConfig(dbx *DB) { if dbx.conn != nil { dbx.conn.Close() } } func connect() (dbx *sqlx.DB, err error) { once.Do(func() { db, err = sqlx.Open(config.Driver, config.DNS) // sqlx.Connect(config.Driver, config.DNS) if err != nil { return } db.DB.SetMaxOpenConns(config.MaxOpenConns) db.DB.SetMaxIdleConns(config.MaxIdle) db.DB.SetConnMaxIdleTime(config.MaxIdleTime) db.DB.SetConnMaxLifetime(config.MaxLifeTime) /* err = db.Ping() if err != nil { return } // */ }) if db == nil { err = ErrNoneConnect return } dbx = db return } func connectContext(ctx context.Context) (dbx *sqlx.DB, err error) { once.Do(func() { db, err = sqlx.ConnectContext(ctx, config.Driver, config.DNS) if err != nil { return } db.DB.SetMaxOpenConns(config.MaxOpenConns) db.DB.SetMaxIdleConns(config.MaxIdle) db.DB.SetConnMaxIdleTime(config.MaxIdleTime) db.DB.SetConnMaxLifetime(config.MaxLifeTime) }) if db == nil { err = ErrNoneConnect return } dbx = db return } // Stats Stats returns database statistics. func (d *DB) Stats() (s sql.DBStats) { s = d.conn.DB.Stats() return } // Connect connect to database func (d *DB) Connect() (err error) { if d.conn != nil { return } d.conn, err = connect() return } // ConnectContext connect to database func (d *DB) ConnectContext(ctx context.Context) (err error) { if d.conn != nil { return } d.conn, err = connectContext(ctx) return } // Close close database connect func (d *DB) Close() { // use pool //d.conn.Close() } // Ping Ping connect func (d *DB) Ping() (err error) { d.conn, err = connect() if err != nil { return } return d.conn.Ping() } // PingContext Ping connect func (d *DB) PingContext(ctx context.Context) (err error) { d.conn, err = connectContext(ctx) if err != nil { return } return d.conn.PingContext(ctx) } // BeginTrans begin trans func (d *DB) BeginTrans() (err error) { d.conn, err = connect() if err != nil { return } d.tx, err = d.conn.Beginx() return } // Begin starts a transaction. The default isolation level is dependent on the driver. func (d *DB) Begin() (tx *Tx, err error) { d.conn, err = connect() if err != nil { return } tx = &Tx{} tx.tx, err = d.conn.Beginx() return } // BeginTransx begin trans func (d *DB) BeginTransx(ctx context.Context, opts *sql.TxOptions) (err error) { d.conn, err = connectContext(ctx) if err != nil { return } d.tx, err = d.conn.BeginTxx(ctx, opts) return } // BeginTx starts a transaction. func (d *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (tx *Tx, err error) { d.conn, err = connectContext(ctx) if err != nil { return } tx = &Tx{} tx.tx, err = d.conn.BeginTxx(ctx, opts) return } // Commit commit func (d *DB) Commit() (err error) { return d.tx.Commit() } // Rollback rollback func (d *DB) Rollback() (err error) { return d.tx.Rollback() } // TransExec trans execute with named args func (d *DB) TransExec(query string, args any) (lastInsertId, rowsAffected int64, err error) { var rs sql.Result if rs, err = d.tx.NamedExec(query, args); err == nil { rowsAffected, _ = rs.RowsAffected() lastInsertId, _ = rs.LastInsertId() } return } // TransExec trans execute with named args func (d *DB) TransExecContext(ctx context.Context, query string, args any) (lastInsertId, rowsAffected int64, err error) { var rs sql.Result if rs, err = d.tx.NamedExecContext(ctx, query, args); err == nil { rowsAffected, _ = rs.RowsAffected() lastInsertId, _ = rs.LastInsertId() } return } // TransUpdate trans update func (d *DB) TransUpdate(query string, args any) (reply Reply) { var ( err error rs sql.Result ) if rs, err = d.tx.NamedExec(query, args); err == nil { a, _ := rs.RowsAffected() reply = ReplyOk(a, 0) } else { reply = ReplyFaild(ErrException, err, errors.New(`数据执行错误`)) } return } // TransRow trans get row func (d *DB) TransRow(dest any, query string, args any) (err error) { nstmt := &sqlx.NamedStmt{} nstmt, err = d.tx.PrepareNamed(query) if err != nil { return err } defer nstmt.Close() err = nstmt.Get(dest, args) //err = d.tx.Get(dest, query, args) return err } // Preparex a statement within a transaction. func (d *DB) Preparex(query string) (stmt *Stmt, err error) { stmt, err = d.conn.Preparex(query) return } // PreparexContext returns an sqlx.Stmt instead of a sql.Stmt. func (d *DB) PreparexContext(ctx context.Context, query string) (stmt *Stmt, err error) { stmt, err = d.conn.PreparexContext(ctx, query) return } // PrepareNamed returns an sqlx.NamedStmt func (d *DB) PrepareNamed(query string) (stmt *NamedStmt, err error) { stmt, err = d.conn.PrepareNamed(query) return } // PrepareNamedContext returns an sqlx.NamedStmt func (d *DB) PrepareNamedContext(ctx context.Context, query string) (stmt *NamedStmt, err error) { stmt, err = d.conn.PrepareNamedContext(ctx, query) return } // Select select func (d *DB) Select(dest any, query string, args ...any) (err error) { err = d.Connect() if err != nil { return err } defer d.Close() err = d.conn.Select(dest, query, args...) return } // SelectContext select func (d *DB) SelectContext(ctx context.Context, dest any, query string, args ...any) (err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() err = d.conn.SelectContext(ctx, dest, query, args...) return } // QueryNamed get rows with named args, Query executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query. func (d *DB) QueryNamed(dest any, query string, args any) (err error) { err = d.Connect() if err != nil { return err } defer d.Close() nstmt := &sqlx.NamedStmt{} nstmt, err = d.conn.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Select(dest, args) return } // QueryNamedContext get rows with named args, QueryContext executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query. func (d *DB) QueryNamedContext(ctx context.Context, dest any, query string, args any) (err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() nstmt := &sqlx.NamedStmt{} nstmt, err = d.conn.PrepareNamedContext(ctx, query) if err != nil { return } defer nstmt.Close() err = nstmt.SelectContext(ctx, dest, args) return } // Rows get rows with named args func (d *DB) Rows(dest any, query string, args any) (err error) { err = d.Connect() if err != nil { return } defer d.Close() nstmt := &sqlx.NamedStmt{} nstmt, err = d.conn.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Select(dest, args) return } // RowsContext get rows with named args func (d *DB) RowsContext(ctx context.Context, dest any, query string, args any) (err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() nstmt := &sqlx.NamedStmt{} nstmt, err = d.conn.PrepareNamedContext(ctx, query) if err != nil { return err } defer nstmt.Close() err = nstmt.SelectContext(ctx, dest, args) return } // QueryRow get row, QueryRow executes a query that is expected to return at most one row. func (d *DB) QueryRow(dest any, query string, args ...any) (err error) { err = d.Connect() if err != nil { return err } defer d.Close() err = d.conn.Get(dest, query, args...) return } // QueryRowContext get row, QueryRowContext executes a query that is expected to return at most one row. func (d *DB) QueryRowContext(ctx context.Context, dest any, query string, args ...any) (err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() err = d.conn.GetContext(ctx, dest, query, args...) return } // Get get row, QueryRow executes a query that is expected to return at most one row. func (d *DB) Get(dest any, query string, args ...any) (err error) { err = d.Connect() if err != nil { return } defer d.Close() err = d.conn.Get(dest, query, args...) return } // GetContext get func (d *DB) GetContext(ctx context.Context, dest any, query string, args ...any) (err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() err = d.conn.GetContext(ctx, dest, query, args...) return } // Row get row with named args func (d *DB) Row(dest any, query string, args any) (err error) { err = d.Connect() if err != nil { return } defer d.Close() nstmt := &sqlx.NamedStmt{} nstmt, err = d.conn.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Get(dest, args) return } // RowContext get row with named args func (d *DB) RowContext(ctx context.Context, dest any, query string, args any) (err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() nstmt := &sqlx.NamedStmt{} nstmt, err = d.conn.PrepareNamedContext(ctx, query) if err != nil { return } defer nstmt.Close() err = nstmt.GetContext(ctx, dest, args) return } // * // In expands slice values in args, returning the modified query string and a new arg list that can be executed by a database. The `query` should use the `?` bindVar. The return value uses the `?` bindVar. func (d *DB) In(query string, args ...any) (q string, params []any, err error) { err = d.Connect() if err != nil { return } defer d.Close() var s string s, params, err = sqlx.In(query, args) q = d.conn.Rebind(s) return } //*/ // InsertReply insert and return DbReply func (d *DB) InsertReply(query string, args any) (reply Reply) { var ( err error rs sql.Result ) err = d.Connect() if err != nil { reply = ReplyFaild(ErrNotConnect, err, errors.New(`数据库连接错误`)) return } defer d.Close() if rs, err = d.conn.NamedExec(query, args); err == nil { a, _ := rs.RowsAffected() n, _ := rs.LastInsertId() reply = ReplyOk(a, n) } else { reply = ReplyFaild(ErrException, err, errors.New(`数据执行错误`)) } return } // UpdateReply update/delete and return DbReply func (d *DB) UpdateReply(query string, args any) (reply Reply) { var ( err error rs sql.Result ) err = d.Connect() if err != nil { reply = ReplyFaild(ErrNotConnect, err, errors.New(`数据库连接错误`)) return } defer d.Close() if rs, err = d.conn.NamedExec(query, args); err == nil { a, _ := rs.RowsAffected() reply = ReplyOk(a, 0) } else { reply = ReplyFaild(ErrException, err, errors.New(`数据执行错误`)) } return } // Insert insert into with named args func (d *DB) Insert(query string, args any) (lastInsertId, rowsAffected int64, err error) { err = d.Connect() if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.NamedExec(query, args); err == nil { lastInsertId, _ = rs.LastInsertId() rowsAffected, _ = rs.RowsAffected() } return } // InsertContext insert into with named args func (d *DB) InsertContext(ctx context.Context, query string, args any) (lastInsertId, rowsAffected int64, err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil { lastInsertId, _ = rs.LastInsertId() rowsAffected, _ = rs.RowsAffected() } return } // Update update/delete with named args func (d *DB) Update(query string, args any) (rowsAffected int64, err error) { err = d.Connect() if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.NamedExec(query, args); err == nil { rowsAffected, _ = rs.RowsAffected() } return } // Update update/delete with named args func (d *DB) UpdateContext(ctx context.Context, query string, args any) (rowsAffected int64, err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil { rowsAffected, _ = rs.RowsAffected() } return } // Exec exec func (d *DB) Exec(query string, args ...any) (lastInsertId, rowsAffected int64, err error) { err = d.Connect() if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.Exec(query, args...); err == nil { lastInsertId, _ = rs.LastInsertId() rowsAffected, _ = rs.RowsAffected() } return } // ExecContext exec func (d *DB) ExecContext(ctx context.Context, query string, args ...any) (lastInsertId, rowsAffected int64, err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.ExecContext(ctx, query, args...); err == nil { lastInsertId, _ = rs.LastInsertId() rowsAffected, _ = rs.RowsAffected() } return } // Exec exec, with named args func (d *DB) NamedExec(query string, args any) (lastInsertId, rowsAffected int64, err error) { err = d.Connect() if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.NamedExec(query, args); err == nil { lastInsertId, _ = rs.LastInsertId() rowsAffected, _ = rs.RowsAffected() } return } // NamedExecContext exec, with named args func (d *DB) NamedExecContext(ctx context.Context, query string, args any) (lastInsertId, rowsAffected int64, err error) { err = d.ConnectContext(ctx) if err != nil { return } defer d.Close() var rs sql.Result if rs, err = d.conn.NamedExecContext(ctx, query, args); err == nil { lastInsertId, _ = rs.LastInsertId() rowsAffected, _ = rs.RowsAffected() } return } // Limit MySQL limit func (d *DB) Limit(page, pagesize int) string { /* // MySQL limit 0, size if d.Driver == `mysql` { return fmt.Sprintf(" LIMIT %d, %d", (page-1)*pagesize, pagesize) } // // PostgreSQL limit size offset 0 // */ return fmt.Sprintf(" LIMIT %d OFFSET %d", pagesize, (page-1)*pagesize) } // Commit commits the transaction. func (t *Tx) Commit() error { return t.tx.Commit() } // Rollback aborts the transaction. func (t *Tx) Rollback() error { return t.tx.Rollback() } // NamedExec executes a query that doesn't return rows. For example: an INSERT and UPDATE. with named args func (t *Tx) NamedExec(query string, args any) (lastInsertId, rowsAffected int64, err error) { var rs sql.Result if rs, err = t.tx.NamedExec(query, args); err == nil { rowsAffected, _ = rs.RowsAffected() lastInsertId, _ = rs.LastInsertId() } return } // NamedExecContext executes a query that doesn't return rows. For example: an INSERT and UPDATE. with named args func (t *Tx) NamedExecContext(ctx context.Context, query string, args any) (lastInsertId, rowsAffected int64, err error) { var rs sql.Result if rs, err = t.tx.NamedExecContext(ctx, query, args); err == nil { rowsAffected, _ = rs.RowsAffected() lastInsertId, _ = rs.LastInsertId() } return } // Exec executes a query that doesn't return rows. For example: an INSERT and UPDATE. func (t *Tx) Exec(query string, args ...any) (lastInsertId, rowsAffected int64, err error) { var rs sql.Result if rs, err = t.tx.Exec(query, args...); err == nil { rowsAffected, _ = rs.RowsAffected() lastInsertId, _ = rs.LastInsertId() } return } // ExecContext executes a query that doesn't return rows. For example: an INSERT and UPDATE. func (t *Tx) ExecContext(ctx context.Context, query string, args ...any) (lastInsertId, rowsAffected int64, err error) { var rs sql.Result if rs, err = t.tx.ExecContext(ctx, query, args...); err == nil { rowsAffected, _ = rs.RowsAffected() lastInsertId, _ = rs.LastInsertId() } return } // Preparex a statement within a transaction. func (t *Tx) Preparex(query string) (stmt *Stmt, err error) { stmt, err = t.tx.Preparex(query) return } // PreparexContext a statement within a transaction. func (t *Tx) PreparexContext(ctx context.Context, query string) (stmt *Stmt, err error) { stmt, err = t.tx.PreparexContext(ctx, query) return } // PrepareNamed returns an sqlx.NamedStmt func (t *Tx) PrepareNamed(query string) (stmt *NamedStmt, err error) { stmt, err = t.tx.PrepareNamed(query) return } // PrepareNamedContext returns an sqlx.NamedStmt func (t *Tx) PrepareNamedContext(ctx context.Context, query string) (stmt *NamedStmt, err error) { stmt, err = t.tx.PrepareNamedContext(ctx, query) return } // Query executes a query that returns rows, typically a SELECT. with named args func (t *Tx) Query(dest any, query string, args any) (err error) { nstmt := &sqlx.NamedStmt{} nstmt, err = t.tx.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Select(dest, args) return } // QueryContext executes a query that returns rows, typically a SELECT. with named args func (t *Tx) QueryContext(ctx context.Context, dest any, query string, args any) (err error) { nstmt := &sqlx.NamedStmt{} nstmt, err = t.tx.PrepareNamedContext(ctx, query) if err != nil { return } defer nstmt.Close() err = nstmt.SelectContext(ctx, dest, args) return } // QueryRow executes a query that returns rows, typically a SELECT. with named args func (t *Tx) QueryRow(dest any, query string, args any) (err error) { nstmt := &sqlx.NamedStmt{} nstmt, err = t.tx.PrepareNamed(query) if err != nil { return err } defer nstmt.Close() err = nstmt.Get(dest, args) return } // QueryRowContext get row with named args func (t *Tx) QueryRowContext(ctx context.Context, dest any, query string, args any) (err error) { nstmt := &sqlx.NamedStmt{} nstmt, err = t.tx.PrepareNamedContext(ctx, query) if err != nil { return err } defer nstmt.Close() err = nstmt.GetContext(ctx, dest, args) return } // Stats returns database statistics. func Stats() (s sql.DBStats, err error) { defaultDB.conn, err = connect() if err != nil { return } s = defaultDB.Stats() return } // Ping ping connect func Ping() (err error) { defaultDB.conn, err = connect() if err != nil { return } err = defaultDB.Ping() return } // PingContext ping connect func PingContext(ctx context.Context) (err error) { defaultDB.conn, err = connectContext(ctx) if err != nil { return } err = defaultDB.PingContext(ctx) return } // Select select func Select(dest any, query string, args ...any) (err error) { defaultDB.conn, err = connect() if err != nil { return err } err = defaultDB.conn.Select(dest, query, args...) return } // Query get rows with named args func Query(dest any, query string, args any) (err error) { defaultDB.conn, err = connect() if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Select(dest, args) return } // QueryContext get rows with named args func QueryContext(ctx context.Context, dest any, query string, args any) (err error) { defaultDB.conn, err = connectContext(ctx) if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query) if err != nil { return } defer nstmt.Close() err = nstmt.SelectContext(ctx, dest, args) return } // Rows get rows with named args func Rows(dest any, query string, args any) (err error) { defaultDB.conn, err = connect() if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Select(dest, args) return } // RowsContext get rows with named args func RowsContext(ctx context.Context, dest any, query string, args any) (err error) { defaultDB.conn, err = connectContext(ctx) if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query) if err != nil { return } defer nstmt.Close() err = nstmt.SelectContext(ctx, dest, args) return } // Get get func Get(dest any, query string, args ...any) (err error) { defaultDB.conn, err = connect() if err != nil { return } err = defaultDB.conn.Get(dest, query, args...) return } // QueryRow get row with named args func QueryRow(dest any, query string, args any) (err error) { defaultDB.conn, err = connect() if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Get(dest, args) return } // QueryRowContext get row with named args func QueryRowContext(ctx context.Context, dest any, query string, args any) (err error) { defaultDB.conn, err = connectContext(ctx) if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query) if err != nil { return } defer nstmt.Close() err = nstmt.GetContext(ctx, dest, args) return } // Row get row with named args func Row(dest any, query string, args any) (err error) { defaultDB.conn, err = connect() if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamed(query) if err != nil { return } defer nstmt.Close() err = nstmt.Get(dest, args) return } // RowContext get row with named args func RowContext(ctx context.Context, dest any, query string, args any) (err error) { defaultDB.conn, err = connectContext(ctx) if err != nil { return } nstmt := &sqlx.NamedStmt{} nstmt, err = defaultDB.conn.PrepareNamedContext(ctx, query) if err != nil { return } defer nstmt.Close() err = nstmt.GetContext(ctx, dest, args) return } /* // In expands slice values in args, returning the modified query string and a new arg list that can be executed by a database. The `query` should use the `?` bindVar. The return value uses the `?` bindVar. func In(query string, args ...any) (q string, params []any, err error) { defaultDB.conn, err = connect() if err != nil { return } q, params, err = defaultDB.In(query, args...) return } // */ // Exec exec func Exec(query string, args ...any) (lastInsertId, rowsAffected int64, err error) { defaultDB.conn, err = connect() if err != nil { return } lastInsertId, rowsAffected, err = defaultDB.Exec(query, args...) return } // Exec exec func ExecContext(ctx context.Context, query string, args ...any) (lastInsertId, rowsAffected int64, err error) { defaultDB.conn, err = connectContext(ctx) if err != nil { return } lastInsertId, rowsAffected, err = defaultDB.ExecContext(ctx, query, args...) return } // NamedExec exec with named args func NamedExec(query string, args any) (lastInsertId, rowsAffected int64, err error) { defaultDB.conn, err = connect() if err != nil { return } lastInsertId, rowsAffected, err = defaultDB.NamedExec(query, args) return } // NamedExecContext exec with named args func NamedExecContext(ctx context.Context, query string, args any) (lastInsertId, rowsAffected int64, err error) { defaultDB.conn, err = connectContext(ctx) if err != nil { return } lastInsertId, rowsAffected, err = defaultDB.NamedExecContext(ctx, query, args) return }