summaryrefslogtreecommitdiffstats
path: root/src/sql
diff options
context:
space:
mode:
authorTimo Kauppinen <timo.kauppinen@nokia.com>2011-04-13 07:18:15 (GMT)
committerHonglei Zhang <honglei.zhang@nokia.com>2011-09-21 12:23:50 (GMT)
commit3107ec55be7053b451a37221b1fb6ab278eb7c1f (patch)
treec3ae3274bf28ed2defad491c9a3db7346b8bb9f3 /src/sql
parent265430f71ac7c3bf79d7fbf7f02e9a911489c475 (diff)
downloadQt-3107ec55be7053b451a37221b1fb6ab278eb7c1f.zip
Qt-3107ec55be7053b451a37221b1fb6ab278eb7c1f.tar.gz
Qt-3107ec55be7053b451a37221b1fb6ab278eb7c1f.tar.bz2
Added SQL driver plugin implementation for Symbian.
The Symbian SQL driver enables the usage of native Symbian SQL server from Qt code. Symbian SQL driver also supports the usage of Symbian platform security capabilities. Task-number: QT-4860 Reviewed-by: Honglei Zhang
Diffstat (limited to 'src/sql')
-rw-r--r--src/sql/drivers/symsql/qsql_symsql.cpp1236
-rw-r--r--src/sql/drivers/symsql/qsql_symsql.h132
-rw-r--r--src/sql/drivers/symsql/qsql_symsql.pri5
3 files changed, 1373 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..a015400
--- /dev/null
+++ b/src/sql/drivers/symsql/qsql_symsql.cpp
@@ -0,0 +1,1236 @@
+/****************************************************************************
+**
+** 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 plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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)
+{
+ //ToDo Check Implmentation
+ 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));
+
+ //int stp = stmt.ColumnType(i); Useless lines of code, comment modified according to review
+ //fld.setSqlType(stp); Useless lines of code, comment modified according to review
+ 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();
+ //Removed debug qDebug()<<"query Reset(1)"<<endl; //Debug
+ return false;
+ case KSqlErrGeneral:
+ // KSqlErrGeneral is a generic error code and we must call stmt.Reset()
+ // to get the specific error message.
+ stmt.Reset();
+ //Removed debug qDebug()<<"query Reset(2)"<<endl; //Debug
+ 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();
+ //Removed debug qDebug()<<"query Reset(3)"<<endl; //Debug
+ 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));
+ //Removed debug qDebug()<<"query:"<<query<<endl; //Debug
+
+ 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();
+
+ //Removed debug qDebug()<<"query Reset(4)"<<endl; //Debug
+
+ 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: {
+ //ToDo check how to handle SQLITE_STATIC
+ 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
+ //res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(),
+ // ba->size(), SQLITE_STATIC);
+ 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
+ //ToDo check how to handle SQLITE_STATIC
+ const QString *str = static_cast<const QString*>(value.constData());
+ res = d->stmt.BindText(i, qt_QString2TPtrC(*str)); // replaced i + 1 with i
+ //res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(),
+ // (str->size()) * sizeof(QChar), SQLITE_STATIC);
+ break; }
+ default: {
+ //ToDo check how to handle SQLITE_TRANSIENT
+ QString str = value.toString();
+ res = d->stmt.BindText(i, qt_QString2TPtrC(str)); //replaced i + 1 with i
+ // SQLITE_TRANSIENT makes sure that sqlite buffers the data
+ //res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
+ // (str.size()) * sizeof(QChar), SQLITE_TRANSIENT);
+ 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);
+}
+
+
+// ToDo is a virtual_hook needed?
+void QSymSQLResult::virtual_hook(int id, void *data)
+{
+ switch (id)
+ {
+ case QSqlResult::DetachFromResultSet:
+ d->stmt.Reset();
+ //Removed debug qDebug()<<"query Reset(5)"<<endl;
+ 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());
+
+ //ToDo - check if this is correct
+ // values[i + idx] = QByteArray(static_cast<const char *>(
+ // sqlite3_column_blob(stmt, i)),
+ // stmt.ColumnSize(i));
+ 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));
+ //values[i + idx] = QString::fromUtf16(res.Ptr(), res.Length());
+
+ //ToDo - Check if the implementation is correct
+ //values[i + idx] = QString::fromUtf16(static_cast<const ushort *>(
+ // sqlite3_column_text16(stmt, i)),
+ // sqlite3_column_bytes16(stmt, i) / sizeof(ushort));
+ break;
+ };
+ break;
+ case ESqlNull:
+ r = QVariant(QVariant::String);
+ break;
+ default:
+ TPtrC res;
+ d->stmt.ColumnText(idx, res);
+ r = QVariant(qt_TDesC2QString(res));
+ //values[i + idx] = QString::fromUtf16(res.Ptr(), res.Length());
+
+ //ToDo - Check if the implementation is correct
+ //values[i + idx] = QString::fromUtf16(static_cast<const ushort *>(
+ // sqlite3_column_text16(stmt, i)),
+ // sqlite3_column_bytes16(stmt, i) / sizeof(ushort));
+ break;
+ }
+ //Removed debug qDebug()<<"SymSql, Data:"<<r<<"ID:"<<idx<<endl;
+
+ 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 {
+ //Removed for benchmark, comment modified according to review qDebug()<<"fetch i="<<i<<endl;
+ 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;
+ qDebug() << at();
+
+ 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);
+ }
+ /*Removed debug
+ for (int i = 0; i< ind.count(); i++)
+ {
+ qDebug() << "QSqlIndex" << ind.field(i).name() << endl;
+ }
+ */
+ 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..a53bc4f
--- /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 plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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
+