diff options
Diffstat (limited to 'src/sql')
-rw-r--r-- | src/sql/drivers/symsql/qsql_symsql.cpp | 1185 | ||||
-rw-r--r-- | src/sql/drivers/symsql/qsql_symsql.h | 132 | ||||
-rw-r--r-- | src/sql/drivers/symsql/qsql_symsql.pri | 5 |
3 files changed, 1322 insertions, 0 deletions
diff --git a/src/sql/drivers/symsql/qsql_symsql.cpp b/src/sql/drivers/symsql/qsql_symsql.cpp new file mode 100644 index 0000000..4ceac9b --- /dev/null +++ b/src/sql/drivers/symsql/qsql_symsql.cpp @@ -0,0 +1,1185 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsql_symsql.h" + + +#define SYMBIAN_ENABLE_PUBLIC_PLATFORM_HEADER_SPLIT + +#include <qcoreapplication.h> +#include <qvariant.h> +#include <qsqlerror.h> +#include <qsqlfield.h> +#include <qsqlindex.h> +#include <qsqlquery.h> +#include <qstringlist.h> +#include <qvector.h> +#include <qdebug.h> +#include "../../../corelib/kernel/qcore_symbian_p.h" + +#if defined Q_OS_WIN +# include <qt_windows.h> +#else +# include <unistd.h> +#endif + +#include <sqldb.h> +#include <e32capability.h> + +const char* qCapabilityNames[ECapability_Limit] = +{ + "TCB", + "CommDD", + "PowerMgmt", + "MultimediaDD", + "ReadDeviceData", + "WriteDeviceData", + "DRM", + "TrustedUI", + "ProtServ", + "DiskAdmin", + "NetworkControl", + "AllFiles", + "SwEvent", + "NetworkServices", + "LocalServices", + "ReadUserData", + "WriteUserData", + "Location", + "SurroundingsDD", + "UserEnvironment" +}; + +const char qCapabilityNone[] = "None"; + + +Q_DECLARE_METATYPE(RSqlDatabase) +Q_DECLARE_METATYPE(RSqlStatement) + +QT_BEGIN_NAMESPACE + +const QString valueSeparator(QLatin1String("=")); +const QString fieldSeparator(QLatin1String(",")); + +static QString _q_escapeIdentifier(const QString &identifier) +{ + QString res = identifier; + if (!identifier.isEmpty() + && identifier.left(1) != QString(QLatin1Char('"')) + && identifier.right(1) != QString(QLatin1Char('"'))) { + res.replace(QLatin1Char('"'), QLatin1String("\"\"")); + res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); + res.replace(QLatin1Char('.'), QLatin1String("\".\"")); + } + return res; +} + +static QVariant::Type qGetColumnType(const TSqlColumnType coltype) +{ + switch(coltype){ + case ESqlInt: + case ESqlInt64: + return QVariant::Int; + case ESqlReal: + return QVariant::Double; + case ESqlBinary: + return QVariant::ByteArray; + case ESqlText: + case ESqlNull: + default: + return QVariant::String; + } +} + +static QVariant::Type qGetColumnType(const QString &tpName) +{ + const QString typeName = tpName.toLower(); + + if (typeName == QLatin1String("integer") + || typeName == QLatin1String("int")) + return QVariant::Int; + if (typeName == QLatin1String("double") + || typeName == QLatin1String("float") + || typeName == QLatin1String("real") + || typeName.startsWith(QLatin1String("numeric"))) + return QVariant::Double; + if (typeName == QLatin1String("blob")) + return QVariant::ByteArray; + return QVariant::String; +} + +static QSqlError qMakeError(RSqlDatabase& access, + const QString &descr, + QSqlError::ErrorType type, + int errorCode = -1) +{ + return QSqlError(descr, + QString::fromUtf16(static_cast<const ushort *>(access.LastErrorMessage().Ptr())), + type, + errorCode); +} + + +static QSqlError gMakeErrorOpen(const QString &descr, + QSqlError::ErrorType type, + TInt errorCode) +{ + return QSqlError(descr, QLatin1String(""), type, errorCode); +} + +class QSymSQLDriverPrivate +{ +public: + inline QSymSQLDriverPrivate() {} + RSqlDatabase access; +}; + +class QSymSQLResultPrivate +{ +public: + QSymSQLResultPrivate(QSymSQLResult *res); + void cleanup(); + bool fetchNext(bool initialFetch); + // initializes the recordInfo + void initColumns(QSqlRecord& rec); + void finalize(); + + QSymSQLResult* q; + RSqlDatabase access; + RSqlStatement stmt; + bool skipRow; // skip the next fetchNext()? + bool skippedStatus; // the status of the fetchNext() that's skipped + bool prepareCalled; +}; + +QSymSQLResultPrivate::QSymSQLResultPrivate(QSymSQLResult* res) : q(res), + skipRow(false), + skippedStatus(false), + prepareCalled(false) +{ +} + +void QSymSQLResultPrivate::cleanup() +{ + finalize(); + skippedStatus = false; + skipRow = false; + q->setAt(QSql::BeforeFirstRow); + q->setActive(false); +} + +void QSymSQLResultPrivate::finalize() +{ + prepareCalled = false; + stmt.Close(); +} + +void QSymSQLResultPrivate::initColumns(QSqlRecord& rec) +{ + int nCols = stmt.ColumnCount(); + if (nCols <= 0) { + q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", + "Error retreving column count"), QSqlError::UnknownError, nCols)); + return; + } + + for (int i = 0; i < nCols; ++i) { + TPtrC cName; + TInt err = stmt.ColumnName(i, cName); + + if (err != KErrNone) { + q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", + "Error retreving column name"), QSqlError::UnknownError, err)); + return; + } + + QString colName = qt_TDesC2QString(cName); + + // must use typeName for resolving the type to match QSymSQLDriver::record + TPtrC tName; + TSqlColumnType decColType; + err = stmt.DeclaredColumnType(i, decColType); + + if (err != KErrNone) { + q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", + "Error retreving column type"), QSqlError::UnknownError, err)); + return; + } + + int dotIdx = colName.lastIndexOf(QLatin1Char('.')); + QSqlField fld(colName.mid(dotIdx == -1 ? 0 : dotIdx + 1), qGetColumnType(decColType)); + + rec.append(fld); + } +} + +bool QSymSQLResultPrivate::fetchNext(bool initialFetch) +{ + int res; + + if (skipRow) { + // already fetched + Q_ASSERT(!initialFetch); + skipRow = false; + return skippedStatus; + } + + skipRow = initialFetch; + res = stmt.Next(); + + switch(res) { + case KSqlAtRow: + return true; + case KSqlAtEnd: + stmt.Reset(); + return false; + case KSqlErrGeneral: + // KSqlErrGeneral is a generic error code and we must call stmt.Reset() + // to get the specific error message. + stmt.Reset(); + q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", + "Unable to fetch row"), QSqlError::ConnectionError, res)); + q->setAt(QSql::AfterLastRow); + return false; + case KSqlErrMisuse: + case KSqlErrBusy: + default: + // something wrong, don't get col info, but still return false + q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", + "Unable to fetch row"), QSqlError::ConnectionError, res)); + stmt.Reset(); + q->setAt(QSql::AfterLastRow); + return false; + } + return false; +} + +////////////////////////////////// QSymSQLResult ///////////////////////////////////////////////// + +QSymSQLResult::QSymSQLResult(const QSymSQLDriver* db) + : QSqlResult(db) +{ + d = new QSymSQLResultPrivate(this); + d->access = db->d->access; +} + + +QSymSQLResult::~QSymSQLResult() +{ + d->cleanup(); + delete d; +} + +bool QSymSQLResult::reset(const QString &query) +{ + if (!prepare(query)) + return false; + + return exec(); +} + +bool QSymSQLResult::prepare(const QString &query) +{ + if (!driver() || !driver()->isOpen() || driver()->isOpenError()) + return false; + + d->cleanup(); + setSelect(false); + + TInt res = d->stmt.Prepare(d->access, qt_QString2TPtrC(query)); + + if (res != KErrNone) { + setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", + "Unable to execute statement"), QSqlError::StatementError, res)); + d->finalize(); + return false; + } + + d->prepareCalled = true; + + return true; +} + +bool QSymSQLResult::exec() +{ + if (d->prepareCalled == false) { + setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", + "Statement is not prepared"), QSqlError::StatementError, KErrGeneral)); + return false; + } + + const QVector<QVariant> values = boundValues(); + + d->skippedStatus = false; + d->skipRow = false; + setAt(QSql::BeforeFirstRow); + setLastError(QSqlError()); + int res = d->stmt.Reset(); + + if (res != KErrNone) { + setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", + "Unable to reset statement"), QSqlError::StatementError, res)); + d->finalize(); + return false; + } + TPtrC tmp; + TInt paramCount = 0; + while (d->stmt.ParamName(paramCount, tmp) == KErrNone) + paramCount++; + + if (paramCount == values.count()) { + for (int i = 0; i < paramCount; ++i) { + res = KErrNone; + const QVariant value = values.at(i); + + if (value.isNull()) { + res = d->stmt.BindNull(i); //replaced i + 1 with i + } else { + switch (value.type()) { + case QVariant::ByteArray: { + const QByteArray *ba = static_cast<const QByteArray*>(value.constData()); + TPtrC8 data(reinterpret_cast<const TUint8 *>(ba->constData()), ba->length()); + res = d->stmt.BindBinary(i, data); //replaced i + 1 with i + break; } + case QVariant::Int: + res = d->stmt.BindInt(i, value.toInt()); //replaced i + 1 with i + break; + case QVariant::Double: + res = d->stmt.BindReal(i, value.toDouble()); //replaced i + 1 with i + + break; + case QVariant::UInt: + case QVariant::LongLong: + res = d->stmt.BindReal(i, value.toLongLong()); //replaced i + 1 with i + break; + + case QVariant::String: { + // lifetime of string == lifetime of its qvariant + const QString *str = static_cast<const QString*>(value.constData()); + res = d->stmt.BindText(i, qt_QString2TPtrC(*str)); // replaced i + 1 with i + break; } + default: { + QString str = value.toString(); + res = d->stmt.BindText(i, qt_QString2TPtrC(str)); //replaced i + 1 with i + break; } + } + } + if (res != KErrNone) { + setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", + "Unable to bind parameters"), QSqlError::StatementError, res)); + d->finalize(); + return false; + } + } + } else { + setLastError(QSqlError(QCoreApplication::translate("QSymSQLResult", + "Parameter count mismatch"), QString(), QSqlError::StatementError)); + return false; + } + + d->skippedStatus = d->fetchNext(true); + + if (lastError().isValid()) { + setSelect(false); + setActive(false); + return false; + } + + if (d->stmt.ColumnCount() > 0) { + //If there is something, it has to be select + setSelect(true); + } else { + //If there isn't it might be just bad query, let's check manually whether we can find SELECT + QString query = this->lastQuery(); + query = query.trimmed(); + query = query.toLower(); + + //Just check whether there is one in the beginning, don't know if this is enough + //Comments should be at the end of line if those are passed + //For some reason, case insensitive indexOf didn't work for me + if (query.indexOf(QLatin1String("select")) == 0) { + setSelect(true); + } else { + setSelect(false); + } + } + + setActive(true); + return true; +} + + +int QSymSQLResult::size() +{ + return -1; +} + +int QSymSQLResult::numRowsAffected() +{ + return -1; +} + +QVariant QSymSQLResult::lastInsertId() const +{ + if (isActive()) { + qint64 id = static_cast<qint64>(d->access.LastInsertedRowId()); + if (id) + return id; + } + + return QVariant(); +} + +QSqlRecord QSymSQLResult::record() const +{ + if (!isActive() || !isSelect()) + return QSqlRecord(); + + QSqlRecord res; + d->initColumns(res); + + return res; +} + +QVariant QSymSQLResult::handle() const +{ + return qVariantFromValue(d->stmt); +} + + +void QSymSQLResult::virtual_hook(int id, void *data) +{ + switch (id) + { + case QSqlResult::DetachFromResultSet: + d->stmt.Reset(); + break; + default: + QSqlResult::virtual_hook(id, data); + } +} + +QVariant QSymSQLResult::data(int idx) +{ + QVariant r; + + switch (d->stmt.ColumnType(idx)) { + case ESqlBinary: + { + TPtrC8 data; + d->stmt.ColumnBinary(idx, data); + return QByteArray(reinterpret_cast<const char *>(data.Ptr()), data.Length()); + break; + } + case ESqlInt: + r = QVariant(d->stmt.ColumnInt(idx)); + break; + case ESqlInt64: + r = QVariant(d->stmt.ColumnInt64(idx)); + break; + case ESqlReal: + switch(numericalPrecisionPolicy()) { + case QSql::LowPrecisionInt32: + r = QVariant(d->stmt.ColumnInt(idx)); + break; + case QSql::LowPrecisionInt64: + r = QVariant(d->stmt.ColumnInt64(idx)); + break; + case QSql::LowPrecisionDouble: + r = QVariant(d->stmt.ColumnReal(idx)); + break; + case QSql::HighPrecision: + default: + TPtrC res; + d->stmt.ColumnText(idx, res); + r = QVariant(qt_TDesC2QString(res)); + break; + }; + break; + case ESqlNull: + r = QVariant(QVariant::String); + break; + default: + TPtrC res; + d->stmt.ColumnText(idx, res); + r = QVariant(qt_TDesC2QString(res)); + break; + } + + return r; +} + +bool QSymSQLResult::isNull(int i) +{ + return d->stmt.IsNull(i); +} + +bool QSymSQLResult::fetch(int i) +{ + //Single return point modified according to review + bool retVal = true; + + if (i < 0 || !isActive()) { + retVal = false; + } else { + if (at() <= -1 || i < at()) { + d->stmt.Reset(); + setAt(-1); + d->skipRow = false; + } + + while (at() < i) { + if (!d->fetchNext(false)) { + retVal = false; + break; + } + + setAt(at() + 1); + } + } + + return retVal; +} + +bool QSymSQLResult::fetchNext() +{ + bool res = d->fetchNext(false); + if (res) { + setAt(at()+1); + } + + return res; +} + +bool QSymSQLResult::fetchPrevious() +{ + return QSqlResult::fetchPrevious(); +} + +bool QSymSQLResult::fetchFirst() +{ + return fetch(0); +} + +bool QSymSQLResult::fetchLast() +{ + if (!isActive()) + return false; + + if (at() <= -1) { + if (!fetchFirst()) + return false; + } + + TInt res; + + do { + res = d->stmt.Next(); + setAt(at()+1); + } while (res == KSqlAtRow); + + if (res != KSqlAtEnd) + return false; + + d->skippedStatus = false; + d->skipRow = false; + + return fetchPrevious(); +} +////////////////////////////////// QSymSQLDriver ////////////////////////////////////////// + +QSymSQLDriver::QSymSQLDriver(QObject * parent) + : QSqlDriver(parent) +{ + d = new QSymSQLDriverPrivate(); +} + +QSymSQLDriver::QSymSQLDriver(RSqlDatabase& connection, QObject *parent) + : QSqlDriver(parent) +{ + d = new QSymSQLDriverPrivate(); + d->access = connection; + setOpen(true); + setOpenError(false); +} + + +QSymSQLDriver::~QSymSQLDriver() +{ + d->access.Close(); + delete d; +} + +bool QSymSQLDriver::hasFeature(DriverFeature f) const +{ + switch (f) { + case BLOB: + case Transactions: + case Unicode: + case PreparedQueries: + case PositionalPlaceholders: + case SimpleLocking: + case FinishQuery: + case LowPrecisionNumbers: + case LastInsertId: + case NamedPlaceholders: + return true; + case QuerySize: + case BatchOperations: + case EventNotifications: + case MultipleResultSets: + return false; + } + return false; +} + +/*! + Converts capability string to TCapability +*/ +TCapability qMatchCapStr(QString& str) +{ + TCapability cap = ECapability_HardLimit; + + for (int i = 0; i < static_cast<int>(ECapability_Limit); i++) { + if (str.compare(QLatin1String(qCapabilityNames[i]), Qt::CaseInsensitive) == 0) { + cap = static_cast<TCapability>(i); + break; + } + } + + //Special case, we allow ECapability_None to be defined + if (cap == ECapability_HardLimit + && str.compare(QLatin1String(qCapabilityNone), Qt::CaseInsensitive) == 0) + cap = ECapability_None; + + return cap; +} + +bool qExtractSecurityPolicyFromString(const QString &string, TSecurityPolicy &policy) +{ + int startPos = string.indexOf(QLatin1Char('=')); + QStringList values; + bool ret = false; + + if (startPos == -1) { + values = string.split(QLatin1Char(','), QString::SkipEmptyParts); + } else { + values = string.mid(startPos + 1).split(QLatin1Char(','), QString::SkipEmptyParts); + } + + if (values.count() > 0) { + const QString findVid(QLatin1String("vid[")); + const QString findSid(QLatin1String("sid[")); + const int MaxCapCount = 7; + const int VidMaxCount = 3; + const int SidMaxCount = 3; + + TCapability capList[MaxCapCount] = { ECapability_None,ECapability_None,ECapability_None, + ECapability_None,ECapability_None,ECapability_None,ECapability_None }; + + bool isVID = false; + bool isSID = false; + + QString idString(QLatin1String("")); + int maxAllowed = MaxCapCount; + + if (values[0].contains(findVid, Qt::CaseInsensitive)) { + idString = values[0].remove(findVid, Qt::CaseInsensitive); + idString = idString.remove(QLatin1Char(']')); + values.removeAt(0); + isVID = true; + maxAllowed = VidMaxCount; + + } else if (values[0].contains(findSid, Qt::CaseInsensitive)) { + idString = values[0].remove(findSid, Qt::CaseInsensitive); + idString = idString.remove(QLatin1Char(']')); + values.removeAt(0); + isSID = true; + maxAllowed = SidMaxCount; + } + + if (values.count() <= maxAllowed) { + bool wasSuccesful = true; + + for (int i = 0; i < values.count(); i++) { + capList[i] = qMatchCapStr(values[i]); + + if (capList[i] == ECapability_HardLimit) { + wasSuccesful = false; + break; + } + } + + if (wasSuccesful) { + if (isVID || isSID){ + bool ok = true; + quint32 id = idString.toUInt(&ok, 16); + + if (ok) { + if (isVID) { + TVendorId vid(id); + policy = TSecurityPolicy(vid, capList[0], capList[1], capList[2]); + } else { + TSecureId sid(id); + policy = TSecurityPolicy(sid, capList[0], capList[1], capList[2]); + } + + ret = true; //Everything is fine + } + } else { + policy = TSecurityPolicy(capList[0], capList[1], capList[2], capList[3], + capList[4], capList[5], capList[6]); + + ret = true; //Everything is fine + } + } + } + } + + return ret; +} + +/*! + Opens the database connection using the given connection options. Returns true on success; otherwise returns false. + Error information can be retrieved using the lastError() function. Symbian SQL dbs have no \a user, \a password, \a host + or \a port just file names. + + \a connOpts Connection options hold definition for security policies and all parameters that does not contain "POLICY_" will be + passed to RSqlDatabase. Policy will be filled according to parsed values. + + Value in database wide parameters starts by definition which can be vendorId or secureId. These come directly from TSecurityPolicy class in Symbian. + + POLICY_DB_DEFAULT + Default security policy which will be used for the database and all database objects. POLICY_DB_DEFAULT must be + defined before any other policy definitions can be used. + POLICY_DB_READ + Read database security policy. An application with read database security policy can read from database. + POLICY_DB_WRITE: + Write database security policy. An application with write database security policy can write to database. + POLICY_DB_SCHEMA: + Schema database security policy. An application with schema database security policy can modify + the database schema, write to database, read from database. + + Format: + POLICY_DB_DEFAULT=cap1,cap2,cap3,cap4,cap5,cap6,cap7 (Up to 7 capabilities) + POLICY_DB_READ=cap1,cap2,cap3,cap4,cap5,cap6,cap7 (Up to 7 capabilities) + POLICY_DB_WRITE=vendorid,cap1,cap2,cap3 (Vendor ID and up to 3 capabilities) + POLICY_DB_SCHEMA=secureid,cap1,cap2,cap3 (Secure ID and up to 3 capabilities) + + Table policies does not support schema policy as database level does. + + Table specific parameters would be as: + POLICY_TABLE_WRITE=tablename,cap1,cap2,cap3,cap4,cap5,cap6,cap7 + POLICY_TABLE_READ=tablename,cap1,cap2,cap3,cap4,cap5,cap6,cap7 + + Vendor Id and Secure id format: + vid[0x12345678] (Hex) + sid[0x12345678] (Hex) + + Example: + \code + QSqlDatabase database = QSqlDatabase::addDatabase("QSYMSQL", "MyConnection"); + database.setConnectOptions("POLICY_DB_DEFAULT=ReadDeviceData"); + database.setDatabaseName("[12345678]myDatabase"); + bool ok = database.open(); + \encode + + \code + QSqlDatabase database = QSqlDatabase::addDatabase("QSYMSQL", "MyConnection"); + database.setConnectOptions("POLICY_DB_DEFAULT=None; POLICY_DB_WRITE=sid[0x12345678], WriteDeviceData"); + database.setDatabaseName("[12345678]myDatabase"); + bool ok = database.open(); + \encode + + FOREIGN KEY: + Enabling foreign key support from underlying SQLite + add: "foreign_keys = ON" to your connection options string. This will be passes to SQLite. + + Foreign key Example: + \code + QSqlDatabase database = QSqlDatabase::addDatabase("QSYMSQL", "MyConnection"); + database.setDatabaseName("[12345678]myDatabase"); + database.setConnectOptions("foreign_keys = ON"); + bool ok = database.open(); + \encode + + More information about Symbian Security Policy can be found from Symbian documentation. + +*/ +bool QSymSQLDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) +{ + if (isOpen()) + close(); + if (db.isEmpty()) + return false; + + //Separating our parameters from Symbian ones and construct new connection options + const QString itemSeparator(QLatin1String(";")); + QRegExp isOurOption(QLatin1String("POLICY_*"), Qt::CaseInsensitive, QRegExp::Wildcard); + + QStringList optionList = conOpts.split(itemSeparator, QString::SkipEmptyParts); + QStringList symbianList; + + for (int i = optionList.count() - 1; i >= 0; i--) { + if (!optionList[i].contains(isOurOption)) { + symbianList.append(optionList[i]); + optionList.removeAt(i); + } else { + //Removing whitespace + QString formatted = optionList[i]; + formatted = formatted.remove(QLatin1Char(' ')); + formatted = formatted.remove(QLatin1Char('\t')); + formatted = formatted.remove(QLatin1Char('\n')); + formatted = formatted.remove(QLatin1Char('\r')); + optionList[i] = formatted; + } + } + + QString symbianOpt; + + for (int i = 0; i < symbianList.count(); i++) { + symbianOpt += symbianList[i]; + symbianOpt += itemSeparator; + } + + TPtrC dbName(qt_QString2TPtrC(db)); + QByteArray conOpts8 = symbianOpt.toUtf8(); + const TPtrC8 config(reinterpret_cast<const TUint8*>(conOpts8.constData()), (conOpts8.length())); + + TInt res = d->access.Open(dbName, &config); + + if (res == KErrNotFound) { + + QRegExp findDefault(QLatin1String("POLICY_DB_DEFAULT=*"), Qt::CaseInsensitive, QRegExp::Wildcard); + QRegExp findRead(QLatin1String("POLICY_DB_READ=*"), Qt::CaseInsensitive, QRegExp::Wildcard); + QRegExp findWrite(QLatin1String("POLICY_DB_WRITE=*"), Qt::CaseInsensitive, QRegExp::Wildcard); + QRegExp findSchema(QLatin1String("POLICY_DB_SCHEMA=*"), Qt::CaseInsensitive, QRegExp::Wildcard); + QRegExp findTableRead(QLatin1String("POLICY_TABLE_READ=*"), Qt::CaseInsensitive, QRegExp::Wildcard); + QRegExp findTableWrite(QLatin1String("POLICY_TABLE_WRITE=*"), Qt::CaseInsensitive, QRegExp::Wildcard); + + int policyIndex = optionList.indexOf(findDefault); + + if (policyIndex != -1) { + QString defaultPolicyString = optionList[policyIndex]; + optionList.removeAt(policyIndex); + + TSecurityPolicy policyItem; + + if (qExtractSecurityPolicyFromString(defaultPolicyString, policyItem)) { + RSqlSecurityPolicy policy; + res = policy.Create(policyItem); + + if (res == KErrNone) { + for (int i = 0; i < optionList.count(); i++) { + QString option = optionList[i]; + + if (option.contains(findRead)) { + if (qExtractSecurityPolicyFromString(option, policyItem)) { + res = policy.SetDbPolicy(RSqlSecurityPolicy::EReadPolicy, policyItem); + } else { + res = KErrArgument; + } + } else if (option.contains(findWrite)) { + if (qExtractSecurityPolicyFromString(option, policyItem)) { + res = policy.SetDbPolicy(RSqlSecurityPolicy::EWritePolicy, policyItem); + } else { + res = KErrArgument; + } + } else if (option.contains(findSchema)) { + if (qExtractSecurityPolicyFromString(option, policyItem)) { + res = policy.SetDbPolicy(RSqlSecurityPolicy::ESchemaPolicy, policyItem); + } else { + res = KErrArgument; + } + } else if (option.contains(findTableWrite)) { + QString tableOption = option.mid(option.indexOf(QLatin1Char('=')) + 1); + int firstComma = tableOption.indexOf(QLatin1Char(',')); + + if (firstComma != -1) { + QString tableName = tableOption.left(firstComma); + tableOption = tableOption.mid(firstComma + 1); + + if (qExtractSecurityPolicyFromString(tableOption, policyItem)) { + TPtrC symTableName(qt_QString2TPtrC(tableName)); + + res = policy.SetPolicy(RSqlSecurityPolicy::ETable, symTableName, + RSqlSecurityPolicy::EWritePolicy, policyItem); + } else { + res = KErrArgument; + } + } else { + res = KErrArgument; + } + } else if (option.contains(findTableRead)) { + QString tableOption = option.mid(option.indexOf(QLatin1Char('=')) + 1); + int firstComma = tableOption.indexOf(QLatin1Char(',')); + + if (firstComma != -1) { + QString tableName = tableOption.left(firstComma); + tableOption = tableOption.mid(firstComma + 1); + + if (qExtractSecurityPolicyFromString(tableOption, policyItem)) { + TPtrC symTableName(qt_QString2TPtrC(tableName)); + + res = policy.SetPolicy(RSqlSecurityPolicy::ETable, symTableName, + RSqlSecurityPolicy::EReadPolicy, policyItem); + } else { + res = KErrArgument; + } + } else { + res = KErrArgument; + } + } else { + res = KErrArgument; + } + + if (res != KErrNone) { + setLastError(gMakeErrorOpen(tr("Invalid option: ") + option, QSqlError::ConnectionError, res)); + break; + } + } + + if (res == KErrNone) { + res = d->access.Create(dbName, policy, &config); + policy.Close(); + + if (res != KErrNone) + setLastError(gMakeErrorOpen(tr("Error opening database"), QSqlError::ConnectionError, res)); + } + } + + } else { + res = KErrArgument; + setLastError(gMakeErrorOpen(tr("Invalid option: ") + defaultPolicyString, QSqlError::ConnectionError, res)); + } + + } else { + //Check whether there is some of our options, fail if so. + policyIndex = optionList.indexOf(isOurOption); + + if (policyIndex == -1) { + res = d->access.Create(dbName, &config); + + if (res != KErrNone) + setLastError(gMakeErrorOpen(tr("Error opening database"), QSqlError::ConnectionError, res)); + } else { + res = KErrArgument; + setLastError(gMakeErrorOpen(tr("POLICY_DB_DEFAULT must be defined before any other POLICY definitions can be used"), QSqlError::ConnectionError, res)); + } + } + } + + if (res == KErrNone) { + setOpen(true); + setOpenError(false); + return true; + } else { + setOpenError(true); + return false; + } +} + +void QSymSQLDriver::close() +{ + if (isOpen()) { + d->access.Close(); + setOpen(false); + setOpenError(false); + } +} + +QSqlResult *QSymSQLDriver::createResult() const +{ + return new QSymSQLResult(this); +} + +bool QSymSQLDriver::beginTransaction() +{ + if (!isOpen() || isOpenError()) + return false; + + TInt err = d->access.Exec(_L("BEGIN")); + if (err < KErrNone) { + setLastError(QSqlError(tr("Unable to begin transaction"), + qt_TDesC2QString(d->access.LastErrorMessage()), QSqlError::TransactionError, err)); + return false; + } + + return true; +} + +bool QSymSQLDriver::commitTransaction() +{ + if (!isOpen() || isOpenError()) + return false; + + TInt err = d->access.Exec(_L("COMMIT")); + if (err < KErrNone) { + setLastError(QSqlError(tr("Unable to commit transaction"), + qt_TDesC2QString(d->access.LastErrorMessage()), QSqlError::TransactionError, err)); + return false; + } + + return true; +} + +bool QSymSQLDriver::rollbackTransaction() +{ + if (!isOpen() || isOpenError()) + return false; + + TInt err = d->access.Exec(_L("ROLLBACK")); + if (err < KErrNone) { + setLastError(QSqlError(tr("Unable to rollback transaction"), + qt_TDesC2QString(d->access.LastErrorMessage()), QSqlError::TransactionError, err)); + return false; + } + + return true; +} + +QStringList QSymSQLDriver::tables(QSql::TableType type) const +{ + QStringList res; + if (!isOpen()) + return res; + + QSqlQuery q(createResult()); + q.setForwardOnly(true); + + QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 " + "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"); + if ((type & QSql::Tables) && (type & QSql::Views)) + sql = sql.arg(QLatin1String("type='table' OR type='view'")); + else if (type & QSql::Tables) + sql = sql.arg(QLatin1String("type='table'")); + else if (type & QSql::Views) + sql = sql.arg(QLatin1String("type='view'")); + else + sql.clear(); + + if (!sql.isEmpty() && q.exec(sql)) { + while (q.next()) + res.append(q.value(0).toString()); + } + + if (type & QSql::SystemTables) + // there are no internal tables beside this one: + res.append(QLatin1String("sqlite_master")); + + return res; +} + +static QSqlIndex qGetTableInfo(QSqlQuery &q, QString &tableName, bool onlyPIndex = false) +{ + QString dbName; + QString table(tableName); + int indexOfSeparator = tableName.indexOf(QLatin1Char('.')); + if (indexOfSeparator > -1) { + dbName = tableName.left(indexOfSeparator +1 ); + table = tableName.mid(indexOfSeparator + 1); + } + q.exec(QLatin1String("PRAGMA ") + dbName + QLatin1String("table_info (") + _q_escapeIdentifier(table) + QLatin1String(")")); + + const int NAME_IDX = 1; + const int TYPE_IDX = 2; + const int NOTNULL_IDX = 3; + const int DFLT_VALUE_IDX = 4; + const int PK_IDX = 5; + + QSqlIndex ind; + while (q.next()) { + bool isPk = q.value(PK_IDX).toInt(); + if (onlyPIndex && !isPk) + continue; + QString typeName = q.value(TYPE_IDX).toString().toLower(); + QSqlField fld(q.value(NAME_IDX).toString(), qGetColumnType(typeName)); + if (isPk && (typeName == QLatin1String("integer"))) + // INTEGER PRIMARY KEY fields are auto-generated in sqlite + // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! + fld.setAutoValue(true); + fld.setRequired(q.value(NOTNULL_IDX).toInt() != 0); + fld.setDefaultValue(q.value(DFLT_VALUE_IDX)); + ind.append(fld); + } + return ind; +} + +QSqlIndex QSymSQLDriver::primaryIndex(const QString &tblname) const +{ + if (!isOpen()) + return QSqlIndex(); + + QString table = tblname; + if (isIdentifierEscaped(table, QSqlDriver::TableName)) + table = stripDelimiters(table, QSqlDriver::TableName); + + QSqlQuery q(createResult()); + q.setForwardOnly(true); + return qGetTableInfo(q, table, true); +} + +QSqlRecord QSymSQLDriver::record(const QString &tbl) const +{ + if (!isOpen()) + return QSqlRecord(); + + QString table = tbl; + if (isIdentifierEscaped(table, QSqlDriver::TableName)) + table = stripDelimiters(table, QSqlDriver::TableName); + + QSqlQuery q(createResult()); + q.setForwardOnly(true); + return qGetTableInfo(q, table); +} + +QVariant QSymSQLDriver::handle() const +{ + return qVariantFromValue(d->access); +} + +QString QSymSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const +{ + Q_UNUSED(type); + return _q_escapeIdentifier(identifier); +} + +QT_END_NAMESPACE diff --git a/src/sql/drivers/symsql/qsql_symsql.h b/src/sql/drivers/symsql/qsql_symsql.h new file mode 100644 index 0000000..7fdc10f --- /dev/null +++ b/src/sql/drivers/symsql/qsql_symsql.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtSql module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSQL_SYMSQL_H +#define QSQL_SYMSQL_H + +#include <QtSql/qsqldriver.h> +#include <QtSql/qsqlresult.h> +#include <QtSql/private/qsqlcachedresult_p.h> + + + +#ifdef QT_PLUGIN +#define Q_EXPORT_SQLDRIVER_SYMSQL +#else +#define Q_EXPORT_SQLDRIVER_SYMSQL Q_SQL_EXPORT +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +class QSymSQLDriverPrivate; +class QSymSQLResultPrivate; +class QSymSQLDriver; +class RSqlDatabase; + +class QSymSQLResult : public QSqlResult +{ + friend class QSymSQLDriver; + friend class QSymSQLResultPrivate; +public: + explicit QSymSQLResult(const QSymSQLDriver* db); + ~QSymSQLResult(); + QVariant handle() const; + +protected: + QVariant data(int field); + bool isNull(int i); + bool fetch(int i); + bool fetchNext(); + bool fetchPrevious(); + bool fetchFirst(); + bool fetchLast(); + + bool reset(const QString &query); + bool prepare(const QString &query); + bool exec(); + int size(); + int numRowsAffected(); + QSqlRecord record() const; + void virtual_hook(int id, void *data); + + QVariant lastInsertId() const; + +private: + QSymSQLResultPrivate* d; +}; + +class Q_EXPORT_SQLDRIVER_SYMSQL QSymSQLDriver : public QSqlDriver +{ + Q_OBJECT + friend class QSymSQLResult; +public: + explicit QSymSQLDriver(QObject *parent = 0); + explicit QSymSQLDriver(RSqlDatabase& connection, QObject *parent = 0); + ~QSymSQLDriver(); + bool hasFeature(DriverFeature f) const; + bool open(const QString & db, + const QString & user, + const QString & password, + const QString & host, + int port, + const QString & connOpts); + void close(); + QSqlResult *createResult() const; + bool beginTransaction(); + bool commitTransaction(); + bool rollbackTransaction(); + QStringList tables(QSql::TableType)const; + + QSqlRecord record(const QString& tablename) const; + QSqlIndex primaryIndex(const QString &table) const; + QVariant handle() const; + QString escapeIdentifier(const QString &identifier, IdentifierType) const; + +private: + QSymSQLDriverPrivate* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSQL_SYMSQL_H diff --git a/src/sql/drivers/symsql/qsql_symsql.pri b/src/sql/drivers/symsql/qsql_symsql.pri new file mode 100644 index 0000000..e18cce9 --- /dev/null +++ b/src/sql/drivers/symsql/qsql_symsql.pri @@ -0,0 +1,5 @@ +HEADERS += $$PWD/qsql_symsql.h +SOURCES += $$PWD/qsql_symsql.cpp + +symbian::LIBS += -lsqldb + |