/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include #include #include "../../../corelib/kernel/qcore_symbian_p.h" #if defined Q_OS_WIN # include #else # include #endif #include #include 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(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 retrieving 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 retrieving 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 retrieving 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 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(value.constData()); TPtrC8 data(reinterpret_cast(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(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(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(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(ECapability_Limit); i++) { if (str.compare(QLatin1String(qCapabilityNames[i]), Qt::CaseInsensitive) == 0) { cap = static_cast(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(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