-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathdriver.go
170 lines (142 loc) · 3.85 KB
/
driver.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package otsql
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"strconv"
"sync"
"time"
)
var regMu sync.Mutex
var (
// Compile time assertions
_ driver.Driver = &otDriver{}
_ driver.Result = &otResult{}
_ driver.Stmt = &otStmt{}
_ driver.StmtExecContext = &otStmt{}
_ driver.StmtQueryContext = &otStmt{}
_ driver.Rows = &otRows{}
)
// Register initializes and registers our otsql wrapped database driver
// identified by its driverName and using provided Options. On success it
// returns the generated driverName to use when calling sql.Open.
// It is possible to register multiple wrappers for the same database driver if
// needing different Options for different connections.
func Register(driverName string, options ...Option) (string, error) {
db, err := sql.Open(driverName, "")
if err != nil {
return "", err
}
dri := db.Driver()
if err = db.Close(); err != nil {
return "", err
}
regMu.Lock()
defer regMu.Unlock()
driverName = driverName + "-j2gg0s-otsql-"
for i := 0; i < 100; i++ {
exist := false
regName := driverName + strconv.Itoa(i)
for _, name := range sql.Drivers() {
if name == regName {
exist = true
break
}
}
if !exist {
sql.Register(regName, Wrap(dri, options...))
return regName, nil
}
}
return "", errors.New("unable to register driver, all slots have been taken")
}
// Wrap takes a SQL driver and wraps it with hook enabled.
func Wrap(dri driver.Driver, opts ...Option) driver.Driver {
return wrapDriver(dri, newOptions(opts))
}
type otConnector struct {
dc driver.Connector
dri driver.Driver
*Options
}
func (oc otConnector) Connect(ctx context.Context) (conn driver.Conn, err error) {
evt := newEvent(oc.Options, "", MethodCreateConn, "", nil)
before(oc.Hooks, ctx, evt)
id := fmt.Sprintf("%d", time.Now().UnixNano())
defer func() {
evt.Err = err
evt.Conn = id
after(oc.Hooks, ctx, evt)
}()
conn, err = oc.dc.Connect(ctx)
if err != nil {
return nil, err
}
return wrapConn(id, conn, oc.Options), nil
}
func (oc otConnector) Driver() driver.Driver {
return oc.dri
}
// WrapConnector allows wrapping a database driver.Connector which eliminates
// the need to register otsql as an available driver.Driver.
func WrapConnector(dc driver.Connector, opts ...Option) driver.Connector {
o := newOptions(opts)
return &otConnector{
dc: dc,
dri: wrapDriver(dc.Driver(), o),
Options: o,
}
}
// WrapConn allows an existing driver.Conn to be wrapped by otsql.
func WrapConn(c driver.Conn, opts ...Option) driver.Conn {
return wrapConn(fmt.Sprintf("%d", time.Now().UnixNano()), c, newOptions(opts))
}
func wrapDriver(dri driver.Driver, o *Options) driver.Driver {
if _, ok := dri.(driver.DriverContext); ok {
return otDriver{Driver: dri, Options: o}
}
return struct{ driver.Driver }{otDriver{Driver: dri, Options: o}}
}
type otDriver struct {
driver.Driver
*Options
}
func (d otDriver) Open(name string) (conn driver.Conn, err error) {
ctx := context.Background()
evt := newEvent(d.Options, "", MethodCreateConn, "", nil)
before(d.Hooks, ctx, evt)
id := fmt.Sprintf("%d", time.Now().UnixNano())
defer func() {
evt.Err = err
evt.Conn = id
after(d.Hooks, ctx, evt)
}()
conn, err = d.Driver.Open(name)
if err != nil {
return nil, err
}
return wrapConn(id, conn, addInstance(d.Options, name)), nil
}
func (d otDriver) OpenConnector(name string) (driver.Connector, error) {
connector, err := d.Driver.(driver.DriverContext).OpenConnector(name)
if err != nil {
return nil, err
}
return &otConnector{
dc: connector,
dri: d,
Options: addInstance(d.Options, name),
}, nil
}
func addInstance(o *Options, dsn string) *Options {
instance, database := parseDSN(dsn)
if o.Instance == "" && instance != "" {
o.Instance = instance
}
if database != "" {
o.Database = database
}
return o
}