📄 qsql_odbc.cpp
字号:
/******************************************************************************** Implementation of ODBC driver classes**** Created : 001103**** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.**** This file is part of the sql module of the Qt GUI Toolkit.**** This file may be distributed under the terms of the Q Public License** as defined by Trolltech AS of Norway and appearing in the file** LICENSE.QPL included in the packaging of this file.**** This file may be distributed and/or modified under the terms of the** GNU General Public License version 2 as published by the Free Software** Foundation and appearing in the file LICENSE.GPL included in the** packaging of this file.**** Licensees holding valid Qt Enterprise Edition licenses may use this** file in accordance with the Qt Commercial License Agreement provided** with the Software.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.**** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for** information about Qt Commercial License Agreements.** See http://www.trolltech.com/qpl/ for QPL licensing information.** See http://www.trolltech.com/gpl/ for GPL licensing information.**** Contact info@trolltech.com if any conditions of this licensing are** not clear to you.************************************************************************/#include "qsql_odbc.h"#include <qsqlrecord.h>#if defined (Q_OS_WIN32)#include <qt_windows.h>#include <qapplication.h>#endif#include <qdatetime.h>#include <private/qsqlextension_p.h>#include <private/qinternal_p.h>#include <stdlib.h>// undefine this to prevent initial check of the ODBC driver #define ODBC_CHECK_DRIVERstatic const int COLNAMESIZE = 255;class QODBCPrivate{public: QODBCPrivate() : hEnv(0), hDbc(0), hStmt(0) { sql_char_type = sql_varchar_type = sql_longvarchar_type = QVariant::CString; unicode = FALSE; } SQLHANDLE hEnv; SQLHANDLE hDbc; SQLHANDLE hStmt; bool unicode; QVariant::Type sql_char_type; QVariant::Type sql_varchar_type; QVariant::Type sql_longvarchar_type; QSqlRecordInfo rInf; bool checkDriver() const; void checkUnicode();};class QODBCPreparedExtension : public QSqlExtension{public: QODBCPreparedExtension( QODBCResult * r ) : result( r ) {} bool prepare( const QString& query ) { return result->prepare( query ); } bool exec() { return result->exec(); } QODBCResult * result;};QString qWarnODBCHandle(int handleType, SQLHANDLE handle){ SQLINTEGER nativeCode_; SQLSMALLINT msgLen; SQLRETURN r = SQL_ERROR; SQLTCHAR state_[SQL_SQLSTATE_SIZE+1]; SQLTCHAR description_[SQL_MAX_MESSAGE_LENGTH]; r = SQLGetDiagRec( handleType, handle, 1, (SQLTCHAR*)state_, &nativeCode_, (SQLTCHAR*)description_, SQL_MAX_MESSAGE_LENGTH-1, /* in bytes, not in characters */ &msgLen); if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO )#ifdef UNICODE return QString( (const QChar*)description_, (uint)msgLen );#else return QString::fromLocal8Bit( (const char*)description_ );#endif return QString::null;}QString qODBCWarn( const QODBCPrivate* odbc){ return ( qWarnODBCHandle( SQL_HANDLE_ENV, odbc->hEnv ) + " " \ + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " " \ + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) );}void qSqlWarning( const QString& message, const QODBCPrivate* odbc ){#ifdef QT_CHECK_RANGE qWarning( message + "\tError:" + qODBCWarn( odbc ) );#endif}QSqlError qMakeError( const QString& err, int type, const QODBCPrivate* p ){ return QSqlError( "QODBC3: " + err, qODBCWarn(p), type );}QVariant::Type qDecodeODBCType( SQLSMALLINT sqltype, const QODBCPrivate* p ){ QVariant::Type type = QVariant::Invalid; switch ( sqltype ) { case SQL_DECIMAL: case SQL_NUMERIC: case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: case SQL_BIGINT: //use high precision binding type = QVariant::Double; break; case SQL_SMALLINT: case SQL_INTEGER: case SQL_BIT: case SQL_TINYINT: type = QVariant::Int; break; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: type = QVariant::ByteArray; break; case SQL_DATE: case SQL_TYPE_DATE: type = QVariant::Date; break; case SQL_TIME: case SQL_TYPE_TIME: type = QVariant::Time; break; case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP: type = QVariant::DateTime; break; case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: type = QVariant::String; break; case SQL_CHAR: type = p->sql_char_type; break; case SQL_VARCHAR: type = p->sql_varchar_type; break; case SQL_LONGVARCHAR: type = p->sql_longvarchar_type; break; default: type = QVariant::CString; break; } return type;}QString qGetStringData( SQLHANDLE hStmt, int column, int colSize, bool& isNull, bool unicode = FALSE ){ QString fieldVal; SQLRETURN r = SQL_ERROR; SQLINTEGER lengthIndicator = 0; if ( colSize <= 0 ) { colSize = 255; } else if ( colSize > 65536 ) { // limit buffer size to 64 KB colSize = 65536; } else { colSize++; // make sure there is room for more than the 0 termination if ( unicode ) { colSize *= 2; // a tiny bit faster, since it saves a SQLGetData() call } } char* buf = new char[ colSize ]; while ( TRUE ) { r = SQLGetData( hStmt, column+1, unicode ? SQL_C_WCHAR : SQL_C_CHAR, (SQLPOINTER)buf, colSize, &lengthIndicator ); if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) { if ( lengthIndicator == SQL_NULL_DATA || lengthIndicator == SQL_NO_TOTAL ) { fieldVal = QString::null; isNull = TRUE; break; } // if SQL_SUCCESS_WITH_INFO is returned, indicating that // more data can be fetched, the length indicator does NOT // contain the number of bytes returned - it contains the // total number of bytes that CAN be fetched // colSize-1: remove 0 termination when there is more data to fetch int rSize = (r == SQL_SUCCESS_WITH_INFO) ? (unicode ? colSize-2 : colSize-1) : lengthIndicator; if ( unicode ) { fieldVal += QString( (QChar*) buf, rSize / 2 ); } else { buf[ rSize ] = 0; fieldVal += buf; } if ( lengthIndicator < colSize ) { // workaround for Drivermanagers that don't return SQL_NO_DATA break; } } else if ( r == SQL_NO_DATA ) { break; } else {#ifdef QT_CHECK_RANGE qWarning( "qGetStringData: Error while fetching data (%d)", r );#endif fieldVal = QString::null; break; } } delete[] buf; return fieldVal;}QByteArray qGetBinaryData( SQLHANDLE hStmt, int column, SQLINTEGER& lengthIndicator, bool& isNull ){ QByteArray fieldVal; SQLSMALLINT colNameLen; SQLSMALLINT colType; SQLUINTEGER colSize; SQLSMALLINT colScale; SQLSMALLINT nullable; SQLRETURN r = SQL_ERROR; SQLTCHAR colName[COLNAMESIZE]; r = SQLDescribeCol( hStmt, column+1, colName, COLNAMESIZE, &colNameLen, &colType, &colSize, &colScale, &nullable );#ifdef QT_CHECK_RANGE if ( r != SQL_SUCCESS ) qWarning( QString("qGetBinaryData: Unable to describe column %1").arg(column) );#endif // SQLDescribeCol may return 0 if size cannot be determined if (!colSize) { colSize = 255; } if ( colSize > 65536 ) { // read the field in 64 KB chunks colSize = 65536; } char * buf = new char[ colSize ]; while ( TRUE ) { r = SQLGetData( hStmt, column+1, SQL_C_BINARY, (SQLPOINTER) buf, colSize, &lengthIndicator ); if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) { if ( lengthIndicator == SQL_NULL_DATA ) { isNull = TRUE; break; } else { int rSize; r == SQL_SUCCESS ? rSize = lengthIndicator : rSize = colSize; if ( lengthIndicator == SQL_NO_TOTAL ) { // size cannot be determined rSize = colSize; } // NB! This is not a memleak - the mem will be deleted by QByteArray when // no longer ref'd char * tmp = (char *) malloc( rSize + fieldVal.size() ); if ( fieldVal.size() ) { memcpy( tmp, fieldVal.data(), fieldVal.size() ); } memcpy( tmp + fieldVal.size(), buf, rSize ); fieldVal = fieldVal.assign( tmp, fieldVal.size() + rSize ); if ( r == SQL_SUCCESS ) { // the whole field was read in one chunk break; } } } else { break; } } delete [] buf; return fieldVal;}int qGetIntData( SQLHANDLE hStmt, int column, bool& isNull ){ SQLINTEGER intbuf; isNull = FALSE; SQLINTEGER lengthIndicator = 0; SQLRETURN r = SQLGetData( hStmt, column+1, SQL_C_SLONG, (SQLPOINTER)&intbuf, 0, &lengthIndicator ); if ( r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO ) { if ( lengthIndicator == SQL_NULL_DATA ) isNull = TRUE; } return (int)intbuf;}// creates a QSqlFieldInfo from a valid hStmt generated// by SQLColumns. The hStmt has to point to a valid position.QSqlFieldInfo qMakeFieldInfo( const SQLHANDLE hStmt, const QODBCPrivate* p ){ bool isNull; QString fname = qGetStringData( hStmt, 3, -1, isNull, p->unicode ); int type = qGetIntData( hStmt, 4, isNull ); // column type int required = qGetIntData( hStmt, 10, isNull ); // nullable-flag // required can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN if ( required == SQL_NO_NULLS ) { required = 1; } else if ( required == SQL_NULLABLE ) { required = 0; } else { required = -1; } int size = qGetIntData( hStmt, 6, isNull ); // column size int prec = qGetIntData( hStmt, 8, isNull ); // precision return QSqlFieldInfo( fname, qDecodeODBCType( type, p ), required, size, prec, QVariant(), type );}QSqlFieldInfo qMakeFieldInfo( const QODBCPrivate* p, int i ){ SQLSMALLINT colNameLen; SQLSMALLINT colType; SQLUINTEGER colSize; SQLSMALLINT colScale; SQLSMALLINT nullable; SQLRETURN r = SQL_ERROR; SQLTCHAR colName[ COLNAMESIZE ]; r = SQLDescribeCol( p->hStmt, i+1, colName, (SQLSMALLINT)COLNAMESIZE, &colNameLen, &colType, &colSize, &colScale, &nullable); if ( r != SQL_SUCCESS ) {#ifdef QT_CHECK_RANGE qSqlWarning( QString("qMakeField: Unable to describe column %1").arg(i), p );#endif return QSqlFieldInfo(); }#ifdef UNICODE QString qColName( (const QChar*)colName, (uint)colNameLen );#else QString qColName = QString::fromLocal8Bit( (const char*)colName );#endif // nullable can be SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN int required = -1; if ( nullable == SQL_NO_NULLS ) { required = 1; } else if ( nullable == SQL_NULLABLE ) { required = 0; } QVariant::Type type = qDecodeODBCType( colType, p ); return QSqlFieldInfo( qColName, type, required, (int)colSize == 0 ? -1 : (int)colSize, (int)colScale == 0 ? -1 : (int)colScale, QVariant(), (int)colType );}void qSplitTableQualifier( const QString & qualifier, QString * catalog, QString * schema, QString * table ){ if ( !catalog || !schema || !table ) return; QStringList l = QStringList::split( ".", qualifier, TRUE ); if ( l.count() > 3 ) return; // can't possibly be a valid table qualifier int i = 0, n = l.count(); if ( n == 1 ) { *table = qualifier; } else { for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) { if ( n == 3 ) { if ( i == 0 ) { *catalog = *it; } else if ( i == 1 ) { *schema = *it; } else if ( i == 2 ) { *table = *it; } } else if ( n == 2 ) { if ( i == 0 ) { *schema = *it; } else if ( i == 1 ) { *table = *it; } } i++; } }}////////////////////////////////////////////////////////////////////////////QODBCResult::QODBCResult( const QODBCDriver * db, QODBCPrivate* p ): QSqlResult(db){ d = new QODBCPrivate(); (*d) = (*p); setExtension( new QODBCPreparedExtension( this ) );}QODBCResult::~QODBCResult(){ if ( d->hStmt && driver()->isOpen() ) { SQLRETURN r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt );#ifdef QT_CHECK_RANGE if ( r!= SQL_SUCCESS ) qSqlWarning( "QODBCDriver: Unable to free statement handle" + QString::number(r), d );#endif } delete d;}bool QODBCResult::reset ( const QString& query ){ setActive( FALSE ); setAt( QSql::BeforeFirst ); SQLRETURN r; d->rInf.clear(); // Always reallocate the statement handle - the statement attributes // are not reset if SQLFreeStmt() is called which causes some problems. if ( d->hStmt ) { r = SQLFreeHandle( SQL_HANDLE_STMT, d->hStmt ); if ( r != SQL_SUCCESS ) {#ifdef QT_CHECK_RANGE qSqlWarning( "QODBCResult::reset: Unable to free statement handle", d );#endif return FALSE; } } r = SQLAllocHandle( SQL_HANDLE_STMT, d->hDbc, &d->hStmt ); if ( r != SQL_SUCCESS ) {#ifdef QT_CHECK_RANGE qSqlWarning( "QODBCResult::reset: Unable to allocate statement handle", d );#endif return FALSE; } if ( isForwardOnly() ) { r = SQLSetStmtAttr( d->hStmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, SQL_IS_UINTEGER ); } else { r = SQLSetStmtAttr( d->hStmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, SQL_IS_UINTEGER ); } if ( r != SQL_SUCCESS ) {#ifdef QT_CHECK_RANGE qSqlWarning( "QODBCResult::reset: Unable to set 'SQL_CURSOR_STATIC' as statement attribute. Please check your ODBC driver configuration", d );#endif return FALSE; }#ifdef UNICODE r = SQLExecDirect( d->hStmt, (SQLWCHAR*) query.unicode(), (SQLINTEGER) query.length() );#else QCString query8 = query.local8Bit(); r = SQLExecDirect( d->hStmt, (SQLCHAR*) query8.data(), (SQLINTEGER) query8.length() );#endif if ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) { setLastError( qMakeError( "Unable to execute statement", QSqlError::Statement, d ) ); return FALSE; } SQLSMALLINT count; r = SQLNumResultCols( d->hStmt, &count ); if ( count ) { setSelect( TRUE ); for ( int i = 0; i < count; ++i ) { d->rInf.append( qMakeFieldInfo( d, i ) ); } } else { setSelect( FALSE ); } setActive( TRUE ); return TRUE;}bool QODBCResult::fetch(int i){ if ( isForwardOnly() && i < at() ) return FALSE; if ( i == at() ) return TRUE; fieldCache.clear(); nullCache.clear(); int actualIdx = i + 1; if ( actualIdx <= 0 ) { setAt( QSql::BeforeFirst ); return FALSE; } SQLRETURN r; if ( isForwardOnly() ) { bool ok = TRUE; while ( ok && i > at() ) ok = fetchNext(); return ok; } else { r = SQLFetchScroll( d->hStmt, SQL_FETCH_ABSOLUTE, actualIdx ); } if ( r != SQL_SUCCESS ){ return FALSE; } setAt( i ); return TRUE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -