├── go.sum ├── go.mod ├── README.md ├── doc.go ├── registry.go ├── querier.go ├── savepoint.go ├── PATENTS ├── LICENSE ├── namer.go ├── pg.go ├── mssql.go ├── quoter.go ├── messages.go └── nest └── nest.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/golang-sql/sqlexp 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang-sql exp 2 | 3 | https://godoc.org/github.com/golang-sql/sqlexp 4 | 5 | All contributions must have a valid golang CLA. 6 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package sqlexp provides interfaces and functions that may be adopted into 6 | // the database/sql package in the future. All features may change or be removed 7 | // in the future. 8 | package sqlexp // imports github.com/golang-sql/sqlexp 9 | -------------------------------------------------------------------------------- /registry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlexp 6 | 7 | import ( 8 | "database/sql/driver" 9 | ) 10 | 11 | var internalDrivers = map[string]driver.Driver{ 12 | "*mssql.MssqlDriver": mssql{}, 13 | "*pq.Driver": postgresql{}, 14 | "*stdlib.Driver": postgresql{}, 15 | } 16 | -------------------------------------------------------------------------------- /querier.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlexp 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | ) 11 | 12 | // Querier is the common interface to execute queries on a DB, Tx, or Conn. 13 | type Querier interface { 14 | ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) 15 | QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) 16 | QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row 17 | } 18 | 19 | var ( 20 | _ Querier = &sql.DB{} 21 | _ Querier = &sql.Tx{} 22 | ) 23 | -------------------------------------------------------------------------------- /savepoint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlexp 6 | 7 | import ( 8 | "database/sql/driver" 9 | "errors" 10 | "reflect" 11 | ) 12 | 13 | type Savepointer interface { 14 | Release(name string) string 15 | Create(name string) string 16 | Rollback(name string) string 17 | } 18 | 19 | type DriverSavepointer interface { 20 | Savepointer() (Savepointer, error) 21 | } 22 | 23 | // SavepointFromDriver 24 | func SavepointFromDriver(d driver.Driver) (Savepointer, error) { 25 | if q, is := d.(DriverSavepointer); is { 26 | return q.Savepointer() 27 | } 28 | dv := reflect.ValueOf(d) 29 | 30 | d, found := internalDrivers[dv.Type().String()] 31 | if found { 32 | if q, is := d.(DriverSavepointer); is { 33 | return q.Savepointer() 34 | } 35 | } 36 | return nil, errors.New("savepointer interface not found") 37 | } 38 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /namer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlexp 6 | 7 | import ( 8 | "context" 9 | "database/sql/driver" 10 | "errors" 11 | "reflect" 12 | ) 13 | 14 | const ( 15 | DialectPostgres = "postgres" 16 | DialectTSQL = "tsql" 17 | DialectMySQL = "mysql" 18 | DialectSQLite = "sqlite" 19 | DialectOracle = "oracle" 20 | ) 21 | 22 | // Namer returns the name of the database and the SQL dialect it 23 | // uses. 24 | type Namer interface { 25 | // Name of the database management system. 26 | // 27 | // Examples: 28 | // "posgresql-9.6" 29 | // "sqlserver-10.54.32" 30 | // "cockroachdb-1.0" 31 | Name() string 32 | 33 | // Dialect of SQL used in the database. 34 | Dialect() string 35 | } 36 | 37 | // DriverNamer may be implemented on the driver.Driver interface. 38 | // It may need to request information from the server to return 39 | // the correct information. 40 | type DriverNamer interface { 41 | Namer(ctx context.Context) (Namer, error) 42 | } 43 | 44 | // NamerFromDriver returns the DriverNamer from the DB if 45 | // it is implemented. 46 | func NamerFromDriver(d driver.Driver, ctx context.Context) (Namer, error) { 47 | if q, is := d.(DriverNamer); is { 48 | return q.Namer(ctx) 49 | } 50 | dv := reflect.ValueOf(d) 51 | 52 | d, found := internalDrivers[dv.Type().String()] 53 | if found { 54 | if q, is := d.(DriverNamer); is { 55 | return q.Namer(ctx) 56 | } 57 | } 58 | return nil, errors.New("namer not found") 59 | } 60 | -------------------------------------------------------------------------------- /pg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlexp 6 | 7 | import ( 8 | "context" 9 | "database/sql/driver" 10 | "fmt" 11 | ) 12 | 13 | type postgresql struct{} 14 | 15 | var ( 16 | _ DriverNamer = postgresql{} 17 | _ DriverQuoter = postgresql{} 18 | _ DriverSavepointer = postgresql{} 19 | ) 20 | 21 | func (postgresql) Open(string) (driver.Conn, error) { 22 | panic("not implemented") 23 | } 24 | 25 | func (postgresql) Namer(ctx context.Context) (Namer, error) { 26 | return pgNamer{}, nil 27 | } 28 | 29 | func (postgresql) Quoter(ctx context.Context) (Quoter, error) { 30 | panic("not implemented") 31 | } 32 | 33 | func (postgresql) Savepointer() (Savepointer, error) { 34 | return pgSavepointer{}, nil 35 | } 36 | 37 | type pgNamer struct{} 38 | 39 | func (pgNamer) Name() string { 40 | return "postgresql" 41 | } 42 | func (pgNamer) Dialect() string { 43 | return DialectPostgres 44 | } 45 | 46 | type pgQuoter struct{} 47 | 48 | func (pgQuoter) ID(name string) string { 49 | return "" 50 | } 51 | func (pgQuoter) Value(v interface{}) string { 52 | return "" 53 | } 54 | 55 | type pgSavepointer struct{} 56 | 57 | func (pgSavepointer) Release(name string) string { 58 | return fmt.Sprintf("release savepoint %s;", name) 59 | } 60 | 61 | func (pgSavepointer) Create(name string) string { 62 | return fmt.Sprintf("savepoint %s;", name) 63 | } 64 | 65 | func (pgSavepointer) Rollback(name string) string { 66 | return fmt.Sprintf("rollback to savepoint %s;", name) 67 | } 68 | -------------------------------------------------------------------------------- /mssql.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlexp 6 | 7 | import ( 8 | "context" 9 | "database/sql/driver" 10 | "fmt" 11 | "strings" 12 | ) 13 | 14 | type mssql struct{} 15 | 16 | var ( 17 | _ DriverNamer = mssql{} 18 | _ DriverQuoter = mssql{} 19 | _ DriverSavepointer = mssql{} 20 | ) 21 | 22 | func (mssql) Open(string) (driver.Conn, error) { 23 | panic("not implemented") 24 | } 25 | 26 | func (mssql) Namer(ctx context.Context) (Namer, error) { 27 | return sqlServerNamer{}, nil 28 | } 29 | 30 | func (mssql) Quoter(ctx context.Context) (Quoter, error) { 31 | return sqlServerQuoter{}, nil 32 | } 33 | 34 | func (mssql) Savepointer() (Savepointer, error) { 35 | return sqlServerSavepointer{}, nil 36 | } 37 | 38 | type sqlServerNamer struct{} 39 | 40 | func (sqlServerNamer) Name() string { 41 | return "sqlserver" 42 | } 43 | func (sqlServerNamer) Dialect() string { 44 | return DialectTSQL 45 | } 46 | 47 | type sqlServerQuoter struct{} 48 | 49 | func (sqlServerQuoter) ID(name string) string { 50 | return "[" + strings.Replace(name, "]", "]]", -1) + "]" 51 | } 52 | func (sqlServerQuoter) Value(v interface{}) string { 53 | switch v := v.(type) { 54 | default: 55 | panic("unsupported value") 56 | case string: 57 | return "'" + strings.Replace(v, "'", "''", -1) + "'" 58 | } 59 | } 60 | 61 | type sqlServerSavepointer struct{} 62 | 63 | func (sqlServerSavepointer) Release(name string) string { 64 | return "" 65 | } 66 | 67 | func (sqlServerSavepointer) Create(name string) string { 68 | return fmt.Sprintf("save tran %s;", name) 69 | } 70 | 71 | func (sqlServerSavepointer) Rollback(name string) string { 72 | return fmt.Sprintf("rollback tran %s;", name) 73 | } 74 | -------------------------------------------------------------------------------- /quoter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlexp 6 | 7 | import ( 8 | "context" 9 | "database/sql/driver" 10 | "errors" 11 | "reflect" 12 | ) 13 | 14 | // BUG(kardianos): Both the Quoter and Namer may need to access the server. 15 | 16 | // Quoter returns safe and valid SQL strings to use when building a SQL text. 17 | type Quoter interface { 18 | // ID quotes identifiers such as schema, table, or column names. 19 | // ID does not operate on multipart identifiers such as "public.Table", 20 | // it only operates on single identifiers such as "public" and "Table". 21 | ID(name string) string 22 | 23 | // Value quotes database values such as string or []byte types as strings 24 | // that are suitable and safe to embed in SQL text. The returned value 25 | // of a string will include all surrounding quotes. 26 | // 27 | // If a value type is not supported it must panic. 28 | Value(v interface{}) string 29 | } 30 | 31 | // DriverQuoter returns a Quoter interface and is suitable for extending 32 | // the driver.Driver type. 33 | // 34 | // The driver may need to hit the database to determine how it is configured to 35 | // ensure the correct escaping rules are used. 36 | type DriverQuoter interface { 37 | Quoter(ctx context.Context) (Quoter, error) 38 | } 39 | 40 | // QuoterFromDriver takes a database driver, often obtained through a sql.DB.Driver 41 | // call or from using it directly to get the quoter interface. 42 | // 43 | // Currently MssqlDriver is hard-coded to also return a valided Quoter. 44 | func QuoterFromDriver(d driver.Driver, ctx context.Context) (Quoter, error) { 45 | if q, is := d.(DriverQuoter); is { 46 | return q.Quoter(ctx) 47 | } 48 | dv := reflect.ValueOf(d) 49 | 50 | d, found := internalDrivers[dv.Type().String()] 51 | if found { 52 | if q, is := d.(DriverQuoter); is { 53 | return q.Quoter(ctx) 54 | } 55 | } 56 | return nil, errors.New("quoter interface not found") 57 | } 58 | -------------------------------------------------------------------------------- /messages.go: -------------------------------------------------------------------------------- 1 | package sqlexp 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | // RawMessage is returned from RowsMessage. 9 | type RawMessage interface{} 10 | 11 | // ReturnMessage may be passed into a Query argument. 12 | // 13 | // Drivers must implement driver.NamedValueChecker, 14 | // call ReturnMessageInit on it, save it internally, 15 | // and return driver.ErrOmitArgument to prevent 16 | // this from appearing in the query arguments. 17 | // 18 | // Queries that recieve this message should also not return 19 | // SQL errors from the Query method, but wait to return 20 | // it in a Message. 21 | type ReturnMessage struct { 22 | queue chan RawMessage 23 | } 24 | 25 | // Message is called by clients after Query to dequeue messages. 26 | func (m *ReturnMessage) Message(ctx context.Context) RawMessage { 27 | select { 28 | case <-ctx.Done(): 29 | return MsgNextResultSet{} 30 | case raw := <-m.queue: 31 | return raw 32 | } 33 | } 34 | 35 | // ReturnMessageEnqueue is called by the driver to enqueue the driver. 36 | // Drivers should not call this until after it returns from Query. 37 | func ReturnMessageEnqueue(ctx context.Context, m *ReturnMessage, raw RawMessage) error { 38 | select { 39 | case <-ctx.Done(): 40 | return ctx.Err() 41 | case m.queue <- raw: 42 | return nil 43 | } 44 | } 45 | 46 | // ReturnMessageInit is called by database/sql setup the ReturnMessage internals. 47 | func ReturnMessageInit(m *ReturnMessage) { 48 | m.queue = make(chan RawMessage, 15) 49 | } 50 | 51 | type ( 52 | // MsgNextResultSet must be checked for. When received, NextResultSet 53 | // should be called and if false the message loop should be exited. 54 | MsgNextResultSet struct{} 55 | 56 | // MsgNext indicates the result set ready to be scanned. 57 | // This message will often be followed with: 58 | // 59 | // for rows.Next() { 60 | // rows.Scan(&v) 61 | // } 62 | MsgNext struct{} 63 | 64 | // MsgRowsAffected returns the number of rows affected. 65 | // Not all operations that affect rows return results, thus this message 66 | // may be received multiple times. 67 | MsgRowsAffected struct{ Count int64 } 68 | 69 | // MsgLastInsertID returns the value of last inserted row. For many 70 | // database systems and tables this will return int64. Some databases 71 | // may return a string or GUID equivalent. 72 | MsgLastInsertID struct{ Value interface{} } 73 | 74 | // MsgNotice is raised from the SQL text and is only informational. 75 | MsgNotice struct{ Message fmt.Stringer } 76 | 77 | // MsgError returns SQL errors from the database system (not transport 78 | // or other system level errors). 79 | MsgError struct{ Error error } 80 | ) 81 | -------------------------------------------------------------------------------- /nest/nest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package nest supports nested transactions allowing a common querier 6 | // to be defined. 7 | package nest 8 | 9 | import ( 10 | "context" 11 | "database/sql" 12 | "database/sql/driver" 13 | "errors" 14 | "fmt" 15 | 16 | "github.com/golang-sql/sqlexp" 17 | ) 18 | 19 | // Wrap a sql.DB with a nestable DB. 20 | func Wrap(db *sql.DB) *DB { 21 | return &DB{db: db} 22 | } 23 | 24 | type TxOptions = sql.TxOptions 25 | type Result = sql.Result 26 | type Rows = sql.Rows 27 | type Row = sql.Row 28 | type Stmt = sql.Stmt 29 | 30 | // Querier is the common interface to execute queries on a DB, Tx, or Conn. 31 | type Querier interface { 32 | ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) 33 | QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) 34 | QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row 35 | PingContext(ctx context.Context) error 36 | PrepareContext(ctx context.Context, query string) (*Stmt, error) 37 | Commit() error 38 | Rollback() error 39 | } 40 | 41 | var ( 42 | _ Querier = &DB{} 43 | _ Querier = &Tx{} 44 | _ Querier = &Conn{} 45 | ) 46 | 47 | type DB struct { 48 | db *sql.DB 49 | } 50 | 51 | func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) { 52 | tx, err := db.db.BeginTx(ctx, opts) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return &Tx{ctx: ctx, tx: tx, db: db, savepointer: savepointFromDriver(db.db.Driver())}, nil 57 | } 58 | 59 | func (db *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) { 60 | return db.db.ExecContext(ctx, query, args...) 61 | } 62 | 63 | func (db *DB) PingContext(ctx context.Context) error { 64 | return db.db.PingContext(ctx) 65 | } 66 | 67 | func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error) { 68 | return db.db.PrepareContext(ctx, query) 69 | } 70 | 71 | func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { 72 | return db.db.QueryContext(ctx, query, args...) 73 | } 74 | 75 | func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row { 76 | return db.db.QueryRowContext(ctx, query, args...) 77 | } 78 | 79 | var errNoTx = errors.New("sqlexp/nest: not in a transaction") 80 | var errNoNested = errors.New("sqlexp/nest: nested transactions not supported") 81 | 82 | func (db *DB) Commit() error { 83 | return errNoTx 84 | } 85 | 86 | func (db *DB) Rollback() error { 87 | return errNoTx 88 | } 89 | 90 | func (db *DB) DB() *sql.DB { 91 | return db.db 92 | } 93 | 94 | type Tx struct { 95 | ctx context.Context 96 | db *DB 97 | tx *sql.Tx 98 | index int 99 | 100 | savepointer sqlexp.Savepointer 101 | savepoint string // Empty if not in a snapshot, otherwise the name of the snapshot. 102 | } 103 | 104 | func savepointFromDriver(d driver.Driver) sqlexp.Savepointer { 105 | sp, _ := sqlexp.SavepointFromDriver(d) 106 | return sp 107 | } 108 | 109 | func (tx *Tx) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) { 110 | if tx.savepointer == nil { 111 | return nil, errNoNested 112 | } 113 | index := tx.index + 1 114 | savepoint := fmt.Sprintf("savept%dx", index) 115 | 116 | _, err := tx.tx.ExecContext(tx.ctx, tx.savepointer.Create(savepoint)) 117 | if err != nil { 118 | return nil, err 119 | } 120 | return &Tx{ctx: ctx, tx: tx.tx, savepoint: savepoint, index: index}, nil 121 | } 122 | 123 | func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) { 124 | return tx.tx.ExecContext(ctx, query, args...) 125 | } 126 | 127 | func (tx *Tx) PingContext(ctx context.Context) error { 128 | return tx.db.PingContext(ctx) 129 | } 130 | 131 | func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) { 132 | return tx.tx.PrepareContext(ctx, query) 133 | } 134 | func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { 135 | return tx.tx.QueryContext(ctx, query, args...) 136 | } 137 | func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row { 138 | return tx.tx.QueryRowContext(ctx, query, args) 139 | } 140 | 141 | func (tx *Tx) Commit() error { 142 | if len(tx.savepoint) == 0 { 143 | return tx.tx.Commit() 144 | } 145 | // Not all databases support savepoint release. 146 | q := tx.savepointer.Release(tx.savepoint) 147 | if q == "" { 148 | return nil 149 | } 150 | _, err := tx.tx.ExecContext(tx.ctx, q) 151 | return err 152 | } 153 | 154 | func (tx *Tx) Rollback() error { 155 | if len(tx.savepoint) == 0 { 156 | return tx.tx.Rollback() 157 | } 158 | _, err := tx.tx.ExecContext(tx.ctx, tx.savepointer.Rollback(tx.savepoint)) 159 | return err 160 | } 161 | 162 | func (tx *Tx) Tx() *sql.Tx { 163 | return tx.tx 164 | } 165 | 166 | type Conn struct { 167 | db *DB 168 | conn *sql.Conn 169 | } 170 | 171 | func (c *Conn) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) { 172 | tx, err := c.conn.BeginTx(ctx, opts) 173 | if err != nil { 174 | return nil, err 175 | } 176 | return &Tx{ctx: ctx, tx: tx, db: c.db, savepointer: savepointFromDriver(c.db.db.Driver())}, nil 177 | } 178 | 179 | func (c *Conn) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) { 180 | return c.conn.ExecContext(ctx, query, args...) 181 | } 182 | 183 | func (c *Conn) PingContext(ctx context.Context) error { 184 | return c.conn.PingContext(ctx) 185 | } 186 | 187 | func (c *Conn) PrepareContext(ctx context.Context, query string) (*Stmt, error) { 188 | return c.conn.PrepareContext(ctx, query) 189 | } 190 | 191 | func (c *Conn) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { 192 | return c.conn.QueryContext(ctx, query, args...) 193 | } 194 | 195 | func (c *Conn) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row { 196 | return c.conn.QueryRowContext(ctx, query, args...) 197 | } 198 | 199 | func (c *Conn) Commit() error { 200 | return errNoTx 201 | } 202 | 203 | func (c *Conn) Rollback() error { 204 | return errNoTx 205 | } 206 | 207 | func (c *Conn) Conn() *sql.Conn { 208 | return c.conn 209 | } 210 | --------------------------------------------------------------------------------