📄 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_DRIVER#if defined(Q_ODBC_VERSION_2)//crude hack to get non-unicode capable driver managers to work# undef UNICODE# define SQLTCHAR SQLCHAR# define SQL_C_WCHAR SQL_C_CHAR#endifstatic const int COLNAMESIZE = 255;//Map Qt parameter types to ODBC typesstatic const SQLSMALLINT qParamType[ 4 ] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };class QODBCPrivate{public: QODBCPrivate() : hEnv(0), hDbc(0), hStmt(0), useSchema(FALSE) { sql_char_type = sql_varchar_type = sql_longvarchar_type = QVariant::CString; unicode = FALSE; } SQLHANDLE hEnv; SQLHANDLE hDbc; SQLHANDLE hStmt; bool unicode; bool useSchema; QVariant::Type sql_char_type; QVariant::Type sql_varchar_type; QVariant::Type sql_longvarchar_type; QSqlRecordInfo rInf; bool checkDriver() const; void checkUnicode(); void checkSchemaUsage(); bool setConnectionOptions( const QString& connOpts ); void splitTableQualifier(const QString &qualifier, QString &catalog, QString &schema, QString &table);};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;};QPtrDict<QSqlOpenExtension> *qSqlOpenExtDict();class QODBCOpenExtension : public QSqlOpenExtension{public: QODBCOpenExtension( QODBCDriver *dri ) : QSqlOpenExtension(), driver(dri) {} ~QODBCOpenExtension() {} bool open( const QString& db, const QString& user, const QString& password, const QString& host, int port, const QString& connOpts );private: QODBCDriver *driver;};bool QODBCOpenExtension::open( const QString& db, const QString& user, const QString& password, const QString& host, int port, const QString& connOpts ){ return driver->open( db, user, password, host, port, connOpts );}static 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;}static QString qODBCWarn( const QODBCPrivate* odbc){ return ( qWarnODBCHandle( SQL_HANDLE_ENV, odbc->hEnv ) + " " + qWarnODBCHandle( SQL_HANDLE_DBC, odbc->hDbc ) + " " + qWarnODBCHandle( SQL_HANDLE_STMT, odbc->hStmt ) );}static void qSqlWarning( const QString& message, const QODBCPrivate* odbc ){#ifdef QT_CHECK_RANGE qWarning( "%s\tError: %s", message.local8Bit().data(), qODBCWarn( odbc ).local8Bit().data() );#endif}static QSqlError qMakeError( const QString& err, int type, const QODBCPrivate* p ){ return QSqlError( "QODBC3: " + err, qODBCWarn(p), type );}static 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: type = QVariant::Double; break; case SQL_SMALLINT: case SQL_INTEGER: case SQL_BIT: case SQL_TINYINT: type = QVariant::Int; break; case SQL_BIGINT: type = QVariant::LongLong; 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;#ifndef Q_ODBC_VERSION_2 case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: type = QVariant::String; break;#endif 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;}static 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;}static 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( "qGetBinaryData: Unable to describe column %d", 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;}static int qGetIntData( SQLHANDLE hStmt, int column, bool& isNull ){ SQLINTEGER intbuf = 0; 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 ) || lengthIndicator == SQL_NULL_DATA ) { isNull = TRUE; return 0; } return (int)intbuf;}static double qGetDoubleData( SQLHANDLE hStmt, int column, bool& isNull ){ SQLDOUBLE dblbuf; SQLINTEGER lengthIndicator = 0; isNull = FALSE; SQLRETURN r = SQLGetData( hStmt, column+1, SQL_C_DOUBLE, (SQLPOINTER)&dblbuf, 0, &lengthIndicator ); if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) { isNull = TRUE; return 0.0; } return (double) dblbuf;}static SQLBIGINT qGetBigIntData( SQLHANDLE hStmt, int column, bool& isNull ){ SQLBIGINT lngbuf = Q_INT64_C( 0 ); isNull = FALSE; SQLINTEGER lengthIndicator = 0; SQLRETURN r = SQLGetData( hStmt, column+1, SQL_C_SBIGINT, (SQLPOINTER) &lngbuf, 0, &lengthIndicator ); if ( ( r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO ) || lengthIndicator == SQL_NULL_DATA ) isNull = TRUE; return lngbuf;}// creates a QSqlFieldInfo from a valid hStmt generated// by SQLColumns. The hStmt has to point to a valid position.static 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 );}static 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 );}bool QODBCPrivate::setConnectionOptions( const QString& connOpts ){ // Set any connection attributes QStringList raw = QStringList::split( ';', connOpts ); QStringList opts; SQLRETURN r = SQL_SUCCESS; QMap<QString, QString> connMap; for ( QStringList::ConstIterator it = raw.begin(); it != raw.end(); ++it ) { QString tmp( *it );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -