#include "qsql_informix.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_FUNCTION #ifdef Q_CC_MSVC float truncf(float in) { return in < 0 ? in + 0.5 : in - 0.5; } #endif Q_DECLARE_METATYPE(ITConnection *) Q_DECLARE_METATYPE(ITSet *) class QInformixDriverPrivate { public: QInformixDriverPrivate(QInformixDriver *parent) : connection(0) , driver(parent) , dbLocale(0) , openCount(0) , connTimeout(-1) , connectOnFirstUse(false) { connectionTimeoutTimer.setSingleShot(true); QObject::connect(&connectionTimeoutTimer, SIGNAL(timeout()), driver, SLOT(timeout())); } ITConnection *connection; QInformixDriver *driver; QTextCodec *dbLocale; ITDBInfo conn_params; QMutex connCountMutex; QAtomicInt openCount; int connTimeout; bool connectOnFirstUse; QTimer connectionTimeoutTimer; inline ITString toInformixEncoding(const QString &str) const { return ITString(dbLocale->fromUnicode(str).constData(), true); } inline QByteArray toInformixEncodingBA(const QString &str) const { return dbLocale->fromUnicode(str).constData(); } inline QString fromInformixEncoding(const ITString &str) const { return dbLocale->toUnicode(str.Data()).trimmed(); } inline QString fromInformixEncoding(const QByteArray &str) const { return dbLocale->toUnicode(str); } void addConnection() { QMutexLocker l(&connCountMutex); checkOpen(); openCount.ref(); #ifdef DEBUG_AUTODISCONNECT fprintf(stderr, "%p %s opencount: %d\n", this, __PRETTY_FUNCTION__, (int)openCount); #endif } void removeConnection() { QMutexLocker l(&connCountMutex); if (openCount.deref() == false && connTimeout >= 0) { #ifdef DEBUG_AUTODISCONNECT fprintf(stderr, "%p singleShot timeout: %d, driver: %p\n", this, connTimeout, driver); #endif connectionTimeoutTimer.start(connTimeout); } #ifdef DEBUG_AUTODISCONNECT fprintf(stderr, "%p %s opencount: %d\n", this, __PRETTY_FUNCTION__, (int)openCount); #endif } void closeConnection(); void checkOpen() { if (!connection) { if (connectOnFirstUse) { connectOnFirstUse = false; if (!driver->openDatabaseInternal()) return; } else return; } if (!connection->IsOpen()) connection->Open(conn_params); } }; void QInformixDriverPrivate::closeConnection() { QMutexLocker l(&connCountMutex); #ifdef DEBUG_AUTODISCONNECT fprintf(stderr, "%p %s\n", this, __PRETTY_FUNCTION__); #endif if (!driver->isOpen()) return; #ifdef DEBUG_AUTODISCONNECT fprintf(stderr, "%p opencount: %d\n", this, (int)openCount); #endif if (openCount > 0) return; connection->Close(); } static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type, const QInformixDriverPrivate *p) { const char *e = p->connection->ErrorText(); QString msg = QStringLiteral("IFX Error: ") + QLatin1String(e ? e : ""); #ifdef DEBUG_QSQL_INFORMIX fprintf(stderr, "qMakeError: %s\n", qPrintable(msg)); #endif const struct sqlca_s *my_sqlca = &sqlca; return QSqlError(QStringLiteral("QInformix: %1").arg(err), msg, type, my_sqlca->sqlcode); } static QVariant::Type qSqlType2Variant(int t) { t &= SQLTYPE; switch (t) { case SQLSMINT: case SQLINT: case SQLINTERVAL: case SQLSERIAL: return QVariant::Int; case SQLINT8: case SQLSERIAL8: return QVariant::LongLong; case SQLFLOAT: case SQLSMFLOAT: case SQLDECIMAL: case SQLMONEY: return QVariant::Double; case SQLDATE: return QVariant::Date; case SQLDTIME: return QVariant::DateTime; case SQLBYTES: return QVariant::ByteArray; case SQLCHAR: case SQLVCHAR: case SQLNCHAR: case SQLNVCHAR: case SQLLVARCHAR: case SQLTEXT: return QVariant::String; default: break; } qWarning("Unknown sql data type %d", t); return QVariant::Invalid; } static int qIfxType2SqlType(const char *typeName) { switch (typeName[0]) { case 'b': if (strcmp(typeName, "byte") == 0) return SQLBYTES; break; case 'c': if (strcmp(typeName, "clob") == 0) return SQLBYTES; if (strcmp(typeName, "char") == 0) return SQLCHAR; break; case 'd': if (strcmp(typeName, "date") == 0) return SQLDATE; if (strcmp(typeName, "datetime") == 0) return SQLDTIME; if (strcmp(typeName, "decimal") == 0) return SQLDECIMAL; break; case 'f': if (strcmp(typeName, "float") == 0) return SQLFLOAT; break; case 'i': if (strcmp(typeName, "integer") == 0) return SQLINT; if (strcmp(typeName, "int8") == 0) return SQLINT8; break; case 'l': if (strcmp(typeName, "lvarchar") == 0) return SQLLVARCHAR; break; case 'n': if (strcmp(typeName, "nchar") == 0) return SQLNCHAR; if (strcmp(typeName, "nvarchar") == 0) return SQLNVCHAR; break; case 's': if (strcmp(typeName, "serial") == 0) return SQLSERIAL; if (strcmp(typeName, "serial8") == 0) return SQLSERIAL8; if (strcmp(typeName, "smallint") == 0) return SQLSMINT; if (strcmp(typeName, "smallfloat") == 0) return SQLFLOAT; break; case 't': if (strcmp(typeName, "text") == 0) return SQLTEXT; break; case 'v': if (strcmp(typeName, "varchar") == 0) return SQLVCHAR; break; default: break; } qWarning("Unknown informix data type '%s'", typeName); return -1; } static inline qint64 getLastSerial(ITConnection *itconn) { MI_CONNECTION *conn = itconn->CheckOutConn(); mi_integer lastSerial; mi_last_serial(conn, &lastSerial); if (lastSerial != 0) { itconn->CheckInConn(); return lastSerial; } mi_int8 lastSerial8; mi_last_serial8(conn, &lastSerial8); itconn->CheckInConn(); const quint64 low = static_cast(lastSerial8.data[0]); const quint64 high = static_cast(lastSerial8.data[1]); qint64 ret = (high << 32) + low; return lastSerial8.sign == -1 ? -ret : ret; } class QInformixResultPrivate { public: QInformixResultPrivate(QInformixResult *qq) : q(qq) , driverPrivate(NULL) , query(NULL) , result(NULL) , row(NULL) , preparedStmt(NULL) , preparedCursor(NULL) , currentSize(-1) , numAffectedRows(-1) , lastInsertId(-1) {} QInformixResult *q; QInformixDriverPrivate *driverPrivate; ITQuery *query; ITSet *result; ITRow *row; ITStatement *preparedStmt; ITCursor *preparedCursor; int currentSize; int numAffectedRows; qint64 lastInsertId; inline ITConnection *getConnection() const { return driverPrivate->connection; } void cleanupInternal() { q->cleanup(); if (preparedStmt) { preparedStmt->Drop(); delete preparedStmt; preparedStmt = NULL; } if (preparedCursor) { preparedCursor->Drop(); preparedCursor->Close(); delete preparedCursor; preparedCursor = NULL; } } }; QInformixResult::QInformixResult(const QInformixDriver *db, QInformixDriverPrivate *p) : QSqlResult(db) { DEBUG_FUNCTION p->addConnection(); d = new QInformixResultPrivate(this); d->driverPrivate = p; } QInformixResult::~QInformixResult() { DEBUG_FUNCTION d->cleanupInternal(); d->driverPrivate->removeConnection(); delete d; } QVariant QInformixResult::handle() const { DEBUG_FUNCTION return qVariantFromValue(d->result); } void QInformixResult::cleanup() { DEBUG_FUNCTION if (d->query) { d->query->Finish(); delete d->query; d->query = NULL; } if (d->result) { d->result->Close(); d->result->Release(); d->result = NULL; } if (d->row) { d->row->Release(); d->row = NULL; } setActive(false); } bool QInformixResult::fetch(int i) { if (!isActive()) return false; if (i < 0) return false; if (at() == i) return true; if (d->row) { d->row->Release(); d->row = NULL; } if (d->result) { if (i >= d->currentSize) return false; ITValue *value = d->result->Fetch(NULL, ITPositionAbsolute, i + 1); if (!value) { qDebug("QInformixResult::fetch(%d): Fetch failed\n", i); return false; } int ret = value->QueryInterface(ITRowIID, (void **) & d->row); if (ret == IT_QUERYINTERFACE_FAILED) { value->Release(); return false; } value->Release(); } else { if (!d->preparedCursor) return false; if (isForwardOnly()) d->row = d->preparedCursor->NextRow(); else d->row = d->preparedCursor->NextRow(NULL, ITPositionAbsolute, i + 1); if (!d->row) return false; } setAt(i); return true; } bool QInformixResult::fetchFirst() { return fetch(0); } bool QInformixResult::fetchLast() { return fetch(d->currentSize - 1); } QVariant QInformixResult::data(int i) { if (!d->row) return QVariant(); if (i >= d->row->NumColumns()) { qWarning("QInformixResult::data: column %d out of range", i); return QVariant(); } ITValue *col = d->row->Column(i); if (!col) { qWarning("QInformixResult::data: column %d out of range", i); return QVariant(); } const int sqlType = qIfxType2SqlType(col->TypeOf().Name()); if (col->IsNull()) { col->Release(); return QVariant(qSqlType2Variant(sqlType)); } QVariant variant; switch (sqlType) { case SQLBYTES: { ITDatum *lobj = NULL; col->QueryInterface(ITDatumIID, (void **) &lobj); variant = QByteArray((const char *) lobj->Data(), lobj->DataLength()); lobj->Release(); break; } case SQLTEXT: { ITDatum *lobj = NULL; col->QueryInterface(ITDatumIID, (void **) &lobj); const QByteArray ba = QByteArray((const char *) lobj->Data()); variant = d->driverPrivate->fromInformixEncoding(ba); lobj->Release(); break; } case SQLCHAR: case SQLNCHAR: case SQLNVCHAR: case SQLVCHAR: case SQLLVARCHAR: variant = QVariant(d->driverPrivate->fromInformixEncoding(col->Printable())); break; case SQLDATE: { ITDateTime *dt = NULL; col->QueryInterface(ITDateTimeIID, (void **) &dt); variant = QVariant(QDate(dt->Year(), dt->Month(), dt->Day())); dt->Release(); break; } case SQLDTIME: { ITDateTime *dt = NULL; col->QueryInterface(ITDateTimeIID, (void **) &dt); const QDate date(QDate(dt->Year(), dt->Month(), dt->Day())); const int seconds = (int) truncf(dt->Second()); const int msecs = (int) truncf((dt->Second() - seconds) * 1000); const QTime time(QTime(dt->Hour(), dt->Minute(), seconds, msecs)); variant = QVariant(QDateTime(date, time)); dt->Release(); break; } case SQLDECIMAL: case SQLFLOAT: { ITConversions *conv = NULL; col->QueryInterface(ITConversionsIID, (void **) &conv); double dbl; conv->ConvertTo(dbl); variant = QVariant(dbl); conv->Release(); break; } case SQLSERIAL: case SQLINT: { ITConversions *conv = NULL; col->QueryInterface(ITConversionsIID, (void **) &conv); int int32; conv->ConvertTo(int32); variant = QVariant(int32); conv->Release(); break; } case SQLSERIAL8: case SQLINT8: { ITConversions *conv = NULL; col->QueryInterface(ITConversionsIID, (void **) &conv); long int int64; conv->ConvertTo(int64); variant = QVariant((qint64) int64); conv->Release(); break; } case SQLSMINT: { ITConversions *conv = NULL; col->QueryInterface(ITConversionsIID, (void **) &conv); short int int16; conv->ConvertTo(int16); variant = QVariant(int16); conv->Release(); break; } default: variant = QVariant(qSqlType2Variant(sqlType)); qWarning("QInformixResult::data: Unknown sql data type %d at row %d\n", sqlType, i); break; } col->Release(); return variant; } bool QInformixResult::isNull(int i) { DEBUG_FUNCTION if (!d->row) return true; if (i >= d->row->NumColumns()) { qWarning("QInformixResult::isNull: column %d out of range", i); return true; } ITValue *col = d->row->Column(i); if (!col) { qWarning("QInformixResult::isNull: column %d out of range", i); return false; } return col->IsNull(); } bool QInformixResult::reset(const QString &query) { #ifdef DEBUG_QSQL_INFORMIX fprintf(stderr, " QInformixResult::reset(%s)\n", qPrintable(query)); #endif d->cleanupInternal(); if (!driver()) return false; if (!driver()->isOpen() || driver()->isOpenError()) return false; d->currentSize = -1; d->numAffectedRows = -1; d->lastInsertId = -1; setSelect(false); d->query = new ITQuery(*d->getConnection()); bool bIsSelect = query.trimmed().startsWith(QLatin1String("SELECT"), Qt::CaseInsensitive); try { if (bIsSelect) { d->result = d->query->ExecToSet(d->driverPrivate->toInformixEncoding(query)); if (!d->result || !d->result->Open()) throw false; } else { bool bOk = d->query->ExecForStatus(d->driverPrivate->toInformixEncoding(query)); if (!bOk) throw false; } if (d->query->Error()) throw false; } catch (bool bOk) { if (!bOk) { setLastError(qMakeError(QObject::tr("Unable to create query"), QSqlError::StatementError, d->driverPrivate)); return false; } } setSelect(bIsSelect); setActive(true); if (bIsSelect) d->currentSize = d->query->RowCount(); else d->numAffectedRows = d->query->RowCount(); d->lastInsertId = getLastSerial(d->getConnection()); return true; } int QInformixResult::size() { return d->currentSize; } int QInformixResult::numRowsAffected() { DEBUG_FUNCTION return d->numAffectedRows; } QVariant QInformixResult::lastInsertId() const { return d->lastInsertId ? QVariant(d->lastInsertId) : QVariant(); } QSqlRecord QInformixResult::record() const { DEBUG_FUNCTION QSqlRecord info; if (!isActive() || !isSelect()) return info; if (!d->query && !d->preparedCursor) return info; const ITTypeInfo *rowInfo = d->query ? d->query->RowType() : d->preparedCursor->RowType(); const int count = rowInfo->ColumnCount(); QSqlField f; for (int i = 0; i < count; ++i) { const ITTypeInfo *colInfo = rowInfo->ColumnType(i); const char *typeName = colInfo->Name(); const int len = colInfo->Size(); const int prec = colInfo->Precision(); const int sqlType = qIfxType2SqlType(typeName); f.setName(d->driverPrivate->fromInformixEncoding(rowInfo->ColumnName(i))); f.setLength(len == -1 ? prec : len); f.setPrecision(len == -1 ? len : prec); f.setType(qSqlType2Variant(sqlType)); f.setSqlType(sqlType); if (qSqlType2Variant(sqlType) == QVariant::Invalid) qWarning("QInformixResult::record(): Unknown datatype %s", typeName); info.append(f); } return info; } inline QString replaceAt(const QString &str) { QString ret(str); QChar *data = ret.data(); const int len = str.length(); for (int i = 0; i < len; ++i) { if (data[i] == QLatin1Char('@')) data[i] = QLatin1Char(':'); } return ret; } bool QInformixResult::prepare(const QString &_query) { QString query = replaceAt(_query.trimmed()); #ifdef DEBUG_QSQL_INFORMIX fprintf(stderr, "QInformixResult::prepare(%s)\n", qPrintable(query)); #endif d->cleanupInternal(); bool bIsSelect = query.startsWith(QLatin1String("SELECT"), Qt::CaseInsensitive); setSelect(bIsSelect); if (bIsSelect) { d->preparedCursor = new ITCursor(*d->getConnection()); if (!d->preparedCursor->Prepare(d->driverPrivate->toInformixEncoding(query))) { setLastError(qMakeError(QObject::tr("Unable to prepare statement"), QSqlError::StatementError, d->driverPrivate)); delete d->preparedCursor; d->preparedCursor = NULL; return false; } } else { d->preparedStmt = new ITStatement(*d->getConnection()); if (!d->preparedStmt->Prepare(d->driverPrivate->toInformixEncoding(query))) { setLastError(qMakeError(QObject::tr("Unable to prepare statement"), QSqlError::StatementError, d->driverPrivate)); delete d->preparedStmt; d->preparedStmt = NULL; return false; } } setActive(true); return true; } template bool bindValues(T *prep, const QVector &boundValues, const QInformixResultPrivate *p, const QSqlDriver *driver) { const int cnt = boundValues.count(); if (cnt != prep->NumParams()) return false; QSqlField f; for (int i = 0; i < cnt; ++i) { const QVariant &val = boundValues.at(i); ITValue *param = prep->Param(i); if (val.metaType().id() == QMetaType::QByteArray) { ITDatum *lobj; ITTypeInfo ti(*p->getConnection(), "byte"); ITMVDesc desc; desc.vf_origtypeinfo = &ti; desc.vf_connection = p->getConnection(); desc.vf_typedesc = NULL; desc.vf_preservedata = NULL; desc.vf_outerunknown = NULL; desc.vf_datalength = ti.Size(); desc.vf_libmivaluetype = MI_NULL_VALUE; desc.vf_data = NULL; ITValue *v = ITFactoryList::DatumToValue(desc); v->QueryInterface(ITDatumIID, (void **) & lobj); QByteArray ba = val.toByteArray(); if (ba.size()) lobj->SetData(ba.data(), ba.size()); else lobj->SetNull(); prep->SetParam(i, lobj); v->Release(); lobj->Release(); } else { const char *t = param->TypeOf().Name(); if (strcmp(t, "text") == 0) { ITDatum *lobj; ITTypeInfo ti(*p->getConnection(), "byte"); ITMVDesc desc; desc.vf_origtypeinfo = &ti; desc.vf_connection = p->getConnection(); desc.vf_typedesc = NULL; desc.vf_preservedata = NULL; desc.vf_outerunknown = NULL; desc.vf_datalength = ti.Size(); desc.vf_libmivaluetype = MI_NULL_VALUE; desc.vf_data = NULL; ITValue *v = ITFactoryList::DatumToValue(desc); v->QueryInterface(ITDatumIID, (void **) & lobj); QByteArray ba = p->driverPrivate->toInformixEncodingBA(val.toString()); if (ba.size()) lobj->SetData(ba.data(), ba.size()); else lobj->SetNull(); lobj->FromPrintable(ba.data()); prep->SetParam(i, lobj); v->Release(); lobj->Release(); } else { // setValue() already sets the type appropriately f.setValue(val); if (!param->FromPrintable(p->driverPrivate->toInformixEncoding(driver->formatValue(f)))) return false; } } param->Release(); } return true; } bool QInformixResult::exec() { DEBUG_FUNCTION if (isSelect()) { if (d->query) return false; if (!d->preparedCursor) return false; cleanup(); setActive(true); const int openMode = isForwardOnly() ? ITCursor::ReadOnly : ITCursor::ReadOnly + ITCursor::Scrollable; if (!bindValues(d->preparedCursor, boundValues(), d, driver()) || !d->preparedCursor->Open(openMode)) { setLastError(qMakeError(QObject::tr("Unable to execute statement"), QSqlError::StatementError, d->driverPrivate)); d->preparedCursor->Drop(); d->preparedCursor->Close(); delete d->preparedCursor; d->preparedCursor = NULL; return false; } d->lastInsertId = -1; d->numAffectedRows = -1; d->currentSize = -1; } else { if (!d->preparedStmt) return false; cleanup(); setActive(true); if (!bindValues(d->preparedStmt, boundValues(), d, driver()) || !d->preparedStmt->Exec()) { setLastError(qMakeError(QObject::tr("Unable to execute statement"), QSqlError::StatementError, d->driverPrivate)); d->preparedStmt->Drop(); delete d->preparedStmt; d->preparedStmt = NULL; return false; } d->currentSize = -1; d->lastInsertId = getLastSerial(d->getConnection()); d->numAffectedRows = d->preparedStmt->RowCount(); } return true; } QInformixDriver::QInformixDriver(QObject *parent) : QSqlDriver(parent) { init(); } void QInformixDriver::init() { d = new QInformixDriverPrivate(this); } QInformixDriver::~QInformixDriver() { if (d->connection) close(); delete d; } QVariant QInformixDriver::handle() const { return qVariantFromValue(d->connection); } bool QInformixDriver::hasFeature(DriverFeature f) const { switch (f) { case LastInsertId: case Transactions: case QuerySize: case PreparedQueries: return true; case LowPrecisionNumbers: case EventNotifications: case SimpleLocking: case FinishQuery: case MultipleResultSets: case PositionalPlaceholders: case BatchOperations: case NamedPlaceholders: case CancelQuery: return false; case BLOB: case Unicode: return true; } return false; } bool QInformixDriver::open(const QString &db, const QString &user, const QString &password, const QString &host, int port, const QString &connOpts) { Q_UNUSED(port) const QVector options = connOpts.splitRef(QLatin1Char(';')); QString dbLocale = QStringLiteral("UTF-8"); d->connTimeout = -1; d->connectOnFirstUse = false; for (const QStringRef &opt : options) { if (opt.startsWith(QLatin1String("DB_LOCALE="))) dbLocale = opt.mid(strlen("DB_LOCALE=")).toString(); if (opt.startsWith(QLatin1String("CONN_TIMEOUT="))) { bool bOk; d->connTimeout = opt.mid(strlen("CONN_TIMEOUT=")).toInt(&bOk); if (bOk && d->connTimeout > 0) { d->connTimeout *= 1000; QTimer::singleShot(d->connTimeout, this, SLOT(timeout())); } else d->connTimeout = -1; } if (opt.startsWith(QLatin1String("CONNECT_ON_FIRST_USE="))) { bool bOk; int val = opt.mid(strlen("CONNECT_ON_FIRST_USE=")).toInt(&bOk); if (bOk) d->connectOnFirstUse = (val != 0); else d->connectOnFirstUse = false; } } #ifdef DEBUG_AUTODISCONNECT fprintf(stderr, "%p %s connTimeout: %d\n", this, __PRETTY_FUNCTION__, d->connTimeout); #endif d->dbLocale = QTextCodec::codecForName(dbLocale.toUtf8()); if (!d->dbLocale) { setLastError(qMakeError(tr("Can't create Codec for Name %1").arg(dbLocale), QSqlError::ConnectionError, d)); setOpenError(true); return false; } if (isOpen()) close(); if (!host.isEmpty()) d->conn_params.SetSystem(d->toInformixEncoding(host)); if (!db.isEmpty()) d->conn_params.SetDatabase(d->toInformixEncoding(db)); if (!user.isEmpty()) d->conn_params.SetUser(d->toInformixEncoding(user)); if (!password.isEmpty()) d->conn_params.SetPassword(d->toInformixEncoding(password)); d->conn_params.Freeze(); d->connection = new ITConnection(); if (d->connectOnFirstUse) return true; return openDatabaseInternal(); } bool QInformixDriver::openDatabaseInternal() { int ret = d->connection->Open(d->conn_params); if (!ret || !d->connection->IsOpen()) { setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d)); setOpenError(true); d->connection->Close(); delete d->connection; d->connection = 0; return false; } setOpen(true); setOpenError(false); return true; } void QInformixDriver::close() { DEBUG_FUNCTION if (isOpen()) { d->connection->Close(); setOpen(false); setOpenError(false); } delete d->connection; d->connection = 0; } bool QInformixDriver::isOpen() const { return (d->connection); } QSqlResult *QInformixDriver::createResult() const { return new QInformixResult(this, d); } QStringList QInformixDriver::tables(QSql::TableType type) const { DEBUG_FUNCTION QStringList tl; if (!isOpen()) return tl; QSqlQuery t(createResult()); t.setForwardOnly(true); if (type == 0) type = QSql::Tables; QString cmd; if ((type & (QSql::SystemTables | QSql::Tables)) == (QSql::SystemTables | QSql::Tables)) cmd = QStringLiteral("select tabname from systables where tabid >= 0"); else if ((type & QSql::SystemTables) == QSql::SystemTables) cmd = QStringLiteral("select tabname from systables where tabid < 100"); else cmd = QStringLiteral("select tabname from systables where tabid >= 100"); if ((type & (QSql::Tables | QSql::Views)) == (QSql::Tables | QSql::Views)) cmd += QStringLiteral(" and (tabtype = 'T' or tabtype = 'V')"); else if ((type & QSql::Tables) == QSql::Tables) cmd += QStringLiteral(" and tabtype = 'T'"); else if ((type & QSql::Views) == QSql::Views) cmd += QStringLiteral(" and tabtype = 'V'"); cmd += QStringLiteral(" order by tabid"); t.exec(cmd); while (t.next()) tl.append(t.value(0).toString()); return tl; } static void qSplitTableName(QString &tablename, QString &schema) { int dot = tablename.indexOf(QLatin1Char('.')); if (dot == -1) return; schema = tablename.left(dot); tablename = tablename.mid(dot + 1); } QSqlIndex QInformixDriver::primaryIndex(const QString &tablename) const { QSqlIndex idx(tablename); if (!isOpen()) return idx; QSqlQuery i(createResult()); QString tbl = tablename; QString schema; qSplitTableName(tbl, schema); QString cmd(QStringLiteral("select syscolumns.colname, syscolumns.coltype, si.idxname " "from syscolumns " "left outer join sysindexes si on syscolumns.tabid = si.tabid " "where syscolumns.tabid = (select tabid from systables where tabname='%1') " "and colno = (select part1 from sysindexes where tabid = ( " "select tabid from systables where tabname='%1') " "and idxname = (select idxname from sysconstraints where tabid=( " "select tabid from systables where tabname='%1') " "and constrtype='P'))" )); i.exec(cmd.arg(tbl.toLower())); while (i.isActive() && i.next()) { QSqlField f(i.value(0).toString(), qSqlType2Variant(i.value(1).toInt())); idx.append(f); idx.setName(i.value(2).toString()); } return idx; } QSqlRecord QInformixDriver::record(const QString &tablename) const { QSqlRecord info; if (!isOpen()) return info; QString tbl = tablename; QString schema; qSplitTableName(tbl, schema); QString cmd(QStringLiteral("select " "syscolumns.colname, syscolumns.coltype, syscolumns.collength " "from systables, syscolumns " "where systables.tabname = '%1' " "and systables.tabid = syscolumns.tabid")); QSqlQuery query(createResult()); query.exec(cmd.arg(tbl.toLower())); while (query.next()) { const int sqltype = query.value(1).toInt(); QSqlField f(query.value(0).toString(), qSqlType2Variant(sqltype)); f.setRequired((sqltype & SQLNONULL) == SQLNONULL); f.setLength(query.value(2).toInt()); f.setSqlType(sqltype & SQLTYPE); f.setAutoValue((sqltype & SQLTYPE) == SQLSERIAL || (sqltype & SQLTYPE) == SQLSERIAL8); info.append(f); } return info; } QString QInformixDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const { QString res = identifier; res.replace(QLatin1Char('"'), QStringLiteral("\"\"")); res.replace(QLatin1Char('.'), QStringLiteral("\".\"")); return res; } QString QInformixDriver::formatValue(const QSqlField &field, bool trimStrings) const { QString r; switch (field.type()) { case QVariant::DateTime: if (field.value().toDateTime().isValid()) { const QDate dt = field.value().toDateTime().date(); const QTime tm = field.value().toDateTime().time(); r = QString::number(dt.year()) + QLatin1Char('-') + QString::number(dt.month()).rightJustified(2, QLatin1Char('0')) + QLatin1Char('-') + QString::number(dt.day()).rightJustified(2, QLatin1Char('0')) + QLatin1Char(' ') + tm.toString(); } else r = QStringLiteral("NULL"); break; case QVariant::Date: if (field.value().toDate().isValid()) { const QDate dt = field.value().toDateTime().date(); r = QString::number(dt.year()) + QLatin1Char('-') + QString::number(dt.month()).rightJustified(2, QLatin1Char('0')) + QLatin1Char('-') + QString::number(dt.day()).rightJustified(2, QLatin1Char('0')); } else r = QStringLiteral("NULL"); break; case QVariant::String: return trimStrings ? field.value().toString().trimmed() : field.value().toString(); default: return QSqlDriver::formatValue(field, trimStrings); } return r; } bool QInformixDriver::beginTransaction() { if (!isOpen()) { qWarning("QInformixDriver::beginTransaction: Database not open"); return false; } d->addConnection(); if (!d->connection->SetTransaction(ITConnection::Begin)) { setLastError(qMakeError(tr("Could not begin transaction"), QSqlError::TransactionError, d)); d->removeConnection(); return false; } return true; } bool QInformixDriver::commitTransaction() { if (!isOpen()) { qWarning("QInformixDriver::commitTransaction: Database not open"); return false; } if (!d->connection->SetTransaction(ITConnection::Commit)) { setLastError(qMakeError(tr("Could not commit transaction"), QSqlError::TransactionError, d)); d->removeConnection(); return false; } d->removeConnection(); return true; } bool QInformixDriver::rollbackTransaction() { if (!isOpen()) { qWarning("QInformixDriver::rollbackTransaction: Database not open"); return false; } if (!d->connection->SetTransaction(ITConnection::Abort)) { setLastError(qMakeError(tr("Could not rollback transaction"), QSqlError::TransactionError, d)); d->removeConnection(); return false; } d->removeConnection(); return true; } void QInformixDriver::timeout() { #ifdef DEBUG_AUTODISCONNECT fprintf(stderr, "%p %s private: %p\n", this, __PRETTY_FUNCTION__, d); #endif d->closeConnection(); }