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  }