go.undefinedlabs.com/scopeagent@v0.4.2/instrumentation/sql/conn.go (about) 1 package sql 2 3 import ( 4 "context" 5 "database/sql/driver" 6 "errors" 7 ) 8 9 type instrumentedConn struct { 10 conn driver.Conn 11 configuration *driverConfiguration 12 } 13 14 // ErrUnsupported is an error returned when the underlying driver doesn't provide a given function. 15 var ErrUnsupported = errors.New("operation unsupported by the underlying driver") 16 17 // Prepare returns a prepared statement, bound to this connection. 18 func (c *instrumentedConn) Prepare(query string) (driver.Stmt, error) { 19 stmt, err := c.conn.Prepare(query) 20 if err != nil { 21 return nil, err 22 } 23 return &instrumentedStmt{ 24 stmt: stmt, 25 configuration: c.configuration, 26 }, nil 27 } 28 29 // PrepareContext returns a prepared statement, bound to this connection. 30 // context is for the preparation of the statement, 31 // it must not store the context within the statement itself. 32 func (c *instrumentedConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { 33 if connPrepareContext, ok := c.conn.(driver.ConnPrepareContext); ok { 34 stmt, err := connPrepareContext.PrepareContext(ctx, query) 35 if err != nil { 36 return nil, err 37 } 38 return &instrumentedStmt{ 39 stmt: stmt, 40 configuration: c.configuration, 41 }, nil 42 } 43 return c.conn.Prepare(query) 44 } 45 46 // Close invalidates and potentially stops any current 47 // prepared statements and transactions, marking this 48 // connection as no longer in use. 49 // 50 // Because the sql package maintains a free pool of 51 // connections and only calls Close when there's a surplus of 52 // idle connections, it shouldn't be necessary for drivers to 53 // do their own connection caching. 54 func (c *instrumentedConn) Close() error { 55 return c.conn.Close() 56 } 57 58 // Begin starts and returns a new transaction. 59 // 60 // Deprecated: Drivers should implement ConnBeginTx instead (or additionally). 61 func (c *instrumentedConn) Begin() (driver.Tx, error) { 62 tx, err := c.conn.Begin() 63 if err != nil { 64 return nil, err 65 } 66 return &instrumentedTx{ 67 tx: tx, 68 configuration: c.configuration, 69 span: nil, 70 }, nil 71 } 72 73 // BeginTx starts and returns a new transaction. 74 // If the context is canceled by the user the sql package will 75 // call Tx.Rollback before discarding and closing the connection. 76 // 77 // This must check opts.Isolation to determine if there is a set 78 // isolation level. If the driver does not support a non-default 79 // level and one is set or if there is a non-default isolation level 80 // that is not supported, an error must be returned. 81 // 82 // This must also check opts.ReadOnly to determine if the read-only 83 // value is true to either set the read-only transaction property if supported 84 // or return an error if it is not supported. 85 func (c *instrumentedConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { 86 s := c.configuration.newSpan("BeginTx", "", nil, c.configuration, ctx) 87 if connBeginTx, ok := c.conn.(driver.ConnBeginTx); ok { 88 tx, err := connBeginTx.BeginTx(ctx, opts) 89 if err != nil { 90 return nil, err 91 } 92 return &instrumentedTx{ 93 tx: tx, 94 configuration: c.configuration, 95 span: s, 96 }, nil 97 } 98 return c.conn.Begin() 99 } 100 101 // Execer is an optional interface that may be implemented by a Conn. 102 // 103 // If a Conn implements neither ExecerContext nor Execer, 104 // the sql package's DB.Exec will first prepare a query, execute the statement, 105 // and then close the statement. 106 // 107 // Exec may return ErrSkip. 108 // 109 // Deprecated: Drivers should implement ExecerContext instead. 110 func (c *instrumentedConn) Exec(query string, args []driver.Value) (driver.Result, error) { 111 if execer, ok := c.conn.(driver.Execer); ok { 112 return execer.Exec(query, args) 113 } 114 return nil, ErrUnsupported 115 } 116 117 // ExecerContext is an optional interface that may be implemented by a Conn. 118 // 119 // If a Conn does not implement ExecerContext, the sql package's DB.Exec 120 // will fall back to Execer; if the Conn does not implement Execer either, 121 // DB.Exec will first prepare a query, execute the statement, and then 122 // close the statement. 123 // 124 // ExecerContext may return ErrSkip. 125 // 126 // ExecerContext must honor the context timeout and return when the context is canceled. 127 func (c *instrumentedConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 128 s := c.configuration.newSpan("ExecContext", query, args, c.configuration, ctx) 129 defer s.Finish() 130 if execerContext, ok := c.conn.(driver.ExecerContext); ok { 131 r, err := execerContext.ExecContext(ctx, query, args) 132 return r, err 133 } 134 values, err := namedValueToValue(args) 135 if err != nil { 136 return nil, err 137 } 138 return c.Exec(query, values) 139 } 140 141 // Pinger is an optional interface that may be implemented by a Conn. 142 // 143 // If a Conn does not implement Pinger, the sql package's DB.Ping and 144 // DB.PingContext will check if there is at least one Conn available. 145 // 146 // If Conn.Ping returns ErrBadConn, DB.Ping and DB.PingContext will remove 147 // the Conn from pool. 148 func (c *instrumentedConn) Ping(ctx context.Context) error { 149 if pinger, ok := c.conn.(driver.Pinger); ok { 150 s := c.configuration.newSpan("Ping", "", nil, c.configuration, ctx) 151 defer s.Finish() 152 return pinger.Ping(ctx) 153 } 154 return ErrUnsupported 155 } 156 157 // Queryer is an optional interface that may be implemented by a Conn. 158 // 159 // If a Conn implements neither QueryerContext nor Queryer, 160 // the sql package's DB.Query will first prepare a query, execute the statement, 161 // and then close the statement. 162 // 163 // Query may return ErrSkip. 164 // 165 // Deprecated: Drivers should implement QueryerContext instead. 166 func (c *instrumentedConn) Query(query string, args []driver.Value) (driver.Rows, error) { 167 if queryer, ok := c.conn.(driver.Queryer); ok { 168 return queryer.Query(query, args) 169 } 170 return nil, ErrUnsupported 171 } 172 173 // QueryerContext is an optional interface that may be implemented by a Conn. 174 // 175 // If a Conn does not implement QueryerContext, the sql package's DB.Query 176 // will fall back to Queryer; if the Conn does not implement Queryer either, 177 // DB.Query will first prepare a query, execute the statement, and then 178 // close the statement. 179 // 180 // QueryerContext may return ErrSkip. 181 // 182 // QueryerContext must honor the context timeout and return when the context is canceled. 183 func (c *instrumentedConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) { 184 s := c.configuration.newSpan("QueryContext", query, args, c.configuration, ctx) 185 defer s.Finish() 186 if queryerContext, ok := c.conn.(driver.QueryerContext); ok { 187 rows, err := queryerContext.QueryContext(ctx, query, args) 188 return rows, err 189 } 190 values, err := namedValueToValue(args) 191 if err != nil { 192 return nil, err 193 } 194 return c.Query(query, values) 195 }