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