Move sqldatabase into a module API plugin
authorCharles Yin <charles.yin@nokia.com>
Tue, 24 Jan 2012 06:07:24 +0000 (16:07 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 25 Jan 2012 05:32:34 +0000 (06:32 +0100)
Change-Id: Icd0bbfe16804abf1bbadbabddf3a30b5b18df30c
Reviewed-by: Martin Jones <martin.jones@nokia.com>
33 files changed:
doc/src/declarative/advtutorial.qdoc
doc/src/declarative/globalobject.qdoc
examples/declarative/samegame/content/samegame.js
examples/declarative/snake/content/HighScoreModel.qml
examples/declarative/sqllocalstorage/hello.qml
examples/declarative/tutorials/samegame/samegame4/content/samegame.js
src/declarative/qml/qdeclarativeengine.cpp
src/declarative/qml/qdeclarativesqldatabase.cpp
src/declarative/qml/qdeclarativesqldatabase_p.h
src/declarative/qml/v8/qv8engine.cpp
src/declarative/qml/v8/qv8sqlerrors.cpp [new file with mode: 0644]
src/declarative/qml/v8/qv8sqlerrors_p.h [new file with mode: 0644]
src/declarative/qml/v8/v8.pri
src/imports/imports.pro
src/imports/localstorage/localstorage.pro [new file with mode: 0644]
src/imports/localstorage/plugin.cpp [new file with mode: 0644]
src/imports/localstorage/qmldir [new file with mode: 0644]
tests/auto/declarative/qdeclarativesqldatabase/data/changeversion.js
tests/auto/declarative/qdeclarativesqldatabase/data/creation-a.js
tests/auto/declarative/qdeclarativesqldatabase/data/creation.js
tests/auto/declarative/qdeclarativesqldatabase/data/error-a.js
tests/auto/declarative/qdeclarativesqldatabase/data/error-b.js
tests/auto/declarative/qdeclarativesqldatabase/data/error-creation.js
tests/auto/declarative/qdeclarativesqldatabase/data/error-notransaction.js
tests/auto/declarative/qdeclarativesqldatabase/data/error-outsidetransaction.js
tests/auto/declarative/qdeclarativesqldatabase/data/iteration-forwardonly.js
tests/auto/declarative/qdeclarativesqldatabase/data/iteration.js
tests/auto/declarative/qdeclarativesqldatabase/data/readonly-error.js
tests/auto/declarative/qdeclarativesqldatabase/data/readonly.js
tests/auto/declarative/qdeclarativesqldatabase/data/reopen1.js
tests/auto/declarative/qdeclarativesqldatabase/data/reopen2.js
tests/auto/declarative/qdeclarativesqldatabase/data/selection-bindnames.js
tests/auto/declarative/qdeclarativesqldatabase/data/selection.js

index 47b8e16..fb88086 100644 (file)
@@ -428,7 +428,7 @@ Here is the \c saveHighScore() function in \c samegame.js:
 
 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.
 
index 0911784..6d50180 100644 (file)
@@ -118,90 +118,6 @@ browser.  The following objects and properties are supported by the QML implemen
 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
index 8c15af7..6117675 100755 (executable)
@@ -1,5 +1,6 @@
 /* This script file handles the game logic */
 .pragma library
+.import QtQuick.LocalStorage 2.0 as Sql
 
 var maxColumn = 10;
 var maxRow = 15;
@@ -229,7 +230,7 @@ function createBlock(column,row)
 
 function initHighScoreBar()
 {
-    var db = openDatabaseSync(
+    var db = Sql.openDatabaseSync(
         "SameGameScores",
         "1.0",
         "Local SameGame High Scores",
@@ -254,7 +255,7 @@ function saveHighScore(name)
     if (scoresURL != "")
         sendHighScore(name);
     // Offline storage
-    var db = openDatabaseSync(
+    var db = Sql.openDatabaseSync(
         "SameGameScores",
         "1.0",
         "Local SameGame High Scores",
index 0a30d73..d6ac762 100644 (file)
@@ -40,6 +40,7 @@
 ****************************************************************************/
 
 import QtQuick 2.0
+import QtQuick.LocalStorage 2.0 as Sql
 
 // Models a high score table.
 //
@@ -84,7 +85,7 @@ ListModel {
 
     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)
     {
index 5290f9c..8312e8c 100644 (file)
@@ -39,6 +39,7 @@
 ****************************************************************************/
 //![0]
 import QtQuick 2.0
+import QtQuick.LocalStorage 2.0 as LS
 
 Rectangle {
     color: "white"
@@ -49,7 +50,7 @@ Rectangle {
         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) {
index ccc3f9d..7bb7243 100755 (executable)
@@ -1,4 +1,6 @@
 /* This script file handles the game logic */
+.import QtQuick.LocalStorage 2.0 as Sql
+
 var maxColumn = 10;
 var maxRow = 15;
 var maxIndex = maxColumn * maxRow;
@@ -191,7 +193,7 @@ function saveHighScore(name) {
     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) {
@@ -222,4 +224,3 @@ function sendHighScore(name) {
     postman.send(postData);
 }
 //![1]
-
index 54f17ca..ade01cf 100644 (file)
@@ -74,6 +74,9 @@
 #include "qdeclarativeincubator.h"
 #include <private/qv8profilerservice_p.h>
 
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qsettings.h>
+
 #include <QtCore/qmetaobject.h>
 #include <QNetworkAccessManager>
 #include <QDebug>
@@ -454,6 +457,11 @@ void QDeclarativeEnginePrivate::init()
         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()
@@ -1543,13 +1551,13 @@ bool QDeclarativeEngine::importPlugin(const QString &filePath, const QString &ur
 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)
index 5f7100e..fd4356e 100644 (file)
 #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)
 
@@ -109,7 +88,6 @@ struct QDeclarativeSqlDatabaseData {
     QDeclarativeSqlDatabaseData(QV8Engine *engine);
     ~QDeclarativeSqlDatabaseData();
 
-    QString offlineStoragePath;
     v8::Persistent<v8::Function> constructor;
     v8::Persistent<v8::Function> queryConstructor;
     v8::Persistent<v8::Function> rowsConstructor;
@@ -201,7 +179,7 @@ QDeclarativeSqlDatabaseData::~QDeclarativeSqlDatabaseData()
 
 static QString qmlsqldatabase_databasesPath(QV8Engine *engine)
 {
-    return QDeclarativeSqlDatabaseData::data(engine)->offlineStoragePath +
+    return engine->engine()->offlineStoragePath() +
            QDir::separator() + QLatin1String("Databases");
 }
 
@@ -264,14 +242,14 @@ static v8::Handle<v8::Value> qmlsqldatabase_executeSql(const v8::Arguments& args
     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);
@@ -319,7 +297,7 @@ static v8::Handle<v8::Value> qmlsqldatabase_executeSql(const v8::Arguments& args
         err = true;
     }
     if (err)
-        V8THROW_SQL(DATABASE_ERR,query.lastError().text());
+        V8THROW_SQL(SQLEXCEPTION_DATABASE_ERR,query.lastError().text());
 
     return result;
 }
@@ -341,7 +319,7 @@ static v8::Handle<v8::Value> qmlsqldatabase_changeVersion(const v8::Arguments& a
     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);
@@ -366,7 +344,7 @@ static v8::Handle<v8::Value> qmlsqldatabase_changeVersion(const v8::Arguments& a
             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;
         }
@@ -394,7 +372,7 @@ static v8::Handle<v8::Value> qmlsqldatabase_transaction_shared(const v8::Argumen
     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]);
@@ -467,7 +445,7 @@ static v8::Handle<v8::Value> qmlsqldatabase_open_sync(const v8::Arguments& args)
             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);
@@ -482,7 +460,7 @@ static v8::Handle<v8::Value> qmlsqldatabase_open_sync(const v8::Arguments& args)
             } 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();
             }
@@ -517,11 +495,6 @@ static v8::Handle<v8::Value> qmlsqldatabase_open_sync(const v8::Arguments& args)
 
 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);
@@ -559,12 +532,6 @@ void *qt_add_qmlsqldatabase(QV8Engine *engine)
     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);
 }
 
@@ -574,16 +541,6 @@ void qt_rem_qmlsqldatabase(QV8Engine * /* engine */, void *d)
     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
index cf093ee..a878b38 100644 (file)
@@ -62,8 +62,6 @@ class QV8Engine;
 
 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
 
index e7fe2c7..efb500c 100644 (file)
@@ -58,6 +58,8 @@
 
 #include "qscript_impl_p.h"
 #include "qv8domerrors_p.h"
+#include "qv8sqlerrors_p.h"
+
 
 Q_DECLARE_METATYPE(QJSValue)
 Q_DECLARE_METATYPE(QList<int>)
@@ -618,6 +620,8 @@ void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global)
 
     qt_add_domexceptions(this);
     m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
+
+    qt_add_sqlexceptions(this);
     m_sqlDatabaseData = qt_add_qmlsqldatabase(this);
 
     {
diff --git a/src/declarative/qml/v8/qv8sqlerrors.cpp b/src/declarative/qml/v8/qv8sqlerrors.cpp
new file mode 100644 (file)
index 0000000..1aa3e77
--- /dev/null
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** 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
diff --git a/src/declarative/qml/v8/qv8sqlerrors_p.h b/src/declarative/qml/v8/qv8sqlerrors_p.h
new file mode 100644 (file)
index 0000000..4ca36a8
--- /dev/null
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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
index 99ecf90..72416c4 100644 (file)
@@ -23,6 +23,7 @@ HEADERS += \
     $$PWD/../../../3rdparty/javascriptcore/DateMath.h \
     $$PWD/qv8engine_impl_p.h \
     $$PWD/qv8domerrors_p.h \
+    $$PWD/qv8sqlerrors_p.h \
     $$PWD/qdeclarativebuiltinfunctions_p.h
 
 SOURCES += \
@@ -40,4 +41,5 @@ 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
index 18400f8..ea8e78d 100644 (file)
@@ -1,5 +1,5 @@
 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
diff --git a/src/imports/localstorage/localstorage.pro b/src/imports/localstorage/localstorage.pro
new file mode 100644 (file)
index 0000000..51a69aa
--- /dev/null
@@ -0,0 +1,15 @@
+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
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
new file mode 100644 (file)
index 0000000..dba7340
--- /dev/null
@@ -0,0 +1,668 @@
+/****************************************************************************
+**
+** 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);
diff --git a/src/imports/localstorage/qmldir b/src/imports/localstorage/qmldir
new file mode 100644 (file)
index 0000000..33288a1
--- /dev/null
@@ -0,0 +1 @@
+plugin qmllocalstorageplugin
\ No newline at end of file
index 680d7a6..178ff7c 100644 (file)
@@ -1,7 +1,9 @@
+.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){
@@ -15,7 +17,7 @@ function test() {
     });
 
 
-    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)
@@ -35,7 +37,7 @@ function test() {
     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');
index bd7d5c5..25cba05 100644 (file)
@@ -1,7 +1,9 @@
+.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)');
@@ -9,7 +11,7 @@ function test() {
                 })
             });
 
-    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";
             });
index 317b4c1..64eb4c7 100644 (file)
@@ -1,7 +1,8 @@
+.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)');
index 10a23f6..d7e9b4f 100644 (file)
@@ -1,5 +1,7 @@
+.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 {
index 4dd0ecf..17e34c9 100644 (file)
@@ -1,5 +1,7 @@
+.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(
index 0ab2a35..d66977d 100644 (file)
@@ -1,7 +1,9 @@
+.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;
index b9cc647..2cce3f6 100644 (file)
@@ -1,5 +1,7 @@
+.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 {
index a7af3bd..7af7c1c 100644 (file)
@@ -1,5 +1,7 @@
+.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;
 
index 45947c0..b3df1be 100644 (file)
@@ -1,5 +1,7 @@
+.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(
index c34cbbb..8d96dc2 100644 (file)
@@ -1,5 +1,7 @@
+.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(
index 69ec67f..39eb398 100644 (file)
@@ -1,6 +1,8 @@
+.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) {
index 5ee862c..985c9ec 100644 (file)
@@ -1,6 +1,8 @@
+.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) {
index c1a8157..2171fb0 100644 (file)
@@ -1,6 +1,8 @@
+.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) {
index 4f7248f..12c6135 100644 (file)
@@ -1,6 +1,8 @@
+.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) {
index 9786821..9333ccf 100644 (file)
@@ -1,5 +1,7 @@
+.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(
index 4ae40b1..18d4dce 100644 (file)
@@ -1,5 +1,7 @@
+.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(