From 64dba2e0de580853bd82ba2ce60229215c633396 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Wed, 16 Sep 2009 15:52:27 +1000 Subject: Basic first-working SQL database interface. A simple Qt-based implementation of HTML5 SQL local storage databases. --- examples/declarative/sql/hello.qml | 30 ++++ src/declarative/qml/qml.pri | 2 + src/declarative/qml/qmlengine.cpp | 2 + src/declarative/qml/qmlsqldatabase.cpp | 259 +++++++++++++++++++++++++++++++++ src/declarative/qml/qmlsqldatabase_p.h | 60 ++++++++ 5 files changed, 353 insertions(+) create mode 100644 examples/declarative/sql/hello.qml create mode 100644 src/declarative/qml/qmlsqldatabase.cpp create mode 100644 src/declarative/qml/qmlsqldatabase_p.h diff --git a/examples/declarative/sql/hello.qml b/examples/declarative/sql/hello.qml new file mode 100644 index 0000000..fb6d21c --- /dev/null +++ b/examples/declarative/sql/hello.qml @@ -0,0 +1,30 @@ +import Qt 4.6 + +Text { + Script { + function test() + { + var db = openDatabase("QmlExampleDB", "", "The Example QML SQL!", 1000000); + print(db) + var r = "" + + db.transaction(function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)', []); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + tx.executeSql('SELECT * FROM Greeting', [], + function(tx, rs) { + for(var i = 0; i < rs.rows.length; i++) { + r += rs.rows[i][0] + ", " + rs.rows[i][1] + "\n" + } + }, + function(tx, error) { + print("ERROR:", error.message) + } + ); + }) + + return r + } + } + text: test() +} diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 29d97ba..9ca69df 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -32,6 +32,7 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlvaluetype.cpp \ qml/qmlbindingoptimizations.cpp \ qml/qmlxmlhttprequest.cpp \ + qml/qmlsqldatabase.cpp \ qml/qmetaobjectbuilder.cpp HEADERS += qml/qmlparser_p.h \ @@ -81,6 +82,7 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlvaluetype_p.h \ qml/qmlbindingoptimizations_p.h \ qml/qmlxmlhttprequest_p.h \ + qml/qmlsqldatabase_p.h \ qml/qmetaobjectbuilder_p.h # for qtscript debugger diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index e3d4840..efe1a89 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -82,6 +82,7 @@ #include #include #include +#include Q_DECLARE_METATYPE(QmlMetaProperty) Q_DECLARE_METATYPE(QList); @@ -121,6 +122,7 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) scriptEngine.globalObject().setProperty(QLatin1String("Qt"), qtObject); qt_add_qmlxmlhttprequest(&scriptEngine); + qt_add_qmlsqldatabase(&scriptEngine); //types qtObject.setProperty(QLatin1String("rgba"), scriptEngine.newFunction(QmlEnginePrivate::rgba, 4)); diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp new file mode 100644 index 0000000..6063ac4 --- /dev/null +++ b/src/declarative/qml/qmlsqldatabase.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qmlsqldatabase_p.h" +#include +#include +#include + +#ifdef Q_OS_WIN // for %APPDATA% +#include "qt_windows.h" +#include "qlibrary.h" +#endif + + +class QmlSqlDatabaseTransaction : public QObject +{ + Q_OBJECT +public: + QmlSqlDatabaseTransaction(QSqlDatabase db, QmlEngine *engine) : database(db) {} + virtual ~QmlSqlDatabaseTransaction(){} + static QScriptValue executeSql(QScriptContext *context, QScriptEngine *engine) + { + QScriptValue tx = context->thisObject(); + QmlSqlDatabaseTransaction *trans = qobject_cast(tx.toQObject()); + QString sql = context->argument(0).toString(); + QScriptValue values = context->argument(1); + QScriptValue cb = context->argument(2); + QScriptValue cberr = context->argument(3); + QSqlQuery query(trans->database); + 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 rows = engine->newArray(); + int i=0; + for (; query.next(); ++i) { + QSqlRecord r = query.record(); + QScriptValue row = engine->newArray(r.count()); + for (int j=0; jnewObject(); + rs.setProperty(QLatin1String("rows"), rows); + cb.call(QScriptValue(), QScriptValueList() << tx << rs); + } else { + err = true; + } + } else { + err = true; + } + if (err) { + QScriptValue error = engine->newObject(); + error.setProperty(QLatin1String("message"), query.lastError().text()); + cberr.call(QScriptValue(), QScriptValueList() << tx << error); + } + return engine->undefinedValue(); + } + +private: + QSqlDatabase database; +}; + +class QmlSqlDatabase : public QObject +{ + Q_OBJECT +public: + QmlSqlDatabase(QmlEngine *engine, QScriptContext *context); + virtual ~QmlSqlDatabase(); + + QScriptValue callback() const; + void setCallback(const QScriptValue &); + + static QScriptValue transaction(QScriptContext *context, QScriptEngine *engine) + { + QmlSqlDatabase *db = qobject_cast(context->thisObject().toQObject()); + if (!db) + return context->throwError(QScriptContext::ReferenceError, QLatin1String("Not an SqlDatabase object")); + if (context->argumentCount() != 1) + return engine->undefinedValue(); + QScriptValue cb = context->argument(0); + if (!cb.isFunction()) + return engine->undefinedValue(); + + // XXX Call synchronously... + QScriptValue tx = engine->newQObject(new QmlSqlDatabaseTransaction(db->database, QmlEnginePrivate::getEngine(engine)), QScriptEngine::ScriptOwnership); + + tx.setProperty(QLatin1String("executeSql"), engine->newFunction(QmlSqlDatabaseTransaction::executeSql,4)); + + db->database.transaction(); + cb.call(QScriptValue(), QScriptValueList() << tx); + if (engine->hasUncaughtException()) { + db->database.rollback(); + QScriptValue cb = context->argument(1); + if (cb.isFunction()) + cb.call(); + } else { + db->database.commit(); + QScriptValue cb = context->argument(2); + if (cb.isFunction()) + cb.call(); + } + return engine->undefinedValue(); + } + +private: + QSqlDatabase database; +}; + +// XXX Something like this belongs in 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; +} + +QmlSqlDatabase::QmlSqlDatabase(QmlEngine *engine, QScriptContext *context) +{ + 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.utf8()); + md5.addData(dbversion.utf8()); + QString dbid(QLatin1String(md5.result().toHex())); + + QSqlDatabase db; + 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("Name", dbname); + ini.setValue("Version", dbversion); + ini.setValue("Description", dbdescription); + ini.setValue("EstimatedSize", dbestimatedsize); + database.open(); + } +} + +QmlSqlDatabase::~QmlSqlDatabase() +{ +} + +static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine *engine) +{ + QScriptValue proto = engine->newQObject(new QmlSqlDatabase(QmlEnginePrivate::getEngine(engine),context), QScriptEngine::ScriptOwnership); + proto.setProperty(QLatin1String("transaction"), engine->newFunction(QmlSqlDatabase::transaction,1)); + return proto; +} + +void qt_add_qmlsqldatabase(QScriptEngine *engine) +{ + QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open, 4); + engine->globalObject().setProperty(QLatin1String("openDatabase"), openDatabase); +} + +#include "qmlsqldatabase.moc" diff --git a/src/declarative/qml/qmlsqldatabase_p.h b/src/declarative/qml/qmlsqldatabase_p.h new file mode 100644 index 0000000..f76a2fd --- /dev/null +++ b/src/declarative/qml/qmlsqldatabase_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLSQLDATABASE_P_H +#define QMLSQLDATABASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +class QScriptEngine; +void qt_add_qmlsqldatabase(QScriptEngine *engine); + +#endif // QMLSQLDATABASE_P_H + -- cgit v0.12