First we call \c sendHighScore() (explained in the section below) if it is possible to send the high scores to an online database.
-Then, we use the \l{Offline Storage API} to maintain a persistent SQL database unique to this application. We create an offline storage database for the high scores using \c openDatabase() and prepare the data and SQL query that we want to use to save it. The offline storage API uses SQL queries for data manipulation and retrieval, and in the \c db.transaction() call we use three SQL queries to initialize the database (if necessary), and then add to and retrieve high scores. To use the returned data, we turn it into a string with one line per row returned, and show a dialog containing that string.
+Then, we use the \l{Offline Storage API} to maintain a persistent SQL database unique to this application. We create an offline storage database for the high scores using \c openDatabaseSync() and prepare the data and SQL query that we want to use to save it. The offline storage API uses SQL queries for data manipulation and retrieval, and in the \c db.transaction() call we use three SQL queries to initialize the database (if necessary), and then add to and retrieve high scores. To use the returned data, we turn it into a string with one line per row returned, and show a dialog containing that string.
This is one way of storing and displaying high scores locally, but certainly not the only way. A more complex alternative would be to create a high score dialog component, and pass it the results for processing and display (instead of reusing the \c Dialog). This would allow a more themeable dialog that could better present the high scores. If your QML is the UI for a C++ application, you could also have passed the score to a C++ function to store it locally in a variety of ways, including a simple format without SQL or in another SQL database.
The \l{declarative/xml/xmlhttprequest}{XMLHttpRequest example} demonstrates how to
use the XMLHttpRequest object to make a request and read the response headers.
-\section1 Offline Storage API
-
-\section2 Database API
-
-The \c openDatabaseSync() and related functions
-provide the ability to access local offline storage in an SQL database.
-
-These databases are user-specific and QML-specific, but accessible to all QML applications.
-They are stored in the \c Databases subdirectory
-of QDeclarativeEngine::offlineStoragePath(), currently as SQLite databases.
-
-Database connections are automatically closed during Javascript garbage collection.
-
-The API can be used from JavaScript functions in your QML:
-
-\snippet declarative/sqllocalstorage/hello.qml 0
-
-The API conforms to the Synchronous API of the HTML5 Web Database API,
-\link http://www.w3.org/TR/2009/WD-webdatabase-20091029/ W3C Working Draft 29 October 2009\endlink.
-
-The \l{declarative/sqllocalstorage}{SQL Local Storage example} demonstrates the basics of
-using the Offline Storage API.
-
-\section3 db = openDatabaseSync(identifier, version, description, estimated_size, callback(db))
-
-Returns the database identified by \i identifier. If the database does not already exist, it
-is created, and the function \i callback is called with the database as a parameter. \i description
-and \i estimated_size are written to the INI file (described below), but are otherwise currently
-unused.
-
-May throw exception with code property SQLException.DATABASE_ERR, or SQLException.VERSION_ERR.
-
-When a database is first created, an INI file is also created specifying its characteristics:
-
-\table
-\header \o \bold {Key} \o \bold {Value}
-\row \o Name \o The name of the database passed to \c openDatabase()
-\row \o Version \o The version of the database passed to \c openDatabase()
-\row \o Description \o The description of the database passed to \c openDatabase()
-\row \o EstimatedSize \o The estimated size (in bytes) of the database passed to \c openDatabase()
-\row \o Driver \o Currently "QSQLITE"
-\endtable
-
-This data can be used by application tools.
-
-\section3 db.changeVersion(from, to, callback(tx))
-
-This method allows you to perform a \i{Scheme Upgrade}.
-
-If the current version of \i db is not \i from, then an exception is thrown.
-
-Otherwise, a database transaction is created and passed to \i callback. In this function,
-you can call \i executeSql on \i tx to upgrade the database.
-
-May throw exception with code property SQLException.DATABASE_ERR or SQLException.UNKNOWN_ERR.
-
-\section3 db.transaction(callback(tx))
-
-This method creates a read/write transaction and passed to \i callback. In this function,
-you can call \i executeSql on \i tx to read and modify the database.
-
-If the callback throws exceptions, the transaction is rolled back.
-
-\section3 db.readTransaction(callback(tx))
-
-This method creates a read-only transaction and passed to \i callback. In this function,
-you can call \i executeSql on \i tx to read the database (with SELECT statements).
-
-\section3 results = tx.executeSql(statement, values)
-
-This method executes a SQL \i statement, binding the list of \i values to SQL positional parameters ("?").
-
-It returns a results object, with the following properties:
-
-\table
-\header \o \bold {Type} \o \bold {Property} \o \bold {Value} \o \bold {Applicability}
-\row \o int \o rows.length \o The number of rows in the result \o SELECT
-\row \o var \o rows.item(i) \o Function that returns row \i i of the result \o SELECT
-\row \o int \o rowsAffected \o The number of rows affected by a modification \o UPDATE, DELETE
-\row \o string \o insertId \o The id of the row inserted \o INSERT
-\endtable
-
-May throw exception with code property SQLException.DATABASE_ERR, SQLException.SYNTAX_ERR, or SQLException.UNKNOWN_ERR.
-
\section1 Logging
\c console.log(), \c console.debug(), \c console.time(), and \c console.timeEnd() can be used to print information
/* This script file handles the game logic */
.pragma library
+.import QtQuick.LocalStorage 2.0 as Sql
var maxColumn = 10;
var maxRow = 15;
function initHighScoreBar()
{
- var db = openDatabaseSync(
+ var db = Sql.openDatabaseSync(
"SameGameScores",
"1.0",
"Local SameGame High Scores",
if (scoresURL != "")
sendHighScore(name);
// Offline storage
- var db = openDatabaseSync(
+ var db = Sql.openDatabaseSync(
"SameGameScores",
"1.0",
"Local SameGame High Scores",
****************************************************************************/
import QtQuick 2.0
+import QtQuick.LocalStorage 2.0 as Sql
// Models a high score table.
//
function __db()
{
- return openDatabaseSync("HighScoreModel", "1.0", "Generic High Score Functionality for QML", 1000000);
+ return Sql.openDatabaseSync("HighScoreModel", "1.0", "Generic High Score Functionality for QML", 1000000);
}
function __ensureTables(tx)
{
****************************************************************************/
//![0]
import QtQuick 2.0
+import QtQuick.LocalStorage 2.0 as LS
Rectangle {
color: "white"
text: "?"
anchors.horizontalCenter: parent.horizontalCenter
function findGreetings() {
- var db = openDatabaseSync("QDeclarativeExampleDB", "1.0", "The Example QML SQL!", 1000000);
+ var db = LS.openDatabaseSync("QDeclarativeExampleDB", "1.0", "The Example QML SQL!", 1000000);
db.transaction(
function(tx) {
/* This script file handles the game logic */
+.import QtQuick.LocalStorage 2.0 as Sql
+
var maxColumn = 10;
var maxRow = 15;
var maxIndex = maxColumn * maxRow;
if (scoresURL != "")
sendHighScore(name);
- var db = openDatabaseSync("SameGameScores", "1.0", "Local SameGame High Scores", 100);
+ var db = Sql.openDatabaseSync("SameGameScores", "1.0", "Local SameGame High Scores", 100);
var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)";
var data = [name, gameCanvas.score, maxColumn + "x" + maxRow, Math.floor(gameDuration / 1000)];
db.transaction(function(tx) {
postman.send(postData);
}
//![1]
-
#include "qdeclarativeincubator.h"
#include <private/qv8profilerservice_p.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qsettings.h>
+
#include <QtCore/qmetaobject.h>
#include <QNetworkAccessManager>
#include <QDebug>
QDeclarativeDebugTrace::initialize();
QDebugMessageService::instance();
}
+
+ QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
+ offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator()) +
+ QDir::separator() + QLatin1String("QML") +
+ QDir::separator() + QLatin1String("OfflineStorage");
}
QDeclarativeWorkerScriptEngine *QDeclarativeEnginePrivate::getWorkerScriptEngine()
void QDeclarativeEngine::setOfflineStoragePath(const QString& dir)
{
Q_D(QDeclarativeEngine);
- qt_qmlsqldatabase_setOfflineStoragePath(d->v8engine(), dir);
+ d->offlineStoragePath = dir;
}
QString QDeclarativeEngine::offlineStoragePath() const
{
Q_D(const QDeclarativeEngine);
- return qt_qmlsqldatabase_getOfflineStoragePath(d->v8engine());
+ return d->offlineStoragePath;
}
static void voidptr_destructor(void *v)
#include <QtCore/qdebug.h>
#include <private/qv8engine_p.h>
+#include <private/qv8sqlerrors_p.h>
QT_BEGIN_NAMESPACE
-enum SqlException {
- UNKNOWN_ERR,
- DATABASE_ERR,
- VERSION_ERR,
- TOO_LARGE_ERR,
- QUOTA_ERR,
- SYNTAX_ERR,
- CONSTRAINT_ERR,
- TIMEOUT_ERR
-};
-
-static const char* sqlerror[] = {
- "UNKNOWN_ERR",
- "DATABASE_ERR",
- "VERSION_ERR",
- "TOO_LARGE_ERR",
- "QUOTA_ERR",
- "SYNTAX_ERR",
- "CONSTRAINT_ERR",
- "TIMEOUT_ERR",
- 0
-};
#define THROW_SQL(error, desc)
QDeclarativeSqlDatabaseData(QV8Engine *engine);
~QDeclarativeSqlDatabaseData();
- QString offlineStoragePath;
v8::Persistent<v8::Function> constructor;
v8::Persistent<v8::Function> queryConstructor;
v8::Persistent<v8::Function> rowsConstructor;
static QString qmlsqldatabase_databasesPath(QV8Engine *engine)
{
- return QDeclarativeSqlDatabaseData::data(engine)->offlineStoragePath +
+ return engine->engine()->offlineStoragePath() +
QDir::separator() + QLatin1String("Databases");
}
QV8Engine *engine = r->engine;
if (!r->inTransaction)
- V8THROW_SQL(DATABASE_ERR,QDeclarativeEngine::tr("executeSql called outside transaction()"));
+ V8THROW_SQL(SQLEXCEPTION_DATABASE_ERR,QDeclarativeEngine::tr("executeSql called outside transaction()"));
QSqlDatabase db = r->database;
QString sql = engine->toString(args[0]);
if (r->readonly && !sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
- V8THROW_SQL(SYNTAX_ERR, QDeclarativeEngine::tr("Read-only Transaction"));
+ V8THROW_SQL(SQLEXCEPTION_SYNTAX_ERR, QDeclarativeEngine::tr("Read-only Transaction"));
}
QSqlQuery query(db);
err = true;
}
if (err)
- V8THROW_SQL(DATABASE_ERR,query.lastError().text());
+ V8THROW_SQL(SQLEXCEPTION_DATABASE_ERR,query.lastError().text());
return result;
}
v8::Handle<v8::Value> callback = args[2];
if (from_version != r->version)
- V8THROW_SQL(VERSION_ERR, QDeclarativeEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(r->version));
+ V8THROW_SQL(SQLEXCEPTION_VERSION_ERR, QDeclarativeEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(r->version));
v8::Local<v8::Object> instance = QDeclarativeSqlDatabaseData::data(engine)->queryConstructor->NewInstance();
QV8SqlDatabaseResource *r2 = new QV8SqlDatabaseResource(engine);
return v8::Handle<v8::Value>();
} else if (!db.commit()) {
db.rollback();
- V8THROW_SQL(UNKNOWN_ERR,QDeclarativeEngine::tr("SQL transaction failed"));
+ V8THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR,QDeclarativeEngine::tr("SQL transaction failed"));
} else {
ok = true;
}
QV8Engine *engine = r->engine;
if (args.Length() == 0 || !args[0]->IsFunction())
- V8THROW_SQL(UNKNOWN_ERR,QDeclarativeEngine::tr("transaction: missing callback"));
+ V8THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR,QDeclarativeEngine::tr("transaction: missing callback"));
QSqlDatabase db = r->database;
v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(args[0]);
database = QSqlDatabase::database(dbid);
version = ini.value(QLatin1String("Version")).toString();
if (version != dbversion && !dbversion.isEmpty() && !version.isEmpty())
- V8THROW_SQL(VERSION_ERR, QDeclarativeEngine::tr("SQL: database version mismatch"));
+ V8THROW_SQL(SQLEXCEPTION_VERSION_ERR, QDeclarativeEngine::tr("SQL: database version mismatch"));
} else {
created = !QFile::exists(basename+QLatin1String(".sqlite"));
database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
} else {
if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) {
// Incompatible
- V8THROW_SQL(VERSION_ERR,QDeclarativeEngine::tr("SQL: database version mismatch"));
+ V8THROW_SQL(SQLEXCEPTION_VERSION_ERR,QDeclarativeEngine::tr("SQL: database version mismatch"));
}
version = ini.value(QLatin1String("Version")).toString();
}
QDeclarativeSqlDatabaseData::QDeclarativeSqlDatabaseData(QV8Engine *engine)
{
- QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
- offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator()) +
- QDir::separator() + QLatin1String("QML") +
- QDir::separator() + QLatin1String("OfflineStorage");
-
{
v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
ft->InstanceTemplate()->SetHasExternalResource(true);
v8::Local<v8::Function> openDatabase = V8FUNCTION(qmlsqldatabase_open_sync, engine);
engine->global()->Set(v8::String::New("openDatabaseSync"), openDatabase);
- v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete);
- v8::Local<v8::Object> sqlExceptionPrototype = v8::Object::New();
- for (int i=0; sqlerror[i]; ++i)
- sqlExceptionPrototype->Set(v8::String::New(sqlerror[i]), v8::Integer::New(i), attributes);
- engine->global()->Set(v8::String::New("SQLException"), sqlExceptionPrototype);
-
return (void *)new QDeclarativeSqlDatabaseData(engine);
}
delete data;
}
-void qt_qmlsqldatabase_setOfflineStoragePath(QV8Engine *engine, const QString &path)
-{
- QDeclarativeSqlDatabaseData::data(engine)->offlineStoragePath = path;
-}
-
-QString qt_qmlsqldatabase_getOfflineStoragePath(const QV8Engine *engine)
-{
- return QDeclarativeSqlDatabaseData::data(const_cast<QV8Engine *>(engine))->offlineStoragePath;
-}
-
/*
HTML5 "spec" says "rs.rows[n]", but WebKit only impelments "rs.rows.item(n)". We do both (and property iterator).
We add a "forwardOnly" property that stops Qt caching results (code promises to only go forward
void *qt_add_qmlsqldatabase(QV8Engine *engine);
void qt_rem_qmlsqldatabase(QV8Engine *engine, void *);
-void qt_qmlsqldatabase_setOfflineStoragePath(QV8Engine *engine, const QString &);
-QString qt_qmlsqldatabase_getOfflineStoragePath(const QV8Engine *);
QT_END_NAMESPACE
#include "qscript_impl_p.h"
#include "qv8domerrors_p.h"
+#include "qv8sqlerrors_p.h"
+
Q_DECLARE_METATYPE(QJSValue)
Q_DECLARE_METATYPE(QList<int>)
qt_add_domexceptions(this);
m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
+
+ qt_add_sqlexceptions(this);
m_sqlDatabaseData = qt_add_qmlsqldatabase(this);
{
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative 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 "qv8sqlerrors_p.h"
+#include "qv8engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void qt_add_sqlexceptions(QV8Engine *engine)
+{
+ // SQL Exception
+ v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete);
+
+ v8::Local<v8::Object> sqlexception = v8::Object::New();
+ sqlexception->Set(v8::String::New("UNKNOWN_ERR"), v8::Integer::New(SQLEXCEPTION_UNKNOWN_ERR), attributes);
+ sqlexception->Set(v8::String::New("DATABASE_ERR"), v8::Integer::New(SQLEXCEPTION_DATABASE_ERR), attributes);
+ sqlexception->Set(v8::String::New("VERSION_ERR"), v8::Integer::New(SQLEXCEPTION_VERSION_ERR), attributes);
+ sqlexception->Set(v8::String::New("TOO_LARGE_ERR"), v8::Integer::New(SQLEXCEPTION_TOO_LARGE_ERR), attributes);
+ sqlexception->Set(v8::String::New("QUOTA_ERR"), v8::Integer::New(SQLEXCEPTION_QUOTA_ERR), attributes);
+ sqlexception->Set(v8::String::New("SYNTAX_ERR"), v8::Integer::New(SQLEXCEPTION_SYNTAX_ERR), attributes);
+ sqlexception->Set(v8::String::New("CONSTRAINT_ERR"), v8::Integer::New(SQLEXCEPTION_CONSTRAINT_ERR), attributes);
+ sqlexception->Set(v8::String::New("TIMEOUT_ERR"), v8::Integer::New(SQLEXCEPTION_TIMEOUT_ERR), attributes);
+ engine->global()->Set(v8::String::New("SQLException"), sqlexception);
+}
+
+QT_END_NAMESPACE
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative 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 QV8SQLERRORS_P_H
+#define QV8SQLERRORS_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.
+//
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+#define SQLEXCEPTION_UNKNOWN_ERR 1
+#define SQLEXCEPTION_DATABASE_ERR 2
+#define SQLEXCEPTION_VERSION_ERR 3
+#define SQLEXCEPTION_TOO_LARGE_ERR 4
+#define SQLEXCEPTION_QUOTA_ERR 5
+#define SQLEXCEPTION_SYNTAX_ERR 6
+#define SQLEXCEPTION_CONSTRAINT_ERR 7
+#define SQLEXCEPTION_TIMEOUT_ERR 8
+
+class QV8Engine;
+void qt_add_sqlexceptions(QV8Engine *engine);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV8SQLERRORS_P_H
$$PWD/../../../3rdparty/javascriptcore/DateMath.h \
$$PWD/qv8engine_impl_p.h \
$$PWD/qv8domerrors_p.h \
+ $$PWD/qv8sqlerrors_p.h \
$$PWD/qdeclarativebuiltinfunctions_p.h
SOURCES += \
$$PWD/qv8bindings.cpp \
$$PWD/../../../3rdparty/javascriptcore/DateMath.cpp \
$$PWD/qv8domerrors.cpp \
- $$PWD/qdeclarativebuiltinfunctions.cpp
+ $$PWD/qv8sqlerrors.cpp \
+ $$PWD/qdeclarativebuiltinfunctions.cpp
\ No newline at end of file
TEMPLATE = subdirs
-SUBDIRS += qtquick2 qtquick1 qt47 folderlistmodel particles gestures etcprovider
+SUBDIRS += qtquick2 qtquick1 qt47 folderlistmodel particles gestures etcprovider localstorage
contains(QT_CONFIG, qmltest): SUBDIRS += testlib
-contains(QT_CONFIG, xmlpatterns) : SUBDIRS += xmllistmodel
+contains(QT_CONFIG, xmlpatterns) : SUBDIRS += xmllistmodel
\ No newline at end of file
--- /dev/null
+TARGET = qmllocalstorageplugin
+TARGETPATH = QtQuick/LocalStorage
+include(../qimportbase.pri)
+
+QT += sql declarative declarative-private v8-private core-private
+
+SOURCES += plugin.cpp
+
+DESTDIR = $$QT.declarative.imports/$$TARGETPATH
+target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH
+
+qmldir.files += $$PWD/qmldir
+qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH
+
+INSTALLS += target qmldir
\ No newline at end of file
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite 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 <QStringList>
+#include <QtDeclarative/qdeclarativeextensionplugin.h>
+#include <QtDeclarative/qdeclarative.h>
+#include <private/qdeclarativeengine_p.h>
+#include <QDebug>
+#include <private/qv8engine_p.h>
+#include <QtSql/qsqldatabase.h>
+#include <QtSql/qsqlquery.h>
+#include <QtSql/qsqlerror.h>
+#include <QtSql/qsqlrecord.h>
+#include <QtSql/qsqlfield.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qsettings.h>
+#include <QtCore/qdir.h>
+#include <private/qv8sqlerrors_p.h>
+
+
+#define V8THROW_SQL(error, desc) \
+{ \
+ v8::Local<v8::Value> v = v8::Exception::Error(engine->toString(desc)); \
+ v->ToObject()->Set(v8::String::New("code"), v8::Integer::New(error)); \
+ v8::ThrowException(v); \
+ return v8::Handle<v8::Value>(); \
+}
+
+#define V8THROW_SQL_VOID(error, desc) \
+{ \
+ v8::Local<v8::Value> v = v8::Exception::Error(engine->toString(desc)); \
+ v->ToObject()->Set(v8::String::New("code"), v8::Integer::New(error)); \
+ v8::ThrowException(v); \
+ return; \
+}
+
+#define V8THROW_REFERENCE(string) { \
+ v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(string))); \
+ return v8::Handle<v8::Value>(); \
+}
+
+#define V8THROW_REFERENCE_VOID(string) { \
+ v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(string))); \
+ return; \
+}
+
+class QDeclarativeSqlDatabaseData : public QV8Engine::Deletable
+{
+public:
+ QDeclarativeSqlDatabaseData(QV8Engine *engine);
+ ~QDeclarativeSqlDatabaseData();
+
+ v8::Persistent<v8::Function> constructor;
+ v8::Persistent<v8::Function> queryConstructor;
+ v8::Persistent<v8::Function> rowsConstructor;
+};
+
+V8_DEFINE_EXTENSION(QDeclarativeSqlDatabaseData, databaseData)
+
+class QV8SqlDatabaseResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(SQLDatabaseType)
+
+public:
+ enum Type { Database, Query, Rows };
+ QV8SqlDatabaseResource(QV8Engine *e)
+ : QV8ObjectResource(e), type(Database), inTransaction(false), readonly(false), forwardOnly(false) {}
+
+ ~QV8SqlDatabaseResource() {
+ }
+
+ Type type;
+ QSqlDatabase database;
+
+ QString version; // type == Database
+
+ bool inTransaction; // type == Query
+ bool readonly; // type == Query
+
+ QSqlQuery query; // type == Rows
+ bool forwardOnly; // type == Rows
+};
+
+static v8::Handle<v8::Value> qmlsqldatabase_version(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Database)
+ V8THROW_REFERENCE("Not a SQLDatabase object");
+
+ return r->engine->toString(r->version);
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_rows_length(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Rows)
+ V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
+
+ int s = r->query.size();
+ if (s < 0) {
+ // Inefficient
+ if (r->query.last()) {
+ s = r->query.at() + 1;
+ } else {
+ s = 0;
+ }
+ }
+ return v8::Integer::New(s);
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_rows_forwardOnly(v8::Local<v8::String> /* property */,
+ const v8::AccessorInfo& info)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Rows)
+ V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
+ return v8::Boolean::New(r->query.isForwardOnly());
+}
+
+static void qmlsqldatabase_rows_setForwardOnly(v8::Local<v8::String> /* property */,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo& info)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Rows)
+ V8THROW_REFERENCE_VOID("Not a SQLDatabase::Rows object");
+
+ r->query.setForwardOnly(value->BooleanValue());
+}
+
+QDeclarativeSqlDatabaseData::~QDeclarativeSqlDatabaseData()
+{
+ qPersistentDispose(constructor);
+ qPersistentDispose(queryConstructor);
+ qPersistentDispose(rowsConstructor);
+}
+
+static QString qmlsqldatabase_databasesPath(QV8Engine *engine)
+{
+ return engine->engine()->offlineStoragePath() +
+ QDir::separator() + QLatin1String("Databases");
+}
+
+static void qmlsqldatabase_initDatabasesPath(QV8Engine *engine)
+{
+ QDir().mkpath(qmlsqldatabase_databasesPath(engine));
+}
+
+static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV8Engine *engine)
+{
+ return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName;
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_rows_index(QV8SqlDatabaseResource *r, uint32_t index)
+{
+ if (r->query.at() == (int)index || r->query.seek(index)) {
+
+ QSqlRecord record = r->query.record();
+ // XXX optimize
+ v8::Local<v8::Object> row = v8::Object::New();
+ for (int ii = 0; ii < record.count(); ++ii) {
+ QVariant v = record.value(ii);
+ if (v.isNull()) {
+ row->Set(r->engine->toString(record.fieldName(ii)), v8::Null());
+ } else {
+ row->Set(r->engine->toString(record.fieldName(ii)),
+ r->engine->fromVariant(v));
+ }
+ }
+ return row;
+ } else {
+ return v8::Undefined();
+ }
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_rows_index(uint32_t index, const v8::AccessorInfo& info)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Rows)
+ V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
+
+ return qmlsqldatabase_rows_index(r, index);
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_rows_item(const v8::Arguments& args)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Rows)
+ V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
+
+ return qmlsqldatabase_rows_index(r, args.Length()?args[0]->Uint32Value():0);
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_executeSql(const v8::Arguments& args)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Query)
+ V8THROW_REFERENCE("Not a SQLDatabase::Query object");
+
+ QV8Engine *engine = r->engine;
+
+ if (!r->inTransaction)
+ V8THROW_SQL(SQLEXCEPTION_DATABASE_ERR,QDeclarativeEngine::tr("executeSql called outside transaction()"));
+
+ QSqlDatabase db = r->database;
+
+ QString sql = engine->toString(args[0]);
+
+ if (r->readonly && !sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
+ V8THROW_SQL(SQLEXCEPTION_SYNTAX_ERR, QDeclarativeEngine::tr("Read-only Transaction"));
+ }
+
+ QSqlQuery query(db);
+ bool err = false;
+
+ v8::Handle<v8::Value> result = v8::Undefined();
+
+ if (query.prepare(sql)) {
+ if (args.Length() > 1) {
+ v8::Local<v8::Value> values = args[1];
+ if (values->IsArray()) {
+ v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(values);
+ uint32_t size = array->Length();
+ for (uint32_t ii = 0; ii < size; ++ii)
+ query.bindValue(ii, engine->toVariant(array->Get(ii), -1));
+ } else if (values->IsObject() && !values->ToObject()->GetExternalResource()) {
+ v8::Local<v8::Object> object = values->ToObject();
+ v8::Local<v8::Array> names = object->GetPropertyNames();
+ uint32_t size = names->Length();
+ for (uint32_t ii = 0; ii < size; ++ii)
+ query.bindValue(engine->toString(names->Get(ii)),
+ engine->toVariant(object->Get(names->Get(ii)), -1));
+ } else {
+ query.bindValue(0, engine->toVariant(values, -1));
+ }
+ }
+ if (query.exec()) {
+ v8::Handle<v8::Object> rows = databaseData(engine)->rowsConstructor->NewInstance();
+ QV8SqlDatabaseResource *r = new QV8SqlDatabaseResource(engine);
+ r->type = QV8SqlDatabaseResource::Rows;
+ r->database = db;
+ r->query = query;
+ rows->SetExternalResource(r);
+
+ v8::Local<v8::Object> resultObject = v8::Object::New();
+ result = resultObject;
+ // XXX optimize
+ resultObject->Set(v8::String::New("rowsAffected"), v8::Integer::New(query.numRowsAffected()));
+ resultObject->Set(v8::String::New("insertId"), engine->toString(query.lastInsertId().toString()));
+ resultObject->Set(v8::String::New("rows"), rows);
+ } else {
+ err = true;
+ }
+ } else {
+ err = true;
+ }
+ if (err)
+ V8THROW_SQL(SQLEXCEPTION_DATABASE_ERR,query.lastError().text());
+
+ return result;
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_changeVersion(const v8::Arguments& args)
+{
+ if (args.Length() < 2)
+ return v8::Undefined();
+
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Database)
+ V8THROW_REFERENCE("Not a SQLDatabase object");
+
+ QV8Engine *engine = r->engine;
+
+ QSqlDatabase db = r->database;
+ QString from_version = engine->toString(args[0]);
+ QString to_version = engine->toString(args[1]);
+ v8::Handle<v8::Value> callback = args[2];
+
+ if (from_version != r->version)
+ V8THROW_SQL(SQLEXCEPTION_VERSION_ERR, QDeclarativeEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(r->version));
+
+ v8::Local<v8::Object> instance = databaseData(engine)->queryConstructor->NewInstance();
+ QV8SqlDatabaseResource *r2 = new QV8SqlDatabaseResource(engine);
+ r2->type = QV8SqlDatabaseResource::Query;
+ r2->database = db;
+ r2->version = r->version;
+ r2->inTransaction = true;
+ instance->SetExternalResource(r2);
+
+ bool ok = true;
+ if (callback->IsFunction()) {
+ ok = false;
+ db.transaction();
+
+ v8::TryCatch tc;
+ v8::Handle<v8::Value> callbackArgs[] = { instance };
+ v8::Handle<v8::Function>::Cast(callback)->Call(engine->global(), 1, callbackArgs);
+
+ if (tc.HasCaught()) {
+ db.rollback();
+ tc.ReThrow();
+ return v8::Handle<v8::Value>();
+ } else if (!db.commit()) {
+ db.rollback();
+ V8THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR,QDeclarativeEngine::tr("SQL transaction failed"));
+ } else {
+ ok = true;
+ }
+ }
+
+ r2->inTransaction = false;
+
+ if (ok) {
+ r2->version = to_version;
+#ifndef QT_NO_SETTINGS
+ QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(),engine) + QLatin1String(".ini"), QSettings::IniFormat);
+ ini.setValue(QLatin1String("Version"), to_version);
+#endif
+ }
+
+ return v8::Undefined();
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_transaction_shared(const v8::Arguments& args, bool readOnly)
+{
+ QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
+ if (!r || r->type != QV8SqlDatabaseResource::Database)
+ V8THROW_REFERENCE("Not a SQLDatabase object");
+
+ QV8Engine *engine = r->engine;
+
+ if (args.Length() == 0 || !args[0]->IsFunction())
+ V8THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR,QDeclarativeEngine::tr("transaction: missing callback"));
+
+ QSqlDatabase db = r->database;
+ v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(args[0]);
+
+ v8::Local<v8::Object> instance = databaseData(engine)->queryConstructor->NewInstance();
+ QV8SqlDatabaseResource *q = new QV8SqlDatabaseResource(engine);
+ q->type = QV8SqlDatabaseResource::Query;
+ q->database = db;
+ q->readonly = readOnly;
+ q->inTransaction = true;
+ instance->SetExternalResource(q);
+
+ db.transaction();
+ v8::TryCatch tc;
+ v8::Handle<v8::Value> callbackArgs[] = { instance };
+ callback->Call(engine->global(), 1, callbackArgs);
+
+ q->inTransaction = false;
+
+ if (tc.HasCaught()) {
+ db.rollback();
+ tc.ReThrow();
+ return v8::Handle<v8::Value>();
+ } else if (!db.commit()) {
+ db.rollback();
+ }
+
+ return v8::Undefined();
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_transaction(const v8::Arguments& args)
+{
+ return qmlsqldatabase_transaction_shared(args, false);
+}
+
+static v8::Handle<v8::Value> qmlsqldatabase_read_transaction(const v8::Arguments& args)
+{
+ return qmlsqldatabase_transaction_shared(args, true);
+}
+
+QDeclarativeSqlDatabaseData::QDeclarativeSqlDatabaseData(QV8Engine *engine)
+{
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->Set(v8::String::New("transaction"),
+ V8FUNCTION(qmlsqldatabase_transaction, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("readTransaction"),
+ V8FUNCTION(qmlsqldatabase_read_transaction, engine));
+ ft->PrototypeTemplate()->SetAccessor(v8::String::New("version"), qmlsqldatabase_version);
+ ft->PrototypeTemplate()->Set(v8::String::New("changeVersion"),
+ V8FUNCTION(qmlsqldatabase_changeVersion, engine));
+ constructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ }
+
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->Set(v8::String::New("executeSql"),
+ V8FUNCTION(qmlsqldatabase_executeSql, engine));
+ queryConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ }
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->Set(v8::String::New("item"), V8FUNCTION(qmlsqldatabase_rows_item, engine));
+ ft->PrototypeTemplate()->SetAccessor(v8::String::New("length"), qmlsqldatabase_rows_length);
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("forwardOnly"), qmlsqldatabase_rows_forwardOnly,
+ qmlsqldatabase_rows_setForwardOnly);
+ ft->InstanceTemplate()->SetIndexedPropertyHandler(qmlsqldatabase_rows_index);
+ rowsConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ }
+}
+
+/*
+HTML5 "spec" says "rs.rows[n]", but WebKit only impelments "rs.rows.item(n)". We do both (and property iterator).
+We add a "forwardOnly" property that stops Qt caching results (code promises to only go forward
+through the data.
+*/
+
+
+/*!
+ \qmlclass LocalStorage QQuickLocalStorage
+ \inqmlmodule QtQuick.LocalStorage 2
+ \since QtQuick 2.0
+
+ \brief The LocalStorage module API provides the ability to access local offline storage in an SQL database.
+
+These databases are user-specific and QML-specific, but accessible to all QML applications.
+They are stored in the \c Databases subdirectory
+of QDeclarativeEngine::offlineStoragePath(), currently as SQLite databases.
+
+Database connections are automatically closed during Javascript garbage collection.
+
+The API can be used from JavaScript functions in your QML:
+
+\snippet declarative/sqllocalstorage/hello.qml 0
+
+The API conforms to the Synchronous API of the HTML5 Web Database API,
+\link http://www.w3.org/TR/2009/WD-webdatabase-20091029/ W3C Working Draft 29 October 2009\endlink.
+
+The \l{declarative/sqllocalstorage}{SQL Local Storage example} demonstrates the basics of
+using the Offline Storage API.
+
+\section3 Open or create a databaseData
+\code
+import QtQuick.LocalStorage 2.0 as LS
+
+db = LS.openDatabaseSync(identifier, version, description, estimated_size, callback(db))
+\endcode
+The above code returns the database identified by \i identifier. If the database does not already exist, it
+is created, and the function \i callback is called with the database as a parameter. \i description
+and \i estimated_size are written to the INI file (described below), but are otherwise currently
+unused.
+
+May throw exception with code property SQLException.DATABASE_ERR, or SQLException.VERSION_ERR.
+
+When a database is first created, an INI file is also created specifying its characteristics:
+
+\table
+\header \o \bold {Key} \o \bold {Value}
+\row \o Name \o The name of the database passed to \c openDatabase()
+\row \o Version \o The version of the database passed to \c openDatabase()
+\row \o Description \o The description of the database passed to \c openDatabase()
+\row \o EstimatedSize \o The estimated size (in bytes) of the database passed to \c openDatabase()
+\row \o Driver \o Currently "QSQLITE"
+\endtable
+
+This data can be used by application tools.
+
+\section3 db.changeVersion(from, to, callback(tx))
+
+This method allows you to perform a \i{Scheme Upgrade}.
+
+If the current version of \i db is not \i from, then an exception is thrown.
+
+Otherwise, a database transaction is created and passed to \i callback. In this function,
+you can call \i executeSql on \i tx to upgrade the database.
+
+May throw exception with code property SQLException.DATABASE_ERR or SQLException.UNKNOWN_ERR.
+
+\section3 db.transaction(callback(tx))
+
+This method creates a read/write transaction and passed to \i callback. In this function,
+you can call \i executeSql on \i tx to read and modify the database.
+
+If the callback throws exceptions, the transaction is rolled back.
+
+\section3 db.readTransaction(callback(tx))
+
+This method creates a read-only transaction and passed to \i callback. In this function,
+you can call \i executeSql on \i tx to read the database (with SELECT statements).
+
+\section3 results = tx.executeSql(statement, values)
+
+This method executes a SQL \i statement, binding the list of \i values to SQL positional parameters ("?").
+
+It returns a results object, with the following properties:
+
+\table
+\header \o \bold {Type} \o \bold {Property} \o \bold {Value} \o \bold {Applicability}
+\row \o int \o rows.length \o The number of rows in the result \o SELECT
+\row \o var \o rows.item(i) \o Function that returns row \i i of the result \o SELECT
+\row \o int \o rowsAffected \o The number of rows affected by a modification \o UPDATE, DELETE
+\row \o string \o insertId \o The id of the row inserted \o INSERT
+\endtable
+
+May throw exception with code property SQLException.DATABASE_ERR, SQLException.SYNTAX_ERR, or SQLException.UNKNOWN_ERR.
+ */
+class QQuickLocalStorage : public QObject
+{
+ Q_OBJECT
+public:
+ QQuickLocalStorage(QObject *parent=0) : QObject(parent)
+ {
+ }
+ ~QQuickLocalStorage() {
+ }
+
+ Q_INVOKABLE void openDatabaseSync(QDeclarativeV8Function* args);
+};
+
+/*!
+ * \qmlmethod object LocalStorage::openDatabaseSync(string name, string version, string description, int estimated_size, jsobject callback(db))
+ * \brief Open or create a local storage sql database by given parameters.
+ *
+ * \c name is the database name
+ * \c version is the database version
+ * \c description is the database display name
+ * \c estimated_size is the database's estimated size, in bytes
+ * \c callback is an optional parameter, which is invoked if the database has not yet been created.
+ * \return the database object
+ */
+void QQuickLocalStorage::openDatabaseSync(QDeclarativeV8Function *args)
+{
+#ifndef QT_NO_SETTINGS
+ QV8Engine *engine = args->engine();
+ qmlsqldatabase_initDatabasesPath(engine);
+
+ QSqlDatabase database;
+
+ QString dbname = engine->toString((*args)[0]);
+ QString dbversion = engine->toString((*args)[1]);
+ QString dbdescription = engine->toString((*args)[2]);
+ int dbestimatedsize = (*args)[3]->Int32Value();
+ v8::Handle<v8::Value> dbcreationCallback = (*args)[4];
+
+ QCryptographicHash md5(QCryptographicHash::Md5);
+ md5.addData(dbname.toUtf8());
+ QString dbid(QLatin1String(md5.result().toHex()));
+
+ QString basename = qmlsqldatabase_databaseFile(dbid, engine);
+ bool created = false;
+ QString version = dbversion;
+
+ {
+ QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);
+
+ if (QSqlDatabase::connectionNames().contains(dbid)) {
+ database = QSqlDatabase::database(dbid);
+ version = ini.value(QLatin1String("Version")).toString();
+ if (version != dbversion && !dbversion.isEmpty() && !version.isEmpty())
+ V8THROW_SQL_VOID(SQLEXCEPTION_VERSION_ERR, QDeclarativeEngine::tr("SQL: database version mismatch"));
+ } else {
+ created = !QFile::exists(basename+QLatin1String(".sqlite"));
+ database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
+ if (created) {
+ ini.setValue(QLatin1String("Name"), dbname);
+ if (dbcreationCallback->IsFunction())
+ version = QString();
+ ini.setValue(QLatin1String("Version"), version);
+ ini.setValue(QLatin1String("Description"), dbdescription);
+ ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize);
+ ini.setValue(QLatin1String("Driver"), QLatin1String("QSQLITE"));
+ } else {
+ if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) {
+ // Incompatible
+ V8THROW_SQL_VOID(SQLEXCEPTION_VERSION_ERR,QDeclarativeEngine::tr("SQL: database version mismatch"));
+ }
+ version = ini.value(QLatin1String("Version")).toString();
+ }
+ database.setDatabaseName(basename+QLatin1String(".sqlite"));
+ }
+ if (!database.isOpen())
+ database.open();
+ }
+
+ v8::Local<v8::Object> instance = databaseData(engine)->constructor->NewInstance();
+
+ QV8SqlDatabaseResource *r = new QV8SqlDatabaseResource(engine);
+ r->database = database;
+ r->version = version;
+ instance->SetExternalResource(r);
+
+ if (created && dbcreationCallback->IsFunction()) {
+ v8::TryCatch tc;
+ v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(dbcreationCallback);
+ v8::Handle<v8::Value> args[] = { instance };
+ callback->Call(engine->global(), 1, args);
+ if (tc.HasCaught()) {
+ tc.ReThrow();
+ return;
+ }
+ }
+
+ args->returnValue(instance);
+#endif // QT_NO_SETTINGS
+}
+
+static QObject *module_api_factory(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+ QQuickLocalStorage *api = new QQuickLocalStorage();
+
+ return api;
+}
+
+class QDeclarativeLocalStoragePlugin : public QDeclarativeExtensionPlugin
+{
+ Q_OBJECT
+public:
+ QDeclarativeLocalStoragePlugin()
+ {
+ }
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "QtQuick.LocalStorage");
+ qmlRegisterModuleApi(uri, 2, 0, module_api_factory);
+ }
+};
+
+#include "plugin.moc"
+
+Q_EXPORT_PLUGIN2(plugin, QDeclarativeLocalStoragePlugin);
--- /dev/null
+plugin qmllocalstorageplugin
\ No newline at end of file
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
- var db = openDatabaseSync("QmlTestDB-changeversion", "", "Test database from Qt autotests", 1000000,
+ var db = Sql.openDatabaseSync("QmlTestDB-changeversion", "", "Test database from Qt autotests", 1000000,
function(db) {
db.changeVersion("","1.0")
db.transaction(function(tx){
});
- db = openDatabaseSync("QmlTestDB-changeversion", "", "Test database from Qt autotests", 1000000);
+ db = Sql.openDatabaseSync("QmlTestDB-changeversion", "", "Test database from Qt autotests", 1000000);
if (db.version == "1.0")
db.changeVersion("1.0","2.0",function(tx)
else
return "db.version should be 1.0, but is " + db.version;
- var db = openDatabaseSync("QmlTestDB-changeversion", "2.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-changeversion", "2.0", "Test database from Qt autotests", 1000000);
db.transaction(function(tx){
var rs = tx.executeSql('SELECT * FROM Utterance');
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
- var db = openDatabaseSync("QmlTestDB-creation-a", "1.0", "Test database from Qt autotests", 1000000,
+ var db = Sql.openDatabaseSync("QmlTestDB-creation-a", "1.0", "Test database from Qt autotests", 1000000,
function(db) {
db.transaction(function(tx){
tx.executeSql('CREATE TABLE Greeting(salutation TEXT, salutee TEXT)');
})
});
- var db = openDatabaseSync("QmlTestDB-creation-a", "1.0", "Test database from Qt autotests", 1000000,
+ var db = Sql.openDatabaseSync("QmlTestDB-creation-a", "1.0", "Test database from Qt autotests", 1000000,
function(db) {
r = "FAILED: should have already been created";
});
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
- var db = openDatabaseSync("QmlTestDB-creation", "1.0", "Test database from Qt autotests", 1000000);
-
+ var db = Sql.openDatabaseSync("QmlTestDB-creation", "1.0", "Test database from Qt autotests", 1000000);
db.transaction(
function(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)');
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-error-a", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-error-a", "1.0", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
try {
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-error-b", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-error-b", "1.0", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
db.transaction(
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
try {
- var db = openDatabaseSync("QmlTestDB-creation", "2.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-creation", "2.0", "Test database from Qt autotests", 1000000);
} catch (err) {
if (err.code != SQLException.VERSION_ERR)
r = "WRONG ERROR CODE="+err.code;
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-data/error-notransaction", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-data/error-notransaction", "1.0", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
try {
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-data/error-notransaction", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-data/error-notransaction", "1.0", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
var v;
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-iteration-forwardonly", "", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-iteration-forwardonly", "", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
db.transaction(
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-iteration", "", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-iteration", "", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
db.transaction(
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
- var db = openDatabaseSync("QmlTestDB-readonly-error", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-readonly-error", "1.0", "Test database from Qt autotests", 1000000);
db.transaction(
function(tx) {
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
- var db = openDatabaseSync("QmlTestDB-readonly", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-readonly", "1.0", "Test database from Qt autotests", 1000000);
db.transaction(
function(tx) {
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
- var db = openDatabaseSync("QmlTestDB-reopen", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-reopen", "1.0", "Test database from Qt autotests", 1000000);
db.transaction(
function(tx) {
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
var r="transaction_not_finished";
- var db = openDatabaseSync("QmlTestDB-reopen", "1.0", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-reopen", "1.0", "Test database from Qt autotests", 1000000);
db.transaction(
function(tx) {
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-bindnames", "", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-bindnames", "", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
db.transaction(
+.import QtQuick.LocalStorage 2.0 as Sql
+
function test() {
- var db = openDatabaseSync("QmlTestDB-selection", "", "Test database from Qt autotests", 1000000);
+ var db = Sql.openDatabaseSync("QmlTestDB-selection", "", "Test database from Qt autotests", 1000000);
var r="transaction_not_finished";
db.transaction(