From 5d35dbc3552f722cf4de220d8737e8201e4f61e5 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Wed, 16 Sep 2009 18:21:12 +1000 Subject: Better SQL database performance / memory usage. Rows accessible incrementally. Rows.forwardOnly for optimization. --- examples/declarative/sql/README | 7 +++ examples/declarative/sql/hello.qml | 12 +++- src/declarative/qml/qmlengine.cpp | 4 +- src/declarative/qml/qmlengine_p.h | 2 + src/declarative/qml/qmlsqldatabase.cpp | 100 +++++++++++++++++++++++++++++---- 5 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 examples/declarative/sql/README diff --git a/examples/declarative/sql/README b/examples/declarative/sql/README new file mode 100644 index 0000000..a7baab2 --- /dev/null +++ b/examples/declarative/sql/README @@ -0,0 +1,7 @@ +Simple demonstration of HTML5-compatible SQL database interface. +Adds another "hello, world" every time you run it. +Database is stored in: + - %APPDATA%/Nokia/Qt/QML/Databases/ (Windows) + - ~/.local/share/Nokia/Qt/QML/Databases/ (Unix) + - ~/Library/Application Support/Nokia/Qt/QML/Databases/ (Mac OS X) +No quota management. diff --git a/examples/declarative/sql/hello.qml b/examples/declarative/sql/hello.qml index fb6d21c..e43d320 100644 --- a/examples/declarative/sql/hello.qml +++ b/examples/declarative/sql/hello.qml @@ -2,10 +2,9 @@ import Qt 4.6 Text { Script { - function test() + function allGreetings() { var db = openDatabase("QmlExampleDB", "", "The Example QML SQL!", 1000000); - print(db) var r = "" db.transaction(function(tx) { @@ -13,9 +12,16 @@ Text { tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); tx.executeSql('SELECT * FROM Greeting', [], function(tx, rs) { + /* Inefficient HTML5-compatible way for(var i = 0; i < rs.rows.length; i++) { r += rs.rows[i][0] + ", " + rs.rows[i][1] + "\n" } + */ + /* Efficient way: forward only, not "length" query */ + rs.rows.forwardOnly = true; + for(var i = 0; rs.rows[i]; i++) { + r += rs.rows[i][0] + ", " + rs.rows[i][1] + "\n" + } }, function(tx, error) { print("ERROR:", error.message) @@ -26,5 +32,5 @@ Text { return r } } - text: test() + text: allGreetings() } diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index efe1a89..c0dd1a8 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -111,7 +111,7 @@ QScriptValue desktopOpenUrl(QScriptContext *ctxt, QScriptEngine *e) QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) : rootContext(0), currentExpression(0), isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), - nodeListClass(0), namedNodeMapClass(0), scriptEngine(this), rootComponent(0), + nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), scriptEngine(this), rootComponent(0), networkAccessManager(0), typeManager(e), uniqueId(1) { QScriptValue qtObject = @@ -156,6 +156,8 @@ QmlEnginePrivate::~QmlEnginePrivate() nodeListClass = 0; delete namedNodeMapClass; namedNodeMapClass = 0; + delete sqlQueryClass; + sqlQueryClass = 0; for(int ii = 0; ii < bindValues.count(); ++ii) clear(bindValues[ii]); diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 15ab40d..0eeae0c 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -154,6 +154,8 @@ public: // Used by DOM Core 3 API QScriptClass *nodeListClass; QScriptClass *namedNodeMapClass; + // Used by SQL database API + QScriptClass *sqlQueryClass; struct QmlScriptEngine : public QScriptEngine { diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp index 90d6e3e..4a7c3a3 100644 --- a/src/declarative/qml/qmlsqldatabase.cpp +++ b/src/declarative/qml/qmlsqldatabase.cpp @@ -65,6 +65,84 @@ #endif Q_DECLARE_METATYPE(QSqlDatabase) +Q_DECLARE_METATYPE(QSqlQuery) + +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) + } + + QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) + { + if (flags & HandlesReadAccess) { + if (name == str_length) { + return HandlesReadAccess; + } else if (name == str_forwardOnly) { + return flags; + } else { + bool ok; + qint32 pos = name.toString().toInt(&ok); + if (pos < 0 || !ok) + return 0; + QSqlQuery query = qscriptvalue_cast(object.data()); + *id = pos; + if (*id < (uint)query.size()) + return HandlesReadAccess; + } + } + if (flags & HandlesWriteAccess) + if (name == str_forwardOnly) + return flags; + return 0; + } + + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id) + { + QSqlQuery query = qscriptvalue_cast(object.data()); + if (name == str_length) { + int s = query.size(); + if (s<0) { + // Inefficient. + query.last(); + return query.at()+1; + } else { + return s; + } + } else if (name == str_forwardOnly) { + return query.isForwardOnly(); + } else { + if (query.at() == id || query.seek(id)) { // Qt 4.6 doesn't optimize at()==id + QSqlRecord r = query.record(); + QScriptValue row = engine()->newArray(r.count()); + for (int j=0; jundefinedValue(); + } + + void setProperty(QScriptValue &object, + const QScriptString &name, uint, const QScriptValue & value) + { + if (name == str_forwardOnly) { + QSqlQuery query = qscriptvalue_cast(object.data()); + query.setForwardOnly(value.toBool()); + } + } + +private: + QScriptString str_length; + QScriptString str_forwardOnly; +}; static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEngine *engine) { @@ -85,18 +163,14 @@ static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEn query.bindValue(0,values.toVariant()); } if (query.exec()) { - QScriptValue rows = engine->newArray(); - int i=0; - for (; query.next(); ++i) { - QSqlRecord r = query.record(); - QScriptValue row = engine->newArray(r.count()); - for (int j=0; jnewObject(); - rs.setProperty(QLatin1String("rows"), rows); + 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))); + 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); } else { err = true; @@ -142,7 +216,7 @@ static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptE return engine->undefinedValue(); } -// XXX Something like this belongs in Qt. +// XXX Something like this should be exported by Qt. static QString userLocalDataPath(const QString& app) { QString result; @@ -196,6 +270,8 @@ static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine * md5.addData(dbversion.utf8()); QString dbid(QLatin1String(md5.result().toHex())); + // Uses SQLLITE (like HTML5), but any could be used. + if (QSqlDatabase::connectionNames().contains(dbid)) { database = QSqlDatabase::database(dbid); } else { -- cgit v0.12