Files
李建琦 6d655c9903
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
2026-08-21 18:30:13 +08:00

312 lines
8.6 KiB
Go

package repository
import (
"context"
"database/sql/driver"
"errors"
"io"
"reflect"
"time"
"github.com/Wei-Shaw/sub2api/internal/pkg/servertiming"
)
type serverTimingConnector struct {
base driver.Connector
}
func newServerTimingConnector(base driver.Connector) driver.Connector {
return &serverTimingConnector{base: base}
}
func (c *serverTimingConnector) Connect(ctx context.Context) (driver.Conn, error) {
startedAt := time.Now()
conn, err := c.base.Connect(ctx)
servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
if err != nil {
return nil, err
}
return &serverTimingConn{Conn: conn}, nil
}
func (c *serverTimingConnector) Driver() driver.Driver {
return c.base.Driver()
}
type serverTimingConn struct {
driver.Conn
}
func (c *serverTimingConn) Prepare(query string) (driver.Stmt, error) {
stmt, err := c.Conn.Prepare(query)
if err != nil {
return nil, err
}
return &serverTimingStmt{Stmt: stmt}, nil
}
func (c *serverTimingConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
startedAt := time.Now()
var (
stmt driver.Stmt
err error
)
if preparer, ok := c.Conn.(driver.ConnPrepareContext); ok {
stmt, err = preparer.PrepareContext(ctx, query)
} else {
stmt, err = c.Conn.Prepare(query)
}
servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
if err != nil {
return nil, err
}
return &serverTimingStmt{Stmt: stmt}, nil
}
func (c *serverTimingConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
execer, ok := c.Conn.(driver.ExecerContext)
if !ok {
return nil, driver.ErrSkip
}
startedAt := time.Now()
result, err := execer.ExecContext(ctx, query, args)
servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
return result, err
}
func (c *serverTimingConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
queryer, ok := c.Conn.(driver.QueryerContext)
if !ok {
return nil, driver.ErrSkip
}
startedAt := time.Now()
rows, err := queryer.QueryContext(ctx, query, args)
servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
if err != nil || rows == nil {
return rows, err
}
return newServerTimingRows(ctx, rows), nil
}
func (c *serverTimingConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
startedAt := time.Now()
var (
tx driver.Tx
err error
)
if beginner, ok := c.Conn.(driver.ConnBeginTx); ok {
tx, err = beginner.BeginTx(ctx, opts)
} else {
if opts.Isolation != driver.IsolationLevel(0) {
return nil, errors.New("driver does not support non-default isolation")
}
if opts.ReadOnly {
return nil, errors.New("driver does not support read-only transactions")
}
// The wrapper exposes ConnBeginTx, so it must retain database/sql's
// legacy fallback for drivers that only implement Conn.Begin.
tx, err = c.Conn.Begin() //nolint:staticcheck // Required driver compatibility fallback.
}
servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
if err != nil || tx == nil {
return tx, err
}
return &serverTimingTx{Tx: tx, ctx: ctx}, nil
}
func (c *serverTimingConn) Ping(ctx context.Context) error {
if pinger, ok := c.Conn.(driver.Pinger); ok {
startedAt := time.Now()
err := pinger.Ping(ctx)
servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
return err
}
return nil
}
func (c *serverTimingConn) ResetSession(ctx context.Context) error {
if resetter, ok := c.Conn.(driver.SessionResetter); ok {
startedAt := time.Now()
err := resetter.ResetSession(ctx)
servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
return err
}
return nil
}
func (c *serverTimingConn) IsValid() bool {
if validator, ok := c.Conn.(driver.Validator); ok {
return validator.IsValid()
}
return true
}
func (c *serverTimingConn) CheckNamedValue(value *driver.NamedValue) error {
if checker, ok := c.Conn.(driver.NamedValueChecker); ok {
return checker.CheckNamedValue(value)
}
return driver.ErrSkip
}
type serverTimingStmt struct {
driver.Stmt
}
func (s *serverTimingStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
startedAt := time.Now()
var (
result driver.Result
err error
)
if execer, ok := s.Stmt.(driver.StmtExecContext); ok {
result, err = execer.ExecContext(ctx, args)
} else {
var values []driver.Value
values, err = namedValues(args)
if err == nil {
// The wrapper exposes StmtExecContext and must preserve the fallback
// database/sql would use for a legacy driver statement.
result, err = s.Stmt.Exec(values) //nolint:staticcheck // Required driver compatibility fallback.
}
}
servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
return result, err
}
func (s *serverTimingStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
startedAt := time.Now()
var (
rows driver.Rows
err error
)
if queryer, ok := s.Stmt.(driver.StmtQueryContext); ok {
rows, err = queryer.QueryContext(ctx, args)
} else {
var values []driver.Value
values, err = namedValues(args)
if err == nil {
// The wrapper exposes StmtQueryContext and must preserve the fallback
// database/sql would use for a legacy driver statement.
rows, err = s.Stmt.Query(values) //nolint:staticcheck // Required driver compatibility fallback.
}
}
servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
if err != nil || rows == nil {
return rows, err
}
return newServerTimingRows(ctx, rows), nil
}
func (s *serverTimingStmt) CheckNamedValue(value *driver.NamedValue) error {
if checker, ok := s.Stmt.(driver.NamedValueChecker); ok {
return checker.CheckNamedValue(value)
}
return driver.ErrSkip
}
func namedValues(args []driver.NamedValue) ([]driver.Value, error) {
values := make([]driver.Value, len(args))
for i, arg := range args {
if arg.Name != "" {
return nil, errors.New("named parameters are not supported")
}
values[i] = arg.Value
}
return values, nil
}
type serverTimingRows struct {
driver.Rows
ctx context.Context
}
func newServerTimingRows(ctx context.Context, rows driver.Rows) *serverTimingRows {
return &serverTimingRows{Rows: rows, ctx: ctx}
}
func (r *serverTimingRows) Close() error {
startedAt := time.Now()
err := r.Rows.Close()
servertiming.RecordInterval(r.ctx, servertiming.MetricDatabase, startedAt, time.Now())
return err
}
func (r *serverTimingRows) Next(dest []driver.Value) error {
startedAt := time.Now()
err := r.Rows.Next(dest)
servertiming.RecordInterval(r.ctx, servertiming.MetricDatabase, startedAt, time.Now())
return err
}
func (r *serverTimingRows) HasNextResultSet() bool {
if rows, ok := r.Rows.(driver.RowsNextResultSet); ok {
return rows.HasNextResultSet()
}
return false
}
func (r *serverTimingRows) NextResultSet() error {
rows, ok := r.Rows.(driver.RowsNextResultSet)
if !ok {
return io.EOF
}
startedAt := time.Now()
err := rows.NextResultSet()
servertiming.RecordInterval(r.ctx, servertiming.MetricDatabase, startedAt, time.Now())
return err
}
func (r *serverTimingRows) ColumnTypeScanType(index int) reflect.Type {
if rows, ok := r.Rows.(driver.RowsColumnTypeScanType); ok {
return rows.ColumnTypeScanType(index)
}
return reflect.TypeOf(new(any)).Elem()
}
func (r *serverTimingRows) ColumnTypeDatabaseTypeName(index int) string {
if rows, ok := r.Rows.(driver.RowsColumnTypeDatabaseTypeName); ok {
return rows.ColumnTypeDatabaseTypeName(index)
}
return ""
}
func (r *serverTimingRows) ColumnTypeLength(index int) (int64, bool) {
if rows, ok := r.Rows.(driver.RowsColumnTypeLength); ok {
return rows.ColumnTypeLength(index)
}
return 0, false
}
func (r *serverTimingRows) ColumnTypeNullable(index int) (bool, bool) {
if rows, ok := r.Rows.(driver.RowsColumnTypeNullable); ok {
return rows.ColumnTypeNullable(index)
}
return false, false
}
func (r *serverTimingRows) ColumnTypePrecisionScale(index int) (int64, int64, bool) {
if rows, ok := r.Rows.(driver.RowsColumnTypePrecisionScale); ok {
return rows.ColumnTypePrecisionScale(index)
}
return 0, 0, false
}
type serverTimingTx struct {
driver.Tx
ctx context.Context
}
func (t *serverTimingTx) Commit() error {
startedAt := time.Now()
err := t.Tx.Commit()
servertiming.RecordInterval(t.ctx, servertiming.MetricDatabase, startedAt, time.Now())
return err
}
func (t *serverTimingTx) Rollback() error {
startedAt := time.Now()
err := t.Tx.Rollback()
servertiming.RecordInterval(t.ctx, servertiming.MetricDatabase, startedAt, time.Now())
return err
}