📄 qsql_ibase.cpp
字号:
/******************************************************************************** Implementation of Interbase driver classes.**** Copyright (C) 1992-2004 Trolltech AS. All rights reserved.**** This file is part of the sql module of the Qt GUI Toolkit.** EDITIONS: FREE, ENTERPRISE**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qsql_ibase.h"#include <qdatetime.h>#include <private/qsqlextension_p.h>#include <ibase.h>#include <stdlib.h>#include <limits.h>#include <math.h>#define QIBASE_DRIVER_NAME "QIBASE"class QIBasePreparedExtension : public QSqlExtension{public: QIBasePreparedExtension(QIBaseResult *r) : result(r) {} bool prepare(const QString &query) { return result->prepare(query); } bool exec() { return result->exec(); } QIBaseResult *result;};static bool getIBaseError(QString& msg, ISC_STATUS* status, long &sqlcode){ if (status[0] != 1 || status[1] <= 0) return FALSE; sqlcode = isc_sqlcode(status); char buf[512]; isc_sql_interprete(sqlcode, buf, 512); msg = QString::fromUtf8(buf); return TRUE;}static void createDA(XSQLDA *&sqlda){ sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1)); sqlda->sqln = 1; sqlda->sqld = 0; sqlda->version = SQLDA_VERSION1; sqlda->sqlvar[0].sqlind = 0; sqlda->sqlvar[0].sqldata = 0;}static void enlargeDA(XSQLDA *&sqlda, int n){ free(sqlda); sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n)); sqlda->sqln = n; sqlda->version = SQLDA_VERSION1; }static void initDA(XSQLDA *sqlda){ for (int i = 0; i < sqlda->sqld; ++i) { switch (sqlda->sqlvar[i].sqltype & ~1) { case SQL_INT64: case SQL_LONG: case SQL_SHORT: case SQL_FLOAT: case SQL_DOUBLE: case SQL_TIMESTAMP: case SQL_TYPE_TIME: case SQL_TYPE_DATE: case SQL_TEXT: case SQL_BLOB: sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen); break; case SQL_VARYING: sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short)); break; default: // not supported - do not bind. sqlda->sqlvar[i].sqldata = 0; break; } if (sqlda->sqlvar[i].sqltype & 1) { sqlda->sqlvar[i].sqlind = (short*)malloc(sizeof(short)); *(sqlda->sqlvar[i].sqlind) = 0; } else { sqlda->sqlvar[i].sqlind = 0; } }}static void delDA(XSQLDA *&sqlda){ if (!sqlda) return; for (int i = 0; i < sqlda->sqld; ++i) { free(sqlda->sqlvar[i].sqlind); free(sqlda->sqlvar[i].sqldata); } free(sqlda); sqlda = 0; }static QVariant::Type qIBaseTypeName(int iType){ switch (iType) { case blr_varying: case blr_varying2: case blr_text: case blr_cstring: case blr_cstring2: return QVariant::String; case blr_sql_time: return QVariant::Time; case blr_sql_date: return QVariant::Date; case blr_timestamp: return QVariant::DateTime; case blr_blob: return QVariant::ByteArray; case blr_quad: case blr_short: case blr_long: return QVariant::Int; case blr_int64: return QVariant::LongLong; case blr_float: case blr_d_float: case blr_double: return QVariant::Double; } return QVariant::Invalid;}static QVariant::Type qIBaseTypeName2(int iType){ switch(iType & ~1) { case SQL_VARYING: case SQL_TEXT: return QVariant::String; case SQL_LONG: case SQL_SHORT: return QVariant::Int; case SQL_INT64: return QVariant::LongLong; case SQL_FLOAT: case SQL_DOUBLE: return QVariant::Double; case SQL_TIMESTAMP: return QVariant::DateTime; case SQL_TYPE_DATE: return QVariant::Date; case SQL_TYPE_TIME: return QVariant::Time; default: return QVariant::Invalid; }}static ISC_TIME toTime(const QTime &t){ static const QTime midnight(0, 0, 0, 0); return (ISC_TIME)midnight.msecsTo(t) * 10;}static ISC_DATE toDate(const QDate &d){ static const QDate basedate(1858, 11, 17); return (ISC_DATE)basedate.daysTo(d);}static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt){ ISC_TIMESTAMP ts; ts.timestamp_time = toTime(dt.time()); ts.timestamp_date = toDate(dt.date()); return ts;}static QTime toQTime(ISC_TIME time){ // have to demangle the structure ourselves because isc_decode_time // strips the msecs static const QTime t; return t.addMSecs(time / 10);}static QDate toQDate(ISC_DATE d){ static const QDate bd(1858, 11, 17); return bd.addDays(d);}static QDateTime toQDateTime(ISC_TIMESTAMP *ts){ return QDateTime(toQDate(ts->timestamp_date), toQTime(ts->timestamp_time));}class QIBaseDriverPrivate{public: QIBaseDriverPrivate(QIBaseDriver *d): q(d) { ibase = 0; trans = 0; } bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown) { QString imsg; long sqlcode; if (!getIBaseError(imsg, status, sqlcode)) return FALSE; q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode)); return TRUE; }public: QIBaseDriver* q; isc_db_handle ibase; isc_tr_handle trans; ISC_STATUS status[20]; };class QIBaseResultPrivate{public: QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb); ~QIBaseResultPrivate() { cleanup(); } void cleanup(); bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown) { QString imsg; long sqlcode; if (!getIBaseError(imsg, status, sqlcode)) return FALSE; q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode)); return TRUE; } bool transaction(); bool commit(); bool isSelect(); QVariant fetchBlob(ISC_QUAD *bId); void writeBlob(int i, const QByteArray &ba);public: QIBaseResult *q; const QIBaseDriver *db; ISC_STATUS status[20]; isc_tr_handle trans; //indicator whether we have a local transaction or a transaction on driver level bool localTransaction; isc_stmt_handle stmt; isc_db_handle ibase; XSQLDA *sqlda; // output sqlda XSQLDA *inda; // input parameters int queryType;};QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb): q(d), db(ddb), trans(0), stmt(0), ibase(ddb->d->ibase), sqlda(0), inda(0), queryType(-1){ localTransaction = (ddb->d->ibase == 0);}void QIBaseResultPrivate::cleanup(){ if (stmt) { isc_dsql_free_statement(status, &stmt, DSQL_drop); stmt = 0; } commit(); if (!localTransaction) trans = 0; delDA(sqlda); delDA(inda); queryType = -1; q->cleanup();}void QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba){ isc_blob_handle handle = 0; ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata; isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0); if (!isError("Unable to create BLOB", QSqlError::Statement)) { uint i = 0; while (i < ba.size()) { isc_put_segment(status, &handle, QMIN(ba.size() - i, SHRT_MAX), ba.data()); if (isError("Unable to write BLOB")) break; i += SHRT_MAX; } } isc_close_blob(status, &handle);}QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId){ isc_blob_handle handle = 0; isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0); if (isError("Unable to open BLOB", QSqlError::Statement)) return QVariant(); unsigned short len = 0; QByteArray ba(255); ISC_STATUS stat = isc_get_segment(status, &handle, &len, ba.size(), ba.data()); while (status[1] == isc_segment) { uint osize = ba.size(); // double the amount of data fetched on each iteration ba.resize(QMIN(ba.size() * 2, SHRT_MAX)); stat = isc_get_segment(status, &handle, &len, osize, ba.data() + osize); } bool isErr = isError("Unable to read BLOB", QSqlError::Statement); isc_close_blob(status, &handle); if (isErr) return QVariant(); if (ba.size() > 255) ba.resize(ba.size() / 2 + len); else ba.resize(len); return ba;}bool QIBaseResultPrivate::isSelect(){ char acBuffer[9]; char qType = isc_info_sql_stmt_type; isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer); if (isError("Could not get query info", QSqlError::Statement)) return FALSE; int iLength = isc_vax_integer(&acBuffer[1], 2); queryType = isc_vax_integer(&acBuffer[3], iLength); return (queryType == isc_info_sql_stmt_select);}bool QIBaseResultPrivate::transaction(){ if (trans) return TRUE; if (db->d->trans) { localTransaction = FALSE; trans = db->d->trans; return TRUE; } localTransaction = TRUE; isc_start_transaction(status, &trans, 1, &ibase, 0, NULL); if (isError("Could not start transaction", QSqlError::Statement)) return FALSE; return TRUE;}// does nothing if the transaction is on the// driver levelbool QIBaseResultPrivate::commit(){ if (!trans) return FALSE; // don't commit driver's transaction, the driver will do it for us if (!localTransaction) return TRUE; isc_commit_transaction(status, &trans); trans = 0; return !isError("Unable to commit transaction", QSqlError::Statement);}//////////QIBaseResult::QIBaseResult(const QIBaseDriver* db): QtSqlCachedResult(db){ d = new QIBaseResultPrivate(this, db); setExtension(new QIBasePreparedExtension(this));}QIBaseResult::~QIBaseResult(){ delete d;}bool QIBaseResult::prepare(const QString& query){ //qDebug("prepare: %s", query.ascii()); if (!driver() || !driver()->isOpen() || driver()->isOpenError()) return FALSE; d->cleanup(); setActive(FALSE); setAt(QSql::BeforeFirst); createDA(d->sqlda); createDA(d->inda); if (!d->transaction()) return FALSE; isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt); if (d->isError("Could not allocate statement", QSqlError::Statement)) return FALSE; isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda); if (d->isError("Could not prepare statement", QSqlError::Statement)) return FALSE; isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda); if (d->isError("Could not describe input statement", QSqlError::Statement)) return FALSE; if (d->inda->sqld > d->inda->sqln) { enlargeDA(d->inda, d->inda->sqld); isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda); if (d->isError("Could not describe input statement", QSqlError::Statement)) return FALSE; } initDA(d->inda); if (d->sqlda->sqld > d->sqlda->sqln) { // need more field descriptors enlargeDA(d->sqlda, d->sqlda->sqld); isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda); if (d->isError("Could not describe statement", QSqlError::Statement)) return FALSE; } initDA(d->sqlda); setSelect(d->isSelect()); if (!isSelect()) { free(d->sqlda); d->sqlda = 0; } return TRUE;}bool QIBaseResult::exec(){ if (!driver() || !driver()->isOpen() || driver()->isOpenError()) return FALSE; setActive(FALSE); setAt(QSql::BeforeFirst); if (d->inda && extension()->index.count() > 0) { QMap<int, QString>::ConstIterator it; if ((int)extension()->index.count() > d->inda->sqld) { qWarning("QIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters", d->inda->sqld, extension()->index.count()); return FALSE; } int para = 0; for (it = extension()->index.constBegin(); it != extension()->index.constEnd(); ++it, ++para) { if (para >= d->inda->sqld) break; if (!d->inda->sqlvar[para].sqldata) continue; const QVariant val(extension()->values[it.data()].value); if (d->inda->sqlvar[para].sqltype & 1) { if (val.isNull()) { // set null indicator *(d->inda->sqlvar[para].sqlind) = 1; // and set the value to 0, otherwise it would count as empty string. *((short*)d->inda->sqlvar[para].sqldata) = 0; continue; } // a value of 0 means non-null. *(d->inda->sqlvar[para].sqlind) = 0; } switch(d->inda->sqlvar[para].sqltype & ~1) { case SQL_INT64: if (d->inda->sqlvar[para].sqlscale < 0) *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = Q_LLONG(val.toDouble() * pow(10, d->inda->sqlvar[para].sqlscale * -1)); else *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = val.toLongLong(); break; case SQL_LONG: *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong(); break; case SQL_SHORT: *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt(); break; case SQL_FLOAT: *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble(); break; case SQL_DOUBLE: *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble(); break; case SQL_TIMESTAMP: *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime()); break; case SQL_TYPE_TIME: *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime()); break; case SQL_TYPE_DATE: *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate()); break; case SQL_VARYING: { QCString str(val.toString().utf8()); // keep a copy of the string alive in this scope short buflen = d->inda->sqlvar[para].sqllen; if (str.length() < (uint)buflen) buflen = str.length(); *(short*)d->inda->sqlvar[para].sqldata = buflen; // first two bytes is the length memcpy(d->inda->sqlvar[para].sqldata + sizeof(short), str.data(), buflen); break; } case SQL_TEXT: { QCString str(val.toString().utf8().leftJustify(d->inda->sqlvar[para].sqllen, ' ', TRUE)); memcpy(d->inda->sqlvar[para].sqldata, str.data(), d->inda->sqlvar[para].sqllen); break; } case SQL_BLOB: d->writeBlob(para, val.toByteArray()); break; default: break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -