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  }