gdb_statement.go 5.1 KB

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