github.com/nakagami/firebirdsql@v0.9.10/statement.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 ) 30 31 type firebirdsqlStmt struct { 32 fc *firebirdsqlConn 33 queryString string 34 stmtHandle int32 35 xsqlda []xSQLVAR 36 blr []byte 37 stmtType int32 38 } 39 40 func (stmt *firebirdsqlStmt) Close() (err error) { 41 if stmt.stmtHandle == -1 { // alredy closed 42 return 43 } 44 err = stmt.fc.wp.opFreeStatement(stmt.stmtHandle, 2) // DSQL_drop 45 stmt.stmtHandle = -1 46 if err != nil { 47 return err 48 } 49 50 if stmt.fc.wp.acceptType == ptype_lazy_send { 51 stmt.fc.wp.lazyResponseCount++ 52 } else { 53 _, _, _, err = stmt.fc.wp.opResponse() 54 } 55 56 if stmt.fc.tx.isAutocommit { 57 stmt.fc.tx.commitRetainging() 58 } 59 return 60 } 61 62 func (stmt *firebirdsqlStmt) NumInput() int { 63 return -1 64 } 65 66 func (stmt *firebirdsqlStmt) sendOpCancel(ctx context.Context, done chan struct{}) { 67 cancel := true 68 select { 69 case <-done: 70 cancel = false 71 case <-ctx.Done(): 72 } 73 if cancel { 74 stmt.fc.wp.opCancel(fb_cancel_raise) 75 } 76 } 77 78 func (stmt *firebirdsqlStmt) exec(ctx context.Context, args []driver.Value) (result driver.Result, err error) { 79 err = stmt.fc.wp.opExecute(stmt.stmtHandle, stmt.fc.tx.transHandle, args) 80 if err != nil { 81 return 82 } 83 84 var done = make(chan struct{}, 1) 85 go stmt.sendOpCancel(ctx, done) 86 _, _, _, err = stmt.fc.wp.opResponse() 87 done <- struct{}{} 88 89 if err != nil { 90 return 91 } 92 93 err = stmt.fc.wp.opInfoSql(stmt.stmtHandle, []byte{isc_info_sql_records}) 94 if err != nil { 95 return 96 } 97 98 _, _, buf, err := stmt.fc.wp.opResponse() 99 if err != nil { 100 return 101 } 102 103 var rowcount int64 104 if len(buf) >= 32 { 105 if stmt.stmtType == isc_info_sql_stmt_select { 106 rowcount = int64(bytes_to_int32(buf[20:24])) 107 } else { 108 rowcount = int64(bytes_to_int32(buf[27:31]) + bytes_to_int32(buf[6:10]) + bytes_to_int32(buf[13:17])) 109 } 110 } else { 111 rowcount = 0 112 } 113 114 result = &firebirdsqlResult{ 115 affectedRows: rowcount, 116 } 117 return 118 } 119 120 func (stmt *firebirdsqlStmt) Exec(args []driver.Value) (result driver.Result, err error) { 121 return stmt.exec(context.Background(), args) 122 } 123 124 func (stmt *firebirdsqlStmt) query(ctx context.Context, args []driver.Value) (driver.Rows, error) { 125 var rows driver.Rows 126 var err error 127 var result []driver.Value 128 var done = make(chan struct{}, 1) 129 130 if stmt.fc.tx.needBegin { 131 err := stmt.fc.tx.begin() 132 if err != nil { 133 return nil, err 134 } 135 } 136 137 if stmt.stmtHandle == -1 { 138 stmt, err = newFirebirdsqlStmt(stmt.fc, stmt.queryString) 139 } 140 141 if stmt.stmtType == isc_info_sql_stmt_exec_procedure { 142 err = stmt.fc.wp.opExecute2(stmt.stmtHandle, stmt.fc.tx.transHandle, args, stmt.blr) 143 if err != nil { 144 return nil, err 145 } 146 147 go stmt.sendOpCancel(ctx, done) 148 result, err = stmt.fc.wp.opSqlResponse(stmt.xsqlda) 149 done <- struct{}{} 150 if err != nil { 151 return nil, err 152 } 153 154 rows = newFirebirdsqlRows(ctx, stmt, result) 155 156 _, _, _, err = stmt.fc.wp.opResponse() 157 if err != nil { 158 return nil, err 159 } 160 } else { 161 err := stmt.fc.wp.opExecute(stmt.stmtHandle, stmt.fc.tx.transHandle, args) 162 if err != nil { 163 return nil, err 164 } 165 166 go stmt.sendOpCancel(ctx, done) 167 _, _, _, err = stmt.fc.wp.opResponse() 168 done <- struct{}{} 169 170 if err != nil { 171 return nil, err 172 } 173 174 rows = newFirebirdsqlRows(ctx, stmt, nil) 175 } 176 return rows, err 177 } 178 179 func (stmt *firebirdsqlStmt) Query(args []driver.Value) (rows driver.Rows, err error) { 180 return stmt.query(context.Background(), args) 181 } 182 183 func newFirebirdsqlStmt(fc *firebirdsqlConn, query string) (stmt *firebirdsqlStmt, err error) { 184 stmt = new(firebirdsqlStmt) 185 stmt.fc = fc 186 stmt.queryString = query 187 188 err = stmt.fc.wp.opAllocateStatement() 189 if err != nil { 190 return nil, err 191 } 192 193 if stmt.fc.wp.acceptType == ptype_lazy_send { 194 stmt.fc.wp.lazyResponseCount++ 195 stmt.stmtHandle = -1 196 } else { 197 stmt.stmtHandle, _, _, err = stmt.fc.wp.opResponse() 198 if err != nil { 199 return 200 } 201 } 202 203 err = stmt.fc.wp.opPrepareStatement(stmt.stmtHandle, stmt.fc.tx.transHandle, query) 204 if err != nil { 205 return nil, err 206 } 207 208 if stmt.fc.wp.acceptType == ptype_lazy_send && stmt.fc.wp.lazyResponseCount > 0 { 209 stmt.fc.wp.lazyResponseCount-- 210 stmt.stmtHandle, _, _, _ = stmt.fc.wp.opResponse() 211 } 212 213 _, _, buf, err := stmt.fc.wp.opResponse() 214 if err != nil { 215 return 216 } 217 218 stmt.stmtType, stmt.xsqlda, err = stmt.fc.wp.parse_xsqlda(buf, stmt.stmtHandle) 219 if err != nil { 220 return nil, err 221 } 222 223 stmt.blr = calcBlr(stmt.xsqlda) 224 225 return 226 }