summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/declarative/qml/qmlsqldatabase.cpp118
-rw-r--r--tests/auto/declarative/sql/data/1-creation.js (renamed from tests/auto/declarative/sql/data/test1.js)0
-rw-r--r--tests/auto/declarative/sql/data/2-selection.js (renamed from tests/auto/declarative/sql/data/test2.js)0
-rw-r--r--tests/auto/declarative/sql/data/3-iteration-item-function.js27
-rw-r--r--tests/auto/declarative/sql/data/5-iteration-iterator.js27
-rw-r--r--tests/auto/declarative/sql/data/6-iteration-efficient.js28
-rw-r--r--tests/auto/declarative/sql/tst_sql.cpp15
7 files changed, 206 insertions, 9 deletions
diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp
index 5869a56..b132c55 100644
--- a/src/declarative/qml/qmlsqldatabase.cpp
+++ b/src/declarative/qml/qmlsqldatabase.cpp
@@ -46,6 +46,7 @@
#include <QtScript/qscriptvalueiterator.h>
#include <QtScript/qscriptcontext.h>
#include <QtScript/qscriptengine.h>
+#include <QtScript/qscriptclasspropertyiterator.h>
#include <QtSql/qsqldatabase.h>
#include <QtSql/qsqlquery.h>
#include <QtSql/qsqlerror.h>
@@ -62,12 +63,14 @@
Q_DECLARE_METATYPE(QSqlDatabase)
Q_DECLARE_METATYPE(QSqlQuery)
+class QmlSqlQueryScriptClassPropertyIterator;
+
class QmlSqlQueryScriptClass: public QScriptClass {
public:
QmlSqlQueryScriptClass(QScriptEngine *engine) : QScriptClass(engine)
{
str_length = engine->toStringHandle(QLatin1String("length"));
- str_forwardOnly = engine->toStringHandle(QLatin1String("forwardOnly")); // not in HTML5 (optimization)
+ str_forwardOnly = engine->toStringHandle(QLatin1String("forwardOnly")); // not in HTML5 (an optimization)
}
QueryFlags queryProperty(const QScriptValue &object,
@@ -112,12 +115,11 @@ public:
} else if (name == str_forwardOnly) {
return query.isForwardOnly();
} else {
- if (query.at() == id || query.seek(id)) { // Qt 4.6 doesn't optimize at()==id
+ if ((uint)query.at() == id || query.seek(id)) { // Qt 4.6 doesn't optimize seek(at())
QSqlRecord r = query.record();
- QScriptValue row = engine()->newArray(r.count());
+ QScriptValue row = engine()->newObject();
for (int j=0; j<r.count(); ++j) {
- // XXX only strings
- row.setProperty(j, QScriptValue(engine(),r.value(j).toString()));
+ row.setProperty(r.fieldName(j), QScriptValue(engine(),r.value(j).toString())); // XXX only strings
}
return row;
}
@@ -134,11 +136,108 @@ public:
}
}
+ QScriptValue::PropertyFlags propertyFlags(const QScriptValue &/*object*/, const QScriptString &name, uint /*id*/)
+ {
+ if (name == str_length) {
+ return QScriptValue::Undeletable
+ | QScriptValue::SkipInEnumeration;
+ }
+ return QScriptValue::Undeletable;
+ }
+
+ QScriptClassPropertyIterator *newIterator(const QScriptValue &object);
+
private:
QScriptString str_length;
QScriptString str_forwardOnly;
};
+class QmlSqlQueryScriptClassPropertyIterator : public QScriptClassPropertyIterator
+{
+public:
+ QmlSqlQueryScriptClassPropertyIterator(const QScriptValue &object)
+ : QScriptClassPropertyIterator(object)
+ {
+ toFront();
+ }
+
+ ~QmlSqlQueryScriptClassPropertyIterator()
+ {
+ }
+
+ bool hasNext() const
+ {
+ QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object().data());
+ return query.at() == m_index || query.seek(m_index); // Qt 4.6 doesn't optimize seek(at())
+ }
+
+ void next()
+ {
+ m_last = m_index;
+ ++m_index;
+ }
+
+ bool hasPrevious() const
+ {
+ return (m_index > 0);
+ }
+
+ void previous()
+ {
+ --m_index;
+ m_last = m_index;
+ }
+
+ void toFront()
+ {
+ m_index = 0;
+ m_last = -1;
+ }
+
+ void toBack()
+ {
+ QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object().data());
+ m_index = query.size();
+ m_last = -1;
+ }
+
+ QScriptString name() const
+ {
+ return object().engine()->toStringHandle(QString::number(m_last));
+ }
+
+ uint id() const
+ {
+ return m_last;
+ }
+
+private:
+ int m_index;
+ int m_last;
+};
+
+QScriptClassPropertyIterator *QmlSqlQueryScriptClass::newIterator(const QScriptValue &object)
+{
+ return new QmlSqlQueryScriptClassPropertyIterator(object);
+}
+
+
+
+static QScriptValue qmlsqldatabase_item(QScriptContext *context, QScriptEngine *engine)
+{
+ QSqlQuery query = qscriptvalue_cast<QSqlQuery>(context->thisObject().data());
+ int i = context->argument(0).toNumber();
+ if (query.at() == i || query.seek(i)) { // Qt 4.6 doesn't optimize seek(at())
+ QSqlRecord r = query.record();
+ QScriptValue row = engine->newObject();
+ for (int j=0; j<r.count(); ++j) {
+ row.setProperty(r.fieldName(j), QScriptValue(engine,r.value(j).toString()));
+ }
+ return row;
+ }
+ return engine->undefinedValue();
+}
+
static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEngine *engine)
{
QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
@@ -163,6 +262,7 @@ static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEn
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
@@ -234,7 +334,7 @@ static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine *
database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
}
if (!database.isOpen()) {
- QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath + "/Databases/";
+ QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath + QLatin1String("/Databases/");
QDir().mkpath(basename);
basename += dbid;
database.setDatabaseName(basename+QLatin1String(".sqllite"));
@@ -257,3 +357,9 @@ void qt_add_qmlsqldatabase(QScriptEngine *engine)
engine->globalObject().setProperty(QLatin1String("openDatabase"), openDatabase);
}
+/*
+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.
+*/
+
diff --git a/tests/auto/declarative/sql/data/test1.js b/tests/auto/declarative/sql/data/1-creation.js
index 95fa99e..95fa99e 100644
--- a/tests/auto/declarative/sql/data/test1.js
+++ b/tests/auto/declarative/sql/data/1-creation.js
diff --git a/tests/auto/declarative/sql/data/test2.js b/tests/auto/declarative/sql/data/2-selection.js
index 3acf686..3acf686 100644
--- a/tests/auto/declarative/sql/data/test2.js
+++ b/tests/auto/declarative/sql/data/2-selection.js
diff --git a/tests/auto/declarative/sql/data/3-iteration-item-function.js b/tests/auto/declarative/sql/data/3-iteration-item-function.js
new file mode 100644
index 0000000..bad9b82
--- /dev/null
+++ b/tests/auto/declarative/sql/data/3-iteration-item-function.js
@@ -0,0 +1,27 @@
+var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000);
+var r=0;
+
+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" }
+);
+
+
+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
new file mode 100644
index 0000000..51f0504
--- /dev/null
+++ b/tests/auto/declarative/sql/data/5-iteration-iterator.js
@@ -0,0 +1,27 @@
+var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000);
+var r=0;
+
+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" }
+);
+
+
+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
new file mode 100644
index 0000000..2222b8a
--- /dev/null
+++ b/tests/auto/declarative/sql/data/6-iteration-efficient.js
@@ -0,0 +1,28 @@
+var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000);
+var r=0;
+
+db.transaction(
+ function(tx) {
+ tx.executeSql('SELECT * FROM Greeting', [],
+ function(tx, rs) {
+ var r1=""
+ rs.rows.forwardOnly = true;
+ 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="passed" }
+);
+
+
+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 cb13427..10ce6d8 100644
--- a/tests/auto/declarative/sql/tst_sql.cpp
+++ b/tests/auto/declarative/sql/tst_sql.cpp
@@ -82,9 +82,14 @@ void tst_sql::testQml_data()
QTest::addColumn<QString>("jsfile"); // The input file
QTest::addColumn<QString>("result"); // The required output from the js test() function
QTest::addColumn<int>("databases"); // The number of databases that should have been created
-
- QTest::newRow("basic creation") << "data/test1.js" << "passed" << 1;
- QTest::newRow("basic select") << "data/test2.js" << "passed" << 1;
+ QTest::addColumn<bool>("qmlextension"); // Things WebKit can't do
+
+ QTest::newRow("creation") << "data/1-creation.js" << "passed" << 1 << false;
+ QTest::newRow("selection") << "data/2-selection.js" << "passed" << 1 << false;
+ QTest::newRow("iteration-item-function") << "data/3-iteration-item-function.js" << "passed" << 1 << false;
+ QTest::newRow("iteration-index") << "data/4-iteration-index.js" << "passed" << 1 << true;
+ QTest::newRow("iteration-iterator") << "data/5-iteration-iterator.js" << "passed" << 1 << true;
+ QTest::newRow("iteration-efficient") << "data/6-iteration-efficient.js" << "passed" << 1 << true;
}
void tst_sql::validateAgainstWebkit_data()
@@ -102,6 +107,10 @@ void tst_sql::validateAgainstWebkit()
QFETCH(QString, jsfile);
QFETCH(QString, result);
QFETCH(int, databases);
+ QFETCH(bool, qmlextension);
+
+ if (qmlextension) // WebKit can't do it (yet?)
+ return;
QFile f(jsfile);
QVERIFY(f.open(QIODevice::ReadOnly));