⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 resultset.cpp.svn-base

📁 絲路server源碼 Silk Road server source
💻 SVN-BASE
📖 第 1 页 / 共 3 页
字号:
/*   This file is part of libodbc++.   Copyright (C) 1999-2000 Manush Dodunekov <manush@stendahls.net>   This library is free software; you can redistribute it and/or   modify it under the terms of the GNU Library General Public   License as published by the Free Software Foundation; either   version 2 of the License, or (at your option) any later version.   This library is distributed in the hope that it will be useful,   but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   Library General Public License for more details.   You should have received a copy of the GNU Library General Public License   along with this library; see the file COPYING.  If not, write to   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,   Boston, MA 02111-1307, USA.*/#include <odbc++/resultset.h>#include <odbc++/statement.h>#include <odbc++/resultsetmetadata.h>#include "datahandler.h"#include "datastream.h"#include "driverinfo.h"#include "dtconv.h"#if defined(ODBCXX_QT)# include <qiodevice.h>#endifusing namespace odbc;using namespace std;// values for location_#define BEFORE_FIRST      (-3)#define AFTER_LAST        (-2)#define INSERT_ROW        (-1)#define UNKNOWN           (0)#define LOCATION_IS_VALID (location_>=0)/*  Our current location in a result set is stored in location_. If  location_>0, we know our position. If it's UNKNOWN we don't  but it's still somewhere in the resultset. If location_<0  we're outside or on the insert row.  We decide on which of the SQL*Fetch* functions to use in the  following way:  if (DM_IS_ODBC3) {    if(forwardOnly) {      SQLFetch();    } else {      SQLFetchScroll();    }  } else {    if(forwardOnly && rowsetSize==1) {      SQLFetch();    } else {      SQLExtendedFetch();    }  }  For scrollable ResultSets, we add an extra row to rowset_. A call to  moveToInsertRow() would put the rowset position on the last row.  When using an ODBC2 driver, a call to insertRow simply does  _applyPosition(SQL_ADD) (while on the insert row).  With an ODBC3 driver, we have to re-bind all columns to point at the  last row with rowset size set to 1 (stupid ODBC bind offsets only work with  row-wise binding, and we use column-wise), and then call SQLBulkOperations.  That binding is kept in effect until moveToCurrentRow() is called, for  the case of the application wanting to insert more rows.  When calling moveToCurrentRow() in this case, we have two possibilities:  1. bindPos_ = 0 -> app didn't call insertRow(), we can just set location_  to locBeforeInsert_ and the rowset pos to rowBeforeInsert_.  2. bindPos_ >0 -> app inserted one or more rows using SQLBulkOperations().  Per ODBC3 specs, the actual rowset position is undefined, and we do the  following:     1. Restore the rowset size (SQL_ROW_ARRAY_SIZE)     2. If locBeforeInsert_ was valid, use an absolute fetch to get there and     restore the rowset size. Otherwise, try to restore it by going after-last     or before-first. With a driver that can't report the current row position     (eg returns 0 for SQLGetStmtAttr(SQL_ATTR_ROW_NUMBER)), this breaks and     leaves a ResultSet before it's first row after an insert. However, I haven't     seen an ODBC3 driver that does this yet.*/namespace odbc {  // a simple utility to save a value and restore it  // at the end of the scope  template <class T> class ValueSaver {  private:    T& orig_;    T val_;  public:    ValueSaver(T& v)      :orig_(v), val_(v) {}    ~ValueSaver() {      orig_=val_;    }  };};#define CHECK_INSERT_ROW                                        \do {                                                                \  if(location_==INSERT_ROW) {                                        \    throw SQLException                                                \      (ODBCXX_STRING_CONST("[libodbc++]: Illegal operation while on insert row"));        \  }                                                                \} while(false)#define CHECK_SCROLLABLE_CURSOR                                                \do {                                                                        \  if(this->getType()==TYPE_FORWARD_ONLY) {                                \    throw SQLException                                                        \      (ODBCXX_STRING_CONST("[libodbc++]: Operation not possible on a forward-only cursor"));        \  }                                                                         \} while(false)ResultSet::ResultSet(Statement* stmt, SQLHSTMT hstmt, bool ownStmt)  :statement_(stmt),   hstmt_(hstmt),   ownStatement_(ownStmt),   currentFetchSize_(stmt->getFetchSize()),   newFetchSize_(currentFetchSize_),   rowset_(NULL),   rowStatus_(NULL),   rowsInRowset_(0),   colsBound_(false),   streamedColsBound_(false),   bindPos_(0),   location_(BEFORE_FIRST){  metaData_=new ResultSetMetaData(this);  //ODBC says GetData cannot be called on a forward-only cursor  //with rowset size > 1  //so, if ResultSetMetaData saw a streamed column,  //we 'adjust' the rowset size to 1  if(metaData_->needsGetData_==true &&     this->getType()==TYPE_FORWARD_ONLY) {    currentFetchSize_=1;  }  newFetchSize_=currentFetchSize_;#if ODBCVER >= 0x0300  // with ODBC3, we call SQLFetchScroll,  // and need to set the following  statement_->_setPointerOption    (SQL_ATTR_ROWS_FETCHED_PTR,(SQLPOINTER)&rowsInRowset_);#endif  //this will do all the work - set the rowset size, bind and so on  this->_applyFetchSize();}ResultSet::~ResultSet(){  if(colsBound_) {    this->_unbindCols();  }  // in case some exception caused us  // to destruct, these can be bound  if(streamedColsBound_) {    this->_unbindStreamedCols();  }#if ODBCVER >= 0x0300  // we don't want anything to point to us  statement_->_setPointerOption    (SQL_ATTR_ROWS_FETCHED_PTR,(SQLPOINTER)NULL);  statement_->_setPointerOption    (SQL_ATTR_ROW_STATUS_PTR,(SQLPOINTER)NULL);#endif  delete rowset_;  delete[] rowStatus_;  delete metaData_;  statement_->_unregisterResultSet(this);  //if we own the statement, nuke it  if(ownStatement_) {    delete statement_;  }}//privatevoid ResultSet::_applyFetchSize(){  statement_->_setNumericOption    (ODBC3_C(SQL_ATTR_ROW_ARRAY_SIZE,SQL_ROWSET_SIZE),     currentFetchSize_);  // it is possible that the driver changes the  // rowset size, so we do an extra check  int driverFetchSize=statement_->_getNumericOption    (ODBC3_C(SQL_ATTR_ROW_ARRAY_SIZE,SQL_ROWSET_SIZE));  if(driverFetchSize!=currentFetchSize_) {    // save these values    currentFetchSize_=driverFetchSize;    newFetchSize_=driverFetchSize;  }  if(colsBound_) {    this->_unbindCols();  }  this->_resetRowset();  if(!colsBound_) {    this->_bindCols();  }}//privatevoid ResultSet::_resetRowset(){  delete rowset_;  delete[] rowStatus_;  //Forward-only resultsets can't have insert rows  int extraRows=(this->getType()==TYPE_FORWARD_ONLY?0:1);  rowset_=new Rowset(currentFetchSize_+extraRows,//1 for the insert row                     ODBC3_DC(true,false)); // possibly use ODBC3 c-types  rowStatus_=new SQLUSMALLINT[currentFetchSize_+extraRows]; //same here#if ODBCVER >= 0x0300  // since SQLFetchScroll doesn't take this argument, we  // need to set it here.  statement_->_setPointerOption    (SQL_ATTR_ROW_STATUS_PTR,(SQLPOINTER)rowStatus_);#endif  //add the datahandlers  int nc=metaData_->getColumnCount();  for(int i=1; i<=nc; i++) {    int realprec;#if ODBCVER < 0x0300    realprec=metaData_->getPrecision(i);#else    if(this->_getDriverInfo()->getMajorVersion()>=3) {      switch(metaData_->getColumnType(i)) {      case Types::CHAR:      case Types::VARCHAR:#if defined(ODBCXX_HAVE_SQLUCODE_H)          case Types::WCHAR:          case Types::WVARCHAR:#endif      case Types::BINARY:      case Types::VARBINARY:        realprec=metaData_->colLengths_[i-1];        break;      default:        realprec=metaData_->getPrecision(i);        break;      }    } else {      realprec=metaData_->getPrecision(i);    }#endif    rowset_->addColumn(metaData_->getColumnType(i),                       realprec,                       metaData_->getScale(i));  }}//privatevoid ResultSet::_prepareForFetch(){  if(newFetchSize_!=currentFetchSize_) {    currentFetchSize_=newFetchSize_;    this->_applyFetchSize();  }}SQLRETURN ResultSet::_applyPosition(int mode){  if(this->getType() != TYPE_FORWARD_ONLY) {    SQLRETURN r=SQLSetPos(hstmt_,                          (SQLUSMALLINT)rowset_->getCurrentRow()+1,                          (SQLUSMALLINT)mode,                          SQL_LOCK_NO_CHANGE);    this->_checkStmtError(hstmt_,r,ODBCXX_STRING_CONST("SQLSetPos failed"));    return r;  }  return SQL_SUCCESS;}void ResultSet::_bindCols(){  int nc=metaData_->getColumnCount();  SQLRETURN r;  bindPos_=rowset_->getCurrentRow();  colsBound_=true;  for(int i=1; i<=nc; i++) {    DataHandler* dh=rowset_->getColumn(i);    if(!dh->isStreamed_) {      r=SQLBindCol(hstmt_,                   (SQLUSMALLINT)i,                   dh->cType_,                   (SQLPOINTER)dh->data(),                   dh->bufferSize_,                   &dh->dataStatus_[dh->currentRow_]);      this->_checkStmtError(hstmt_,r,ODBCXX_STRING_CONST("Error binding column"));    }  }}//this we do before an update or insertvoid ResultSet::_bindStreamedCols(){  int nc=metaData_->getColumnCount();  unsigned int cr=rowset_->getCurrentRow();  for(int i=1; i<=nc; i++) {    DataHandler* dh=rowset_->getColumn(i);    if(dh->isStreamed_) {      streamedColsBound_=true;      SQLRETURN r=SQLBindCol(hstmt_,                             (SQLUSMALLINT)i,                             dh->cType_,                             (SQLPOINTER)i, //our column number (for SQLParamData)                             0,                             &dh->dataStatus_[dh->currentRow_]);      //dh->dataStatus_ should be SQL_LEN_DATA_AT_EXEC(len) by this point      this->_checkStmtError(hstmt_,r,ODBCXX_STRING_CONST("Error binding column"));    }  }}void ResultSet::_unbindStreamedCols(){  int nc=metaData_->getColumnCount();  streamedColsBound_=false;  for(int i=1; i<=nc; i++) {    DataHandler* dh=rowset_->getColumn(i);    if(dh->isStreamed_) {      SQLRETURN r=SQLBindCol(hstmt_,                           (SQLUSMALLINT)i,                           dh->cType_,                           0, //setting this to NULL means unbind this column                           0,                           dh->dataStatus_);      this->_checkStmtError(hstmt_,r,ODBCXX_STRING_CONST("Error unbinding column"));    }  }}//privatevoid ResultSet::_unbindCols(){  //we don't perform an error check here, since  //this is called from the destructor;  //egcs dislikes exceptions thrown from destructors.  SQLFreeStmt(hstmt_,SQL_UNBIND);  colsBound_=false;}//private//all actual fetching is done herevoid ResultSet::_doFetch(int fetchType, int rowNum){  SQLRETURN r;  bool isfo=this->getType()==TYPE_FORWARD_ONLY;  // if isfo is true fetchType can only be SQL_FETCH_NEXT  if(isfo#if ODBCVER < 0x0300     && currentFetchSize_==1#endif     ) {    // the only way to get here    assert(fetchType==SQL_FETCH_NEXT);    // ODBC3 says SQLFetch can do rowset sizes > 1    r=SQLFetch(hstmt_);#if ODBCVER < 0x0300    // this won't get updated otherwise    rowsInRowset_=1;#endif  } else {    // we have either a scrolling cursor or a    // rowset size > 1 in ODBC2#if ODBCVER < 0x0300    r=SQLExtendedFetch(hstmt_,                       (SQLUSMALLINT)fetchType,                       (SQLINTEGER)rowNum,                       &rowsInRowset_,                       rowStatus_);#else    r=SQLFetchScroll(hstmt_,                     (SQLUSMALLINT)fetchType,                     (SQLINTEGER)rowNum);#endif  }  this->_checkStmtError(hstmt_,r,ODBCXX_STRING_CONST("Error fetching data from datasource"));  rowset_->setCurrentRow(0);  // fixup for weird behaviour experienced with myodbc 2.50.28:  // while on the first row of the result set, a call to SQLExtendedFetch(SQL_FETCH_PRIOR)  // would set rowsInRowset_ to 0 but return SQL_SUCCESS_WITH_INFO.  if(rowsInRowset_==0 &&     r!=ODBC3_C(SQL_NO_DATA,SQL_NO_DATA_FOUND)) {    r=ODBC3_C(SQL_NO_DATA,SQL_NO_DATA_FOUND);  }  if(r==ODBC3_C(SQL_NO_DATA,SQL_NO_DATA_FOUND)) {    rowsInRowset_=0;    switch(fetchType) {    case SQL_FETCH_RELATIVE:      if(rowNum<0) {        location_=BEFORE_FIRST;      } else if(rowNum>0) {        location_=AFTER_LAST;      }      break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -