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  }