Clean up declarative includes
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativesqldatabase.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativesqldatabase_p.h"
43
44 #include "qdeclarativeengine.h"
45 #include "qdeclarativeengine_p.h"
46 #include <private/qdeclarativerefcount_p.h>
47
48 #include <QtCore/qobject.h>
49 #include <QtSql/qsqldatabase.h>
50 #include <QtSql/qsqlquery.h>
51 #include <QtSql/qsqlerror.h>
52 #include <QtSql/qsqlrecord.h>
53 #include <QtSql/qsqlfield.h>
54 #include <QtGui/qdesktopservices.h>
55 #include <QtCore/qstack.h>
56 #include <QtCore/qcryptographichash.h>
57 #include <QtCore/qsettings.h>
58 #include <QtCore/qdir.h>
59 #include <QtCore/qdebug.h>
60
61 #include <private/qv8engine_p.h>
62
63 QT_BEGIN_NAMESPACE
64
65 enum SqlException {
66     UNKNOWN_ERR,
67     DATABASE_ERR,
68     VERSION_ERR,
69     TOO_LARGE_ERR,
70     QUOTA_ERR,
71     SYNTAX_ERR,
72     CONSTRAINT_ERR,
73     TIMEOUT_ERR
74 };
75
76 static const char* sqlerror[] = {
77     "UNKNOWN_ERR",
78     "DATABASE_ERR",
79     "VERSION_ERR",
80     "TOO_LARGE_ERR",
81     "QUOTA_ERR",
82     "SYNTAX_ERR",
83     "CONSTRAINT_ERR",
84     "TIMEOUT_ERR",
85     0
86 };
87
88 #define THROW_SQL(error, desc)
89
90 #define V8THROW_SQL(error, desc) \
91 { \
92     v8::Local<v8::Value> v = v8::Exception::Error(engine->toString(desc)); \
93     v->ToObject()->Set(v8::String::New("code"), v8::Integer::New(error)); \
94     v8::ThrowException(v); \
95     return v8::Handle<v8::Value>(); \
96 }
97
98 #define V8THROW_REFERENCE(string) { \
99     v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(string))); \
100     return v8::Handle<v8::Value>(); \
101 }
102
103 #define V8THROW_REFERENCE_VOID(string) { \
104     v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(string))); \
105     return; \
106 }
107
108 struct QDeclarativeSqlDatabaseData {
109     QDeclarativeSqlDatabaseData(QV8Engine *engine);
110     ~QDeclarativeSqlDatabaseData();
111
112     QString offlineStoragePath;
113     v8::Persistent<v8::Function> constructor;
114     v8::Persistent<v8::Function> queryConstructor;
115     v8::Persistent<v8::Function> rowsConstructor;
116
117     static inline QDeclarativeSqlDatabaseData *data(QV8Engine *e) {
118         return (QDeclarativeSqlDatabaseData *)e->sqlDatabaseData();
119     }
120     static inline QDeclarativeSqlDatabaseData *data(void *d) {
121         return (QDeclarativeSqlDatabaseData *)d;
122     }
123 };
124
125 class QV8SqlDatabaseResource : public QV8ObjectResource
126 {
127     V8_RESOURCE_TYPE(SQLDatabaseType)
128
129 public:
130     enum Type { Database, Query, Rows };
131
132     QV8SqlDatabaseResource(QV8Engine *e) 
133     : QV8ObjectResource(e), type(Database), inTransaction(false), readonly(false), forwardOnly(false) {}
134
135     Type type;
136     QSqlDatabase database;
137
138     QString version; // type == Database
139
140     bool inTransaction; // type == Query
141     bool readonly;   // type == Query
142
143     QSqlQuery query; // type == Rows
144     bool forwardOnly; // type == Rows
145 };
146
147 static v8::Handle<v8::Value> qmlsqldatabase_version(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info)
148 {
149     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
150     if (!r || r->type != QV8SqlDatabaseResource::Database)
151         V8THROW_REFERENCE("Not a SQLDatabase object");
152
153     return r->engine->toString(r->version);
154 }
155
156 static v8::Handle<v8::Value> qmlsqldatabase_rows_length(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info)
157 {
158     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
159     if (!r || r->type != QV8SqlDatabaseResource::Rows)
160         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
161
162     int s = r->query.size();
163     if (s < 0) {
164         // Inefficient
165         if (r->query.last()) {
166             s = r->query.at() + 1;
167         } else {
168             s = 0;
169         }
170     }
171     return v8::Integer::New(s);
172 }
173
174 static v8::Handle<v8::Value> qmlsqldatabase_rows_forwardOnly(v8::Local<v8::String> /* property */,
175                                                              const v8::AccessorInfo& info)
176 {
177     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
178     if (!r || r->type != QV8SqlDatabaseResource::Rows) 
179         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
180
181     return v8::Boolean::New(r->query.isForwardOnly());
182 }
183
184 static void qmlsqldatabase_rows_setForwardOnly(v8::Local<v8::String> /* property */,
185                                                v8::Local<v8::Value> value,
186                                                const v8::AccessorInfo& info)
187 {
188     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
189     if (!r || r->type != QV8SqlDatabaseResource::Rows)
190         V8THROW_REFERENCE_VOID("Not a SQLDatabase::Rows object");
191
192     r->query.setForwardOnly(value->BooleanValue());
193 }
194
195 QDeclarativeSqlDatabaseData::~QDeclarativeSqlDatabaseData()
196 {
197     qPersistentDispose(constructor);
198     qPersistentDispose(queryConstructor);
199     qPersistentDispose(rowsConstructor);
200 }
201
202 static QString qmlsqldatabase_databasesPath(QV8Engine *engine)
203 {
204     return QDeclarativeSqlDatabaseData::data(engine)->offlineStoragePath +
205            QDir::separator() + QLatin1String("Databases");
206 }
207
208 static void qmlsqldatabase_initDatabasesPath(QV8Engine *engine)
209 {
210     QDir().mkpath(qmlsqldatabase_databasesPath(engine));
211 }
212
213 static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV8Engine *engine)
214 {
215     return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName;
216 }
217
218 static v8::Handle<v8::Value> qmlsqldatabase_rows_index(QV8SqlDatabaseResource *r, uint32_t index)
219 {
220     if (r->query.at() == index || r->query.seek(index)) {
221
222         QSqlRecord record = r->query.record();
223         // XXX optimize
224         v8::Local<v8::Object> row = v8::Object::New();
225         for (int ii = 0; ii < record.count(); ++ii) {
226             QVariant v = record.value(ii);
227             if (v.isNull()) {
228                 row->Set(r->engine->toString(record.fieldName(ii)), v8::Null());
229             } else {
230                 row->Set(r->engine->toString(record.fieldName(ii)),
231                          r->engine->fromVariant(v));
232             }
233         }
234         return row;
235     } else {
236         return v8::Undefined();
237     }
238 }
239
240 static v8::Handle<v8::Value> qmlsqldatabase_rows_index(uint32_t index, const v8::AccessorInfo& info)
241 {
242     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
243     if (!r || r->type != QV8SqlDatabaseResource::Rows)
244         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
245
246     return qmlsqldatabase_rows_index(r, index);
247 }
248
249 static v8::Handle<v8::Value> qmlsqldatabase_rows_item(const v8::Arguments& args)
250 {
251     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
252     if (!r || r->type != QV8SqlDatabaseResource::Rows)
253         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
254
255     return qmlsqldatabase_rows_index(r, args.Length()?args[0]->Uint32Value():0);
256 }
257
258 static v8::Handle<v8::Value> qmlsqldatabase_executeSql(const v8::Arguments& args)
259 {
260     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
261     if (!r || r->type != QV8SqlDatabaseResource::Query)
262         V8THROW_REFERENCE("Not a SQLDatabase::Query object");
263
264     QV8Engine *engine = r->engine;
265
266     if (!r->inTransaction)
267         V8THROW_SQL(DATABASE_ERR,QDeclarativeEngine::tr("executeSql called outside transaction()"));
268
269     QSqlDatabase db = r->database;
270
271     QString sql = engine->toString(args[0]);
272
273     if (r->readonly && !sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
274         V8THROW_SQL(SYNTAX_ERR, QDeclarativeEngine::tr("Read-only Transaction"));
275     }
276
277     QSqlQuery query(db);
278     bool err = false;
279
280     v8::Handle<v8::Value> result = v8::Undefined();
281
282     if (query.prepare(sql)) {
283         if (args.Length() > 1) {
284             v8::Local<v8::Value> values = args[1];
285             if (values->IsArray()) {
286                 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(values);
287                 uint32_t size = array->Length();
288                 for (uint32_t ii = 0; ii < size; ++ii) 
289                     query.bindValue(ii, engine->toVariant(array->Get(ii), -1));
290             } else if (values->IsObject() && !values->ToObject()->GetExternalResource()) {
291                 v8::Local<v8::Object> object = values->ToObject();
292                 v8::Local<v8::Array> names = object->GetPropertyNames();
293                 uint32_t size = names->Length();
294                 for (uint32_t ii = 0; ii < size; ++ii) 
295                     query.bindValue(engine->toString(names->Get(ii)), 
296                                     engine->toVariant(object->Get(names->Get(ii)), -1));
297             } else {
298                 query.bindValue(0, engine->toVariant(values, -1));
299             }
300         }
301         if (query.exec()) {
302             v8::Handle<v8::Object> rows = QDeclarativeSqlDatabaseData::data(engine)->rowsConstructor->NewInstance();
303             QV8SqlDatabaseResource *r = new QV8SqlDatabaseResource(engine);
304             r->type = QV8SqlDatabaseResource::Rows;
305             r->database = db;
306             r->query = query;
307             rows->SetExternalResource(r);
308
309             v8::Local<v8::Object> resultObject = v8::Object::New();
310             result = resultObject;
311             // XXX optimize
312             resultObject->Set(v8::String::New("rowsAffected"), v8::Integer::New(query.numRowsAffected()));
313             resultObject->Set(v8::String::New("insertId"), engine->toString(query.lastInsertId().toString()));
314             resultObject->Set(v8::String::New("rows"), rows);
315         } else {
316             err = true;
317         }
318     } else {
319         err = true;
320     }
321     if (err)
322         V8THROW_SQL(DATABASE_ERR,query.lastError().text());
323
324     return result;
325 }
326
327 static v8::Handle<v8::Value> qmlsqldatabase_changeVersion(const v8::Arguments& args)
328 {
329     if (args.Length() < 2)
330         return v8::Undefined();
331
332     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
333     if (!r || r->type != QV8SqlDatabaseResource::Database)
334         V8THROW_REFERENCE("Not a SQLDatabase object");
335
336     QV8Engine *engine = r->engine;
337
338     QSqlDatabase db = r->database;
339     QString from_version = engine->toString(args[0]);
340     QString to_version = engine->toString(args[1]);
341     v8::Handle<v8::Value> callback = args[2];
342
343     if (from_version != r->version) 
344         V8THROW_SQL(VERSION_ERR, QDeclarativeEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(r->version));
345
346     v8::Local<v8::Object> instance = QDeclarativeSqlDatabaseData::data(engine)->queryConstructor->NewInstance();
347     QV8SqlDatabaseResource *r2 = new QV8SqlDatabaseResource(engine);
348     r2->type = QV8SqlDatabaseResource::Query;
349     r2->database = db;
350     r2->version = r->version;
351     r2->inTransaction = true;
352     instance->SetExternalResource(r2);
353
354     bool ok = true;
355     if (callback->IsFunction()) {
356         ok = false;
357         db.transaction();
358
359         v8::TryCatch tc;
360         v8::Handle<v8::Value> callbackArgs[] = { instance };
361         v8::Handle<v8::Function>::Cast(callback)->Call(engine->global(), 1, callbackArgs);
362
363         if (tc.HasCaught()) {
364             db.rollback();
365             tc.ReThrow();
366             return v8::Handle<v8::Value>();
367         } else if (!db.commit()) {
368             db.rollback();
369             V8THROW_SQL(UNKNOWN_ERR,QDeclarativeEngine::tr("SQL transaction failed"));
370         } else {
371             ok = true;
372         }
373     }
374
375     r2->inTransaction = false;
376
377     if (ok) {
378         r2->version = to_version;
379 #ifndef QT_NO_SETTINGS
380         QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(),engine) + QLatin1String(".ini"), QSettings::IniFormat);
381         ini.setValue(QLatin1String("Version"), to_version);
382 #endif
383     }
384
385     return v8::Undefined();
386 }
387
388 static v8::Handle<v8::Value> qmlsqldatabase_transaction_shared(const v8::Arguments& args, bool readOnly)
389 {
390     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
391     if (!r || r->type != QV8SqlDatabaseResource::Database)
392         V8THROW_REFERENCE("Not a SQLDatabase object");
393
394     QV8Engine *engine = r->engine;
395
396     if (args.Length() == 0 || !args[0]->IsFunction())
397         V8THROW_SQL(UNKNOWN_ERR,QDeclarativeEngine::tr("transaction: missing callback"));
398     
399     QSqlDatabase db = r->database;
400     v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(args[0]);
401
402     v8::Local<v8::Object> instance = QDeclarativeSqlDatabaseData::data(engine)->queryConstructor->NewInstance();
403     QV8SqlDatabaseResource *q = new QV8SqlDatabaseResource(engine);
404     q->type = QV8SqlDatabaseResource::Query;
405     q->database = db;
406     q->readonly = readOnly;
407     q->inTransaction = true;
408     instance->SetExternalResource(q);
409
410     db.transaction();
411     v8::TryCatch tc;
412     v8::Handle<v8::Value> callbackArgs[] = { instance };
413     callback->Call(engine->global(), 1, callbackArgs);
414
415     q->inTransaction = false;
416
417     if (tc.HasCaught()) {
418         db.rollback();
419         tc.ReThrow();
420         return v8::Handle<v8::Value>();
421     } else if (!db.commit()) {
422         db.rollback();
423     }
424
425     return v8::Undefined();
426 }
427
428 static v8::Handle<v8::Value> qmlsqldatabase_transaction(const v8::Arguments& args)
429 {
430     return qmlsqldatabase_transaction_shared(args, false);
431 }
432
433 static v8::Handle<v8::Value> qmlsqldatabase_read_transaction(const v8::Arguments& args)
434 {
435     return qmlsqldatabase_transaction_shared(args, true);
436 }
437
438 /*
439     Currently documented in doc/src/declarative/globalobject.qdoc
440 */
441 static v8::Handle<v8::Value> qmlsqldatabase_open_sync(const v8::Arguments& args)
442 {
443 #ifndef QT_NO_SETTINGS
444     QV8Engine *engine = V8ENGINE();
445     qmlsqldatabase_initDatabasesPath(engine);
446
447     QSqlDatabase database;
448
449     QString dbname = engine->toString(args[0]);
450     QString dbversion = engine->toString(args[1]);
451     QString dbdescription = engine->toString(args[2]);
452     int dbestimatedsize = args[3]->Int32Value();
453     v8::Handle<v8::Value> dbcreationCallback = args[4];
454
455     QCryptographicHash md5(QCryptographicHash::Md5);
456     md5.addData(dbname.toUtf8());
457     QString dbid(QLatin1String(md5.result().toHex()));
458
459     QString basename = qmlsqldatabase_databaseFile(dbid, engine);
460     bool created = false;
461     QString version = dbversion;
462
463     {
464         QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);
465
466         if (QSqlDatabase::connectionNames().contains(dbid)) {
467             database = QSqlDatabase::database(dbid);
468             version = ini.value(QLatin1String("Version")).toString();
469             if (version != dbversion && !dbversion.isEmpty() && !version.isEmpty())
470                 V8THROW_SQL(VERSION_ERR, QDeclarativeEngine::tr("SQL: database version mismatch"));
471         } else {
472             created = !QFile::exists(basename+QLatin1String(".sqlite"));
473             database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
474             if (created) {
475                 ini.setValue(QLatin1String("Name"), dbname);
476                 if (dbcreationCallback->IsFunction())
477                     version = QString();
478                 ini.setValue(QLatin1String("Version"), version);
479                 ini.setValue(QLatin1String("Description"), dbdescription);
480                 ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize);
481                 ini.setValue(QLatin1String("Driver"), QLatin1String("QSQLITE"));
482             } else {
483                 if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) {
484                     // Incompatible
485                     V8THROW_SQL(VERSION_ERR,QDeclarativeEngine::tr("SQL: database version mismatch"));
486                 }
487                 version = ini.value(QLatin1String("Version")).toString();
488             }
489             database.setDatabaseName(basename+QLatin1String(".sqlite"));
490         }
491         if (!database.isOpen())
492             database.open();
493     }
494
495     v8::Local<v8::Object> instance = QDeclarativeSqlDatabaseData::data(engine)->constructor->NewInstance();
496     QV8SqlDatabaseResource *r = new QV8SqlDatabaseResource(engine);
497     r->database = database;
498     r->version = version;
499     instance->SetExternalResource(r);
500
501     if (created && dbcreationCallback->IsFunction()) {
502         v8::TryCatch tc;
503         v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(dbcreationCallback);
504         v8::Handle<v8::Value> args[] = { instance };
505         callback->Call(engine->global(), 1, args);
506         if (tc.HasCaught()) {
507             tc.ReThrow();
508             return v8::Handle<v8::Value>();
509         }
510     }
511
512     return instance;
513 #else
514     return v8::Undefined();
515 #endif // QT_NO_SETTINGS
516 }
517
518 QDeclarativeSqlDatabaseData::QDeclarativeSqlDatabaseData(QV8Engine *engine)
519 {
520     QString dataLocation = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
521     offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator()) +
522                          QDir::separator() + QLatin1String("QML") +
523                          QDir::separator() + QLatin1String("OfflineStorage");
524
525     {
526     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
527     ft->InstanceTemplate()->SetHasExternalResource(true);
528     ft->PrototypeTemplate()->Set(v8::String::New("transaction"), 
529                                  V8FUNCTION(qmlsqldatabase_transaction, engine));
530     ft->PrototypeTemplate()->Set(v8::String::New("readTransaction"), 
531                                  V8FUNCTION(qmlsqldatabase_read_transaction, engine));
532     ft->PrototypeTemplate()->SetAccessor(v8::String::New("version"), qmlsqldatabase_version);
533     ft->PrototypeTemplate()->Set(v8::String::New("changeVersion"), 
534                                  V8FUNCTION(qmlsqldatabase_changeVersion, engine));
535     constructor = qPersistentNew<v8::Function>(ft->GetFunction());
536     }
537
538     {
539     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
540     ft->InstanceTemplate()->SetHasExternalResource(true);
541     ft->PrototypeTemplate()->Set(v8::String::New("executeSql"), 
542                                  V8FUNCTION(qmlsqldatabase_executeSql, engine));
543     queryConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
544     }
545     {
546     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
547     ft->InstanceTemplate()->SetHasExternalResource(true);
548     ft->PrototypeTemplate()->Set(v8::String::New("item"), V8FUNCTION(qmlsqldatabase_rows_item, engine));
549     ft->PrototypeTemplate()->SetAccessor(v8::String::New("length"), qmlsqldatabase_rows_length);
550     ft->InstanceTemplate()->SetAccessor(v8::String::New("forwardOnly"), qmlsqldatabase_rows_forwardOnly, 
551                                         qmlsqldatabase_rows_setForwardOnly);
552     ft->InstanceTemplate()->SetIndexedPropertyHandler(qmlsqldatabase_rows_index);
553     rowsConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
554     }
555 }
556
557 void *qt_add_qmlsqldatabase(QV8Engine *engine)
558 {
559     v8::Local<v8::Function> openDatabase = V8FUNCTION(qmlsqldatabase_open_sync, engine);
560     engine->global()->Set(v8::String::New("openDatabaseSync"), openDatabase);
561
562     v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete);
563     v8::Local<v8::Object> sqlExceptionPrototype = v8::Object::New();
564     for (int i=0; sqlerror[i]; ++i)
565         sqlExceptionPrototype->Set(v8::String::New(sqlerror[i]), v8::Integer::New(i), attributes);
566     engine->global()->Set(v8::String::New("SQLException"), sqlExceptionPrototype);
567
568     return (void *)new QDeclarativeSqlDatabaseData(engine);
569 }
570
571 void qt_rem_qmlsqldatabase(QV8Engine * /* engine */, void *d)
572 {
573     QDeclarativeSqlDatabaseData *data = (QDeclarativeSqlDatabaseData *)d;
574     delete data;
575 }
576
577 void qt_qmlsqldatabase_setOfflineStoragePath(QV8Engine *engine, const QString &path)
578 {
579     QDeclarativeSqlDatabaseData::data(engine)->offlineStoragePath = path;
580 }
581
582 QString qt_qmlsqldatabase_getOfflineStoragePath(const QV8Engine *engine)
583 {
584     return QDeclarativeSqlDatabaseData::data(const_cast<QV8Engine *>(engine))->offlineStoragePath;
585 }
586
587 /*
588 HTML5 "spec" says "rs.rows[n]", but WebKit only impelments "rs.rows.item(n)". We do both (and property iterator).
589 We add a "forwardOnly" property that stops Qt caching results (code promises to only go forward
590 through the data.
591 */
592
593 QT_END_NAMESPACE