summaryrefslogtreecommitdiffstats
path: root/src/declarative/qml/qmlsqldatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/qml/qmlsqldatabase.cpp')
-rw-r--r--src/declarative/qml/qmlsqldatabase.cpp304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp
new file mode 100644
index 0000000..144a170
--- /dev/null
+++ b/src/declarative/qml/qmlsqldatabase.cpp
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qobject.h>
+#include <QtDeclarative/qmlengine.h>
+#include <private/qmlengine_p.h>
+#include <QtScript/qscriptvalue.h>
+#include <QtScript/qscriptvalueiterator.h>
+#include <QtScript/qscriptcontext.h>
+#include <QtScript/qscriptengine.h>
+#include <QtSql/qsqldatabase.h>
+#include <QtSql/qsqlquery.h>
+#include <QtSql/qsqlerror.h>
+#include <QtSql/qsqlrecord.h>
+#include <private/qmlrefcount_p.h>
+#include <private/qmlengine_p.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qcryptographichash.h>
+#include "qmlsqldatabase_p.h"
+#include <QtCore/qsettings.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qdebug.h>
+
+#ifdef Q_OS_WIN // for %APPDATA%
+#include "qt_windows.h"
+#include "qlibrary.h"
+#define CSIDL_APPDATA 0x001a // <username>\Application Data
+#endif
+
+Q_DECLARE_METATYPE(QSqlDatabase)
+Q_DECLARE_METATYPE(QSqlQuery)
+
+class QmlSqlQueryScriptClass: public QScriptClass {
+public:
+ QmlSqlQueryScriptClass(QScriptEngine *engine) : QScriptClass(engine)
+ {
+ str_length = engine->toStringHandle(QLatin1String("length"));
+ str_forwardOnly = engine->toStringHandle(QLatin1String("forwardOnly")); // not in HTML5 (optimization)
+ }
+
+ QueryFlags queryProperty(const QScriptValue &object,
+ const QScriptString &name,
+ QueryFlags flags, uint *id)
+ {
+ if (flags & HandlesReadAccess) {
+ if (name == str_length) {
+ return HandlesReadAccess;
+ } else if (name == str_forwardOnly) {
+ return flags;
+ } else {
+ bool ok;
+ qint32 pos = name.toString().toInt(&ok);
+ if (pos < 0 || !ok)
+ return 0;
+ QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object.data());
+ *id = pos;
+ if (*id < (uint)query.size())
+ return HandlesReadAccess;
+ }
+ }
+ if (flags & HandlesWriteAccess)
+ if (name == str_forwardOnly)
+ return flags;
+ return 0;
+ }
+
+ QScriptValue property(const QScriptValue &object,
+ const QScriptString &name, uint id)
+ {
+ QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object.data());
+ if (name == str_length) {
+ int s = query.size();
+ if (s<0) {
+ // Inefficient.
+ query.last();
+ return query.at()+1;
+ } else {
+ return s;
+ }
+ } else if (name == str_forwardOnly) {
+ return query.isForwardOnly();
+ } else {
+ if (query.at() == id || query.seek(id)) { // Qt 4.6 doesn't optimize at()==id
+ QSqlRecord r = query.record();
+ QScriptValue row = engine()->newArray(r.count());
+ for (int j=0; j<r.count(); ++j) {
+ // XXX only strings
+ row.setProperty(j, QScriptValue(engine(),r.value(j).toString()));
+ }
+ return row;
+ }
+ }
+ return engine()->undefinedValue();
+ }
+
+ void setProperty(QScriptValue &object,
+ const QScriptString &name, uint, const QScriptValue & value)
+ {
+ if (name == str_forwardOnly) {
+ QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object.data());
+ query.setForwardOnly(value.toBool());
+ }
+ }
+
+private:
+ QScriptString str_length;
+ QScriptString str_forwardOnly;
+};
+
+static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEngine *engine)
+{
+ QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
+ QString sql = context->argument(0).toString();
+ QScriptValue values = context->argument(1);
+ QScriptValue cb = context->argument(2);
+ QScriptValue cberr = context->argument(3);
+ QSqlQuery query(db);
+ bool err = false;
+ if (query.prepare(sql)) {
+ if (values.isArray()) {
+ for (QScriptValueIterator it(values); it.hasNext();) {
+ it.next();
+ query.addBindValue(it.value().toVariant());
+ }
+ } else {
+ query.bindValue(0,values.toVariant());
+ }
+ if (query.exec()) {
+ QScriptValue rs = engine->newObject();
+ if (!QmlEnginePrivate::get(engine)->sqlQueryClass)
+ QmlEnginePrivate::get(engine)->sqlQueryClass= new QmlSqlQueryScriptClass(engine);
+ QScriptValue rows = engine->newObject(QmlEnginePrivate::get(engine)->sqlQueryClass);
+ rows.setData(engine->newVariant(qVariantFromValue(query)));
+ rs.setProperty(QLatin1String("rows"),rows);
+ rs.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected());
+ rs.setProperty(QLatin1String("insertId"),query.lastInsertId().toString()); // XXX only string
+ cb.call(QScriptValue(), QScriptValueList() << context->thisObject() << rs);
+ } else {
+ err = true;
+ }
+ } else {
+ err = true;
+ }
+ if (err) {
+ QScriptValue error = engine->newObject();
+ error.setProperty(QLatin1String("message"), query.lastError().text());
+ cberr.call(QScriptValue(), QScriptValueList() << context->thisObject() << error);
+ }
+ return engine->undefinedValue();
+}
+
+static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine)
+{
+ QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
+ if (context->argumentCount() < 1)
+ return engine->undefinedValue();
+ QScriptValue cb = context->argument(0);
+ if (!cb.isFunction())
+ return engine->undefinedValue();
+
+ // Call synchronously... - XXX could do asynch with threads
+ QScriptValue instance = engine->newObject();
+ instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,4));
+ QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db));
+
+ db.transaction();
+ cb.call(QScriptValue(), QScriptValueList() << tx);
+ if (engine->hasUncaughtException()) {
+ db.rollback();
+ QScriptValue cb = context->argument(1);
+ if (cb.isFunction())
+ cb.call();
+ } else {
+ db.commit();
+ QScriptValue cb = context->argument(2);
+ if (cb.isFunction())
+ cb.call();
+ }
+ return engine->undefinedValue();
+}
+
+// XXX Something like this should be exported by Qt.
+static QString userLocalDataPath(const QString& app)
+{
+ QString result;
+
+#ifdef Q_OS_WIN
+#ifndef Q_OS_WINCE
+ QLibrary library(QLatin1String("shell32"));
+#else
+ QLibrary library(QLatin1String("coredll"));
+#endif // Q_OS_WINCE
+ typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPWSTR, int, BOOL);
+ GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW");
+ if (SHGetSpecialFolderPath) {
+ wchar_t path[MAX_PATH];
+ SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, FALSE);
+ result = QString::fromWCharArray(path);
+ }
+#endif // Q_OS_WIN
+
+#ifdef Q_OS_MAC
+ result = QLatin1String(qgetenv("HOME"));
+ result += "/Library/Application Support";
+#else
+ if (result.isEmpty()) {
+ // Fallback: UNIX style
+ result = QLatin1String(qgetenv("XDG_DATA_HOME"));
+ if (result.isEmpty()) {
+ result = QLatin1String(qgetenv("HOME"));
+ result += QLatin1String("/.local/share");
+ }
+ }
+#endif
+
+ result += QLatin1Char('/');
+ result += app;
+ return result;
+}
+
+
+static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine *engine)
+{
+ QSqlDatabase database;
+
+ QString dbname = context->argument(0).toString();
+ QString dbversion = context->argument(1).toString();
+ QString dbdescription = context->argument(2).toString();
+ int dbestimatedsize = context->argument(3).toNumber();
+
+ QCryptographicHash md5(QCryptographicHash::Md5);
+ md5.addData(dbname.toUtf8());
+ md5.addData(dbversion.toUtf8());
+ QString dbid(QLatin1String(md5.result().toHex()));
+
+ // Uses SQLLITE (like HTML5), but any could be used.
+
+ if (QSqlDatabase::connectionNames().contains(dbid)) {
+ database = QSqlDatabase::database(dbid);
+ } else {
+ database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
+ }
+ if (!database.isOpen()) {
+ QString basename = userLocalDataPath(QLatin1String("Nokia/Qt/QML/Databases/"));
+ QDir().mkpath(basename);
+ basename += dbid;
+ database.setDatabaseName(basename+QLatin1String(".sqllite"));
+ QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);
+ ini.setValue(QLatin1String("Name"), dbname);
+ ini.setValue(QLatin1String("Version"), dbversion);
+ ini.setValue(QLatin1String("Description"), dbdescription);
+ ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize);
+ database.open();
+ }
+
+ QScriptValue instance = engine->newObject();
+ instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,3));
+ return engine->newVariant(instance,qVariantFromValue(database));
+}
+
+void qt_add_qmlsqldatabase(QScriptEngine *engine)
+{
+ QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open, 4);
+ engine->globalObject().setProperty(QLatin1String("openDatabase"), openDatabase);
+}
+