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  }