github.com/nakagami/firebirdsql@v0.9.10/connection.go (about) 1 /******************************************************************************* 2 The MIT License (MIT) 3 4 Copyright (c) 2013-2019 Hajime Nakagami 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy of 7 this software and associated documentation files (the "Software"), to deal in 8 the Software without restriction, including without limitation the rights to 9 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 the Software, and to permit persons to whom the Software is furnished to do so, 11 subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in all 14 copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 *******************************************************************************/ 23 24 package firebirdsql 25 26 import ( 27 "context" 28 "database/sql/driver" 29 "math/big" 30 ) 31 32 type firebirdsqlConn struct { 33 wp *wireProtocol 34 tx *firebirdsqlTx 35 dsn *firebirdDsn 36 columnNameToLower bool 37 isAutocommit bool 38 clientPublic *big.Int 39 clientSecret *big.Int 40 transactionSet map[*firebirdsqlTx]struct{} 41 } 42 43 // ============ driver.Conn implementation 44 45 func (fc *firebirdsqlConn) begin(isolationLevel int) (driver.Tx, error) { 46 tx, err := newFirebirdsqlTx(fc, isolationLevel, false, true) 47 fc.tx = tx 48 return driver.Tx(tx), err 49 } 50 51 // Begin starts and returns a new transaction. 52 // 53 // Deprecated: Drivers should implement ConnBeginTx instead (or additionally). 54 // -> is implemented in driver_go18.go with BeginTx() 55 func (fc *firebirdsqlConn) Begin() (driver.Tx, error) { 56 return fc.begin(ISOLATION_LEVEL_READ_COMMITED) 57 } 58 59 // Close invalidates and potentially stops any current 60 // prepared statements and transactions, marking this 61 // connection as no longer in use. 62 // 63 // Because the sql package maintains a free pool of 64 // connections and only calls Close when there's a surplus of 65 // idle connections, it shouldn't be necessary for drivers to 66 // do their own connection caching. 67 func (fc *firebirdsqlConn) Close() (err error) { 68 for tx := range fc.transactionSet { 69 tx.Rollback() 70 } 71 72 err = fc.wp.opDetach() 73 if err != nil { 74 return 75 } 76 _, _, _, err = fc.wp.opResponse() 77 fc.wp.conn.Close() 78 return 79 } 80 81 func (fc *firebirdsqlConn) prepare(ctx context.Context, query string) (driver.Stmt, error) { 82 if fc.tx.needBegin { 83 err := fc.tx.begin() 84 if err != nil { 85 return nil, err 86 } 87 } 88 89 return newFirebirdsqlStmt(fc, query) 90 } 91 92 // Prepare returns a prepared statement, bound to this connection. 93 func (fc *firebirdsqlConn) Prepare(query string) (driver.Stmt, error) { 94 return fc.prepare(context.Background(), query) 95 } 96 97 // ============ driver.Tx implementation 98 99 func (fc *firebirdsqlConn) exec(ctx context.Context, query string, args []driver.Value) (result driver.Result, err error) { 100 101 stmt, err := fc.prepare(ctx, query) 102 if err != nil { 103 return 104 } 105 106 result, err = stmt.(*firebirdsqlStmt).exec(ctx, args) 107 if err != nil { 108 return 109 } 110 111 stmt.Close() 112 113 return 114 } 115 116 func (fc *firebirdsqlConn) Exec(query string, args []driver.Value) (result driver.Result, err error) { 117 return fc.exec(context.Background(), query, args) 118 } 119 120 func (fc *firebirdsqlConn) query(ctx context.Context, query string, args []driver.Value) (rows driver.Rows, err error) { 121 122 stmt, err := fc.prepare(ctx, query) 123 if err != nil { 124 return 125 } 126 rows, err = stmt.(*firebirdsqlStmt).query(ctx, args) 127 return 128 } 129 130 func (fc *firebirdsqlConn) Query(query string, args []driver.Value) (rows driver.Rows, err error) { 131 return fc.query(context.Background(), query, args) 132 } 133 134 func newFirebirdsqlConn(dsn *firebirdDsn) (fc *firebirdsqlConn, err error) { 135 136 wp, err := newWireProtocol(dsn.addr, dsn.options["timezone"], dsn.options["charset"]) 137 if err != nil { 138 return 139 } 140 141 column_name_to_lower := convertToBool(dsn.options["column_name_to_lower"], false) 142 143 clientPublic, clientSecret := getClientSeed() 144 145 err = wp.opConnect(dsn.dbName, dsn.user, dsn.passwd, dsn.options, clientPublic) 146 if err != nil { 147 return 148 } 149 150 err = wp._parse_connect_response(dsn.user, dsn.passwd, dsn.options, clientPublic, clientSecret) 151 if err != nil { 152 return 153 } 154 155 err = wp.opAttach(dsn.dbName, dsn.user, dsn.passwd, dsn.options["role"]) 156 if err != nil { 157 return 158 } 159 160 wp.dbHandle, _, _, err = wp.opResponse() 161 if err != nil { 162 return 163 } 164 165 fc = new(firebirdsqlConn) 166 fc.transactionSet = make(map[*firebirdsqlTx]struct{}) 167 fc.wp = wp 168 fc.dsn = dsn 169 fc.columnNameToLower = column_name_to_lower 170 fc.isAutocommit = true 171 fc.tx, err = newFirebirdsqlTx(fc, ISOLATION_LEVEL_READ_COMMITED, fc.isAutocommit, false) 172 fc.clientPublic = clientPublic 173 fc.clientSecret = clientSecret 174 175 return fc, err 176 } 177 178 func createFirebirdsqlConn(dsn *firebirdDsn) (fc *firebirdsqlConn, err error) { 179 180 wp, err := newWireProtocol(dsn.addr, dsn.options["timezone"], dsn.options["charset"]) 181 if err != nil { 182 return 183 } 184 column_name_to_lower := convertToBool(dsn.options["column_name_to_lower"], false) 185 186 clientPublic, clientSecret := getClientSeed() 187 188 err = wp.opConnect(dsn.dbName, dsn.user, dsn.passwd, dsn.options, clientPublic) 189 if err != nil { 190 return 191 } 192 193 err = wp._parse_connect_response(dsn.user, dsn.passwd, dsn.options, clientPublic, clientSecret) 194 if err != nil { 195 return 196 } 197 198 err = wp.opCreate(dsn.dbName, dsn.user, dsn.passwd, dsn.options["role"]) 199 if err != nil { 200 return 201 } 202 203 wp.dbHandle, _, _, err = wp.opResponse() 204 if err != nil { 205 return 206 } 207 208 fc = new(firebirdsqlConn) 209 fc.transactionSet = make(map[*firebirdsqlTx]struct{}) 210 fc.wp = wp 211 fc.dsn = dsn 212 fc.columnNameToLower = column_name_to_lower 213 fc.isAutocommit = true 214 fc.tx, err = newFirebirdsqlTx(fc, ISOLATION_LEVEL_READ_COMMITED, fc.isAutocommit, false) 215 fc.clientPublic = clientPublic 216 fc.clientSecret = clientSecret 217 218 return fc, err 219 }