From 7eb051d82a5bd35f86b5f692e2da5ea133e6b7b0 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Wed, 18 Nov 2009 12:41:39 +1000 Subject: Update SQL database support to current HTML5 specification. --- doc/src/declarative/globalobject.qdoc | 30 +-- src/declarative/qml/qmlsqldatabase.cpp | 245 ++++++++++++++++----- tests/auto/declarative/sql/data/1-creation.js | 27 +-- .../declarative/sql/data/2-selection-bindnames.js | 32 +-- tests/auto/declarative/sql/data/2-selection.js | 41 ++-- .../sql/data/3-iteration-item-function.js | 38 ++-- .../auto/declarative/sql/data/4-iteration-index.js | 38 ++-- .../declarative/sql/data/5-iteration-iterator.js | 38 ++-- .../declarative/sql/data/6-iteration-efficient.js | 43 ++-- tests/auto/declarative/sql/data/7a-error.js | 32 ++- tests/auto/declarative/sql/tst_sql.cpp | 19 +- 11 files changed, 317 insertions(+), 266 deletions(-) diff --git a/doc/src/declarative/globalobject.qdoc b/doc/src/declarative/globalobject.qdoc index dc08c28..6d9d1f2 100644 --- a/doc/src/declarative/globalobject.qdoc +++ b/doc/src/declarative/globalobject.qdoc @@ -220,25 +220,13 @@ provide the ability to access local offline storage in an SQL database. These databases are user-specific and QML-specific. They are stored in the \c Databases subdirectory of QmlEngine::offlineStoragePath(), currently as SQLite databases. -The API is based on the HTML5 offline storage SQL draft API. The main difference is that this QML -API is currently synchronous. You should avoid relying on synchronicity to make your scripts more -portable, both to/from HTML5 and to future QML versions. +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 API can be used from JavaScript functions in your QML: \quotefile declarative/sql/hello.qml -\section2 DbObject openDatabase(string name, string version, string description, int estimatedsize) - -Opens a database identified by the given \c name and \c version. -If a database with this name and version does not exist, it is created. The \c version should -always be "1.0" until schema upgrade semantics are defined. - -The \c description and \c estimatedsize are provided to allow application tools to give the user -control over the databases created, but are otherwise not used by QML. - -The returned DbObject has a \c transaction() method by which SQL transactions can be done. - When a database is first created, an INI file is also created specifying its characteristics: \table @@ -247,21 +235,9 @@ When a database is first created, an INI file is also created specifying its cha \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 of the database passed to \c openDatabase() -\row \o DbType \o Currently "QSQLITE" +\row \o Driver \o Currently "QSQLITE" \endtable This data can be used by application tools. - -\section2 void DbObject::transaction(function usesql(DbTxObject), function errorcb=0, function successcb=0) - -Executes \c usesql() in a database transaction. The \c DbTxObject object has a \c executeSql() method by which the -code of \c usesql can execute SQL. The optional second and third arguments are an error callback and success -callback respectively (note that the order of these is the opposite to DbTxObject::executeSql()). - -\section2 void DbTxObject::executeSql(string sql, function successcb=0, function errorcb=0) - -Executes \c sql as an SQL block. The optional second and third arguments are a success callback and error -callback respectively (note that the order of these is the opposite to DbObject::transaction()). - */ diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp index 6e32fb7..123b85f 100644 --- a/src/declarative/qml/qmlsqldatabase.cpp +++ b/src/declarative/qml/qmlsqldatabase.cpp @@ -119,7 +119,7 @@ public: QSqlRecord r = query.record(); QScriptValue row = engine()->newObject(); for (int j=0; jthrowError(desc); \ + errorValue.setProperty(QLatin1String("code"), error); \ + return errorValue; \ +} + + +static QString databaseFile(const QString& connectionName, QScriptEngine *engine) +{ + QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath + + QDir::separator() + QLatin1String("Databases") + QDir::separator(); + basename += connectionName; + return basename; +} + static QScriptValue qmlsqldatabase_item(QScriptContext *context, QScriptEngine *engine) @@ -242,80 +281,142 @@ static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEn { QSqlDatabase db = qscriptvalue_cast(context->thisObject()); QString sql = context->argument(0).toString(); - QScriptValue values = context->argument(1); - QScriptValue cb = context->argument(2); - QScriptValue cberr = context->argument(3); QSqlQuery query(db); bool err = false; + + QScriptValue result; + if (query.prepare(sql)) { - if (values.isObject()) { - for (QScriptValueIterator it(values); it.hasNext();) { - it.next(); - query.bindValue(it.name(),it.value().toVariant()); + if (context->argumentCount() > 1) { + QScriptValue values = context->argument(1); + if (values.isObject()) { + for (QScriptValueIterator it(values); it.hasNext();) { + it.next(); + query.bindValue(it.name(),it.value().toVariant()); + } + } else { + query.bindValue(0,values.toVariant()); } - } else { - query.bindValue(0,values.toVariant()); } if (query.exec()) { - QScriptValue rs = engine->newObject(); + result = engine->newObject(); if (!QmlEnginePrivate::get(engine)->sqlQueryClass) QmlEnginePrivate::get(engine)->sqlQueryClass= new QmlSqlQueryScriptClass(engine); QScriptValue rows = engine->newObject(QmlEnginePrivate::get(engine)->sqlQueryClass); rows.setData(engine->newVariant(qVariantFromValue(query))); rows.setProperty(QLatin1String("item"), engine->newFunction(qmlsqldatabase_item,1), QScriptValue::SkipInEnumeration); - rs.setProperty(QLatin1String("rows"),rows); - rs.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected()); - rs.setProperty(QLatin1String("insertId"),query.lastInsertId().toString()); // XXX only string - cb.call(QScriptValue(), QScriptValueList() << context->thisObject() << rs); + result.setProperty(QLatin1String("rows"),rows); + result.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected()); + result.setProperty(QLatin1String("insertId"),query.lastInsertId().toString()); } else { err = true; } } else { err = true; } - if (err) { - QScriptValue error = engine->newObject(); - error.setProperty(QLatin1String("message"), query.lastError().text()); - cberr.call(QScriptValue(), QScriptValueList() << context->thisObject() << error); + if (err) + THROW_SQL(DATABASE_ERR,query.lastError().text()); + return result; +} + +static QScriptValue qmlsqldatabase_executeSql_readonly(QScriptContext *context, QScriptEngine *engine) +{ + QString sql = context->argument(0).toString(); + if (sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) { + qmlsqldatabase_executeSql(context,engine); + } else { + THROW_SQL(SYNTAX_ERR,QmlEngine::tr("Read-only Transaction")) } return engine->undefinedValue(); } -static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine) +static QScriptValue qmlsqldatabase_change_version(QScriptContext *context, QScriptEngine *engine) { - QSqlDatabase db = qscriptvalue_cast(context->thisObject()); - if (context->argumentCount() < 1) + if (context->argumentCount() < 2) return engine->undefinedValue(); - QScriptValue cb = context->argument(0); - if (!cb.isFunction()) + + QSqlDatabase db = qscriptvalue_cast(context->thisObject()); + QString from_version = context->argument(0).toString(); + QString to_version = context->argument(1).toString(); + QScriptValue callback = context->argument(2); + + QScriptValue instance = engine->newObject(); + instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1)); + QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); + + if (from_version!=context->thisObject().property(QLatin1String("version")).toString()) { + THROW_SQL(2,QmlEngine::tr("Version mismatch")); return engine->undefinedValue(); + } + + if (callback.isFunction()) { + db.transaction(); + callback.call(QScriptValue(), QScriptValueList() << tx); + if (engine->hasUncaughtException()) { + db.rollback(); + } else { + if (!db.commit()) { + db.rollback(); + THROW_SQL(0,QmlEngine::tr("SQL transaction failed")); + } else { + context->thisObject().setProperty(QLatin1String("version"), to_version, QScriptValue::ReadOnly); + QSettings ini(databaseFile(db.connectionName(),engine)+QLatin1String(".ini"),QSettings::IniFormat); + ini.setValue(QLatin1String("Version"), to_version); + } + } + } + + return engine->undefinedValue(); +} + +static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine) +{ + QSqlDatabase db = qscriptvalue_cast(context->thisObject()); + QScriptValue callback = context->argument(0); + if (!callback.isFunction()) + THROW_SQL(UNKNOWN_ERR,QmlEngine::tr("transaction: missing callback")); - // Call synchronously... - XXX could do asynch with threads QScriptValue instance = engine->newObject(); - instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,4)); + instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1)); QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); db.transaction(); - cb.call(QScriptValue(), QScriptValueList() << tx); + callback.call(QScriptValue(), QScriptValueList() << tx); if (engine->hasUncaughtException()) { db.rollback(); - QScriptValue cb = context->argument(1); - if (cb.isFunction()) - cb.call(); } else { - db.commit(); - QScriptValue cb = context->argument(2); - if (cb.isFunction()) - cb.call(); + if (!db.commit()) + db.rollback(); } return engine->undefinedValue(); } +static QScriptValue qmlsqldatabase_read_transaction(QScriptContext *context, QScriptEngine *engine) +{ + QSqlDatabase db = qscriptvalue_cast(context->thisObject()); + QScriptValue callback = context->argument(0); + if (!callback.isFunction()) + return engine->undefinedValue(); + + QScriptValue instance = engine->newObject(); + instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql_readonly,1)); + QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); + + db.transaction(); + callback.call(QScriptValue(), QScriptValueList() << tx); + if (engine->hasUncaughtException()) { + db.rollback(); + } else { + if (!db.commit()) + db.rollback(); + } + return engine->undefinedValue(); +} /* Currently documented in doc/src/declarastive/globalobject.qdoc */ -static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine *engine) +static QScriptValue qmlsqldatabase_open_sync(QScriptContext *context, QScriptEngine *engine) { QSqlDatabase database; @@ -323,43 +424,71 @@ static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine * QString dbversion = context->argument(1).toString(); QString dbdescription = context->argument(2).toString(); int dbestimatedsize = context->argument(3).toNumber(); + QScriptValue dbcreationCallback = context->argument(4); QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(dbname.toUtf8()); - md5.addData(dbversion.toUtf8()); QString dbid(QLatin1String(md5.result().toHex())); - // Uses SQLLITE (like HTML5), but any could be used. + QString basename = databaseFile(dbid,engine); + bool created = false; + QString version = dbversion; - if (QSqlDatabase::connectionNames().contains(dbid)) { - database = QSqlDatabase::database(dbid); - } else { - database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid); - } - if (!database.isOpen()) { - QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath - + QDir::separator() + QLatin1String("Databases") + QDir::separator(); - QDir().mkpath(basename); - basename += dbid; - database.setDatabaseName(basename+QLatin1String(".sqlite")); + { QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat); - ini.setValue(QLatin1String("Name"), dbname); - ini.setValue(QLatin1String("Version"), dbversion); - ini.setValue(QLatin1String("Description"), dbdescription); - ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize); - ini.setValue(QLatin1String("DbType"), QLatin1String("QSQLITE")); - database.open(); + + if (QSqlDatabase::connectionNames().contains(dbid)) { + database = QSqlDatabase::database(dbid); + } else { + database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid); + QDir().mkpath(basename); + if (!QFile::exists(basename+QLatin1String(".sqlite"))) { + created = true; + 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 + THROW_SQL(VERSION_ERR,QmlEngine::tr("SQL: database version mismatch")); + } + } + database.setDatabaseName(basename+QLatin1String(".sqlite")); + } + if (!database.isOpen()) + database.open(); } QScriptValue instance = engine->newObject(); - instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,3)); - return engine->newVariant(instance,qVariantFromValue(database)); + instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,1)); + instance.setProperty(QLatin1String("readTransaction"), engine->newFunction(qmlsqldatabase_read_transaction,1)); + instance.setProperty(QLatin1String("version"), version, QScriptValue::ReadOnly); + instance.setProperty(QLatin1String("changeVersion"), engine->newFunction(qmlsqldatabase_change_version,3)); + + QScriptValue result = engine->newVariant(instance,qVariantFromValue(database)); + + if (created && dbcreationCallback.isFunction()) { + dbcreationCallback.call(QScriptValue(), QScriptValueList() << result); + } + + return result; } void qt_add_qmlsqldatabase(QScriptEngine *engine) { - QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open, 4); - engine->globalObject().setProperty(QLatin1String("openDatabase"), openDatabase); + QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open_sync, 4); + engine->globalObject().setProperty(QLatin1String("openDatabaseSync"), openDatabase); + + QScriptValue sqlExceptionPrototype = engine->newObject(); + for (int i=0; sqlerror[i]; ++i) + sqlExceptionPrototype.setProperty(QLatin1String(sqlerror[i]), + i,QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + + engine->globalObject().setProperty(QLatin1String("SQLException"), sqlExceptionPrototype); } /* diff --git a/tests/auto/declarative/sql/data/1-creation.js b/tests/auto/declarative/sql/data/1-creation.js index 95fa99e..aab9b5d 100644 --- a/tests/auto/declarative/sql/data/1-creation.js +++ b/tests/auto/declarative/sql/data/1-creation.js @@ -1,20 +1,15 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r="transaction_not_finished"; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -// Asynchronous in WebKit, so must wait before calling test() -db.transaction( - function(tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)', [], - function(tx, rs) { }, function(tx, error) { r="CREATE FAILED: "+error.message }); - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { r="INSERT FAILED: "+error.message }); - }, - function(tx, error) { r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r=="transaction_not_finished") r="passed" } -); + // Asynchronous in WebKit, so must wait before calling test() + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)'); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + r = "passed"; + } + ); - -function test() -{ return r; } diff --git a/tests/auto/declarative/sql/data/2-selection-bindnames.js b/tests/auto/declarative/sql/data/2-selection-bindnames.js index c00acc14..21f34db 100644 --- a/tests/auto/declarative/sql/data/2-selection-bindnames.js +++ b/tests/auto/declarative/sql/data/2-selection-bindnames.js @@ -1,24 +1,16 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting WHERE salutation=:p2 AND salutee=:p1', {':p1':'world', ':p2':'hello'}, - function(tx, rs) { - if ( rs.rows.length != 4 ) { - if (r==0) r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows.item(0)+rs.rows.item(1) - } - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting WHERE salutation=:p2 AND salutee=:p1', {':p1':'world', ':p2':'hello'}); + if ( rs.rows.length != 4 ) + r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows.item(0)+rs.rows.item(1) + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/2-selection.js b/tests/auto/declarative/sql/data/2-selection.js index 3acf686..f141d2c 100644 --- a/tests/auto/declarative/sql/data/2-selection.js +++ b/tests/auto/declarative/sql/data/2-selection.js @@ -1,30 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { if (r==0) r="INSERT 1 FAILED: "+error.message }); - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { if (r==0) r="INSERT 2 FAILED: "+error.message }); - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { if (r==0) r="INSERT 3 FAILED: "+error.message }); - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - if ( rs.rows.length != 4 ) { // 1 from test1, 3 from this test. - if (r==0) r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows[0]+rs.rows[1] - } - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + var rs = tx.executeSql('SELECT * FROM Greeting'); + if ( rs.rows.length != 4 ) // 1 from test1, 3 from this test. + r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows[0]+rs.rows[1] + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/3-iteration-item-function.js b/tests/auto/declarative/sql/data/3-iteration-item-function.js index bad9b82..57c8a17 100644 --- a/tests/auto/declarative/sql/data/3-iteration-item-function.js +++ b/tests/auto/declarative/sql/data/3-iteration-item-function.js @@ -1,27 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - for(var i = 0; i < rs.rows.length; i++) { - r1 += rs.rows.item(i).salutation + ", " + rs.rows.item(i).salutee + ";" - } - if (r1 != "hello, world;hello, world;hello, world;hello, world;") - r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + var r1="" + for(var i = 0; i < rs.rows.length; i++) + r1 += rs.rows.item(i).salutation + ", " + rs.rows.item(i).salutee + ";" + if (r1 != "hello, world;hello, world;hello, world;hello, world;") + r = "SELECTED DATA WRONG: "+r1; + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/4-iteration-index.js b/tests/auto/declarative/sql/data/4-iteration-index.js index 298737d..512cf8d 100644 --- a/tests/auto/declarative/sql/data/4-iteration-index.js +++ b/tests/auto/declarative/sql/data/4-iteration-index.js @@ -1,27 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - for(var i = 0; i < rs.rows.length; i++) { - r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" - } - if (r1 != "hello, world;hello, world;hello, world;hello, world;") - r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + var r1="" + for(var i = 0; i < rs.rows.length; i++) + r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" + if (r1 != "hello, world;hello, world;hello, world;hello, world;") + r = "SELECTED DATA WRONG: "+r1; + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/5-iteration-iterator.js b/tests/auto/declarative/sql/data/5-iteration-iterator.js index 51f0504..ae4fd34 100644 --- a/tests/auto/declarative/sql/data/5-iteration-iterator.js +++ b/tests/auto/declarative/sql/data/5-iteration-iterator.js @@ -1,27 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - for(var i in rs.rows) { - r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" - } - if (r1 != "hello, world;hello, world;hello, world;hello, world;") - r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting') + var r1="" + for(var i in rs.rows) + r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" + if (r1 != "hello, world;hello, world;hello, world;hello, world;") + r = "SELECTED DATA WRONG: "+r1; + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/6-iteration-efficient.js b/tests/auto/declarative/sql/data/6-iteration-efficient.js index 6711fb0..fe0acfc 100644 --- a/tests/auto/declarative/sql/data/6-iteration-efficient.js +++ b/tests/auto/declarative/sql/data/6-iteration-efficient.js @@ -1,32 +1,29 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; -var fbefore="FORWARD WRONG" -var fafter="FORWARD WRONG" +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - if (!rs.rows.forwardOnly) fbefore="" - rs.rows.forwardOnly = true; - if (rs.rows.forwardOnly) fafter=""; + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + var r1="" + var fbefiore = rs.rows.forwardOnly; + rs.rows.forwardOnly = true; + var fafter = rs.rows.forwardOnly; + if (fbefore) + r = "forward wrong before"; + else if (!fafter) + r = "forward wrong after"; + else { for(var i=0; rs.rows[i]; ++i) { r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" } if (r1 != "hello, world;hello, world;hello, world;hello, world;") r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r=fbefore+"passed"+fafter } -); + else + r = "passed"; + } + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/7a-error.js b/tests/auto/declarative/sql/data/7a-error.js index 0bb39ef..65d0c03 100644 --- a/tests/auto/declarative/sql/data/7a-error.js +++ b/tests/auto/declarative/sql/data/7a-error.js @@ -1,26 +1,20 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM NotExists', [], - function(tx, rs) { + try { + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM NotExists'); r = "SHOULD NOT SUCCEED"; - }, - function(tx, error) { - if (error.message == "no such table: NotExists Unable to execute statement" // QML - || error.message == "no such table: NotExists") r="passed" // WebKit - else r="WRONG ERROR:"+error.message } ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="SHOULD NOT SUCCEED2" } -); + } catch (err) { + if (err.message == "no such table: NotExists Unable to execute statement") + r = "passed"; + else + r = "WRONG ERROR="+err.message; + } - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/tst_sql.cpp b/tests/auto/declarative/sql/tst_sql.cpp index a929aba..cbd14ab 100644 --- a/tests/auto/declarative/sql/tst_sql.cpp +++ b/tests/auto/declarative/sql/tst_sql.cpp @@ -149,16 +149,18 @@ void tst_sql::testQml_data() void tst_sql::validateAgainstWebkit_data() { - testQml_data(); + QTest::addColumn("jsfile"); // The input file + QTest::addColumn("result"); // The required output from the js test() function + QTest::addColumn("databases"); // The number of databases that should have been created + QTest::addColumn("qmlextension"); // Things WebKit can't do + QTest::newRow("creation") << "data/1-creation.js" << "passed" << 1 << false; } void tst_sql::validateAgainstWebkit() { // Validates tests against WebKit (HTML5) support. // - // WebKit SQL is asynchronous, so tests are divided into code plus a test() - // function which is executed "later" (via QTRY_). - // + QFETCH(QString, jsfile); QFETCH(QString, result); QFETCH(int, databases); @@ -175,14 +177,15 @@ void tst_sql::validateAgainstWebkit() webpage.settings()->setOfflineStoragePath(dbDir()); webpage.settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); - webpage.mainFrame()->evaluateJavaScript(js); - QTest::qWait(200); // WebKit db access is asynchronous - QTRY_COMPARE(webpage.mainFrame()->evaluateJavaScript("test()").toString(),result); - QTest::qWait(1000); // WebKit crashes if you quit it too fast + QEXPECT_FAIL("","WebKit doesn't support openDatabaseSync yet", Continue); + QCOMPARE(webpage.mainFrame()->evaluateJavaScript(js).toString(),result); + /* + QTest::qWait(100); // WebKit crashes if you quit it too fast QWebSecurityOrigin origin = webpage.mainFrame()->securityOrigin(); QList dbs = origin.databases(); QCOMPARE(dbs.count(), databases); + */ } void tst_sql::testQml() -- cgit v0.12