From f3700a772da3ba62743f29d10ab7f5dfc2ae7505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Wed, 7 Dec 2022 23:21:36 +0100 Subject: [PATCH] v0.0.30 --- go.mod | 2 ++ go.sum | 5 +++ sq/database.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++ sq/listener.go | 10 ++++++ sq/params.go | 3 ++ sq/queryable.go | 12 +++++++ sq/transaction.go | 69 +++++++++++++++++++++++++++++++++++++ 7 files changed, 189 insertions(+) create mode 100644 sq/database.go create mode 100644 sq/listener.go create mode 100644 sq/params.go create mode 100644 sq/queryable.go create mode 100644 sq/transaction.go diff --git a/go.mod b/go.mod index aa6fc5a..57df7ed 100644 --- a/go.mod +++ b/go.mod @@ -6,3 +6,5 @@ require ( golang.org/x/sys v0.1.0 golang.org/x/term v0.1.0 ) + +require github.com/jmoiron/sqlx v1.3.5 // indirect diff --git a/go.sum b/go.sum index 24ae6a6..7fab807 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,8 @@ +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= diff --git a/sq/database.go b/sq/database.go new file mode 100644 index 0000000..1ccb375 --- /dev/null +++ b/sq/database.go @@ -0,0 +1,88 @@ +package sq + +import ( + "context" + "database/sql" + "github.com/jmoiron/sqlx" + "sync" +) + +type DB interface { + Exec(ctx context.Context, sql string, prep PP) (sql.Result, error) + Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error) + Ping(ctx context.Context) error + BeginTransaction(ctx context.Context, iso sql.IsolationLevel) (Tx, error) +} + +type database struct { + db *sqlx.DB + txctr uint16 + lock sync.Mutex + lstr Listener +} + +func NewDB(db *sqlx.DB) DB { + return &database{ + db: db, + txctr: 0, + lock: sync.Mutex{}, + } +} + +func (db *database) SetListener(listener Listener) { + db.lstr = listener +} + +func (db *database) Exec(ctx context.Context, sql string, prep PP) (sql.Result, error) { + if db.lstr != nil { + db.lstr.OnExec(nil, sql, &prep) + } + + res, err := db.db.NamedExecContext(ctx, sql, prep) + if err != nil { + return nil, err + } + return res, nil +} + +func (db *database) Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error) { + if db.lstr != nil { + db.lstr.OnQuery(nil, sql, &prep) + } + + rows, err := db.db.NamedQueryContext(ctx, sql, prep) + if err != nil { + return nil, err + } + return rows, nil +} + +func (db *database) Ping(ctx context.Context) error { + if db.lstr != nil { + db.lstr.OnPing() + } + + err := db.db.PingContext(ctx) + if err != nil { + return err + } + return nil +} + +func (db *database) BeginTransaction(ctx context.Context, iso sql.IsolationLevel) (Tx, error) { + db.lock.Lock() + txid := db.txctr + db.txctr += 1 // with overflow ! + db.lock.Unlock() + + if db.lstr != nil { + db.lstr.OnTxBegin(txid) + } + + xtx, err := db.db.BeginTxx(ctx, &sql.TxOptions{Isolation: iso}) + if err != nil { + return nil, err + } + + return NewTransaction(xtx, txid, db.lstr), nil +} diff --git a/sq/listener.go b/sq/listener.go new file mode 100644 index 0000000..9d27bc3 --- /dev/null +++ b/sq/listener.go @@ -0,0 +1,10 @@ +package sq + +type Listener interface { + OnQuery(txID *uint16, sql string, params *PP) + OnExec(txID *uint16, sql string, params *PP) + OnPing() + OnTxBegin(txid uint16) + OnTxCommit(txid uint16) + OnTxRollback(txid uint16) +} diff --git a/sq/params.go b/sq/params.go new file mode 100644 index 0000000..33cc03c --- /dev/null +++ b/sq/params.go @@ -0,0 +1,3 @@ +package sq + +type PP map[string]any diff --git a/sq/queryable.go b/sq/queryable.go new file mode 100644 index 0000000..07aee31 --- /dev/null +++ b/sq/queryable.go @@ -0,0 +1,12 @@ +package sq + +import ( + "context" + "database/sql" + "github.com/jmoiron/sqlx" +) + +type Queryable interface { + Exec(ctx context.Context, sql string, prep PP) (sql.Result, error) + Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error) +} diff --git a/sq/transaction.go b/sq/transaction.go new file mode 100644 index 0000000..be2e539 --- /dev/null +++ b/sq/transaction.go @@ -0,0 +1,69 @@ +package sq + +import ( + "context" + "database/sql" + "github.com/jmoiron/sqlx" + "gogs.mikescher.com/BlackForestBytes/goext/langext" +) + +type Tx interface { + Rollback() error + Commit() error + Exec(ctx context.Context, sql string, prep PP) (sql.Result, error) + Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error) +} + +type transaction struct { + tx *sqlx.Tx + id uint16 + lstr Listener +} + +func NewTransaction(xtx *sqlx.Tx, txid uint16, lstr Listener) Tx { + return &transaction{ + tx: xtx, + id: txid, + lstr: lstr, + } +} + +func (tx *transaction) Rollback() error { + if tx.lstr != nil { + tx.lstr.OnTxRollback(tx.id) + } + + return tx.tx.Rollback() +} + +func (tx *transaction) Commit() error { + if tx.lstr != nil { + tx.lstr.OnTxCommit(tx.id) + } + + return tx.tx.Commit() +} + +func (tx *transaction) Exec(ctx context.Context, sql string, prep PP) (sql.Result, error) { + if tx.lstr != nil { + tx.lstr.OnExec(langext.Ptr(tx.id), sql, &prep) + } + + res, err := tx.tx.NamedExecContext(ctx, sql, prep) + if err != nil { + return nil, err + } + return res, nil +} + +func (tx *transaction) Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error) { + if tx.lstr != nil { + tx.lstr.OnQuery(langext.Ptr(tx.id), sql, &prep) + } + + rows, err := sqlx.NamedQueryContext(ctx, tx.tx, sql, prep) + if err != nil { + return nil, err + } + return rows, nil +}