github.com/gogf/gf@v1.16.9/database/gdb/gdb_statement.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package gdb 8 9 import ( 10 "context" 11 "database/sql" 12 "github.com/gogf/gf/errors/gcode" 13 14 "github.com/gogf/gf/errors/gerror" 15 "github.com/gogf/gf/os/gtime" 16 ) 17 18 // Stmt is a prepared statement. 19 // A Stmt is safe for concurrent use by multiple goroutines. 20 // 21 // If a Stmt is prepared on a Tx or Conn, it will be bound to a single 22 // underlying connection forever. If the Tx or Conn closes, the Stmt will 23 // become unusable and all operations will return an error. 24 // If a Stmt is prepared on a DB, it will remain usable for the lifetime of the 25 // DB. When the Stmt needs to execute on a new underlying connection, it will 26 // prepare itself on the new connection automatically. 27 type Stmt struct { 28 *sql.Stmt 29 core *Core 30 link Link 31 sql string 32 } 33 34 const ( 35 stmtTypeExecContext = "Statement.ExecContext" 36 stmtTypeQueryContext = "Statement.QueryContext" 37 stmtTypeQueryRowContext = "Statement.QueryRowContext" 38 ) 39 40 // doStmtCommit commits statement according to given `stmtType`. 41 func (s *Stmt) doStmtCommit(ctx context.Context, stmtType string, args ...interface{}) (result interface{}, err error) { 42 var ( 43 cancelFuncForTimeout context.CancelFunc 44 timestampMilli1 = gtime.TimestampMilli() 45 ) 46 switch stmtType { 47 case stmtTypeExecContext: 48 ctx, cancelFuncForTimeout = s.core.GetCtxTimeout(ctxTimeoutTypeExec, ctx) 49 defer cancelFuncForTimeout() 50 result, err = s.Stmt.ExecContext(ctx, args...) 51 52 case stmtTypeQueryContext: 53 ctx, cancelFuncForTimeout = s.core.GetCtxTimeout(ctxTimeoutTypeQuery, ctx) 54 defer cancelFuncForTimeout() 55 result, err = s.Stmt.QueryContext(ctx, args...) 56 57 case stmtTypeQueryRowContext: 58 ctx, cancelFuncForTimeout = s.core.GetCtxTimeout(ctxTimeoutTypeQuery, ctx) 59 defer cancelFuncForTimeout() 60 result = s.Stmt.QueryRowContext(ctx, args...) 61 62 default: 63 panic(gerror.NewCodef(gcode.CodeInvalidParameter, `invalid stmtType: %s`, stmtType)) 64 } 65 var ( 66 timestampMilli2 = gtime.TimestampMilli() 67 sqlObj = &Sql{ 68 Sql: s.sql, 69 Type: stmtType, 70 Args: args, 71 Format: FormatSqlWithArgs(s.sql, args), 72 Error: err, 73 Start: timestampMilli1, 74 End: timestampMilli2, 75 Group: s.core.db.GetGroup(), 76 IsTransaction: s.link.IsTransaction(), 77 } 78 ) 79 // Tracing and logging. 80 s.core.addSqlToTracing(ctx, sqlObj) 81 if s.core.db.GetDebug() { 82 s.core.writeSqlToLogger(ctx, sqlObj) 83 } 84 return result, err 85 } 86 87 // ExecContext executes a prepared statement with the given arguments and 88 // returns a Result summarizing the effect of the statement. 89 func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) { 90 result, err := s.doStmtCommit(ctx, stmtTypeExecContext, args...) 91 if result != nil { 92 return result.(sql.Result), err 93 } 94 return nil, err 95 } 96 97 // QueryContext executes a prepared query statement with the given arguments 98 // and returns the query results as a *Rows. 99 func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error) { 100 result, err := s.doStmtCommit(ctx, stmtTypeQueryContext, args...) 101 if result != nil { 102 return result.(*sql.Rows), err 103 } 104 return nil, err 105 } 106 107 // QueryRowContext executes a prepared query statement with the given arguments. 108 // If an error occurs during the execution of the statement, that error will 109 // be returned by a call to Scan on the returned *Row, which is always non-nil. 110 // If the query selects no rows, the *Row's Scan will return ErrNoRows. 111 // Otherwise, the *Row's Scan scans the first selected row and discards 112 // the rest. 113 func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row { 114 result, _ := s.doStmtCommit(ctx, stmtTypeQueryRowContext, args...) 115 if result != nil { 116 return result.(*sql.Row) 117 } 118 return nil 119 } 120 121 // Exec executes a prepared statement with the given arguments and 122 // returns a Result summarizing the effect of the statement. 123 func (s *Stmt) Exec(args ...interface{}) (sql.Result, error) { 124 return s.ExecContext(context.Background(), args...) 125 } 126 127 // Query executes a prepared query statement with the given arguments 128 // and returns the query results as a *Rows. 129 func (s *Stmt) Query(args ...interface{}) (*sql.Rows, error) { 130 return s.QueryContext(context.Background(), args...) 131 } 132 133 // QueryRow executes a prepared query statement with the given arguments. 134 // If an error occurs during the execution of the statement, that error will 135 // be returned by a call to Scan on the returned *Row, which is always non-nil. 136 // If the query selects no rows, the *Row's Scan will return ErrNoRows. 137 // Otherwise, the *Row's Scan scans the first selected row and discards 138 // the rest. 139 // 140 // Example usage: 141 // 142 // var name string 143 // err := nameByUseridStmt.QueryRow(id).Scan(&name) 144 func (s *Stmt) QueryRow(args ...interface{}) *sql.Row { 145 return s.QueryRowContext(context.Background(), args...) 146 } 147 148 // Close closes the statement. 149 func (s *Stmt) Close() error { 150 return s.Stmt.Close() 151 }