Files
qt6-migration-pipeline/Z/src/qsql_informix/qsql_informix.cpp
T
torstenandCopilot e1b5c2f918 Fix QSqlQuery copy and Qt6 deprecation issues
- Suppress deprecated QSqlQuery copy-constructor warning locally in NSqlQuery::operator=
- Replace qChecksum overload with QByteArrayView
- Use QTimeZone::utc(), QLibraryInfo::path(), check QTranslator::load return values
- Replace deprecated operator+ for key combinations and other small Qt6 fixes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-14 23:32:33 +02:00

1335 lines
32 KiB
C++
Executable File

#include "qsql_informix.h"
#include <it.h>
#include <itcppop.h>
#include <sqltypes.h>
#include <sqlca.h>
#include <math.h>
#include <QAtomicInt>
#include <QDateTime>
#include <QMutex>
#include <QSqlError>
#include <QSqlField>
#include <QSqlIndex>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QStringList>
#include <QTextCodec>
#include <QTimer>
#include <QVariant>
#include <QMetaType>
#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 : "<UNKNOWN>");
#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<quint64>(lastSerial8.data[0]);
const quint64 high = static_cast<quint64>(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 <class T>
bool bindValues(T *prep, const QVector<QVariant> &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<QStringRef> 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();
}