summaryrefslogtreecommitdiffstats
path: root/src/partition
diff options
context:
space:
mode:
authorDenis Dzyubenko <denis.dzyubenko@nokia.com>2012-04-11 15:59:00 +0200
committerDenis Dzyubenko <denis.dzyubenko@nokia.com>2012-04-11 16:54:46 +0200
commit6b7df60a752422b1f6ff07bb2682c21e18a43985 (patch)
tree3e56bd3f4ee34a228777d00ff328ef1c53cb1df1 /src/partition
parent20549eedb5b5e852c0e384cd76bfbff14168dbda (diff)
parent7ef36e3c5a88560eb4e3a81c2c9f14059739108b (diff)
Merge remote-tracking branch 'gerrit/master' into hbtreehbtree
Conflicts: src/daemon/daemon.pri src/daemon/jsondbview.cpp src/partition/jsondbindex.cpp src/partition/jsondbindex.h src/partition/jsondbindexquery.h src/partition/jsondbmanagedbtree.cpp src/partition/jsondbmanagedbtree.h src/partition/jsondbmanagedbtreetxn.cpp src/partition/jsondbmanagedbtreetxn.h src/partition/jsondbobjecttable.cpp src/partition/jsondbobjecttable.h src/partition/jsondbpartition.cpp src/partition/jsondbpartition.h tests/auto/auto.pro tests/auto/partition/testpartition.cpp tests/benchmarks/benchmarks.pro Change-Id: I963adefd6d32fca9b3537981306b67538c759034
Diffstat (limited to 'src/partition')
-rw-r--r--src/partition/jsondb.qrc9
-rw-r--r--src/partition/jsondbbtree.cpp164
-rw-r--r--src/partition/jsondbbtree.h142
-rw-r--r--src/partition/jsondbcollator.cpp341
-rw-r--r--src/partition/jsondbcollator.h173
-rw-r--r--src/partition/jsondbcollator_p.h87
-rw-r--r--src/partition/jsondberrors.cpp98
-rw-r--r--src/partition/jsondberrors.h88
-rw-r--r--src/partition/jsondbindex.cpp607
-rw-r--r--src/partition/jsondbindex.h154
-rw-r--r--src/partition/jsondbindexquery.cpp448
-rw-r--r--src/partition/jsondbindexquery.h249
-rw-r--r--src/partition/jsondbmapdefinition.cpp474
-rw-r--r--src/partition/jsondbmapdefinition.h124
-rw-r--r--src/partition/jsondbnotification.cpp83
-rw-r--r--src/partition/jsondbnotification.h92
-rw-r--r--src/partition/jsondbobject.cpp610
-rw-r--r--src/partition/jsondbobject.h114
-rw-r--r--src/partition/jsondbobjectkey.h104
-rw-r--r--src/partition/jsondbobjecttable.cpp707
-rw-r--r--src/partition/jsondbobjecttable.h178
-rw-r--r--src/partition/jsondbobjecttypes_impl_p.h352
-rw-r--r--src/partition/jsondbobjecttypes_p.h180
-rw-r--r--src/partition/jsondbowner.cpp292
-rw-r--r--src/partition/jsondbowner.h101
-rw-r--r--src/partition/jsondbpartition.cpp1920
-rw-r--r--src/partition/jsondbpartition.h288
-rw-r--r--src/partition/jsondbpartitionglobal.h71
-rw-r--r--src/partition/jsondbproxy.cpp145
-rw-r--r--src/partition/jsondbproxy.h114
-rw-r--r--src/partition/jsondbquery.cpp688
-rw-r--r--src/partition/jsondbquery.h201
-rw-r--r--src/partition/jsondbreducedefinition.cpp359
-rw-r--r--src/partition/jsondbreducedefinition.h127
-rw-r--r--src/partition/jsondbschemamanager_impl_p.h108
-rw-r--r--src/partition/jsondbschemamanager_p.h78
-rw-r--r--src/partition/jsondbscriptengine.cpp136
-rw-r--r--src/partition/jsondbscriptengine.h75
-rw-r--r--src/partition/jsondbsettings.cpp109
-rw-r--r--src/partition/jsondbsettings.h141
-rw-r--r--src/partition/jsondbstat.h79
-rw-r--r--src/partition/jsondbstrings.cpp118
-rw-r--r--src/partition/jsondbstrings.h126
-rw-r--r--src/partition/jsondbview.cpp538
-rw-r--r--src/partition/jsondbview.h123
-rw-r--r--src/partition/partition.pro78
-rw-r--r--src/partition/schema-validation/checkpoints.h837
-rw-r--r--src/partition/schema-validation/object.h256
-rw-r--r--src/partition/schema/Capability.json33
-rw-r--r--src/partition/schema/Index.json24
-rw-r--r--src/partition/schema/RootCapability.json17
-rw-r--r--src/partition/schema/View.json10
-rw-r--r--src/partition/schema/notification.json12
53 files changed, 12782 insertions, 0 deletions
diff --git a/src/partition/jsondb.qrc b/src/partition/jsondb.qrc
new file mode 100644
index 00000000..2a12e110
--- /dev/null
+++ b/src/partition/jsondb.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/">
+ <file>schema/notification.json</file>
+ <file>schema/View.json</file>
+ <file>schema/Capability.json</file>
+ <file>schema/RootCapability.json</file>
+ <file>schema/Index.json</file>
+ </qresource>
+</RCC>
diff --git a/src/partition/jsondbbtree.cpp b/src/partition/jsondbbtree.cpp
new file mode 100644
index 00000000..14c7bb30
--- /dev/null
+++ b/src/partition/jsondbbtree.cpp
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QDebug>
+#include <QFile>
+#include <errno.h>
+
+#include "jsondbbtree.h"
+#include "jsondbsettings.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbBtree::JsonDbBtree()
+ : mBtree(new Btree())
+{
+#ifndef JSONDB_USE_HBTREE
+ mBtree->setAutoCompactRate(jsondbSettings->compactRate());
+#endif
+}
+
+JsonDbBtree::~JsonDbBtree()
+{
+ close();
+ delete mBtree;
+}
+
+bool JsonDbBtree::open(const QString &filename, OpenFlags flags)
+{
+ mBtree->setFileName(filename);
+#ifdef JSONDB_USE_HBTREE
+ HBtree::OpenMode mode = HBtree::ReadWrite;
+ if (flags & ReadOnly)
+ mode = HBtree::ReadOnly;
+ mBtree->setOpenMode(mode);
+ return mBtree->open();
+#else
+ QBtree::DbFlags dbFlags = QBtree::UseSyncMarker | QBtree::NoSync;
+ if (flags & ReadOnly)
+ dbFlags |= QBtree::ReadOnly;
+ return mBtree->open(flags);
+#endif
+}
+
+void JsonDbBtree::close()
+{
+ Q_ASSERT(mBtree);
+ mBtree->close();
+}
+
+bool JsonDbBtree::putOne(const QByteArray &key, const QByteArray &value)
+{
+ bool inTransaction = mBtree->isWriting();
+ Transaction *txn = inTransaction ? mBtree->writeTransaction() : mBtree->beginWrite();
+ bool ok = txn->put(key, value);
+ if (!inTransaction) {
+ qWarning() << "JsonDbBtree::putOne" << "auto commiting tag 0";
+ ok &= txn->commit(0);
+ }
+ return ok;
+}
+
+bool JsonDbBtree::getOne(const QByteArray &key, QByteArray *value)
+{
+ bool inTransaction = mBtree->isWriting();
+ Transaction *txn = inTransaction ? mBtree->writeTransaction() : mBtree->beginWrite();
+ bool ok = txn->get(key, value);
+ if (!inTransaction)
+ txn->abort();
+ return ok;
+}
+
+bool JsonDbBtree::removeOne(const QByteArray &key)
+{
+ bool inTransaction = mBtree->isWriting();
+ Transaction *txn = inTransaction ? mBtree->writeTransaction() : mBtree->beginWrite();
+ bool ok = txn->remove(key);
+ if (!inTransaction){
+ qWarning() << "JsonDbBtree::removeOne" << "auto commiting tag 0";
+ ok &= txn->commit(0);
+ }
+ return ok;
+}
+
+bool JsonDbBtree::clearData()
+{
+ Q_ASSERT(isWriting() == false);
+ close();
+ QFile::remove(mBtree->fileName());
+ return mBtree->open();
+}
+
+bool JsonDbBtree::compact()
+{
+ Q_ASSERT(mBtree);
+#ifdef JSONDB_USE_HBTREE
+ return true;
+#else
+ return mBtree->compact();
+#endif
+}
+
+bool JsonDbBtree::rollback()
+{
+ Q_ASSERT(mBtree && !isWriting());
+ return mBtree->rollback();
+}
+
+void JsonDbBtree::setAutoCompactRate(int rate) const
+{
+ Q_ASSERT(mBtree);
+#ifdef JSONDB_USE_HBTREE
+ Q_UNUSED(rate);
+#else
+ mBtree->setAutoCompactRate(rate);
+#endif
+}
+
+JsonDbBtree::Stat JsonDbBtree::stats() const
+{
+ if (mBtree)
+ return mBtree->stats();
+ else
+ return Stat();
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbbtree.h b/src/partition/jsondbbtree.h
new file mode 100644
index 00000000..18387376
--- /dev/null
+++ b/src/partition/jsondbbtree.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_BTREE_H
+#define JSONDB_BTREE_H
+
+#define JSONDB_USE_HBTREE
+
+#include "jsondbpartitionglobal.h"
+
+#ifndef JSONDB_USE_HBTREE
+#include "qbtree.h"
+#include "qbtreecursor.h"
+#include "qbtreetxn.h"
+#else
+#include "hbtree.h"
+#include "hbtreecursor.h"
+#include "hbtreetransaction.h"
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbBtree
+{
+public:
+
+ enum OpenFlag {
+ Default,
+ ReadOnly
+ };
+ Q_DECLARE_FLAGS(OpenFlags, OpenFlag)
+
+#ifdef JSONDB_USE_HBTREE
+ typedef HBtree Btree;
+#else
+ typedef QBtree Btree;
+#endif
+
+ typedef Btree::CursorType Cursor;
+ typedef Btree::TransactionType Transaction;
+ typedef Btree::StatType Stat;
+
+ typedef int (*CompareFunction)(const QByteArray &, const QByteArray &);
+
+ JsonDbBtree();
+ ~JsonDbBtree();
+
+ bool open(const QString &filename, OpenFlags flags = Default);
+ void close();
+
+ Transaction *beginRead()
+ { Q_ASSERT(mBtree); return mBtree->beginRead(); }
+ Transaction *beginWrite()
+ { Q_ASSERT(mBtree); return mBtree->beginWrite(); }
+
+ bool isWriting() const
+ { Q_ASSERT(mBtree); return mBtree->isWriting(); }
+
+ Transaction *writeTransaction()
+ { Q_ASSERT(mBtree); return mBtree->writeTransaction(); }
+
+ QString errorMessage() const
+ { Q_ASSERT(mBtree); return mBtree->errorMessage(); }
+
+ QString fileName() const
+ { Q_ASSERT(mBtree); return mBtree->fileName(); }
+ quint64 count() const
+ { Q_ASSERT(mBtree); return mBtree->count(); }
+ quint32 tag() const
+ { Q_ASSERT(mBtree); return mBtree->tag(); }
+ void setCompareFunction(CompareFunction cmp)
+ { Q_ASSERT(mBtree); mBtree->setCompareFunction(cmp); }
+ void setCacheSize(int size)
+ { Q_ASSERT(mBtree); mBtree->setCacheSize(size); }
+ Btree *btree() const
+ { return mBtree; }
+ Stat stats() const;
+ bool sync()
+ { Q_ASSERT(mBtree); return mBtree->sync(); }
+
+ bool putOne(const QByteArray &key, const QByteArray &value);
+ bool getOne(const QByteArray &key, QByteArray *value);
+ bool removeOne(const QByteArray &key);
+
+ bool clearData();
+
+ bool compact();
+ bool rollback();
+ void setAutoCompactRate(int rate) const;
+
+private:
+ Btree *mBtree;
+ JsonDbBtree(const JsonDbBtree&);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(JsonDbBtree::OpenFlags)
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_BTREE_H
diff --git a/src/partition/jsondbcollator.cpp b/src/partition/jsondbcollator.cpp
new file mode 100644
index 00000000..869bf3f8
--- /dev/null
+++ b/src/partition/jsondbcollator.cpp
@@ -0,0 +1,341 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 NO_COLLATION_SUPPORT
+
+#include "jsondbcollator.h"
+#include "jsondbcollator_p.h"
+
+#include <unicode/utypes.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbCollatorPrivate::~JsonDbCollatorPrivate()
+{
+ if (collator)
+ ucol_close(collator);
+}
+
+static const int collationStringsCount = 13;
+static const char * const collationStrings[collationStringsCount] = {
+ "default",
+ "big5han",
+ "dict",
+ "direct",
+ "gb2312",
+ "phonebk",
+ "pinyin",
+ "phonetic",
+ "reformed",
+ "standard",
+ "stroke",
+ "trad",
+ "unihan"
+};
+
+JsonDbCollator::JsonDbCollator(const QLocale &locale, JsonDbCollator::Collation collation)
+ : d(new JsonDbCollatorPrivate)
+{
+ d->locale = locale;
+ if ((int)collation >= 0 && (int)collation < collationStringsCount)
+ d->collation = collation;
+
+ init();
+}
+
+JsonDbCollator::JsonDbCollator(const JsonDbCollator &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+JsonDbCollator::~JsonDbCollator()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+JsonDbCollator &JsonDbCollator::operator=(const JsonDbCollator &other)
+{
+ if (this != &other) {
+ if (!d->ref.deref())
+ delete d;
+ *d = *other.d;
+ d->ref.ref();
+ }
+ return *this;
+}
+
+void JsonDbCollator::setLocale(const QLocale &locale)
+{
+ if (d->ref.load() != 1)
+ detach();
+ if (d->collator)
+ ucol_close(d->collator);
+ d->locale = locale;
+
+ init();
+}
+
+QLocale JsonDbCollator::locale() const
+{
+ return d->locale;
+}
+
+void JsonDbCollator::setCollation(JsonDbCollator::Collation collation)
+{
+ if ((int)collation < 0 || (int)collation >= collationStringsCount)
+ return;
+
+ if (d->ref.load() != 1)
+ detach();
+ if (d->collator)
+ ucol_close(d->collator);
+ d->collation = collation;
+
+ init();
+}
+
+JsonDbCollator::Collation JsonDbCollator::collation() const
+{
+ return d->collation;
+}
+
+void JsonDbCollator::init()
+{
+ Q_ASSERT((int)d->collation < collationStringsCount);
+ const char *collationString = collationStrings[(int)d->collation];
+ UErrorCode status = U_ZERO_ERROR;
+ QByteArray name = (d->locale.bcp47Name().replace(QLatin1Char('-'), QLatin1Char('_')) + QLatin1String("@collation=") + QLatin1String(collationString)).toLatin1();
+ d->collator = ucol_open(name.constData(), &status);
+ if (U_FAILURE(status))
+ qWarning("Could not create collator: %d", status);
+
+ // enable normalization by default
+ ucol_setAttribute(d->collator, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
+
+ // fetch options from the collator
+ d->options = 0;
+
+ switch (ucol_getAttribute(d->collator, UCOL_CASE_FIRST, &status)) {
+ case UCOL_UPPER_FIRST: d->options |= JsonDbCollator::PreferUpperCase; break;
+ case UCOL_LOWER_FIRST: d->options |= JsonDbCollator::PreferLowerCase; break;
+ case UCOL_OFF:
+ default:
+ break;
+ }
+
+ switch (ucol_getAttribute(d->collator, UCOL_FRENCH_COLLATION, &status)) {
+ case UCOL_ON: d->options |= JsonDbCollator::FrenchCollation; break;
+ case UCOL_OFF:
+ default:
+ break;
+ }
+
+ switch (ucol_getAttribute(d->collator, UCOL_ALTERNATE_HANDLING, &status)) {
+ case UCOL_SHIFTED: d->options |= JsonDbCollator::IgnorePunctuation; break;
+ case UCOL_NON_IGNORABLE:
+ default:
+ break;
+ }
+
+
+ switch (ucol_getAttribute(d->collator, UCOL_CASE_LEVEL, &status)) {
+ case UCOL_ON: d->options |= JsonDbCollator::ExtraCaseLevel; break;
+ case UCOL_OFF:
+ default:
+ break;
+ }
+
+ switch (ucol_getAttribute(d->collator, UCOL_HIRAGANA_QUATERNARY_MODE, &status)) {
+ case UCOL_ON: d->options |= JsonDbCollator::HiraganaQuaternaryMode; break;
+ case UCOL_OFF:
+ default:
+ break;
+ }
+
+ switch (ucol_getAttribute(d->collator, UCOL_NUMERIC_COLLATION, &status)) {
+ case UCOL_ON: d->options |= JsonDbCollator::NumericMode; break;
+ case UCOL_OFF:
+ default:
+ break;
+ }
+}
+
+void JsonDbCollator::detach()
+{
+ if (d->ref.load() != 1) {
+ JsonDbCollatorPrivate *x = new JsonDbCollatorPrivate;
+ x->ref.store(1);
+ x->strength = d->strength;
+ x->options = d->options;
+ x->modified = true;
+ x->collator = 0;
+ if (!d->ref.deref())
+ delete d;
+ d = x;
+ }
+}
+
+void JsonDbCollator::setStrength(JsonDbCollator::Strength strength)
+{
+ if (d->ref.load() != 1)
+ detach();
+
+ switch (strength) {
+ case JsonDbCollator::PrimaryStrength: ucol_setStrength(d->collator, UCOL_PRIMARY); break;
+ case JsonDbCollator::SecondaryStrength: ucol_setStrength(d->collator, UCOL_SECONDARY); break;
+ case JsonDbCollator::TertiaryStrength: ucol_setStrength(d->collator, UCOL_TERTIARY); break;
+ case JsonDbCollator::QuaternaryStrength: ucol_setStrength(d->collator, UCOL_QUATERNARY); break;
+ case JsonDbCollator::IdenticalStrength: ucol_setStrength(d->collator, UCOL_IDENTICAL); break;
+ default:
+ qWarning("Invalid strength mode %d", strength);
+ }
+}
+
+JsonDbCollator::Strength JsonDbCollator::strength() const
+{
+ switch (ucol_getStrength(d->collator)) {
+ case UCOL_PRIMARY: return JsonDbCollator::PrimaryStrength;
+ case UCOL_SECONDARY: return JsonDbCollator::SecondaryStrength;
+ case UCOL_QUATERNARY:return JsonDbCollator::QuaternaryStrength;
+ case UCOL_IDENTICAL: return JsonDbCollator::IdenticalStrength;
+ case UCOL_TERTIARY:
+ default:
+ return JsonDbCollator::TertiaryStrength;
+ }
+ return JsonDbCollator::TertiaryStrength;
+}
+
+void JsonDbCollator::setOptions(JsonDbCollator::Options options)
+{
+ if (d->ref.load() != 1)
+ detach();
+
+ d->options = options;
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ if (options & JsonDbCollator::PreferUpperCase)
+ ucol_setAttribute(d->collator, UCOL_CASE_FIRST, UCOL_UPPER_FIRST, &status);
+ else if (options & JsonDbCollator::PreferLowerCase)
+ ucol_setAttribute(d->collator, UCOL_CASE_FIRST, UCOL_LOWER_FIRST, &status);
+ else
+ ucol_setAttribute(d->collator, UCOL_CASE_FIRST, UCOL_OFF, &status);
+ if (U_FAILURE(status))
+ qWarning("ucol_setAttribute: Case First failed: %d", status);
+
+ ucol_setAttribute(d->collator, UCOL_FRENCH_COLLATION,
+ options & JsonDbCollator::FrenchCollation ? UCOL_ON : UCOL_OFF, &status);
+ if (U_FAILURE(status))
+ qWarning("ucol_setAttribute: French collation failed: %d", status);
+
+ ucol_setAttribute(d->collator, UCOL_NORMALIZATION_MODE,
+ options & JsonDbCollator::DisableNormalization ? UCOL_OFF : UCOL_ON, &status);
+ if (U_FAILURE(status))
+ qWarning("ucol_setAttribute: Normalization mode failed: %d", status);
+
+ ucol_setAttribute(d->collator, UCOL_ALTERNATE_HANDLING,
+ (options & JsonDbCollator::IgnorePunctuation) ? UCOL_SHIFTED : UCOL_NON_IGNORABLE, &status);
+ if (U_FAILURE(status))
+ qWarning("ucol_setAttribute: Alternate handling failed: %d", status);
+
+ ucol_setAttribute(d->collator, UCOL_CASE_LEVEL,
+ options & JsonDbCollator::ExtraCaseLevel ? UCOL_ON : UCOL_OFF, &status);
+ if (U_FAILURE(status))
+ qWarning("ucol_setAttribute: Case level failed: %d", status);
+
+ ucol_setAttribute(d->collator, UCOL_HIRAGANA_QUATERNARY_MODE,
+ options & JsonDbCollator::HiraganaQuaternaryMode ? UCOL_ON : UCOL_OFF, &status);
+ if (U_FAILURE(status))
+ qWarning("ucol_setAttribute: hiragana mode failed: %d", status);
+
+ ucol_setAttribute(d->collator, UCOL_NUMERIC_COLLATION,
+ options & JsonDbCollator::NumericMode ? UCOL_ON : UCOL_OFF, &status);
+ if (U_FAILURE(status))
+ qWarning("ucol_setAttribute: numeric collation failed: %d", status);
+}
+
+JsonDbCollator::Options JsonDbCollator::options() const
+{
+ return d->options;
+}
+
+int JsonDbCollator::compare(const QString &s1, const QString &s2) const
+{
+ return d->compare((ushort *)s1.constData(), s1.size(), (ushort *)s2.constData(), s2.size());
+}
+
+int JsonDbCollator::compare(const QStringRef &s1, const QStringRef &s2) const
+{
+ return d->compare((ushort *)s1.constData(), s1.size(), (ushort *)s2.constData(), s2.size());
+}
+
+int JsonDbCollatorPrivate::compare(ushort *s1, int len1, ushort *s2, int len2)
+{
+ const UCollationResult result =
+ ucol_strcoll(collator, (const UChar *)s1, len1, (const UChar *)s2, len2);
+ return result;
+}
+
+QByteArray JsonDbCollator::sortKey(const QString &string) const
+{
+ QByteArray result(16 + string.size() + (string.size() >> 2), Qt::Uninitialized);
+ int size = ucol_getSortKey(d->collator, (const UChar *)string.constData(),
+ string.size(), (uint8_t *)result.data(), result.size());
+ if (size > result.size()) {
+#ifdef _DEBUG
+ qDebug() << "### sortKey realloc from" << result.size() << "to" << size << "for string" << string.size();
+#endif
+ result.resize(size);
+ size = ucol_getSortKey(d->collator, (const UChar *)string.constData(),
+ string.size(), (uint8_t *)result.data(), result.size());
+ }
+ result.truncate(size);
+ return result;
+}
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+#endif // NO_COLLATION_SUPPORT
diff --git a/src/partition/jsondbcollator.h b/src/partition/jsondbcollator.h
new file mode 100644
index 00000000..d23ea1d1
--- /dev/null
+++ b/src/partition/jsondbcollator.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDBCOLLATOR_H
+#define JSONDBCOLLATOR_H
+
+#ifndef NO_COLLATION_SUPPORT
+
+#include <QString>
+#include <QLocale>
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbCollatorPrivate;
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbCollator
+{
+public:
+ enum Strength {
+ PrimaryStrength = 1,
+ BaseLetterStrength = PrimaryStrength,
+
+ SecondaryStrength = 2,
+ AccentsStrength = SecondaryStrength,
+
+ TertiaryStrength = 3,
+ CaseStrength = TertiaryStrength,
+
+ QuaternaryStrength = 4,
+ PunctuationStrength = QuaternaryStrength,
+
+ IdenticalStrength = 5,
+ CodepointStrength = IdenticalStrength
+ };
+
+ enum Option {
+ PreferUpperCase = 0x01,
+ PreferLowerCase = 0x02,
+ FrenchCollation = 0x04,
+ DisableNormalization = 0x08,
+ IgnorePunctuation = 0x10,
+ ExtraCaseLevel = 0x20,
+ HiraganaQuaternaryMode = 0x40,
+ NumericMode = 0x80
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ enum Collation {
+ Default,
+ Big5Han,
+ Dictionary,
+ Direct,
+ GB2312Han,
+ PhoneBook,
+ Pinyin,
+ Phonetic,
+ Reformed,
+ Standard,
+ Stroke,
+ Traditional,
+ UniHan
+ };
+
+ JsonDbCollator(const QLocale &locale = QLocale(), JsonDbCollator::Collation collation = JsonDbCollator::Default);
+ JsonDbCollator(const JsonDbCollator &);
+ ~JsonDbCollator();
+ JsonDbCollator &operator=(const JsonDbCollator &);
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ void setCollation(Collation collation);
+ Collation collation() const;
+
+ void setStrength(Strength);
+ Strength strength() const;
+
+ void setOptions(Options);
+ Options options() const;
+
+ enum CasePreference {
+ IgnoreCase = 0x0,
+ UpperCase = 0x1,
+ LowerCase = 0x2
+ };
+
+ bool isCaseSensitive() const
+ { return options() & (PreferUpperCase | PreferLowerCase); }
+ CasePreference casePreference() const
+ {
+ int value = options() & 0x3;
+ if (value == 3)
+ return UpperCase;
+ return CasePreference(value);
+ }
+ void setCasePreference(CasePreference c)
+ {
+ Options o = options() & ~(PreferUpperCase | PreferLowerCase);
+ if (c == UpperCase)
+ o |= PreferUpperCase;
+ else if (c == LowerCase)
+ o |= PreferLowerCase;
+ setOptions(o);
+ }
+
+ void setNumericMode(bool on)
+ { setOptions(on ? options() | NumericMode : options() & ~NumericMode); }
+ bool numericMode() const
+ { return options() & NumericMode; }
+
+ int compare(const QString &s1, const QString &s2) const;
+ int compare(const QStringRef &s1, const QStringRef &s2) const;
+ bool operator()(const QString &s1, const QString &s2) const
+ { return compare(s1, s2) < 0; }
+
+ QByteArray sortKey(const QString &string) const;
+
+private:
+ JsonDbCollatorPrivate *d;
+
+ void detach();
+ void init();
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // NO_COLLATION_SUPPORT
+
+#endif // JSONDBCOLLATOR_H
diff --git a/src/partition/jsondbcollator_p.h b/src/partition/jsondbcollator_p.h
new file mode 100644
index 00000000..1ea44fd7
--- /dev/null
+++ b/src/partition/jsondbcollator_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDBCOLLATOR_P_H
+#define JSONDBCOLLATOR_P_H
+
+#ifndef NO_COLLATION_SUPPORT
+
+#include "jsondbcollator.h"
+
+#include <unicode/utypes.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbCollatorPrivate
+{
+public:
+ QAtomicInt ref;
+ bool modified;
+ QLocale locale;
+ JsonDbCollator::Collation collation;
+ JsonDbCollator::Strength strength;
+ JsonDbCollator::Options options;
+
+ UCollator *collator;
+
+ JsonDbCollatorPrivate()
+ : modified(true),
+ collation(JsonDbCollator::Default),
+ strength(JsonDbCollator::TertiaryStrength),
+ options(0),
+ collator(0)
+ { ref.store(1); }
+ ~JsonDbCollatorPrivate();
+
+ int compare(ushort *s1, int len1, ushort *s2, int len2);
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // NO_COLLATION_SUPPORT
+
+#endif // JSONDBCOLLATOR_P_H
diff --git a/src/partition/jsondberrors.cpp b/src/partition/jsondberrors.cpp
new file mode 100644
index 00000000..3a66ab49
--- /dev/null
+++ b/src/partition/jsondberrors.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbpartitionglobal.h"
+#include "jsondberrors.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+/*!
+ \class JsonDbError
+ \brief The JsonDbError class lists possible error codes.
+ \sa JsonDbError::ErrorCode
+ */
+
+/*!
+ \enum JsonDbError::ErrorCode
+ \omitvalue NoError
+ \value InvalidMessage
+ Unable to parse the query message.
+ \value InvalidRequest
+ Request object doesn't contain correct elements.
+ \value MissingObject
+ Invalid or missing "object" field.
+ \value DatabaseError
+ Error directly from the database.
+ \value MissingUUID
+ Missing id field.
+ \value MissingType
+ Missing _type field.
+ \value MissingQuery
+ Missing query field.
+ \value InvalidLimit
+ Invalid limit field.
+ \value InvalidOffset
+ Invalid offset field.
+ \value MismatchedNotifyId
+ Request to delete notify doesn't match existing notification.
+ \value InvalidActions
+ List of actions supplied to setNotification is invalid.
+ \value UpdatingStaleVersion
+ Updating stale version of object.
+ \value OperationNotPermitted
+ Operation prohibited by access control policy.
+ \value QuotaExceeded
+ Operation would exceed quota.
+ \value FailedSchemaValidation
+ Object to be created/updated was invalid according to the schema.
+ \value InvalidMap
+ The Map definition is invalid.
+ \value InvalidReduce
+ The Reduce definition is invalid.
+ \value InvalidSchemaOperation
+ Attempted to create a schema that already exists or to remove a schema when there are still objects belonging to the schema's type.
+ \value InvalidPartition
+ Invalid partition.
+ \value InvalidIndexOperation
+ An error when creating an index object
+ */
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondberrors.h b/src/partition/jsondberrors.h
new file mode 100644
index 00000000..482d25e3
--- /dev/null
+++ b/src/partition/jsondberrors.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_ERRORS_H
+#define JSONDB_ERRORS_H
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbError {
+public:
+ enum ErrorCode {
+ // common errors
+ NoError = 0,
+ InvalidRequest = 1,
+ OperationNotPermitted = 2,
+ InvalidPartition = 3,
+ DatabaseConnectionError = 4,
+
+ // read / notify errors
+ MissingQuery = 5,
+ InvalidMessage= 6,
+ InvalidLimit = 7,
+ InvalidOffset = 8,
+ InvalidStateNumber = 9,
+
+ // write errors
+ MissingObject = 10,
+ DatabaseError = 11,
+ MissingUUID = 12,
+ MissingType = 13,
+ UpdatingStaleVersion = 14,
+ QuotaExceeded = 15,
+ FailedSchemaValidation = 16,
+ InvalidMap = 17,
+ InvalidReduce = 18,
+ InvalidSchemaOperation = 19,
+ InvalidIndexOperation = 20,
+ InvalidType = 21
+ };
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_ERRORS_H
diff --git a/src/partition/jsondbindex.cpp b/src/partition/jsondbindex.cpp
new file mode 100644
index 00000000..568fcdf3
--- /dev/null
+++ b/src/partition/jsondbindex.cpp
@@ -0,0 +1,607 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QObject>
+#include <QByteArray>
+#include <QVariant>
+#include <QFile>
+#include <QFileInfo>
+#include <QDir>
+#include <QLocale>
+
+#include "jsondbstrings.h"
+#include "jsondbproxy.h"
+#include "jsondbindex.h"
+#include "jsondbbtree.h"
+#include "jsondbsettings.h"
+#include "jsondbobjecttable.h"
+#include "jsondbscriptengine.h"
+#include "qbtree.h"
+#include "qbtreecursor.h"
+#include "qbtreetxn.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+static const int collationStringsCount = 13;
+static const char * const collationStrings[collationStringsCount] = {
+ "default",
+ "big5han",
+ "dict",
+ "direct",
+ "gb2312",
+ "phonebk",
+ "pinyin",
+ "phonetic",
+ "reformed",
+ "standard",
+ "stroke",
+ "trad",
+ "unihan"
+};
+
+static const int casePreferenceStringsCount = 3;
+static const char * const casePreferenceStrings[casePreferenceStringsCount] = {
+ "IgnoreCase",
+ "PreferUpperCase",
+ "PreferLowerCase"
+};
+
+#ifndef NO_COLLATION_SUPPORT
+JsonDbCollator::Collation _q_correctCollationString(const QString &s)
+{
+ for (int i = 0; i < collationStringsCount; ++i) {
+ if (s == collationStrings[i])
+ return JsonDbCollator::Collation(i);
+ }
+ return JsonDbCollator::Default;
+}
+JsonDbCollator::CasePreference _q_correctCasePreferenceString(const QString &s)
+{
+ for (int i = 0; i < casePreferenceStringsCount; ++i) {
+ if (s == casePreferenceStrings[i])
+ return JsonDbCollator::CasePreference(i);
+ }
+ return JsonDbCollator::IgnoreCase;
+}
+#else
+int _q_correctCollationString(const QString &s)
+{
+ Q_UNUSED(s);
+ return 0;
+}
+int _q_correctCasePreferenceString(const QString &s)
+{
+ Q_UNUSED(s);
+ return 0;
+}
+#endif //NO_COLLATION_SUPPORT
+
+QString _q_bytesToHexString(const QByteArray &ba)
+{
+ static const ushort digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ uint len = ba.size();
+ const char *data = ba.constData();
+ uint idx = 0;
+
+ QString result(len*2, Qt::Uninitialized);
+ QChar *resultData = result.data();
+
+ for (uint i = 0; i < len; ++i, idx += 2) {
+ uint j = (data[i] >> 4) & 0xf;
+ resultData[idx] = QChar(digits[j]);
+ j = data[i] & 0xf;
+ resultData[idx+1] = QChar(digits[j]);
+ }
+
+ return result;
+}
+
+class JsonDbIndexCursor
+{
+public:
+ JsonDbIndexCursor(JsonDbIndex *index);
+ ~JsonDbIndexCursor();
+
+ bool seek(const QJsonValue &value);
+ bool seekRange(const QJsonValue &value);
+
+ bool first();
+ bool current(QJsonValue &key, ObjectKey &value);
+ bool currentKey(QJsonValue &key);
+ bool currentValue(ObjectKey &value);
+ bool next();
+ bool prev();
+
+private:
+ bool isOwnTransaction;
+ JsonDbBtree::Transaction *mTxn;
+ JsonDbBtree::Cursor mCursor;
+ JsonDbIndex *mIndex;
+
+ JsonDbIndexCursor(const JsonDbIndexCursor&);
+};
+
+JsonDbIndex::JsonDbIndex(const QString &fileName, const QString &indexName, const QString &propertyName,
+ const QString &propertyType, const QStringList &objectType, const QString &locale, const QString &collation,
+ const QString &casePreference, Qt::CaseSensitivity caseSensitivity, JsonDbObjectTable *objectTable)
+ : QObject(objectTable)
+ , mObjectTable(objectTable)
+ , mIndexName(indexName)
+ , mPropertyName(propertyName)
+ , mPath(propertyName.split('.'))
+ , mPropertyType(propertyType)
+ , mObjectType(objectType)
+ , mLocale(locale)
+ , mCollation(collation)
+ , mCasePreference(casePreference)
+ , mCaseSensitivity(caseSensitivity)
+#ifndef NO_COLLATION_SUPPORT
+ , mCollator(JsonDbCollator(QLocale(locale), _q_correctCollationString(collation)))
+#endif
+ , mStateNumber(0)
+ , mBdb(0)
+ , mScriptEngine(0)
+ , mCacheSize(0)
+{
+ QFileInfo fi(fileName);
+ QString dirName = fi.dir().path();
+ QString baseName = fi.fileName();
+ if (baseName.endsWith(".db"))
+ baseName.chop(3);
+ mFileName = QString("%1/%2-%3-Index.db").arg(dirName).arg(baseName).arg(indexName);
+#ifndef NO_COLLATION_SUPPORT
+ mCollator.setCasePreference(_q_correctCasePreferenceString(mCasePreference));
+#endif
+}
+
+JsonDbIndex::~JsonDbIndex()
+{
+ if (mBdb) {
+ close();
+ mBdb.reset();
+ }
+}
+
+bool JsonDbIndex::setPropertyFunction(const QString &propertyFunction)
+{
+ if (!mScriptEngine)
+ mScriptEngine = JsonDbScriptEngine::scriptEngine();
+
+ // for "emit"
+ JsonDbJoinProxy *mapProxy = new JsonDbJoinProxy(0, 0, this);
+ connect(mapProxy, SIGNAL(viewObjectEmitted(QJSValue)),
+ this, SLOT(propertyValueEmitted(QJSValue)));
+ QString proxyName(QString("_jsondbIndexProxy%1").arg(mIndexName));
+ proxyName.replace(".", "$");
+ mScriptEngine->globalObject().setProperty(proxyName, mScriptEngine->newQObject(mapProxy));
+
+ QString script(QString("(function() { var jsondb={emit: %2.create, lookup: %2.lookup }; var fcn = (%1); return fcn})()").arg(propertyFunction).arg(proxyName));
+ mPropertyFunction = mScriptEngine->evaluate(script);
+ if (mPropertyFunction.isError() || !mPropertyFunction.isCallable()) {
+ qDebug() << "Unable to parse index value function: " << mPropertyFunction.toString();
+ return false;
+ }
+
+ return true;
+}
+
+bool JsonDbIndex::open()
+{
+ if (mPropertyName == JsonDbString::kUuidStr)
+ return true;
+
+ mBdb.reset(new JsonDbBtree());
+
+ if (mCacheSize)
+ mBdb->setCacheSize(mCacheSize);
+
+ if (!mBdb->open(mFileName, JsonDbBtree::Default)) {
+ qCritical() << "mBdb->open" << mBdb->errorMessage();
+ return false;
+ }
+
+ mBdb->setCompareFunction(forwardKeyCmp);
+
+ mStateNumber = mBdb->tag();
+ if (jsondbSettings->debug() && jsondbSettings->verbose())
+ qDebug() << "JsonDbIndex::open" << mStateNumber << mFileName;
+ return true;
+}
+
+void JsonDbIndex::close()
+{
+ if (mBdb)
+ mBdb->close();
+}
+
+/*!
+ Returns true if the index's btree file exists.
+*/
+bool JsonDbIndex::exists() const
+{
+ QFile file(mFileName);
+ return file.exists();
+}
+
+bool JsonDbIndex::validateIndex(const JsonDbObject &newIndex, const JsonDbObject &oldIndex, QString &message)
+{
+ message.clear();
+
+ if (!newIndex.isEmpty() && !oldIndex.isEmpty() && oldIndex.type() == JsonDbString::kIndexTypeStr) {
+ if (oldIndex.value(JsonDbString::kPropertyNameStr).toString() != newIndex.value(JsonDbString::kPropertyNameStr).toString())
+ message = QString("Changing old index propertyName '%1' to '%2' not supported")
+ .arg(oldIndex.value(JsonDbString::kPropertyNameStr).toString())
+ .arg(newIndex.value(JsonDbString::kPropertyNameStr).toString());
+ else if (oldIndex.value(JsonDbString::kPropertyTypeStr).toString() != newIndex.value(JsonDbString::kPropertyTypeStr).toString())
+ message = QString("Changing old index propertyType from '%1' to '%2' not supported")
+ .arg(oldIndex.value(JsonDbString::kPropertyTypeStr).toString())
+ .arg(newIndex.value(JsonDbString::kPropertyTypeStr).toString());
+ else if (oldIndex.value(JsonDbString::kObjectTypeStr) != newIndex.value(JsonDbString::kObjectTypeStr))
+ message = QString("Changing old index objectType from '%1' to '%2' not supported")
+ .arg(oldIndex.value(JsonDbString::kObjectTypeStr).toString())
+ .arg(newIndex.value(JsonDbString::kObjectTypeStr).toString());
+ else if (oldIndex.value(JsonDbString::kPropertyFunctionStr).toString() != newIndex.value(JsonDbString::kPropertyFunctionStr).toString())
+ message = QString("Changing old index propertyFunction from '%1' to '%2' not supported")
+ .arg(oldIndex.value(JsonDbString::kPropertyFunctionStr).toString())
+ .arg(newIndex.value(JsonDbString::kPropertyFunctionStr).toString());
+ }
+
+ if (!(newIndex.contains(JsonDbString::kPropertyFunctionStr) ^ newIndex.contains(JsonDbString::kPropertyNameStr)))
+ message = QString("Index object must have one of propertyName or propertyFunction set");
+ else if (newIndex.contains(JsonDbString::kPropertyFunctionStr) && !newIndex.contains(JsonDbString::kNameStr))
+ message = QString("Index object with propertyFunction must have name");
+
+ return message.isEmpty();
+}
+
+QString JsonDbIndex::determineName(const JsonDbObject &index)
+{
+ QString indexName = index.value(JsonDbString::kNameStr).toString();
+ QString propertyName = index.value(JsonDbString::kPropertyNameStr).toString();
+
+ if (indexName.isEmpty())
+ return propertyName;
+ return indexName;
+}
+
+JsonDbBtree *JsonDbIndex::bdb()
+{
+ if (!mBdb)
+ open();
+ return mBdb.data();
+}
+
+QJsonValue JsonDbIndex::indexValue(const QJsonValue &v)
+{
+ if (!v.isString())
+ return v;
+
+ QJsonValue result;
+ if (mCaseSensitivity == Qt::CaseInsensitive)
+ result = v.toString().toLower();
+ else
+ result = v;
+
+#ifndef NO_COLLATION_SUPPORT
+ if (!mCollation.isEmpty() && !mLocale.isEmpty())
+ result = _q_bytesToHexString(mCollator.sortKey(v.toString()));
+#endif
+
+ return result;
+}
+
+QList<QJsonValue> JsonDbIndex::indexValues(JsonDbObject &object)
+{
+ mFieldValues.clear();
+ if (!mScriptEngine) {
+ int size = mPath.size();
+ if (mPath[size-1] == QString("*")) {
+ QJsonValue v = object.propertyLookup(mPath.mid(0, size-1));
+ QJsonArray array = v.toArray();
+ mFieldValues.reserve(array.size());
+ for (int i = 0; i < array.size(); ++i) {
+ mFieldValues.append(indexValue(array.at(i)));
+ }
+ } else {
+ QJsonValue v = object.propertyLookup(mPath);
+ if (!v.isUndefined()) {
+ mFieldValues.append(indexValue(v));
+ }
+ }
+ } else {
+ QJSValueList args;
+ args << mScriptEngine->toScriptValue(object.toVariantMap());
+ mPropertyFunction.call(args);
+ }
+ return mFieldValues;
+}
+
+void JsonDbIndex::propertyValueEmitted(QJSValue value)
+{
+ if (!value.isUndefined())
+ mFieldValues.append(JsonDbScriptEngine::fromJSValue(value));
+}
+
+void JsonDbIndex::indexObject(const ObjectKey &objectKey, JsonDbObject &object, quint32 stateNumber)
+{
+ if (mPropertyName == JsonDbString::kUuidStr)
+ return;
+
+ if (!mObjectType.isEmpty() && !mObjectType.contains(object.value(JsonDbString::kTypeStr).toString()))
+ return;
+
+ Q_ASSERT(!object.contains(JsonDbString::kDeletedStr)
+ && !object.value(JsonDbString::kDeletedStr).toBool());
+ QList<QJsonValue> fieldValues = indexValues(object);
+ if (!fieldValues.size())
+ return;
+ bool ok;
+ if (!mBdb)
+ open();
+ if (!mBdb->isWriting())
+ mObjectTable->begin(this);
+ JsonDbBtree::Transaction *txn = mBdb->writeTransaction();
+ for (int i = 0; i < fieldValues.size(); i++) {
+ QJsonValue fieldValue = fieldValues.at(i);
+ fieldValue = makeFieldValue(fieldValue, mPropertyType);
+ if (fieldValue.isUndefined())
+ continue;
+ QByteArray forwardKey(makeForwardKey(fieldValue, objectKey));
+ QByteArray forwardValue(makeForwardValue(objectKey));
+
+ if (jsondbSettings->debug())
+ qDebug() << "indexing" << objectKey << mPropertyName << fieldValue
+ << "forwardIndex" << "key" << forwardKey.toHex()
+ << "forwardIndex" << "value" << forwardValue.toHex()
+ << object;
+ ok = txn->put(forwardKey, forwardValue);
+ if (!ok) qCritical() << __FUNCTION__ << "putting fowardIndex" << mBdb->errorMessage();
+ }
+ if (jsondbSettings->debug() && (stateNumber < mStateNumber))
+ qDebug() << "JsonDbIndex::indexObject" << "stale update" << stateNumber << mStateNumber << mFileName;
+ mStateNumber = qMax(stateNumber, mStateNumber);
+
+#ifdef CHECK_INDEX_ORDERING
+ checkIndex()
+#endif
+}
+
+void JsonDbIndex::deindexObject(const ObjectKey &objectKey, JsonDbObject &object, quint32 stateNumber)
+{
+ if (mPropertyName == JsonDbString::kUuidStr)
+ return;
+ if (!mObjectType.isEmpty() && !mObjectType.contains(object.value(JsonDbString::kTypeStr).toString()))
+ return;
+ if (!mBdb)
+ open();
+ QList<QJsonValue> fieldValues = indexValues(object);
+ if (!fieldValues.size())
+ return;
+ if (!mBdb->isWriting())
+ mObjectTable->begin(this);
+ JsonDbBtree::Transaction *txn = mBdb->writeTransaction();
+ for (int i = 0; i < fieldValues.size(); i++) {
+ QJsonValue fieldValue = fieldValues.at(i);
+ fieldValue = makeFieldValue(fieldValue, mPropertyType);
+ if (fieldValue.isUndefined())
+ continue;
+ if (jsondbSettings->debug())
+ qDebug() << "deindexing" << objectKey << mPropertyName << fieldValue;
+ QByteArray forwardKey(makeForwardKey(fieldValue, objectKey));
+ if (!txn->remove(forwardKey)) {
+ qDebug() << "deindexing failed" << objectKey << mPropertyName << fieldValue << object << forwardKey.toHex();
+ }
+ }
+ if (jsondbSettings->verbose() && (stateNumber < mStateNumber))
+ qDebug() << "JsonDbIndex::deindexObject" << "stale update" << stateNumber << mStateNumber << mFileName;
+#ifdef CHECK_INDEX_ORDERING
+ checkIndex();
+#endif
+}
+
+quint32 JsonDbIndex::stateNumber() const
+{
+ return mStateNumber;
+}
+
+JsonDbBtree::Transaction *JsonDbIndex::begin()
+{
+ if (!mBdb)
+ open();
+ return mBdb->beginWrite();
+}
+bool JsonDbIndex::commit(quint32 stateNumber)
+{
+ if (mBdb->isWriting())
+ mBdb->writeTransaction()->commit(stateNumber);
+ return false;
+}
+bool JsonDbIndex::abort()
+{
+ if (mBdb->isWriting())
+ mBdb->writeTransaction()->abort();
+ return true;
+}
+bool JsonDbIndex::clearData()
+{
+ return mBdb->clearData();
+}
+
+void JsonDbIndex::checkIndex()
+{
+ if (mPropertyName == JsonDbString::kUuidStr)
+ return;
+
+ qDebug() << "checkIndex" << mPropertyName;
+ int countf = 0;
+ bool isInTransaction = mBdb.data()->btree()->writeTransaction();
+ JsonDbBtree::Transaction *txnf = mBdb.data()->btree()->writeTransaction() ? mBdb.data()->btree()->writeTransaction() : mBdb.data()->btree()->beginWrite();
+ JsonDbBtree::Cursor cursorf(txnf);
+ bool ok = cursorf.first();
+ if (ok) {
+ countf++;
+ QByteArray outkey1;
+ ok = cursorf.current(&outkey1, 0);
+ while (cursorf.next()) {
+ countf++;
+ QByteArray outkey2;
+ cursorf.current(&outkey2, 0);
+ //qDebug() << outkey1.toHex() << outkey2.toHex();
+ if (memcmp(outkey1.constData(), outkey2.constData(), outkey1.size()) >= 0) {
+ qDebug() << "out of order index" << mPropertyName << endl
+ << outkey1.toHex() << endl
+ << outkey2.toHex() << endl;
+ }
+ outkey1 = outkey2;
+ }
+ }
+ if (!isInTransaction)
+ txnf->abort();
+
+ qDebug() << "checkIndex" << mPropertyName << "reversed";
+ // now check other direction
+ int countr = 0;
+ isInTransaction = mBdb.data()->btree()->writeTransaction();
+ JsonDbBtree::Transaction *txnr = mBdb.data()->btree()->writeTransaction() ? mBdb.data()->btree()->writeTransaction() : mBdb.data()->btree()->beginWrite();
+ JsonDbBtree::Cursor cursorr(txnr);
+ ok = cursorr.last();
+ if (ok) {
+ countr++;
+ QByteArray outkey1;
+ ok = cursorr.current(&outkey1, 0);
+ while (cursorr.previous()) {
+ countr++;
+ QByteArray outkey2;
+ cursorr.current(&outkey2, 0);
+ //qDebug() << outkey1.toHex() << outkey2.toHex();
+ if (memcmp(outkey1.constData(), outkey2.constData(), outkey1.size()) <= 0) {
+ qDebug() << "reverse walk: out of order index" << mPropertyName << endl
+ << outkey1.toHex() << endl
+ << outkey2.toHex() << endl;
+ }
+ outkey1 = outkey2;
+ }
+ }
+ if (!isInTransaction)
+ txnr->abort();
+ qDebug() << "checkIndex" << mPropertyName << "done" << countf << countr << "entries checked";
+
+}
+
+void JsonDbIndex::setCacheSize(quint32 cacheSize)
+{
+ mCacheSize = cacheSize;
+ if (mBdb)
+ mBdb->setCacheSize(cacheSize);
+}
+
+JsonDbIndexCursor::JsonDbIndexCursor(JsonDbIndex *index)
+ : isOwnTransaction(!index->bdb()->writeTransaction()),
+ mTxn(isOwnTransaction ? index->bdb()->beginWrite() : index->bdb()->writeTransaction()),
+ mCursor(mTxn)
+{
+}
+
+JsonDbIndexCursor::~JsonDbIndexCursor()
+{
+ if (isOwnTransaction)
+ mTxn->abort();
+}
+
+bool JsonDbIndexCursor::seek(const QJsonValue &value)
+{
+ QByteArray forwardKey(makeForwardKey(makeFieldValue(value, mIndex->propertyType()), ObjectKey()));
+ return mCursor.seek(forwardKey);
+}
+
+bool JsonDbIndexCursor::seekRange(const QJsonValue &value)
+{
+ QByteArray forwardKey(makeForwardKey(makeFieldValue(value, mIndex->propertyType()), ObjectKey()));
+ return mCursor.seekRange(forwardKey);
+}
+
+bool JsonDbIndexCursor::current(QJsonValue &key, ObjectKey &value)
+{
+ QByteArray baKey, baValue;
+ if (!mCursor.current(&baKey, &baValue))
+ return false;
+ forwardKeySplit(baKey, key);
+ forwardValueSplit(baValue, value);
+ return true;
+}
+
+bool JsonDbIndexCursor::currentKey(QJsonValue &key)
+{
+ QByteArray baKey;
+ if (!mCursor.current(&baKey, 0))
+ return false;
+ forwardKeySplit(baKey, key);
+ return true;
+}
+
+bool JsonDbIndexCursor::currentValue(ObjectKey &value)
+{
+ QByteArray baValue;
+ if (!mCursor.current(0, &baValue))
+ return false;
+ forwardValueSplit(baValue, value);
+ return true;
+}
+
+bool JsonDbIndexCursor::first()
+{
+ return mCursor.first();
+}
+
+bool JsonDbIndexCursor::next()
+{
+ return mCursor.next();
+}
+
+bool JsonDbIndexCursor::prev()
+{
+ return mCursor.previous();
+}
+
+#include "moc_jsondbindex.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbindex.h b/src/partition/jsondbindex.h
new file mode 100644
index 00000000..c892723b
--- /dev/null
+++ b/src/partition/jsondbindex.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_INDEX_H
+#define JSONDB_INDEX_H
+
+#include <QObject>
+#include <QJSEngine>
+#include <QPointer>
+#include <QStringList>
+
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+#include "jsondbobject.h"
+
+#include "jsondbpartitionglobal.h"
+#include "jsondbobjectkey.h"
+#include "jsondbbtree.h"
+#include "jsondbcollator.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbPartition;
+class JsonDbObjectTable;
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbIndex : public QObject
+{
+ Q_OBJECT
+public:
+ JsonDbIndex(const QString &fileName, const QString &indexName, const QString &propertyName,
+ const QString &propertyType, const QStringList &objectType, const QString &locale, const QString &collation,
+ const QString &casePreference, Qt::CaseSensitivity caseSensitivity,
+ JsonDbObjectTable *objectTable);
+ ~JsonDbIndex();
+
+ QString propertyName() const { return mPropertyName; }
+ QStringList fieldPath() const { return mPath; }
+ QString propertyType() const { return mPropertyType; }
+ QStringList objectType() const { return mObjectType; }
+
+ JsonDbBtree *bdb();
+
+ bool setPropertyFunction(const QString &propertyFunction);
+ void indexObject(const ObjectKey &objectKey, JsonDbObject &object, quint32 stateNumber);
+ void deindexObject(const ObjectKey &objectKey, JsonDbObject &object, quint32 stateNumber);
+ QList<QJsonValue> indexValues(JsonDbObject &object);
+
+ quint32 stateNumber() const;
+
+ JsonDbBtree::Transaction *begin();
+ bool commit(quint32);
+ bool abort();
+ bool clearData();
+
+ void checkIndex();
+ void setCacheSize(quint32 cacheSize);
+ bool open();
+ void close();
+ bool exists() const;
+
+ static bool validateIndex(const JsonDbObject &newIndex, const JsonDbObject &oldIndex, QString &message);
+ static QString determineName(const JsonDbObject &index);
+
+private:
+ QJsonValue indexValue(const QJsonValue &v);
+
+private slots:
+ void propertyValueEmitted(QJSValue);
+
+private:
+ JsonDbObjectTable *mObjectTable;
+ QString mFileName;
+ QString mIndexName;
+ QString mPropertyName;
+ QStringList mPath;
+ QString mPropertyType;
+ QStringList mObjectType;
+ QString mLocale;
+ QString mCollation;
+ QString mCasePreference;
+ Qt::CaseSensitivity mCaseSensitivity;
+#ifndef NO_COLLATION_SUPPORT
+ JsonDbCollator mCollator;
+#endif
+ quint32 mStateNumber;
+ QScopedPointer<JsonDbBtree> mBdb;
+ QJSEngine *mScriptEngine;
+ QJSValue mPropertyFunction;
+ QList<QJsonValue> mFieldValues;
+ quint32 mCacheSize;
+};
+
+class IndexSpec {
+public:
+ QString name;
+ QString propertyName;
+ QStringList path;
+ QString propertyType;
+ QString locale;
+ QString collation;
+ QString casePreference;
+ Qt::CaseSensitivity caseSensitivity;
+ QStringList objectType;
+ bool lazy;
+ QPointer<JsonDbIndex> index;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_INDEX_H
diff --git a/src/partition/jsondbindexquery.cpp b/src/partition/jsondbindexquery.cpp
new file mode 100644
index 00000000..02086b1b
--- /dev/null
+++ b/src/partition/jsondbindexquery.cpp
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbindex.h"
+#include "jsondbindexquery.h"
+#include "jsondbobjecttable.h"
+#include "jsondbpartition.h"
+#include "jsondbsettings.h"
+#include "qbtree.h"
+#include "qbtreecursor.h"
+#include "qbtreetxn.h"
+
+#include <QJsonDocument>
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbIndexQuery *JsonDbIndexQuery::indexQuery(JsonDbPartition *partition, JsonDbObjectTable *table,
+ const QString &propertyName, const QString &propertyType,
+ const JsonDbOwner *owner, bool ascending)
+{
+ if (propertyName == JsonDbString::kUuidStr)
+ return new JsonDbUuidQuery(partition, table, propertyName, owner, ascending);
+ else
+ return new JsonDbIndexQuery(partition, table, propertyName, propertyType, owner, ascending);
+}
+
+JsonDbUuidQuery::JsonDbUuidQuery(JsonDbPartition *partition, JsonDbObjectTable *table, const QString &propertyName, const JsonDbOwner *owner, bool ascending)
+ : JsonDbIndexQuery(partition, table, propertyName, QString(), owner, ascending)
+{
+}
+
+JsonDbIndexQuery::JsonDbIndexQuery(JsonDbPartition *partition, JsonDbObjectTable *table,
+ const QString &propertyName, const QString &propertyType,
+ const JsonDbOwner *owner, bool ascending)
+ : mPartition(partition)
+ , mObjectTable(table)
+ , mBdbIndex(0)
+ , mCursor(0)
+ , mOwner(owner)
+ , mMin(QJsonValue::Undefined)
+ , mMax(QJsonValue::Undefined)
+ , mAscending(ascending)
+ , mPropertyName(propertyName)
+ , mPropertyType(propertyType)
+ , mSparseMatchPossible(false)
+ , mResidualQuery(0)
+{
+ if (propertyName != JsonDbString::kUuidStr) {
+ mBdbIndex = table->indexSpec(propertyName)->index->bdb();
+ isOwnTransaction = !mBdbIndex->writeTransaction();
+ mTxn = isOwnTransaction ? mBdbIndex->beginWrite() : mBdbIndex->writeTransaction();
+ mCursor = new JsonDbBtree::Cursor(mTxn);
+ } else {
+ isOwnTransaction = !table->bdb()->writeTransaction();
+ mTxn = isOwnTransaction ? table->bdb()->beginWrite() : table->bdb()->writeTransaction();
+ mCursor = new JsonDbBtree::Cursor(mTxn);
+ }
+}
+JsonDbIndexQuery::~JsonDbIndexQuery()
+{
+ delete mResidualQuery;
+ if (isOwnTransaction)
+ mTxn->abort();
+ delete mCursor;
+ for (int i = 0; i < mQueryConstraints.size(); i++)
+ delete mQueryConstraints[i];
+}
+
+QString JsonDbIndexQuery::partition() const
+{
+ return mPartition->name();
+}
+
+quint32 JsonDbIndexQuery::stateNumber() const
+{
+ return mBdbIndex->tag();
+}
+
+bool JsonDbIndexQuery::matches(const QJsonValue &fieldValue)
+{
+ for (int i = 0; i < mQueryConstraints.size(); i++) {
+ if (!mQueryConstraints[i]->matches(fieldValue))
+ return false;
+ }
+ return true;
+}
+
+void JsonDbIndexQuery::setMin(const QJsonValue &value)
+{
+ mMin = makeFieldValue(value, mPropertyType);
+}
+
+void JsonDbIndexQuery::setMax(const QJsonValue &value)
+{
+ mMax = makeFieldValue(value, mPropertyType);
+}
+
+bool JsonDbIndexQuery::seekToStart(QJsonValue &fieldValue)
+{
+ QByteArray forwardKey;
+ if (mAscending) {
+ forwardKey = makeForwardKey(mMin, ObjectKey());
+ if (jsondbSettings->debugQuery())
+ qDebug() << __FUNCTION__ << __LINE__ << "mMin" << mMin << "key" << forwardKey.toHex();
+ } else {
+ forwardKey = makeForwardKey(mMax, ObjectKey());
+ if (jsondbSettings->debugQuery())
+ qDebug() << __FUNCTION__ << __LINE__ << "mMax" << mMin << "key" << forwardKey.toHex();
+ }
+
+ bool ok = false;
+ if (mAscending) {
+ if (!mMin.isUndefined()) {
+ ok = mCursor->seekRange(forwardKey);
+ if (jsondbSettings->debugQuery())
+ qDebug() << "IndexQuery::first" << __LINE__ << "ok after seekRange" << ok;
+ }
+ if (!ok) {
+ ok = mCursor->first();
+ }
+ } else {
+ // need a seekDescending
+ ok = mCursor->last();
+ }
+ if (ok) {
+ QByteArray baKey;
+ mCursor->current(&baKey, 0);
+ forwardKeySplit(baKey, fieldValue);
+ }
+ //qDebug() << "IndexQuery::seekToStart" << (mAscending ? mMin : mMax) << "ok" << ok << fieldValue;
+ return ok;
+}
+
+bool JsonDbIndexQuery::seekToNext(QJsonValue &fieldValue)
+{
+ bool ok = mAscending ? mCursor->next() : mCursor->previous();
+ if (ok) {
+ QByteArray baKey;
+ mCursor->current(&baKey, 0);
+ forwardKeySplit(baKey, fieldValue);
+ }
+ //qDebug() << "IndexQuery::seekToNext" << "ok" << ok << fieldValue;
+ return ok;
+}
+
+JsonDbObject JsonDbIndexQuery::currentObjectAndTypeNumber(ObjectKey &objectKey)
+{
+ QByteArray baValue;
+ mCursor->current(0, &baValue);
+ forwardValueSplit(baValue, objectKey);
+
+ if (jsondbSettings->debugQuery())
+ qDebug() << __FILE__ << __LINE__ << "objectKey" << objectKey << baValue.toHex();
+ JsonDbObject object;
+ mObjectTable->get(objectKey, &object);
+ return object;
+}
+
+quint32 JsonDbUuidQuery::stateNumber() const
+{
+ return mObjectTable->stateNumber();
+}
+
+bool JsonDbUuidQuery::seekToStart(QJsonValue &fieldValue)
+{
+ bool ok;
+ if (mAscending) {
+ if (!mMin.isUndefined()) {
+ ObjectKey objectKey(mMin.toString());
+ ok = mCursor->seekRange(objectKey.toByteArray());
+ } else {
+ ok = mCursor->first();
+ }
+ } else {
+ if (!mMax.isUndefined()) {
+ ObjectKey objectKey(mMax.toString());
+ ok = mCursor->seekRange(objectKey.toByteArray());
+ } else {
+ ok = mCursor->last();
+ }
+ }
+ QByteArray baKey;
+ while (ok) {
+ mCursor->current(&baKey, 0);
+ if (baKey.size() == 16)
+ break;
+ if (mAscending)
+ ok = mCursor->next();
+ else
+ ok = mCursor->previous();
+ }
+ if (ok) {
+ QUuid quuid(QUuid::fromRfc4122(baKey));
+ ObjectKey objectKey(quuid);
+ fieldValue = objectKey.key.toString();
+ }
+ return ok;
+}
+
+bool JsonDbUuidQuery::seekToNext(QJsonValue &fieldValue)
+{
+ bool ok = mAscending ? mCursor->next() : mCursor->previous();
+ QByteArray baKey;
+ while (ok) {
+ mCursor->current(&baKey, 0);
+ if (baKey.size() == 16)
+ break;
+ if (mAscending)
+ ok = mCursor->next();
+ else
+ ok = mCursor->previous();
+ }
+ if (ok) {
+ QUuid quuid(QUuid::fromRfc4122(baKey));
+ ObjectKey objectKey(quuid);
+ fieldValue = objectKey.key.toString();
+ }
+ return ok;
+}
+
+JsonDbObject JsonDbUuidQuery::currentObjectAndTypeNumber(ObjectKey &objectKey)
+{
+ QByteArray baKey, baValue;
+ mCursor->current(&baKey, &baValue);
+ objectKey = ObjectKey(baKey);
+
+ if (jsondbSettings->debugQuery())
+ qDebug() << __FILE__ << __LINE__ << "objectKey" << objectKey << baKey.toHex();
+ JsonDbObject object(QJsonDocument::fromBinaryData(baValue).object());
+ return object;
+}
+
+void JsonDbIndexQuery::setResultExpressionList(const QStringList &resultExpressionList)
+{
+ mResultExpressionList = resultExpressionList;
+ int numExpressions = resultExpressionList.size();
+ mJoinPaths.resize(numExpressions);
+ for (int i = 0; i < numExpressions; i++) {
+ const QString &propertyName = resultExpressionList.at(i);
+ QStringList joinPath = propertyName.split("->");
+ int joinPathSize = joinPath.size();
+ QVector<QStringList> fieldPaths(joinPathSize);
+ for (int j = 0; j < joinPathSize; j++) {
+ QString joinField = joinPath[j];
+ fieldPaths[j] = joinField.split('.');
+ }
+ mJoinPaths[i] = fieldPaths;
+ }
+}
+
+JsonDbObject JsonDbIndexQuery::first()
+{
+ mSparseMatchPossible = false;
+ for (int i = 0; i < mQueryConstraints.size(); i++) {
+ mSparseMatchPossible |= mQueryConstraints[i]->sparseMatchPossible();
+ }
+
+ QJsonValue fieldValue;
+ bool ok = seekToStart(fieldValue);
+ if (jsondbSettings->debugQuery())
+ qDebug() << "IndexQuery::first" << __LINE__ << "ok after first/last()" << ok;
+ for (; ok; ok = seekToNext(fieldValue)) {
+ mFieldValue = fieldValue;
+ if (jsondbSettings->debugQuery())
+ qDebug() << "IndexQuery::first()"
+ << "mPropertyName" << mPropertyName
+ << "fieldValue" << fieldValue
+ << (mAscending ? "ascending" : "descending");
+
+ if (jsondbSettings->debugQuery())
+ qDebug() << "IndexQuery::first()" << "matches(fieldValue)" << matches(fieldValue);
+
+ if (!matches(fieldValue))
+ continue;
+
+ ObjectKey objectKey;
+ JsonDbObject object(currentObjectAndTypeNumber(objectKey));
+ if (jsondbSettings->debugQuery())
+ qDebug() << "IndexQuery::first()" << __LINE__ << "objectKey" << objectKey << object.value(JsonDbString::kDeletedStr).toBool();
+ if (object.contains(JsonDbString::kDeletedStr) && object.value(JsonDbString::kDeletedStr).toBool())
+ continue;
+
+ if (!mTypeNames.isEmpty() && !mTypeNames.contains(object.value(JsonDbString::kTypeStr).toString()))
+ continue;
+ if (jsondbSettings->debugQuery())
+ qDebug() << "mTypeName" << mTypeNames << "!contains" << object << "->" << object.value(JsonDbString::kTypeStr);
+
+ if (mResidualQuery && !mResidualQuery->match(object, &mObjectCache, mPartition))
+ continue;
+
+ if (jsondbSettings->debugQuery())
+ qDebug() << "IndexQuery::first()" << "returning objectKey" << objectKey;
+
+ return object;
+ }
+ mUuid.clear();
+ return QJsonObject();
+}
+
+JsonDbObject JsonDbIndexQuery::next()
+{
+ QJsonValue fieldValue;
+ while (seekToNext(fieldValue)) {
+ mFieldValue = fieldValue;
+ if (jsondbSettings->debugQuery()) {
+ qDebug() << "IndexQuery::next()" << "mPropertyName" << mPropertyName
+ << "fieldValue" << fieldValue
+ << (mAscending ? "ascending" : "descending");
+ qDebug() << "IndexQuery::next()" << "matches(fieldValue)" << matches(fieldValue);
+ }
+ if (!matches(fieldValue)) {
+ if (mSparseMatchPossible)
+ continue;
+ else
+ break;
+ }
+
+ ObjectKey objectKey;
+ JsonDbObject object(currentObjectAndTypeNumber(objectKey));
+ if (object.contains(JsonDbString::kDeletedStr) && object.value(JsonDbString::kDeletedStr).toBool())
+ continue;
+
+ if (!mTypeNames.isEmpty() && !mTypeNames.contains(object.value(JsonDbString::kTypeStr).toString()))
+ continue;
+
+ if (jsondbSettings->debugQuery())
+ qDebug() << "IndexQuery::next()" << "objectKey" << objectKey;
+
+ if (mResidualQuery && !mResidualQuery->match(object, &mObjectCache, mPartition))
+ continue;
+
+ return object;
+ }
+ mUuid.clear();
+ return QJsonObject();
+}
+
+JsonDbObject JsonDbIndexQuery::resultObject(const JsonDbObject &object)
+{
+ int numExpressions = mResultExpressionList.length();
+ QJsonObject result(object);
+
+ // insert the computed index value
+ result.insert(QLatin1String("_indexValue"), mFieldValue);
+
+ if (!numExpressions)
+ return result;
+
+ for (int i = 0; i < numExpressions; i++) {
+ QJsonValue v;
+
+ QVector<QStringList> &joinPath = mJoinPaths[i];
+ int joinPathSize = joinPath.size();
+ JsonDbObject baseObject(result);
+ for (int j = 0; j < joinPathSize-1; j++) {
+ QJsonValue uuidQJsonValue = baseObject.propertyLookup(joinPath[j]).toString();
+ QString uuid = uuidQJsonValue.toString();
+ if (uuid.isEmpty()) {
+ baseObject = JsonDbObject();
+ } else if (mObjectCache.contains(uuid)) {
+ baseObject = mObjectCache.value(uuid);
+ } else {
+ ObjectKey objectKey(uuid);
+ bool gotBaseObject = mPartition->getObject(objectKey, baseObject);
+ if (gotBaseObject)
+ mObjectCache.insert(uuid, baseObject);
+ }
+ }
+ v = baseObject.propertyLookup(joinPath[joinPathSize-1]);
+ result.insert(mResultKeyList[i], v);
+ }
+
+ return result;
+}
+
+bool JsonDbIndexQuery::lessThan(const QJsonValue &a, const QJsonValue &b)
+{
+ if (a.type() == b.type()) {
+ if (a.type() == QJsonValue::Double) {
+ return a.toDouble() < b.toDouble();
+ } else if (a.type() == QJsonValue::String) {
+ return a.toString() < b.toString();
+ } else if (a.type() == QJsonValue::Bool) {
+ return a.toBool() < b.toBool();
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+bool JsonDbIndexQuery::greaterThan(const QJsonValue &a, const QJsonValue &b)
+{
+ if (a.type() == b.type()) {
+ if (a.type() == QJsonValue::Double) {
+ return a.toDouble() > b.toDouble();
+ } else if (a.type() == QJsonValue::String) {
+ return a.toString() > b.toString();
+ } else if (a.type() == QJsonValue::Bool) {
+ return a.toBool() > b.toBool();
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbindexquery.h b/src/partition/jsondbindexquery.h
new file mode 100644
index 00000000..66845445
--- /dev/null
+++ b/src/partition/jsondbindexquery.h
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_INDEXQUERY_H
+#define JSONDB_INDEXQUERY_H
+
+#include <QJsonValue>
+#include <QRegExp>
+#include <QSet>
+#include <QVector>
+#include <QStringList>
+
+#include "jsondbpartitionglobal.h"
+#include "jsondbobject.h"
+#include "jsondbobjectkey.h"
+#include "jsondbbtree.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbObjectTable;
+class JsonDbOwner;
+class JsonDbPartition;
+class JsonDbQuery;
+
+class QueryConstraint {
+public:
+ virtual ~QueryConstraint() { }
+ virtual bool matches(const QJsonValue &value) = 0;
+ virtual bool sparseMatchPossible() const { return false; }
+};
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbIndexQuery {
+protected:
+ JsonDbIndexQuery(JsonDbPartition *partition, JsonDbObjectTable *table,
+ const QString &propertyName, const QString &propertyType,
+ const JsonDbOwner *owner, bool ascending = true);
+public:
+ static JsonDbIndexQuery *indexQuery(JsonDbPartition *partition, JsonDbObjectTable *table,
+ const QString &propertyName, const QString &propertyType,
+ const JsonDbOwner *owner, bool ascending = true);
+ ~JsonDbIndexQuery();
+
+ JsonDbObjectTable *objectTable() const { return mObjectTable; }
+ QString partition() const;
+ void addConstraint(QueryConstraint *qc) { mQueryConstraints.append(qc); }
+ bool ascending() const { return mAscending; }
+ QString propertyName() const { return mPropertyName; }
+ void setTypeNames(const QSet<QString> typeNames) { mTypeNames = typeNames; }
+ void setMin(const QJsonValue &minv);
+ void setMax(const QJsonValue &maxv);
+ QString aggregateOperation() const { return mAggregateOperation; }
+ void setAggregateOperation(QString op) { mAggregateOperation = op; }
+ void setResultExpressionList(const QStringList &resultExpressionList);
+ void setResultKeyList(QStringList resultKeyList) { mResultKeyList = resultKeyList; }
+
+ JsonDbObject first(); // returns first matching object
+ JsonDbObject next(); // returns next matching object
+ bool matches(const QJsonValue &value);
+ QJsonValue fieldValue() const { return mFieldValue; }
+ JsonDbQuery *residualQuery() const { return mResidualQuery; }
+ void setResidualQuery(JsonDbQuery *residualQuery) { mResidualQuery = residualQuery; }
+ virtual quint32 stateNumber() const;
+
+ JsonDbObject resultObject(const JsonDbObject &object);
+
+ static bool lessThan(const QJsonValue &a, const QJsonValue &b);
+ static bool greaterThan(const QJsonValue &a, const QJsonValue &b);
+
+protected:
+ virtual bool seekToStart(QJsonValue &fieldValue);
+ virtual bool seekToNext(QJsonValue &fieldValue);
+ virtual JsonDbObject currentObjectAndTypeNumber(ObjectKey &objectKey);
+
+protected:
+ JsonDbPartition *mPartition;
+ JsonDbObjectTable *mObjectTable;
+ JsonDbBtree *mBdbIndex;
+ bool isOwnTransaction;
+ JsonDbBtree::Transaction *mTxn;
+ JsonDbBtree::Cursor *mCursor;
+ const JsonDbOwner *mOwner;
+ QJsonValue mMin, mMax;
+ QSet<QString> mTypeNames;
+ bool mAscending;
+ QString mUuid;
+ QVector<QueryConstraint*> mQueryConstraints;
+ QString mAggregateOperation;
+ QString mPropertyName;
+ QString mPropertyType;
+ QJsonValue mFieldValue; // value of field for the object the cursor is pointing at
+ bool mSparseMatchPossible;
+ QHash<QString, JsonDbObject> mObjectCache;
+ QStringList mResultExpressionList;
+ QStringList mResultKeyList;
+ QVector<QVector<QStringList> > mJoinPaths;
+ JsonDbQuery *mResidualQuery;
+};
+
+class JsonDbUuidQuery : public JsonDbIndexQuery {
+protected:
+ JsonDbUuidQuery(JsonDbPartition *partition, JsonDbObjectTable *table, const QString &propertyName, const JsonDbOwner *owner, bool ascending = true);
+ virtual bool seekToStart(QJsonValue &fieldValue);
+ virtual bool seekToNext(QJsonValue &fieldValue);
+ virtual JsonDbObject currentObjectAndTypeNumber(ObjectKey &objectKey);
+ virtual quint32 stateNumber() const;
+ friend class JsonDbIndexQuery;
+};
+
+class QueryConstraintGt: public QueryConstraint {
+public:
+ QueryConstraintGt(const QJsonValue &v) { mValue = v; }
+ inline bool matches(const QJsonValue &v) { return JsonDbIndexQuery::greaterThan(v, mValue); }
+private:
+ QJsonValue mValue;
+};
+class QueryConstraintGe: public QueryConstraint {
+public:
+ QueryConstraintGe(const QJsonValue &v) { mValue = v; }
+ inline bool matches(const QJsonValue &v) { return JsonDbIndexQuery::greaterThan(v, mValue) || (v == mValue); }
+private:
+ QJsonValue mValue;
+};
+class QueryConstraintLt: public QueryConstraint {
+public:
+ QueryConstraintLt(const QJsonValue &v) { mValue = v; }
+ inline bool matches(const QJsonValue &v) { return JsonDbIndexQuery::lessThan(v, mValue); }
+private:
+ QJsonValue mValue;
+};
+class QueryConstraintLe: public QueryConstraint {
+public:
+ QueryConstraintLe(const QJsonValue &v) { mValue = v; }
+ inline bool matches(const QJsonValue &v) { return JsonDbIndexQuery::lessThan(v, mValue) || (v == mValue); }
+private:
+ QJsonValue mValue;
+};
+class QueryConstraintEq: public QueryConstraint {
+public:
+ QueryConstraintEq(const QJsonValue &v) { mValue = v; }
+ inline bool matches(const QJsonValue &v) { return v == mValue; }
+private:
+ QJsonValue mValue;
+};
+class QueryConstraintNe: public QueryConstraint {
+public:
+ QueryConstraintNe(const QJsonValue &v) { mValue = v; }
+ inline bool sparseMatchPossible() const { return true; }
+ inline bool matches(const QJsonValue &v) { return v != mValue; }
+private:
+ QJsonValue mValue;
+};
+class QueryConstraintExists: public QueryConstraint {
+public:
+ QueryConstraintExists() { }
+ inline bool matches(const QJsonValue &v) { return !v.isUndefined(); }
+};
+class QueryConstraintNotExists: public QueryConstraint {
+public:
+ QueryConstraintNotExists() { }
+ // this will never match
+ inline bool matches(const QJsonValue &v) { return v.isUndefined(); }
+};
+class QueryConstraintIn: public QueryConstraint {
+public:
+ QueryConstraintIn(const QJsonValue &v) { mList = v.toArray();}
+ inline bool sparseMatchPossible() const { return true; }
+ inline bool matches(const QJsonValue &v) { return mList.contains(v); }
+private:
+ QJsonArray mList;
+};
+class QueryConstraintNotIn: public QueryConstraint {
+public:
+ QueryConstraintNotIn(const QJsonValue &v) { mList = v.toArray();}
+ inline bool sparseMatchPossible() const { return true; }
+ inline bool matches(const QJsonValue &v) { return !mList.contains(v); }
+private:
+ QJsonArray mList;
+};
+class QueryConstraintContains: public QueryConstraint {
+public:
+ QueryConstraintContains(const QJsonValue &v) { mValue = v;}
+ inline bool sparseMatchPossible() const { return true; }
+ inline bool matches(const QJsonValue &v) { return v.toArray().contains(mValue); }
+private:
+ QJsonValue mValue;
+};
+class QueryConstraintStartsWith: public QueryConstraint {
+public:
+ QueryConstraintStartsWith(const QString &v) { mValue = v;}
+ inline bool sparseMatchPossible() const { return true; }
+ inline bool matches(const QJsonValue &v) { return (v.type() == QJsonValue::String) && v.toString().startsWith(mValue); }
+private:
+ QString mValue;
+};
+class QueryConstraintRegExp: public QueryConstraint {
+public:
+ QueryConstraintRegExp(const QRegExp &regexp) : mRegExp(regexp) {}
+ inline bool matches(const QJsonValue &v) { return mRegExp.exactMatch(v.toString()); }
+ inline bool sparseMatchPossible() const { return true; }
+private:
+ QString mValue;
+ QRegExp mRegExp;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_INDEXQUERY_H
diff --git a/src/partition/jsondbmapdefinition.cpp b/src/partition/jsondbmapdefinition.cpp
new file mode 100644
index 00000000..07c4a1df
--- /dev/null
+++ b/src/partition/jsondbmapdefinition.cpp
@@ -0,0 +1,474 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QDebug>
+#include <QElapsedTimer>
+#include <QRegExp>
+#include <QJSValue>
+#include <QJSValueIterator>
+#include <QStringList>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "jsondbpartition.h"
+#include "jsondbstrings.h"
+#include "jsondberrors.h"
+
+#include "jsondbproxy.h"
+#include "jsondbobjecttable.h"
+#include "jsondbmapdefinition.h"
+#include "jsondbsettings.h"
+#include "jsondbscriptengine.h"
+#include "jsondbview.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbMapDefinition::JsonDbMapDefinition(const JsonDbOwner *owner, JsonDbPartition *partition, QJsonObject definition, QObject *parent) :
+ QObject(parent)
+ , mPartition(partition)
+ , mOwner(owner)
+ , mDefinition(definition)
+ , mScriptEngine(0)
+ , mUuid(definition.value(JsonDbString::kUuidStr).toString())
+ , mTargetType(definition.value("targetType").toString())
+ , mTargetTable(mPartition->findObjectTable(mTargetType))
+{
+ mMapId = mUuid;
+ mMapId.replace(QRegExp("[-{}]"), "$");
+ QJsonObject sourceFunctions(mDefinition.contains("join")
+ ? mDefinition.value("join").toObject()
+ : mDefinition.value("map").toObject());
+ mSourceTypes = sourceFunctions.keys();
+ for (int i = 0; i < mSourceTypes.size(); i++) {
+ const QString &sourceType = mSourceTypes[i];
+ mSourceTables[sourceType] = mPartition->findObjectTable(sourceType);
+ }
+ if (mDefinition.contains("targetKeyName"))
+ mTargetKeyName = mDefinition.value("targetKeyName").toString();
+}
+
+void JsonDbMapDefinition::definitionCreated()
+{
+ initScriptEngine();
+ initIndexes();
+
+ foreach (const QString &sourceType, mSourceTypes) {
+ GetObjectsResult getObjectResponse = mPartition->getObjects(JsonDbString::kTypeStr, sourceType);
+ if (!getObjectResponse.error.isNull()) {
+ if (jsondbSettings->verbose())
+ qDebug() << "createMapDefinition" << mSourceTypes << sourceType << mTargetType << getObjectResponse.error.toString();
+ setError(getObjectResponse.error.toString());
+ return;
+ }
+ JsonDbObjectList objects = getObjectResponse.data;
+ bool isJoin = mDefinition.contains(QLatin1String("join"));
+ for (int i = 0; i < objects.size(); i++) {
+ JsonDbObject object(objects.at(i));
+ if (isJoin)
+ unmapObject(object);
+ updateObject(JsonDbObject(), objects.at(i));
+ }
+ }
+}
+
+void JsonDbMapDefinition::definitionRemoved(JsonDbPartition *partition, JsonDbObjectTable *table, const QString targetType, const QString &definitionUuid)
+{
+ // remove the output objects
+ GetObjectsResult getObjectResponse = table->getObjects(QLatin1String("_sourceUuids.*"),
+ definitionUuid,
+ targetType);
+ JsonDbObjectList objects = getObjectResponse.data;
+ for (int i = 0; i < objects.size(); i++) {
+ JsonDbObject o = objects[i];
+ o.markDeleted();
+ partition->updateObject(partition->defaultOwner(), o, JsonDbPartition::ViewObject);
+ }
+}
+
+void JsonDbMapDefinition::initScriptEngine()
+{
+ if (mScriptEngine)
+ return;
+
+ mScriptEngine = JsonDbScriptEngine::scriptEngine();
+ mJoinProxy = new JsonDbJoinProxy(mOwner, mPartition, this);
+ connect(mJoinProxy, SIGNAL(lookupRequested(QJSValue,QJSValue)),
+ this, SLOT(lookupRequested(QJSValue,QJSValue)));
+ connect(mJoinProxy, SIGNAL(viewObjectEmitted(QJSValue)),
+ this, SLOT(viewObjectEmitted(QJSValue)));
+
+ QString message;
+ bool compiled = compileMapFunctions(mScriptEngine, mDefinition, mJoinProxy, mMapFunctions, message);
+ if (!compiled)
+ setError(message);
+}
+
+bool JsonDbMapDefinition::compileMapFunctions(QJSEngine *scriptEngine, QJsonObject definition, JsonDbJoinProxy *joinProxy, QMap<QString,QJSValue> &mapFunctions, QString &message)
+{
+ bool status = true;
+ QJsonObject sourceFunctions(definition.contains("join")
+ ? definition.value("join").toObject()
+ : definition.value("map").toObject());
+ QJSValue svJoinProxyValue = joinProxy ? scriptEngine->newQObject(joinProxy) : QJSValue(QJSValue::UndefinedValue);
+ for (QJsonObject::const_iterator it = sourceFunctions.begin(); it != sourceFunctions.end(); ++it) {
+ const QString &sourceType = it.key();
+ const QString &script = it.value().toString();
+ QString jsonDbBinding;
+ if (definition.contains("join"))
+ // only joins can use lookup()
+ jsonDbBinding = QString("{ emit: proxy.create, lookup: proxy.lookup, createUuidFromString: proxy.createUuidFromString}");
+ else
+ jsonDbBinding = QString("{ emit: proxy.create, createUuidFromString: proxy.createUuidFromString }");
+
+ // first, package it as a function that takes a jsondb proxy and returns the map function
+ QJSValue moduleFunction =
+ scriptEngine->evaluate(QString("(function (proxy) { var jsondb=%3; map_%1 = (%2); return map_%1; })")
+ .arg(QString(sourceType).replace(".", "_"))
+ .arg(script)
+ .arg(jsonDbBinding));
+ if (moduleFunction.isError() || !moduleFunction.isCallable()) {
+ message = QString( "Unable to parse map function: " + moduleFunction.toString());
+ status = false;
+ }
+
+ // now pass it the jsondb proxy to get the map function
+ QJSValueList args;
+ args << svJoinProxyValue;
+ QJSValue mapFunction = moduleFunction.call(args);
+ if (moduleFunction.isError() || !moduleFunction.isCallable()) {
+ message = QString( "Unable to evaluate map function: " + moduleFunction.toString());
+ status = false;
+ }
+ mapFunctions[sourceType] = mapFunction;
+ }
+ return status;
+}
+
+void JsonDbMapDefinition::releaseScriptEngine()
+{
+ mMapFunctions.clear();
+ mScriptEngine = 0;
+}
+
+
+void JsonDbMapDefinition::initIndexes()
+{
+ mTargetTable->addIndexOnProperty(QLatin1String("_sourceUuids.*"), QLatin1String("string"), mTargetType);
+}
+
+void JsonDbMapDefinition::updateObject(const JsonDbObject &beforeObject, const JsonDbObject &afterObject, JsonDbUpdateList *changeList)
+{
+ initScriptEngine();
+ QHash<QString, JsonDbObject> unmappedObjects;
+ mEmittedObjects.clear();
+
+ if (!beforeObject.isEmpty()) {
+ QJsonValue uuid = beforeObject.value(JsonDbString::kUuidStr);
+ GetObjectsResult getObjectResponse = mTargetTable->getObjects("_sourceUuids.*", uuid, mTargetType);
+ foreach (const JsonDbObject &unmappedObject, getObjectResponse.data) {
+ QString uuid = unmappedObject.value(JsonDbString::kUuidStr).toString();
+ unmappedObjects[uuid] = unmappedObject;
+ }
+ }
+
+ if (!afterObject.isDeleted()) {
+ if (jsondbSettings->verbose())
+ qDebug() << "Mapping object" << afterObject;
+ mapObject(afterObject);
+ }
+
+ JsonDbObjectList objectsToUpdate;
+ for (QHash<QString, JsonDbObject>::const_iterator it = unmappedObjects.begin();
+ it != unmappedObjects.end();
+ ++it) {
+ JsonDbObject unmappedObject = it.value();
+ QString uuid = unmappedObject.value(JsonDbString::kUuidStr).toString();
+ JsonDbWriteResult res;
+ if (mEmittedObjects.contains(uuid)) {
+ JsonDbObject emittedObject(mEmittedObjects.value(uuid));
+ emittedObject.insert(JsonDbString::kVersionStr, unmappedObject.value(JsonDbString::kVersionStr));
+ emittedObject.insert(JsonDbString::kOwnerStr, unmappedObject.value(JsonDbString::kOwnerStr));
+ if (emittedObject == it.value())
+ // skip duplicates
+ continue;
+ else
+ // update changed view objects
+ objectsToUpdate.append(emittedObject);
+
+ mEmittedObjects.remove(uuid);
+ } else {
+ // remove unmatched objects
+ unmappedObject.markDeleted();
+ if (jsondbSettings->verbose())
+ qDebug() << "Unmapping object" << unmappedObject;
+ objectsToUpdate.append(unmappedObject);
+ }
+ }
+
+ for (QHash<QString, JsonDbObject>::const_iterator it = mEmittedObjects.begin();
+ it != mEmittedObjects.end();
+ ++it)
+ objectsToUpdate.append(JsonDbObject(it.value()));
+
+ JsonDbWriteResult res = mPartition->updateObjects(mOwner, objectsToUpdate, JsonDbPartition::ViewObject, changeList);
+ if (res.code != JsonDbError::NoError)
+ setError("Error creating view object: " + res.message);
+}
+
+QJSValue JsonDbMapDefinition::mapFunction(const QString &sourceType) const
+{
+ if (mMapFunctions.contains(sourceType))
+ return mMapFunctions[sourceType];
+ else
+ return QJSValue();
+}
+
+void JsonDbMapDefinition::mapObject(JsonDbObject object)
+{
+ const QString &sourceType = object.value(JsonDbString::kTypeStr).toString();
+
+ QJSValue sv = JsonDbScriptEngine::toJSValue(object, mScriptEngine);
+ QString uuid = object.value(JsonDbString::kUuidStr).toString();
+ mSourceUuids.clear();
+ mSourceUuids.append(mUuid); // depends on the map definition object
+ mSourceUuids.append(uuid); // depends on the source object
+ QJSValue mapped;
+
+ QJSValueList mapArgs;
+ mapArgs << sv;
+ mapped = mapFunction(sourceType).call(mapArgs);
+
+ if (mapped.isError())
+ setError("Error executing map function: " + mapped.toString());
+}
+
+void JsonDbMapDefinition::unmapObject(const JsonDbObject &object)
+{
+ Q_ASSERT(object.value(JsonDbString::kUuidStr).type() == QJsonValue::String);
+ QJsonValue uuid = object.value(JsonDbString::kUuidStr);
+ GetObjectsResult getObjectResponse = mTargetTable->getObjects("_sourceUuids.*", uuid, mTargetType);
+ JsonDbObjectList dependentObjects = getObjectResponse.data;
+
+ for (int i = 0; i < dependentObjects.size(); i++) {
+ JsonDbObject dependentObject = dependentObjects.at(i);
+ if (dependentObject.value(JsonDbString::kTypeStr).toString() != mTargetType)
+ continue;
+
+ dependentObject.markDeleted();
+ mPartition->updateObject(mOwner, dependentObject, JsonDbPartition::ViewObject);
+ }
+}
+
+void JsonDbMapDefinition::lookupRequested(const QJSValue &query, const QJSValue &context)
+{
+ QString objectType = query.property("objectType").toString();
+ // compatibility for old style maps
+ if (mDefinition.value("map").isObject()) {
+ if (objectType.isEmpty()) {
+ setError("No objectType provided to jsondb.lookup");
+ return;
+ }
+ if (!mSourceTypes.contains(objectType)) {
+ setError(QString("lookup requested for type %1 not in source types: %2")
+ .arg(objectType)
+ .arg(mSourceTypes.join(", ")));
+ return;
+ }
+ }
+ QString findKey = query.property("index").toString();
+ QJSValue findValue = query.property("value");
+ GetObjectsResult getObjectResponse =
+ mPartition->getObjects(findKey, JsonDbScriptEngine::fromJSValue(findValue), objectType, false);
+ if (!getObjectResponse.error.isNull()) {
+ if (jsondbSettings->verbose())
+ qDebug() << "lookupRequested" << mSourceTypes << mTargetType
+ << getObjectResponse.error.toString();
+ setError(getObjectResponse.error.toString());
+ }
+ JsonDbObjectList objectList = getObjectResponse.data;
+ for (int i = 0; i < objectList.size(); ++i) {
+ JsonDbObject object = objectList.at(i);
+ const QString uuid = object.value(JsonDbString::kUuidStr).toString();
+ if (mSourceUuids.contains(uuid)) {
+ if (jsondbSettings->verbose())
+ qDebug() << "Lookup cycle detected" << "key" << findKey << JsonDbScriptEngine::fromJSValue(findValue) << "matching object" << uuid << "source uuids" << mSourceUuids;
+ continue;
+ }
+ mSourceUuids.append(uuid);
+ QJSValueList mapArgs;
+ QJSValue sv = JsonDbScriptEngine::toJSValue(object, mScriptEngine);
+
+ mapArgs << sv << context;
+ QJSValue mapped = mMapFunctions[objectType].call(mapArgs);
+
+ if (mapped.isError())
+ setError("Error executing map function during lookup: " + mapped.toString());
+
+ mSourceUuids.removeOne(uuid);
+ }
+}
+
+void JsonDbMapDefinition::viewObjectEmitted(const QJSValue &value)
+{
+ JsonDbObject newItem(JsonDbScriptEngine::fromJSValue(value).toObject());
+ newItem.insert(JsonDbString::kTypeStr, mTargetType);
+ mSourceUuids.sort();
+ QJsonArray sourceUuidArray;
+ foreach (const QString &sourceUuid, mSourceUuids)
+ sourceUuidArray.append(sourceUuid);
+ newItem.insert("_sourceUuids", sourceUuidArray);
+
+ if (!newItem.contains(JsonDbString::kUuidStr)) {
+ if (newItem.contains(QLatin1String("_id")))
+ newItem.generateUuid();
+ else {
+ QString targetKeyString;
+ if (!mTargetKeyName.isEmpty())
+ targetKeyString = JsonDbObject(newItem).propertyLookup(mTargetKeyName).toString();
+
+ // colon separated sorted source uuids
+ QString sourceUuidString = mSourceUuids.join(":");
+ QString identifier =
+ QString("%1:%2%3%4")
+ .arg(mTargetType)
+ .arg(sourceUuidString)
+ .arg(targetKeyString.isEmpty() ? "" : ":")
+ .arg(targetKeyString);
+ newItem.insert(JsonDbString::kUuidStr,
+ JsonDbObject::createUuidFromString(identifier).toString());
+ }
+ }
+
+ QString uuid = newItem.value(JsonDbString::kUuidStr).toString();
+ mEmittedObjects.insert(uuid, newItem);
+}
+
+bool JsonDbMapDefinition::isActive() const
+{
+ return !mDefinition.contains(JsonDbString::kActiveStr) || mDefinition.value(JsonDbString::kActiveStr).toBool();
+}
+
+void JsonDbMapDefinition::setError(const QString &errorMsg)
+{
+ mDefinition.insert(JsonDbString::kActiveStr, false);
+ mDefinition.insert(JsonDbString::kErrorStr, errorMsg);
+ if (mPartition)
+ mPartition->updateObject(mOwner, mDefinition, JsonDbPartition::ForcedWrite);
+}
+
+bool JsonDbMapDefinition::validateDefinition(const JsonDbObject &map, JsonDbPartition *partition, QString &message)
+{
+ message.clear();
+ QString targetType = map.value("targetType").toString();
+ QString uuid = map.value(JsonDbString::kUuidStr).toString();
+ JsonDbView *view = partition->findView(targetType);
+ QStringList sourceTypes;
+
+ if (targetType.isEmpty()) {
+ message = QLatin1Literal("targetType property for Map not specified");
+ } else if (map.contains(QLatin1Literal("sourceType"))) {
+ message = QLatin1Literal("sourceType property for Map no longer supported");
+ } else if (!view) {
+ message = QLatin1Literal("targetType must be of a type that extends View");
+ } else if (map.contains("join")) {
+ QJsonObject sourceFunctions = map.value("join").toObject();
+
+ if (sourceFunctions.isEmpty())
+ message = QLatin1Literal("sourceTypes and functions for Map with join not specified");
+
+ sourceTypes = sourceFunctions.keys();
+
+ foreach (const QString &sourceType, sourceTypes) {
+ if (sourceFunctions.value(sourceType).toString().isEmpty())
+ message = QString("join function for source type '%1' not specified for Map").arg(sourceType);
+ if (view->mMapDefinitionsBySource.contains(sourceType)
+ && view->mMapDefinitionsBySource.value(sourceType)->uuid() != uuid)
+ message =
+ QString("duplicate Map definition on source %1 and target %2")
+ .arg(sourceType).arg(targetType);
+ }
+
+ if (map.contains("map"))
+ message = QLatin1Literal("Map 'join' and 'map' options are mutually exclusive");
+ } else {
+ QJsonValue mapValue = map.value("map");
+ if (!mapValue.isObject())
+ message = QLatin1String("sourceType property for Map not specified");
+ else if (!mapValue.isString() && !mapValue.isObject())
+ message = QLatin1String("map function for Map not specified");
+
+ if (mapValue.isObject()) {
+
+ QJsonObject sourceFunctions = mapValue.toObject();
+
+ if (sourceFunctions.isEmpty())
+ message = QLatin1Literal("sourceTypes and functions for Map with map not specified");
+
+ sourceTypes = sourceFunctions.keys();
+
+ foreach (const QString &sourceType, sourceTypes) {
+ if (sourceFunctions.value(sourceType).toString().isEmpty())
+ message = QString("map function for source type '%1' not specified for Map").arg(sourceType);
+ if (view->mMapDefinitionsBySource.contains(sourceType)
+ && view->mMapDefinitionsBySource.value(sourceType)->uuid() != uuid)
+ message =
+ QString("duplicate Map definition on source %1 and target %2")
+ .arg(sourceType).arg(targetType);
+ }
+ }
+ }
+
+ // check for parse errors
+ if (message.isEmpty()) {
+ QJSEngine *scriptEngine = JsonDbScriptEngine::scriptEngine();
+ QMap<QString,QJSValue> mapFunctions;
+ compileMapFunctions(scriptEngine, map, 0, mapFunctions, message);
+ scriptEngine->collectGarbage();
+ }
+
+ return message.isEmpty();
+}
+
+#include "moc_jsondbmapdefinition.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbmapdefinition.h b/src/partition/jsondbmapdefinition.h
new file mode 100644
index 00000000..a25f7d7d
--- /dev/null
+++ b/src/partition/jsondbmapdefinition.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_MAP_DEFINITION_H
+#define JSONDB_MAP_DEFINITION_H
+
+#include <QJSEngine>
+#include <QStringList>
+
+#include "jsondbpartition.h"
+#include "jsondbpartitionglobal.h"
+
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+#include <jsondbobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbOwner;
+class JsonDbJoinProxy;
+class JsonDbMapProxy;
+class JsonDbObjectTable;
+
+class JsonDbMapDefinition : public QObject
+{
+ Q_OBJECT
+public:
+ JsonDbMapDefinition(const JsonDbOwner *mOwner, JsonDbPartition *partition, QJsonObject mapDefinition, QObject *parent = 0);
+ QString uuid() const { return mUuid; }
+ QString targetType() const { return mTargetType; }
+ const QStringList &sourceTypes() const { return mSourceTypes; }
+ QString partitionName() const { return mPartition->name(); }
+ bool isActive() const;
+ QJsonObject definition() const { return mDefinition; }
+ QJSValue mapFunction(const QString &sourceType) const;
+ JsonDbObjectTable *sourceTable(const QString &sourceType) const { return mSourceTables.value(sourceType); }
+ const JsonDbOwner *owner() const { return mOwner; }
+
+ static void definitionRemoved(JsonDbPartition *partition, JsonDbObjectTable *table, const QString targetType, const QString &definitionUuid);
+ void definitionCreated();
+
+ void initScriptEngine();
+ void releaseScriptEngine();
+ void initIndexes();
+
+ void setError(const QString &errorMsg);
+ void updateObject(const JsonDbObject &before, const JsonDbObject &after, JsonDbUpdateList *changeList = 0);
+ static bool validateDefinition(const JsonDbObject &map, JsonDbPartition *partition, QString &message);
+ static bool compileMapFunctions(QJSEngine *scriptEngine, QJsonObject definition, JsonDbJoinProxy *joinProxy, QMap<QString,QJSValue> &mapFunctions, QString &message);
+
+public slots:
+ void viewObjectEmitted(const QJSValue &value);
+ void lookupRequested(const QJSValue &spec, const QJSValue &context);
+
+private:
+ void mapObject(JsonDbObject object);
+ void unmapObject(const JsonDbObject &object);
+
+private:
+ JsonDbPartition *mPartition;
+ const JsonDbOwner *mOwner;
+ QJsonObject mDefinition;
+ QString mTargetKeyName;
+ QJSEngine *mScriptEngine;
+ JsonDbMapProxy *mMapProxy; // to be removed when old map/lookup converted to join/lookup
+ JsonDbJoinProxy *mJoinProxy;
+ QMap<QString,QJSValue> mMapFunctions;
+ QString mUuid;
+ QString mMapId; // uuid with special characters converted to '$'
+ QString mTargetType;
+ QStringList mSourceTypes;
+ JsonDbObjectTable *mTargetTable;
+ QMap<QString,JsonDbObjectTable *> mSourceTables;
+ QStringList mSourceUuids; // a set of uuids with sorted elements
+ QHash<QString,JsonDbObject> mEmittedObjects;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_MAP_DEFINITION_H
diff --git a/src/partition/jsondbnotification.cpp b/src/partition/jsondbnotification.cpp
new file mode 100644
index 00000000..d8687309
--- /dev/null
+++ b/src/partition/jsondbnotification.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QDebug>
+#include <QList>
+#include <QMap>
+#include <QVariantList>
+#include <QString>
+#include <QStringList>
+
+#include "jsondbnotification.h"
+#include "jsondbquery.h"
+#include "jsondbstrings.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbNotification::JsonDbNotification(const JsonDbOwner *owner, const QString &uuid, const QString& query,
+ QStringList actions, const QString &partition)
+ : mOwner(owner)
+ , mUuid(uuid)
+ , mQuery(query)
+ , mCompiledQuery(NULL)
+ , mActions(None)
+ , mPartition(partition)
+ , mInitialStateNumber(0)
+ , mLastStateNumber(0)
+{
+ foreach (QString s, actions) {
+ if (s == JsonDbString::kCreateStr)
+ mActions |= Create;
+ else if (s == JsonDbString::kUpdateStr)
+ mActions |= Update;
+ else if ((s == "delete") || (s == JsonDbString::kRemoveStr))
+ mActions |= Delete;
+ }
+}
+JsonDbNotification::~JsonDbNotification()
+{
+ if (mCompiledQuery) {
+ delete mCompiledQuery;
+ mCompiledQuery = 0;
+ }
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbnotification.h b/src/partition/jsondbnotification.h
new file mode 100644
index 00000000..9e52d4bb
--- /dev/null
+++ b/src/partition/jsondbnotification.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_NOTIFICATION_H
+#define JSONDB_NOTIFICATION_H
+
+#include <QObject>
+#include <QJSValue>
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbOwner;
+class JsonDbQuery;
+class Q_JSONDB_PARTITION_EXPORT JsonDbNotification {
+public:
+ enum Action { None = 0x0000, Create = 0x0001, Update = 0x0002, Delete = 0x0004 };
+ Q_DECLARE_FLAGS(Actions, Action)
+
+ JsonDbNotification(const JsonDbOwner *owner, const QString &uuid, const QString &query, QStringList actions, const QString &partition);
+ ~JsonDbNotification();
+
+ const JsonDbOwner *owner() const { return mOwner; }
+ const QString& uuid() const { return mUuid; }
+ const QString& query() const { return mQuery; }
+ Actions actions() const { return mActions; }
+ JsonDbQuery *parsedQuery() const { return mCompiledQuery; }
+ void setCompiledQuery(JsonDbQuery *parsedQuery) { mCompiledQuery = parsedQuery; }
+ const QString & partition() const { return mPartition; }
+ quint32 initialStateNumber() const { return mInitialStateNumber; }
+ void setInitialStateNumber(quint32 stateNumber) { mInitialStateNumber = stateNumber; }
+ quint32 lastStateNumber() const { return mLastStateNumber; }
+ void setLastStateNumber(quint32 stateNumber) { mLastStateNumber = stateNumber; }
+private:
+ const JsonDbOwner *mOwner;
+ QString mUuid;
+ QString mQuery;
+ JsonDbQuery *mCompiledQuery;
+ Actions mActions;
+ QString mPartition;
+ quint32 mInitialStateNumber;
+ quint32 mLastStateNumber;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(JsonDbNotification::Actions)
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_NOTIFICATION_H
diff --git a/src/partition/jsondbobject.cpp b/src/partition/jsondbobject.cpp
new file mode 100644
index 00000000..e61d6ebf
--- /dev/null
+++ b/src/partition/jsondbobject.cpp
@@ -0,0 +1,610 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbobject.h"
+
+#include <QJSValue>
+#include <QJSValueIterator>
+#include <QStringBuilder>
+#include <QStringList>
+#include <QCryptographicHash>
+
+#include <qjsondocument.h>
+
+#include "jsondbstrings.h"
+#include "jsondbproxy.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbObject::JsonDbObject()
+{
+}
+
+JsonDbObject::JsonDbObject(const QJsonObject &object)
+ : QJsonObject(object)
+{
+}
+
+JsonDbObject::~JsonDbObject()
+{
+}
+
+QByteArray JsonDbObject::toBinaryData() const
+{
+ return QJsonDocument(*this).toBinaryData();
+}
+
+QUuid JsonDbObject::uuid() const
+{
+ return QUuid(value(JsonDbString::kUuidStr).toString());
+}
+
+QString JsonDbObject::version() const
+{
+ return value(JsonDbString::kVersionStr).toString();
+}
+
+QString JsonDbObject::type() const
+{
+ return value(JsonDbString::kTypeStr).toString();
+}
+
+bool JsonDbObject::isDeleted() const
+{
+ QJsonValue deleted = value(JsonDbString::kDeletedStr);
+
+ if (deleted.isUndefined() || (deleted.isBool() && deleted.toBool() == false))
+ return false;
+
+ return true;
+}
+
+void JsonDbObject::markDeleted()
+{
+ insert(JsonDbString::kDeletedStr, true);
+}
+
+struct Uuid
+{
+ uint data1;
+ ushort data2;
+ ushort data3;
+ uchar data4[8];
+};
+
+// copied from src/client/qjsondbobject.cpp:
+static const Uuid JsonDbNamespace = {0x6ba7b810, 0x9dad, 0x11d1, { 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} };
+
+/*!
+ Returns deterministic uuid that can be used to identify given \a identifier.
+
+ The uuid is generated using QtJsonDb UUID namespace on a value of the
+ given \a identifier.
+
+ \sa QJsonDbObject::createUuidFromString(), QJsonDbObject::createUuid()
+*/
+QUuid JsonDbObject::createUuidFromString(const QString &identifier)
+{
+ const QUuid ns(JsonDbNamespace.data1, JsonDbNamespace.data2, JsonDbNamespace.data3,
+ JsonDbNamespace.data4[0], JsonDbNamespace.data4[1], JsonDbNamespace.data4[2],
+ JsonDbNamespace.data4[3], JsonDbNamespace.data4[4], JsonDbNamespace.data4[5],
+ JsonDbNamespace.data4[6], JsonDbNamespace.data4[7]);
+ return QUuid::createUuidV3(ns, identifier);
+}
+
+void JsonDbObject::generateUuid()
+{
+ QLatin1String idStr("_id");
+ if (contains(idStr)) {
+ QUuid uuid(createUuidFromString(value(idStr).toString()));
+ insert(JsonDbString::kUuidStr, uuid.toString());
+ } else {
+ QUuid uuid(QUuid::createUuid());
+ insert(JsonDbString::kUuidStr, uuid.toString());
+ }
+}
+
+/*!
+ * \brief JsonDbObject::computeVersion mostly for legacy reasons.
+ * Same as calling updateVersionOptimistic(*this, &versionWritten)
+ *
+ * \sa updateVersionOptimistic(), version()
+ * \return the new version, which is also written to the object
+ */
+QString JsonDbObject::computeVersion()
+{
+ QString versionWritten;
+ updateVersionOptimistic(*this, &versionWritten);
+ return versionWritten;
+}
+
+/*!
+ * \brief JsonDbObject::updateVersionOptimistic implement an optimisticWrite
+ * \param other the object containing the update to be written. Do NOT call computeVersion()
+ * on the other object before passing it in! other._meta.history is assumed untrusted.
+ * \param versionWritten contains the version string of the write upon return
+ * \return true if the passed object is a valid write. As this version can operate
+ * on conflicts too, version() and versionWritten can differ.
+ */
+bool JsonDbObject::updateVersionOptimistic(const JsonDbObject &other, QString *versionWrittenOut)
+{
+ QString versionWritten;
+ // this is trusted and expected to contain a _meta object with book keeping info
+ QJsonObject meta = value(JsonDbString::kMetaStr).toObject();
+
+ // an array of all versions this object has replaced
+ QJsonArray history = meta.value(QStringLiteral("history")).toArray();
+
+ // all known conflicts
+ QJsonArray conflicts = meta.value(JsonDbString::kConflictsStr).toArray();
+
+ QString replacedVersion = other.version();
+
+ int replacedCount;
+ QString replacedHash = tokenizeVersion(replacedVersion, &replacedCount);
+
+ int updateCount = replacedCount;
+ QString hash = replacedHash;
+
+ // we don't trust other._meta.history, so other._version must be replacedVersion
+ // if other.computeVersion() was called before updateVersionOptimistic(), other can at max be a replay
+ // as we lost which version other is replacing.
+ bool isReplay = !other.computeVersion(replacedCount, replacedHash, &updateCount, &hash);
+
+ bool isValidWrite = false;
+
+ // first we check if this version can eliminate a conflict
+ for (QJsonArray::const_iterator ii = conflicts.begin(); ii < conflicts.end(); ii++) {
+
+ JsonDbObject conflict((*ii).toObject());
+ if (conflict.version() == replacedVersion) {
+ if (!isReplay)
+ conflicts.removeAt(ii.i);
+ if (!isValidWrite) {
+ addAncestor(&history, updateCount, hash);
+ versionWritten = versionAsString(updateCount, hash);
+ }
+ isValidWrite = true;
+ }
+ }
+
+ // now we check if this version can progress the head
+ if (version() == replacedVersion) {
+ if (!isReplay)
+ *this = other;
+ if (!isValidWrite)
+ versionWritten = versionAsString(updateCount, hash);
+ insert(JsonDbString::kVersionStr, versionWritten);
+ isValidWrite = true;
+ }
+
+ // make sure we can resurrect a tombstone
+ // Issue: Recreating a _uuid must have a updateCount higher than the tombstone
+ // otherwise it is considered a conflict.
+ if (!isValidWrite && isDeleted()) {
+ if (!isReplay) {
+ addAncestor(&history, replacedCount, replacedHash);
+ isReplay = false;
+ }
+
+ replacedHash = tokenizeVersion(version(), &replacedCount);
+ updateCount = replacedCount + 1;
+ versionWritten = versionAsString(updateCount, hash);
+
+ *this = other;
+ insert(JsonDbString::kVersionStr, versionWritten);
+ isValidWrite = true;
+ }
+
+ // update the book keeping of what versions we have replaced in this version branch
+ if (isValidWrite && !isReplay) {
+ addAncestor(&history, replacedCount, replacedHash);
+
+ meta = QJsonObject();
+
+ if (history.size())
+ meta.insert(QStringLiteral("history"), history);
+ if (conflicts.size())
+ meta.insert(JsonDbString::kConflictsStr, history);
+
+ if (!meta.isEmpty())
+ insert(JsonDbString::kMetaStr, meta);
+ }
+
+ // last chance for a valid write: other is a replay from history
+ if (!isValidWrite && isAncestorOf(history, updateCount, hash)) {
+ isValidWrite = true;
+ versionWritten = versionAsString(updateCount, hash);
+ }
+
+ if (versionWrittenOut)
+ *versionWrittenOut = versionWritten;
+
+ return isValidWrite;
+}
+
+/*!
+ * \brief JsonDbObject::updateVersionReplicating implements a replicatedWrite
+ * \param other the (remote) object to include into this one.
+ * \return if the passed object was a valid replication
+ */
+bool JsonDbObject::updateVersionReplicating(const JsonDbObject &other)
+{
+ // these two will be the final _meta content
+ QJsonArray history;
+ QJsonArray conflicts;
+
+ // let's go thru all version, i.e. this, this._conflicts, other, and other._conflicts
+ {
+ // thanks to the operator <, documents will sort and remove duplicates
+ // the value is just for show, QSet is based on QHash, which does not sort
+ QMap<JsonDbObject,bool> documents;
+
+ QUuid id = uuid();
+ populateMerge(&documents, id, *this);
+ if (!populateMerge(&documents, id, other, true))
+ return false;
+
+ // now we have all versions sorted and duplicates removed
+ // let's figure out what to keep, what to toss
+ // this is O(n^2) but should be fine in real world situations
+ for (QMap<JsonDbObject,bool>::const_iterator ii = documents.begin(); ii != documents.end(); ii++) {
+ bool alive = !ii.key().isDeleted();
+ for (QMap<JsonDbObject,bool>::const_iterator jj = ii + 1; alive && jj != documents.end(); jj++)
+ if (ii.key().isAncestorOf(jj.key()))
+ alive = false;
+
+ if (ii+1 == documents.end()) {
+ // last element, so found the winner,
+ // assigning to *this, which is head
+ *this = ii.key();
+ populateHistory(&history, *this, false);
+ } else if (alive) {
+ // this is a conflict, strip _meta and keep it
+ JsonDbObject conflict(ii.key());
+ conflict.remove(JsonDbString::kMetaStr);
+ conflicts.append(conflict);
+ } else {
+ // this version was replaced, just keep history
+ populateHistory(&history, ii.key(), true);
+ }
+ }
+ }
+
+ // let's write a new _meta into head
+ if (history.size() || conflicts.size()) {
+ QJsonObject meta;
+ if (history.size())
+ meta.insert(QStringLiteral("history"), history);
+ if (conflicts.size())
+ meta.insert(JsonDbString::kConflictsStr, conflicts);
+ insert(JsonDbString::kMetaStr, meta);
+ } else {
+ // this is really just for sanity reason, but it feels better to have it
+ // aka: this branch should never be reached in real world situations
+ remove(JsonDbString::kMetaStr);
+ }
+
+ return true;
+}
+
+bool JsonDbObject::populateMerge(QMap<JsonDbObject,bool> *documents, const QUuid &id, const JsonDbObject &source, bool validateSource, bool recurse) const
+{
+ // is this the same uuid?
+ bool valid = source.uuid() == id;
+
+ if (valid && validateSource) {
+ // validate that the version is actually correct
+ int count;
+ QString hash = tokenizeVersion(source.version(), &count);
+ if (count == 0 || source.computeVersion(count, hash, 0, 0))
+ valid = false;
+ }
+
+ // there is source._meta.conflicts to explore
+ if (recurse && source.contains(JsonDbString::kMetaStr)) {
+ QJsonArray conflicts = source.value(JsonDbString::kMetaStr).toObject().value(JsonDbString::kConflictsStr).toArray();
+ for (int ii = 0; ii < conflicts.size(); ii++)
+ if (!populateMerge(documents, id, conflicts.at(ii).toObject(), validateSource, false))
+ valid = false;
+ }
+
+ if (valid && documents)
+ documents->insert(source,true);
+
+ return valid;
+}
+
+void JsonDbObject::populateHistory(QJsonArray *history, const JsonDbObject &doc, bool includeCurrent) const
+{
+ QJsonArray versions = doc.value(JsonDbString::kMetaStr).toObject().value(QStringLiteral("history")).toArray();
+
+ for (int ii = 0; ii < versions.size(); ii++) {
+ QJsonValue hash = versions.at(ii);
+ if (hash.isString()) {
+ addAncestor(history, ii + 1, hash.toString());
+ } else if (hash.isArray()) {
+ QJsonArray hashArray = hash.toArray();
+ for (QJsonArray::const_iterator jj = hashArray.begin(); jj != hashArray.end(); jj++) {
+ if ((*jj).isString())
+ addAncestor(history, ii + 1, (*jj).toString());
+ }
+ }
+ }
+
+ if (includeCurrent) {
+ int updateCount;
+ QString hash = tokenizeVersion(doc.version(), &updateCount);
+ addAncestor(history, updateCount, hash);
+ }
+}
+
+QString JsonDbObject::tokenizeVersion(const QString &versionIn, int *updateCountOut) const
+{
+ int updateCount;
+ QString hash;
+
+ if (versionIn.isEmpty()) {
+ updateCount = 0;
+ } else {
+ QStringList splitUp = versionIn.split(QChar('-'));
+ if (splitUp.size() == 2) {
+ updateCount = qMax(1, splitUp.at(0).toInt());
+ hash = splitUp.at(1);
+ } else {
+ updateCount = 1;
+ hash = versionIn;
+ }
+ }
+
+ if (updateCountOut)
+ *updateCountOut = updateCount;
+
+ return hash;
+}
+
+QString JsonDbObject::versionAsString(const int updateCount, const QString &hash) const
+{
+ return QString::number(updateCount) % QStringLiteral("-") % hash;
+}
+
+
+bool JsonDbObject::computeVersion(const int oldUpdateCount, const QString& oldHash, int *newUpdateCount, QString *newHash) const
+{
+ QCryptographicHash md5(QCryptographicHash::Md5);
+
+ for (const_iterator ii = begin(); ii != end(); ii++) {
+ QString key = ii.key();
+ if (key == JsonDbString::kUuidStr || key == JsonDbString::kVersionStr || key == JsonDbString::kMetaStr)
+ continue;
+
+ md5.addData((char *) key.constData(), key.size() * 2);
+
+ char kar = ii.value().type();
+ md5.addData((char *) &kar, 1);
+
+ switch (ii.value().type()) {
+ case QJsonValue::Bool:
+ kar = ii.value().toBool() ? '1' : '0';
+ md5.addData((char *) &kar, 1);
+ break;
+ case QJsonValue::Double: {
+ double value = ii.value().toDouble();
+ md5.addData((char *) &value, sizeof(double));
+ break;
+ }
+ case QJsonValue::String: {
+ QString value = ii.value().toString();
+ md5.addData((char *) value.constData(), value.size() * 2);
+ break;
+ }
+ case QJsonValue::Array: {
+ QJsonDocument doc(ii.value().toArray());
+ int size;
+ const char *data = doc.rawData(&size);
+ md5.addData(data, size);
+ break;
+ }
+ case QJsonValue::Object: {
+ QJsonDocument doc(ii.value().toObject());
+ int size;
+ const char *data = doc.rawData(&size);
+ md5.addData(data, size);
+ break;
+ }
+ default:;
+ // do nothing
+ }
+ }
+
+ QString computedHash = QString::fromLatin1(md5.result().toHex().constData(), 10);
+
+ if (computedHash != oldHash) {
+ if (newUpdateCount)
+ *newUpdateCount = oldUpdateCount + 1;
+ if (newHash)
+ *newHash = computedHash;
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ * \brief JsonDbObject::isAncestorOf tests if this JsonDbObject contains an ancestor version
+ * of the passed JsonDbObject. It does NOT take _uuid into account, it works on version()
+ * only.
+ *
+ * For this method to return a valid answer, the passed object needs to have an intact
+ * _meta object.
+ *
+ * \param other the object to check ancestorship
+ * \return true if this object is an ancestor version of the passed object
+ */
+bool JsonDbObject::isAncestorOf(const JsonDbObject &other) const
+{
+ QJsonArray history = other.value(JsonDbString::kMetaStr).toObject().value(QStringLiteral("history")).toArray();
+
+ int updateCount;
+ QString hash = tokenizeVersion(version(), &updateCount);
+
+ return isAncestorOf(history, updateCount, hash);
+}
+
+bool JsonDbObject::isAncestorOf(const QJsonArray &history, const int updateCount, const QString &hash) const
+{
+ if (updateCount < 1 || history.size() < updateCount)
+ return false;
+
+ QJsonValue knownHashes = history.at(updateCount - 1);
+ if (knownHashes.isString())
+ return knownHashes.toString() == hash;
+ else if (knownHashes.isArray())
+ return knownHashes.toArray().contains(hash);
+ else
+ return false;
+}
+
+void JsonDbObject::addAncestor(QJsonArray *history, const int updateCount, const QString &hash) const
+{
+ if (updateCount < 1 || !history)
+ return;
+
+ int pos = updateCount - 1;
+ for (int ii = history->size(); ii < updateCount; ii++)
+ history->append(QJsonValue::Null);
+
+ QJsonValue old = history->at(pos);
+ if (old.isArray()) {
+ QJsonArray multi = old.toArray();
+ for (int ii = multi.size(); ii-- > 0;) {
+ QString oldHash = multi.at(ii).toString();
+ if (oldHash == hash) {
+ return;
+ } else if (oldHash < hash) {
+ multi.insert(ii + 1, hash);
+ history->replace(pos, multi);
+ return;
+ }
+ }
+ multi.prepend(hash);
+ history->replace(pos, multi);
+ } else if (!old.isString()) {
+ history->replace(pos, hash);
+ } else {
+ QString oldHash = old.toString();
+ if (oldHash == hash)
+ return;
+
+ QJsonArray multi;
+ if (oldHash < hash) {
+ multi.append(oldHash);
+ multi.append(hash);
+ } else if (oldHash > hash) {
+ multi.append(hash);
+ multi.append(oldHash);
+ }
+ history->replace(pos, multi);
+ }
+}
+
+/*!
+ * \brief JsonDbObject::operator < only operates based on version number.
+ * Versions are sorted by update count first, then by string comparing
+ * the hash. This operator does NOT sort by _uuid.
+ *
+ * \sa computeVersion(), version()
+ * \param other the JsonDbObject to compare it to.
+ * \return bool when left side is considered to be an early version
+ */
+bool JsonDbObject::operator <(const JsonDbObject &other) const
+{
+ int myCount;
+ QString myHash = tokenizeVersion(version(), &myCount);
+ int otherCount;
+ QString otherHash = tokenizeVersion(other.version(), &otherCount);
+
+ if (myCount != otherCount)
+ return myCount < otherCount;
+
+ return myHash < otherHash;
+}
+
+
+QJsonValue JsonDbObject::propertyLookup(const QString &path) const
+{
+ return propertyLookup(path.split('.'));
+}
+
+QJsonValue JsonDbObject::propertyLookup(const QStringList &path) const
+{
+ if (!path.size()) {
+ qCritical() << "JsonDb::propertyLookup empty path";
+ abort();
+ return QJsonValue(QJsonValue::Undefined);
+ }
+ // TODO: one malloc here
+ QJsonValue value(*this);
+ for (int i = 0; i < path.size(); i++) {
+ const QString &key = path.at(i);
+ // this part of the property is a list
+ if (value.isArray()) {
+ QJsonArray objectList = value.toArray();
+ bool ok = false;
+ int index = key.toInt(&ok);
+ if (ok && (index >= 0) && (objectList.size() > index))
+ value = objectList.at(index);
+ else
+ value = QJsonValue(QJsonValue::Undefined);
+ } else if (value.isObject()) {
+ QJsonObject o = value.toObject();
+ if (o.contains(key))
+ value = o.value(key);
+ else
+ value = QJsonValue(QJsonValue::Undefined);
+ } else {
+ value = QJsonValue(QJsonValue::Undefined);
+ }
+ }
+ return value;
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbobject.h b/src/partition/jsondbobject.h
new file mode 100644
index 00000000..f5860a47
--- /dev/null
+++ b/src/partition/jsondbobject.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_OBJECT_H
+#define JSONDB_OBJECT_H
+
+#include <QJSEngine>
+#include <QUuid>
+#include <QDebug>
+#include <QVariant>
+
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbObject : public QJsonObject
+{
+public:
+ JsonDbObject();
+ JsonDbObject(const QJsonObject &object);
+ ~JsonDbObject();
+
+ QByteArray toBinaryData() const;
+
+ QUuid uuid() const;
+ QString version() const;
+ QString type() const;
+ bool isDeleted() const;
+ void markDeleted();
+
+ void generateUuid();
+ static QUuid createUuidFromString(const QString &id);
+ QString computeVersion();
+
+ bool updateVersionOptimistic(const JsonDbObject &other, QString *versionWritten);
+ bool updateVersionReplicating(const JsonDbObject & other);
+
+ bool operator <(const JsonDbObject &other) const;
+ bool isAncestorOf(const JsonDbObject &other) const;
+
+ QJsonValue propertyLookup(const QString &path) const;
+ QJsonValue propertyLookup(const QStringList &path) const;
+
+private:
+ bool populateMerge(QMap<JsonDbObject, bool> *documents, const QUuid &id, const JsonDbObject &source, bool validateSource = false, bool recurse = true) const;
+ void populateHistory(QJsonArray *history, const JsonDbObject &doc, bool includeCurrent) const;
+ QString tokenizeVersion(const QString &version, int *updateCount) const;
+ QString versionAsString(const int updateCount, const QString &hash) const;
+
+ bool computeVersion(const int oldUpdateCount, const QString& oldHash, int *newUpdateCount, QString *newHash) const;
+
+ bool isAncestorOf(const QJsonArray &history, const int updateCount, const QString &hash) const;
+ void addAncestor(QJsonArray *history, const int updateCount, const QString &hash) const;
+};
+
+
+typedef QList<JsonDbObject> JsonDbObjectList;
+
+struct GetObjectsResult
+{
+ JsonDbObjectList data;
+ QJsonValue error;
+
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_OBJECT_H
diff --git a/src/partition/jsondbobjectkey.h b/src/partition/jsondbobjectkey.h
new file mode 100644
index 00000000..ca51295d
--- /dev/null
+++ b/src/partition/jsondbobjectkey.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_OBJECT_KEY_H
+#define JSONDB_OBJECT_KEY_H
+
+#include <qbytearray.h>
+#include <qendian.h>
+#include <qdebug.h>
+#include <quuid.h>
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class Q_JSONDB_PARTITION_EXPORT ObjectKey
+{
+public:
+ QUuid key; // object uuid
+
+ ObjectKey() {}
+ ObjectKey(const QUuid &uuid) : key(uuid) {}
+ ObjectKey(const QByteArray &uuid) : key(QUuid::fromRfc4122(uuid)) {}
+ inline bool isNull() const { return key.isNull(); }
+
+ inline QByteArray toByteArray() const
+ {
+ return key.toRfc4122();
+ }
+ inline bool operator==(const ObjectKey &rhs) const
+ { return key == rhs.key; }
+
+ inline bool operator < (const ObjectKey &rhs) const
+ { return key < rhs.key; }
+};
+
+inline QDebug &operator<<(QDebug &qdb, const ObjectKey &objectKey)
+{
+ qdb << objectKey.key.toString();
+ return qdb;
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_BEGIN_NAMESPACE
+
+template <> inline void qToBigEndian(QT_PREPEND_NAMESPACE_JSONDB_PARTITION(ObjectKey) src, uchar *dest)
+{
+ //TODO: improve me
+ QByteArray key = src.key.toRfc4122();
+ memcpy(dest, key.constData(), key.size());
+}
+template <> inline QT_PREPEND_NAMESPACE_JSONDB_PARTITION(ObjectKey) qFromBigEndian(const uchar *src)
+{
+ QT_PREPEND_NAMESPACE_JSONDB_PARTITION(ObjectKey) key;
+ key.key = QUuid::fromRfc4122(QByteArray::fromRawData((const char *)src, 16));
+ return key;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // JSONDB_OBJECT_KEY_H
diff --git a/src/partition/jsondbobjecttable.cpp b/src/partition/jsondbobjecttable.cpp
new file mode 100644
index 00000000..b5d606a0
--- /dev/null
+++ b/src/partition/jsondbobjecttable.cpp
@@ -0,0 +1,707 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QFileInfo>
+#include <QDir>
+#include <QElapsedTimer>
+#include <QJsonDocument>
+
+#include "jsondbobjecttable.h"
+#include "jsondbindex.h"
+#include "jsondbstrings.h"
+#include "jsondbbtree.h"
+#include "jsondbobject.h"
+#include "jsondbsettings.h"
+#include "qbtree.h"
+#include "qbtreecursor.h"
+#include "qbtreetxn.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+void makeStateKey(QByteArray &baStateKey, quint32 stateNumber)
+{
+ baStateKey.resize(5);
+ char *data = baStateKey.data();
+ data[4] = 'S';
+ qToBigEndian(stateNumber, (uchar *)baStateKey.data());
+}
+
+bool isStateKey(const QByteArray &baStateKey)
+{
+ return (baStateKey.size() == 5)
+ && (baStateKey.constData()[4] == 'S');
+}
+
+JsonDbObjectTable::JsonDbObjectTable(JsonDbPartition *partition) :
+ QObject(partition)
+ , mPartition(partition)
+ , mBdb(0)
+{
+ mBdb = new JsonDbBtree();
+}
+
+JsonDbObjectTable::~JsonDbObjectTable()
+{
+ delete mBdb;
+ mBdb = 0;
+}
+
+bool JsonDbObjectTable::open(const QString &fileName)
+{
+ mFilename = fileName;
+#if 0
+ if (!mBdb->setCmpFunc(objectKeyCmp, 0)) {
+ qCritical() << "mBdb->setCmpFunc" << mBdb->errorMessage();
+ return false;
+ }
+#endif
+ mBdb->setCacheSize(jsondbSettings->cacheSize());
+ if (!mBdb->open(mFilename)) {
+ qCritical() << "mBdb->open" << mBdb->errorMessage();
+ return false;
+ }
+ mStateNumber = mBdb->tag();
+ if (jsondbSettings->verbose())
+ qDebug() << "ObjectTable::open" << mStateNumber << mFilename;
+ return true;
+}
+
+void JsonDbObjectTable::close()
+{
+ mBdb->close();
+}
+
+bool JsonDbObjectTable::begin()
+{
+ Q_ASSERT(!mBdb->isWriting());
+ Q_ASSERT(mBdbTransactions.isEmpty());
+ return mBdb->beginWrite() != NULL;
+}
+
+void JsonDbObjectTable::begin(JsonDbIndex *index)
+{
+ if (!index->bdb()->isWriting())
+ mBdbTransactions.append(index->begin());
+}
+
+bool JsonDbObjectTable::commit(quint32 tag)
+{
+ Q_ASSERT(mBdb->isWriting());
+ //qDebug() << "ObjectTable::commit" << tag << mFilename;
+
+ QByteArray baStateKey(5, 0);
+ makeStateKey(baStateKey, tag);
+ bool ok = mBdb->writeTransaction()->put(baStateKey, mStateChanges);
+ if (!ok)
+ qDebug() << "putting statekey ok" << ok << "baStateKey" << baStateKey.toHex();
+ for (int i = 0; i < mStateObjectChanges.size(); ++i) {
+ JsonDbObject object = mStateObjectChanges.at(i);
+ bool ok = mBdb->writeTransaction()->put(baStateKey + object.uuid().toRfc4122(), object.toBinaryData());
+ if (!ok) {
+ qDebug() << "putting state object ok" << ok << "baStateKey" << baStateKey.toHex()
+ << "object" << object;
+ }
+ }
+ mStateChanges.clear();
+ mStateObjectChanges.clear();
+ mStateNumber = tag;
+
+ for (int i = 0; i < mBdbTransactions.size(); i++) {
+ JsonDbBtree::Transaction *txn = mBdbTransactions.at(i);
+ if (!txn->commit(tag)) {
+ qCritical() << __FILE__ << __LINE__ << txn->btree()->errorMessage();
+ }
+ }
+ mBdbTransactions.clear();
+ return mBdb->writeTransaction()->commit(tag);
+}
+
+bool JsonDbObjectTable::abort()
+{
+ Q_ASSERT(mBdb->isWriting());
+ mStateChanges.clear();
+ mStateObjectChanges.clear();
+ for (int i = 0; i < mBdbTransactions.size(); i++) {
+ JsonDbBtree::Transaction *txn = mBdbTransactions.at(i);
+ txn->abort();
+ }
+ mBdbTransactions.clear();
+ mBdb->writeTransaction()->abort();
+ return true;
+}
+
+bool JsonDbObjectTable::compact()
+{
+ for (QHash<QString,IndexSpec>::const_iterator it = mIndexes.begin();
+ it != mIndexes.end();
+ ++it) {
+ const IndexSpec &indexSpec = it.value();
+ // _uuid index does not have bdb() because it is actually the object table itself
+ if (!indexSpec.index->bdb())
+ continue;
+ if (!indexSpec.index->bdb()->compact())
+ return false;
+ }
+ return mBdb->compact();
+}
+
+void JsonDbObjectTable::sync(JsonDbObjectTable::SyncFlags flags)
+{
+ if (flags & SyncObjectTable)
+ mBdb->sync();
+
+ if (flags & SyncIndexes) {
+ foreach (const IndexSpec &spec, mIndexes.values()) {
+ JsonDbIndex *index = spec.index;
+ if (index->bdb()) {
+ quint32 stateNumber = index->stateNumber();
+ if (stateNumber != mStateNumber) {
+ index->begin();
+ index->commit(mStateNumber);
+ }
+ spec.index->bdb()->sync();
+ }
+ }
+ }
+}
+
+JsonDbStat JsonDbObjectTable::stat() const
+{
+ JsonDbStat result;
+ for (QHash<QString,IndexSpec>::const_iterator it = mIndexes.begin();
+ it != mIndexes.end();
+ ++it) {
+ const IndexSpec &indexSpec = it.value();
+ if (indexSpec.index->bdb()) {
+ JsonDbBtree::Stat stat = indexSpec.index->bdb()->btree() ?
+ indexSpec.index->bdb()->stats() :
+ JsonDbBtree::Stat();
+ result += JsonDbStat(stat.reads, stat.hits, stat.writes);
+ }
+ // _uuid index does not have bdb() because it is actually the object table itself
+ }
+ JsonDbBtree::Stat stat = mBdb->btree() ? mBdb->btree()->stats() : JsonDbBtree::Stat();
+ result += JsonDbStat(stat.reads, stat.hits, stat.writes);
+ return result;
+}
+
+
+void JsonDbObjectTable::flushCaches()
+{
+ for (QHash<QString,IndexSpec>::const_iterator it = mIndexes.begin();
+ it != mIndexes.end();
+ ++it) {
+ const IndexSpec &indexSpec = it.value();
+ // _uuid index does not have bdb() because it is actually the object table itself
+ if (!indexSpec.index->bdb())
+ continue;
+ indexSpec.index->bdb()->setCacheSize(1);
+ indexSpec.index->bdb()->setCacheSize(jsondbSettings->cacheSize());
+ }
+}
+
+IndexSpec *JsonDbObjectTable::indexSpec(const QString &indexName)
+{
+ //qDebug() << "ObjectTable::indexSpec" << propertyName << mFilename << (mIndexes.contains(propertyName) ? "exists" : "missing") << (long)this << mIndexes.keys();
+ if (mIndexes.contains(indexName))
+ return &mIndexes[indexName];
+ else
+ return 0;
+}
+
+QHash<QString, IndexSpec> JsonDbObjectTable::indexSpecs() const
+{
+ return mIndexes;
+}
+
+bool JsonDbObjectTable::addIndex(const QString &indexName, const QString &propertyName,
+ const QString &propertyType, const QStringList &objectTypes, const QString &propertyFunction,
+ const QString &locale, const QString &collation, const QString &casePreference,
+ Qt::CaseSensitivity caseSensitivity)
+{
+ Q_ASSERT(propertyName.isEmpty() ^ propertyFunction.isEmpty());
+
+ QString name = indexName.isEmpty() ? propertyName : indexName;
+ //qDebug() << "ObjectTable::addIndex" << propertyName << mFilename << (mIndexes.contains(propertyName) ? "exists" : "to be created");
+ if (mIndexes.contains(name))
+ return true;
+
+ //if (gVerbose) qDebug() << "ObjectTable::addIndex" << propertyName << mFilename;
+
+ QStringList path = propertyName.split('.');
+
+ IndexSpec &indexSpec = mIndexes[name];
+ indexSpec.name = name;
+ indexSpec.propertyName = propertyName;
+ indexSpec.path = path;
+ indexSpec.propertyType = propertyType;
+ indexSpec.locale = locale;
+ indexSpec.collation = collation;
+ indexSpec.casePreference = casePreference;
+ indexSpec.caseSensitivity = caseSensitivity;
+ indexSpec.objectType = objectTypes;
+ indexSpec.lazy = false; //lazy;
+ indexSpec.index = new JsonDbIndex(mFilename, name, propertyName, propertyType, objectTypes, locale, collation, casePreference, caseSensitivity, this);
+ if (!propertyFunction.isEmpty() && propertyName.isEmpty()) // propertyName takes precedence
+ indexSpec.index->setPropertyFunction(propertyFunction);
+ indexSpec.index->setCacheSize(jsondbSettings->cacheSize());
+ bool indexExists = indexSpec.index->exists();
+ indexSpec.index->open();
+
+ QJsonObject indexObject;
+ indexObject.insert(JsonDbString::kTypeStr, JsonDbString::kIndexTypeStr);
+ indexObject.insert(JsonDbString::kNameStr, name);
+ indexObject.insert(JsonDbString::kPropertyNameStr, propertyName);
+ indexObject.insert(JsonDbString::kPropertyTypeStr, propertyType);
+ indexObject.insert(JsonDbString::kLocaleStr, locale);
+ indexObject.insert(JsonDbString::kCollationStr, collation);
+ indexObject.insert(JsonDbString::kCaseSensitiveStr, (bool)caseSensitivity);
+ indexObject.insert(JsonDbString::kCasePreferenceStr, casePreference);
+ QJsonArray objectTypeList;
+ foreach (const QString objectType, objectTypes)
+ objectTypeList.append(objectType);
+ indexObject.insert(JsonDbString::kObjectTypeStr, objectTypeList);
+ indexObject.insert("lazy", false);
+ indexObject.insert(JsonDbString::kPropertyFunctionStr, propertyFunction);
+ Q_ASSERT(!name.isEmpty());
+ Q_ASSERT(mIndexes.contains(name));
+
+ QByteArray baIndexObject;
+ bool needsReindexing = !indexExists;
+ if (indexExists && (indexSpec.index->stateNumber() != mStateNumber)) {
+ needsReindexing = true;
+ if (jsondbSettings->verbose())
+ qDebug() << "Index" << name << "stateNumber" << indexSpec.index->stateNumber() << "objectTable.stateNumber" << mStateNumber << "reindexing" << "clearing";
+ indexSpec.index->clearData();
+ }
+ if (needsReindexing)
+ reindexObjects(name, path, stateNumber());
+
+ return true;
+}
+
+bool JsonDbObjectTable::removeIndex(const QString &indexName)
+{
+ IndexSpec *spec = indexSpec(indexName);
+ if (!spec)
+ return false;
+
+ QString propertyName = spec->propertyName;
+ if (propertyName == JsonDbString::kUuidStr || propertyName == JsonDbString::kTypeStr)
+ return true;
+
+ if (spec->index) {
+ if (spec->index->bdb()
+ && spec->index->bdb()->isWriting()) { // Incase index is removed via Jdb::remove( _type=Index )
+ mBdbTransactions.remove(mBdbTransactions.indexOf(spec->index->bdb()->writeTransaction()));
+ spec->index->abort();
+ }
+ spec->index->close();
+ if (spec->index->bdb())
+ QFile::remove(spec->index->bdb()->fileName());
+ delete spec->index;
+ mIndexes.remove(indexName);
+ }
+
+ return true;
+}
+
+void JsonDbObjectTable::reindexObjects(const QString &indexName, const QStringList &path, quint32 stateNumber)
+{
+ Q_ASSERT(mIndexes.contains(indexName));
+
+ if (jsondbSettings->verbose())
+ qDebug() << "reindexObjects" << indexName << "{";
+ if (indexName == JsonDbString::kUuidStr) {
+ return;
+ }
+
+ IndexSpec &indexSpec = mIndexes[indexName];
+ JsonDbIndex *index = indexSpec.index;
+ bool isInIndexTransaction = index->bdb()->writeTransaction();
+ bool isInObjectTableTransaction = mBdb->writeTransaction();
+ JsonDbBtree::Transaction *bdbTxn = mBdb->writeTransaction() ? mBdb->writeTransaction() : mBdb->beginWrite();
+ JsonDbBtree::Cursor cursor(bdbTxn);
+ if (!isInIndexTransaction)
+ index->begin();
+ for (bool ok = cursor.first(); ok; ok = cursor.next()) {
+ QByteArray baKey, baObject;
+ bool rok = cursor.current(&baKey, &baObject);
+ Q_ASSERT(rok);
+ if (baKey.size() != 16) // state key is 5 bytes, or history key is 5 + 16 bytes
+ continue;
+ ObjectKey objectKey(baKey);
+ JsonDbObject object = QJsonDocument::fromBinaryData(baObject).object();
+ if (object.value(JsonDbString::kDeletedStr).toBool())
+ continue;
+ QJsonValue fieldValue = object.propertyLookup(path);
+ if (!fieldValue.isNull())
+ index->indexObject(objectKey, object, stateNumber);
+ }
+ if (!isInIndexTransaction)
+ index->commit(stateNumber);
+ if (!isInObjectTableTransaction)
+ bdbTxn->abort();
+ if (jsondbSettings->verbose())
+ qDebug() << "} reindexObjects";
+}
+
+void JsonDbObjectTable::indexObject(const ObjectKey &objectKey, JsonDbObject object, quint32 stateNumber)
+{
+ if (jsondbSettings->debug())
+ qDebug() << "ObjectTable::indexObject" << objectKey << object.value(JsonDbString::kVersionStr).toString() << endl << mIndexes.keys();
+ for (QHash<QString,IndexSpec>::const_iterator it = mIndexes.begin();
+ it != mIndexes.end();
+ ++it) {
+ Q_ASSERT(mBdb->isWriting());
+ const IndexSpec &indexSpec = it.value();
+ if (indexSpec.propertyName == JsonDbString::kUuidStr)
+ continue;
+ if (indexSpec.lazy)
+ continue;
+ indexSpec.index->indexObject(objectKey, object, stateNumber);
+ }
+}
+
+void JsonDbObjectTable::deindexObject(const ObjectKey &objectKey, JsonDbObject object, quint32 stateNumber)
+{
+ if (jsondbSettings->debug())
+ qDebug() << "ObjectTable::deindexObject" << objectKey << object.value(JsonDbString::kVersionStr).toString() << endl << mIndexes.keys();
+
+ for (QHash<QString,IndexSpec>::const_iterator it = mIndexes.begin();
+ it != mIndexes.end();
+ ++it) {
+ Q_ASSERT(mBdb->isWriting());
+ const IndexSpec &indexSpec = it.value();
+ if (jsondbSettings->debug())
+ qDebug() << "ObjectTable::deindexObject" << indexSpec.propertyName;
+ if (indexSpec.propertyName == JsonDbString::kUuidStr)
+ continue;
+ if (indexSpec.lazy)
+ continue;
+ indexSpec.index->deindexObject(objectKey, object, stateNumber);
+ }
+}
+
+void JsonDbObjectTable::updateIndex(JsonDbIndex *index)
+{
+ quint32 indexStateNumber = qMax(1u, index->bdb()->tag());
+ if (indexStateNumber == stateNumber())
+ return;
+ JsonDbUpdateList changeList;
+ changesSince(indexStateNumber, QSet<QString>(), &changeList);
+ bool inTransaction = mBdb->isWriting();
+ if (!inTransaction)
+ index->begin();
+ else if (!index->bdb()->isWriting()) {
+ index->begin();
+ mBdbTransactions.append(index->begin());
+ }
+ foreach (const JsonDbUpdate &change, changeList) {
+ JsonDbObject before = change.oldObject;
+ JsonDbObject after = change.newObject;
+ ObjectKey objectKey(after.value(JsonDbString::kUuidStr).toString());
+ if (!before.isEmpty())
+ index->deindexObject(objectKey, before, stateNumber());
+ if (!after.isDeleted())
+ index->indexObject(objectKey, after, stateNumber());
+ }
+ if (!inTransaction)
+ index->commit(stateNumber());
+}
+
+
+bool JsonDbObjectTable::get(const ObjectKey &objectKey, QJsonObject *object, bool includeDeleted)
+{
+ QByteArray baObjectKey(objectKey.toByteArray());
+ QByteArray baObject;
+ bool ok = mBdb->getOne(baObjectKey, &baObject);
+ if (!ok)
+ return false;
+ QJsonObject o(QJsonDocument::fromBinaryData(baObject).object());
+ if (!includeDeleted && o.value(JsonDbString::kDeletedStr).toBool())
+ return false;
+ *object = o;
+ return true;
+}
+
+bool JsonDbObjectTable::put(const ObjectKey &objectKey, const JsonDbObject &object)
+{
+ QByteArray baObjectKey(objectKey.toByteArray());
+ return mBdb->putOne(baObjectKey, object.toBinaryData());
+}
+
+bool JsonDbObjectTable::remove(const ObjectKey &objectKey)
+{
+ QByteArray baObjectKey(objectKey.toByteArray());
+ return mBdb->removeOne(baObjectKey);
+}
+
+QString JsonDbObjectTable::errorMessage() const
+{
+ return mBdb->errorMessage();
+}
+
+GetObjectsResult JsonDbObjectTable::getObjects(const QString &keyName, const QJsonValue &keyValue, const QString &objectType)
+{
+ GetObjectsResult result;
+ JsonDbObjectList objectList;
+ bool typeSpecified = !objectType.isEmpty();
+
+ if (keyName == JsonDbString::kUuidStr) {
+ ObjectKey objectKey(keyValue.toString());
+ JsonDbObjectList objectList;
+ JsonDbObject object;
+ bool ok = get(objectKey, &object);
+ if (ok
+ && (!typeSpecified
+ || (object.value(JsonDbString::kTypeStr).toString() == objectType)))
+ objectList.append(object);
+ result.data = objectList;
+ return result;
+ }
+
+ if (!mIndexes.contains(keyName) && (keyName != JsonDbString::kTypeStr)) {
+ qDebug() << "ObjectTable::getObject" << "no index for" << keyName << mFilename;
+ return result;
+ }
+
+ if (!mIndexes.contains(keyName) && (keyName == JsonDbString::kTypeStr)) {
+ bool isInTransaction = mBdb->writeTransaction();
+ JsonDbBtree::Transaction *txn = mBdb->writeTransaction() ? mBdb->writeTransaction() : mBdb->beginWrite();
+ JsonDbBtree::Cursor cursor(txn);
+ for (bool ok = cursor.first(); ok; ok = cursor.next()) {
+ QByteArray baKey, baObject;
+ ok = cursor.current(&baKey, &baObject);
+ if (!ok)
+ break;
+ if (baKey.size() != 16) // state key is 5 bytes, or history key is 5 + 16 bytes
+ continue;
+ JsonDbObject object = QJsonDocument::fromBinaryData(baObject).object();
+ if (object.value(JsonDbString::kDeletedStr).toBool())
+ continue;
+ objectList.append(object);
+ }
+ if (!isInTransaction)
+ txn->abort();
+ result.data = objectList;
+ return result;
+ }
+
+ const IndexSpec *indexSpec = &mIndexes[keyName];
+ QByteArray forwardKey(makeForwardKey(makeFieldValue(keyValue, indexSpec->propertyType), ObjectKey()));
+ //fprintf(stderr, "getObject bdb=%p\n", indexSpec->index->bdb());
+ if (indexSpec->lazy)
+ updateIndex(indexSpec->index);
+ bool isInTransaction = indexSpec->index->bdb()->writeTransaction();
+ JsonDbBtree::Transaction *txn = indexSpec->index->bdb()->writeTransaction() ? indexSpec->index->bdb()->writeTransaction() : indexSpec->index->bdb()->beginWrite();
+ JsonDbBtree::Cursor cursor(txn);
+ if (cursor.seekRange(forwardKey)) {
+ do {
+ QByteArray checkKey;
+ QByteArray forwardValue;
+ bool ok = cursor.current(&checkKey, &forwardValue);
+ QJsonValue checkValue;
+ forwardKeySplit(checkKey, checkValue);
+ if (checkValue != keyValue)
+ break;
+
+ ObjectKey objectKey;
+ forwardValueSplit(forwardValue, objectKey);
+ if (jsondbSettings->debug() && jsondbSettings->verbose())
+ qDebug() << "ok" << ok << "forwardValue" << forwardValue << "objectKey" << objectKey;
+
+ JsonDbObject map;
+ if (get(objectKey, &map)) {
+ //qDebug() << "ObjectTable::getObject" << "deleted" << map.value(JsonDbString::kDeletedStr, false).toBool();
+ if (map.contains(JsonDbString::kDeletedStr) && map.value(JsonDbString::kDeletedStr).toBool())
+ continue;
+ if (typeSpecified && (map.value(JsonDbString::kTypeStr).toString() != objectType))
+ continue;
+
+ objectList.append(map);
+ } else {
+ if (jsondbSettings->debug())
+ qDebug() << "Failed to get object" << objectKey << errorMessage();
+ }
+ } while (cursor.next());
+ }
+ if (!isInTransaction)
+ txn->abort();
+
+ result.data = objectList;
+ return result;
+}
+
+quint32 JsonDbObjectTable::storeStateChange(const ObjectKey &key, const JsonDbUpdate &change)
+{
+ quint32 stateNumber = mStateNumber + 1;
+
+ int oldSize = mStateChanges.size();
+ mStateChanges.resize(oldSize + 20);
+ uchar *data = (uchar *)mStateChanges.data() + oldSize;
+
+ qToBigEndian(key, data);
+ qToBigEndian<quint32>(change.action, data+16);
+ if (!change.oldObject.isEmpty())
+ mStateObjectChanges.append(change.oldObject);
+ return stateNumber;
+}
+
+quint32 JsonDbObjectTable::changesSince(quint32 stateNumber, QMap<ObjectKey,JsonDbUpdate> *changes)
+{
+ if (!changes)
+ return -1;
+ stateNumber = qMax(quint32(1), stateNumber+1);
+
+ QElapsedTimer timer;
+ if (jsondbSettings->performanceLog())
+ timer.start();
+ bool inTransaction = mBdb->writeTransaction();
+ JsonDbBtree::Transaction *txn = mBdb->writeTransaction() ? mBdb->writeTransaction() : mBdb->beginWrite();
+ JsonDbBtree::Cursor cursor(txn);
+ QByteArray baStateKey(5, 0);
+ makeStateKey(baStateKey, stateNumber);
+
+ QMap<ObjectKey,JsonDbUpdate> changeMap; // collect one change per uuid
+
+ if (cursor.seekRange(baStateKey)) {
+ do {
+ QByteArray baObject;
+ bool ok = cursor.current(&baStateKey, &baObject);
+ if (!ok)
+ break;
+
+ if (!isStateKey(baStateKey))
+ continue;
+ stateNumber = qFromBigEndian<quint32>((const uchar *)baStateKey.constData());
+
+ if (baObject.size() % 20 != 0) {
+ qWarning() << __FUNCTION__ << __LINE__ << "state size must be a multiplier 20"
+ << baObject.size() << baObject.toHex();
+ continue;
+ }
+
+ for (int i = 0; i < baObject.size() / 20; ++i) {
+ const uchar *data = (const uchar *)baObject.constData() + i*20;
+ ObjectKey objectKey = qFromBigEndian<ObjectKey>(data);
+ QByteArray baObjectKey(objectKey.key.toRfc4122());
+ quint32 action = qFromBigEndian<quint32>(data + 16);
+ QByteArray baValue;
+ QJsonObject oldObject;
+ if ((action != JsonDbNotification::Create)
+ && mBdb->getOne(baStateKey + baObjectKey, &baValue)) {
+ oldObject = QJsonDocument::fromBinaryData(baValue).object();
+ Q_ASSERT(objectKey == ObjectKey(oldObject.value(JsonDbString::kUuidStr).toString()));
+ }
+ QJsonObject newObject;
+ mBdb->getOne(baObjectKey, &baValue);
+ newObject = QJsonDocument::fromBinaryData(baValue).object();
+ if (jsondbSettings->debug())
+ qDebug() << "change" << action << endl << oldObject << endl << newObject;
+
+ JsonDbUpdate change(oldObject, newObject, JsonDbNotification::Action(action));
+ if (changeMap.contains(objectKey)) {
+ JsonDbUpdate oldChange = changeMap.value(objectKey);
+ // create followed by delete cancels out
+ JsonDbNotification::Action newAction = JsonDbNotification::Action(action);
+ JsonDbNotification::Action oldAction = oldChange.action;
+ if ((oldAction == JsonDbNotification::Create)
+ && (newAction == JsonDbNotification::Delete)) {
+ changeMap.remove(objectKey);
+ } else {
+ if ((oldAction == JsonDbNotification::Delete)
+ && (newAction == JsonDbNotification::Create))
+ oldChange.action = JsonDbNotification::Update;
+ changeMap.insert(objectKey, oldChange);
+ }
+ } else {
+ changeMap.insert(objectKey, change);
+ }
+ }
+ } while (cursor.next());
+ }
+ *changes = changeMap;
+ if (!inTransaction)
+ txn->abort();
+
+ if (jsondbSettings->performanceLog())
+ qDebug() << "changesSince" << mFilename << timer.elapsed() << "ms";
+ return mStateNumber;
+}
+
+quint32 JsonDbObjectTable::changesSince(quint32 stateNumber, const QSet<QString> &limitTypes, QList<JsonDbUpdate> *updateList, JsonDbObjectTable::TypeChangeMode splitTypeChanges)
+{
+ if (jsondbSettings->verbose())
+ qDebug() << "changesSince" << stateNumber << limitTypes << "{";
+ QMap<ObjectKey,JsonDbUpdate> changeSet;
+ changesSince(stateNumber, &changeSet);
+ QList<JsonDbUpdate> changeList;
+ bool allTypes = limitTypes.isEmpty();
+ foreach (const JsonDbUpdate &update, changeSet) {
+ const JsonDbObject &oldObject = update.oldObject;
+ const JsonDbObject &newObject = update.newObject;
+ QString oldType = oldObject.value(JsonDbString::kTypeStr).toString();
+ QString newType = newObject.value(JsonDbString::kTypeStr).toString();
+ // if the types don't match, split into two updates
+ if (!oldObject.isEmpty() && oldType != newType && splitTypeChanges == JsonDbObjectTable::SplitTypeChanges) {
+ if (allTypes || limitTypes.contains(oldType)) {
+ JsonDbObject tombstone(oldObject);
+ tombstone.insert(QLatin1String("_deleted"), true);
+ changeList.append(JsonDbUpdate(oldObject, tombstone, JsonDbNotification::Delete));
+ }
+ if (allTypes || limitTypes.contains(newType)) {
+ changeList.append(JsonDbUpdate(JsonDbObject(), newObject, JsonDbNotification::Create));
+ }
+ } else if (allTypes || limitTypes.contains(oldType) || limitTypes.contains(newType)) {
+ changeList.append(update);
+ }
+ }
+ if (updateList)
+ *updateList = changeList;
+ if (jsondbSettings->verbose())
+ qDebug() << "changesSince" << changeList.size() << "changes" << "}";
+ return mStateNumber;
+}
+
+#include "moc_jsondbobjecttable.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbobjecttable.h b/src/partition/jsondbobjecttable.h
new file mode 100644
index 00000000..a98b6b47
--- /dev/null
+++ b/src/partition/jsondbobjecttable.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_OBJECT_TABLE_H
+#define JSONDB_OBJECT_TABLE_H
+
+#include <QObject>
+#include <QHash>
+#include <QList>
+#include <QPair>
+#include <QtEndian>
+
+#include "jsondbobjectkey.h"
+#include "jsondbbtree.h"
+
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+#include <jsondbobject.h>
+#include "jsondbpartition.h"
+#include "jsondbindex.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbBtree;
+
+inline QDebug &operator<<(QDebug &qdb, const JsonDbUpdate &oc)
+{
+ qdb.nospace() << "JsonDbUpdate(";
+ //qdb.nospace() << oc.objectKey;
+ qdb.nospace() << "action = ";
+ switch (oc.action) {
+ case JsonDbNotification::None: qdb.nospace() << "None"; break;
+ case JsonDbNotification::Create: qdb.nospace() << "Created"; break;
+ case JsonDbNotification::Update: qdb.nospace() << "Updated"; break;
+ case JsonDbNotification::Delete: qdb.nospace() << "Deleted"; break;
+ }
+ if (oc.action != JsonDbNotification::Create)
+ qdb.nospace() << ", oldObject = " << oc.oldObject;
+ qdb.nospace() << ")";
+ return qdb;
+}
+
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbObjectTable : public QObject
+{
+ Q_OBJECT
+public:
+ enum SyncFlag {
+ SyncObjectTable = 0x1,
+ SyncIndexes = 0x2
+ };
+ Q_DECLARE_FLAGS(SyncFlags, SyncFlag)
+
+ JsonDbObjectTable(JsonDbPartition *parent=0);
+ ~JsonDbObjectTable();
+
+ QString filename() const { return mFilename; }
+ bool open(const QString &filename);
+ void close();
+ JsonDbPartition *partition() const { return mPartition; }
+ JsonDbBtree *bdb() const { return mBdb; }
+ bool begin();
+ void begin(JsonDbIndex *btree);
+ bool commit(quint32);
+ bool abort();
+ bool compact();
+ void sync(SyncFlags flags);
+
+ JsonDbStat stat() const;
+ void flushCaches();
+
+ quint32 stateNumber() const { return mStateNumber; }
+ quint32 storeStateChange(const ObjectKey &key, const JsonDbUpdate &stateChange);
+ enum TypeChangeMode {
+ SplitTypeChanges, KeepTypeChanges
+ };
+ quint32 changesSince(quint32 stateNumber, const QSet<QString> &limitTypes, QList<JsonDbUpdate> *updateList, TypeChangeMode mode=KeepTypeChanges);
+
+ IndexSpec *indexSpec(const QString &indexName);
+ QHash<QString, IndexSpec> indexSpecs() const;
+
+ bool addIndex(const QString &indexName,
+ const QString &propertyName = QString(),
+ const QString &propertyType = QString("string"),
+ const QStringList &objectTypes = QStringList(),
+ const QString &propertyFunction = QString(),
+ const QString &locale = QString(),
+ const QString &collation = QString(),
+ const QString &casePreference = QString(),
+ Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive);
+ bool addIndexOnProperty(const QString &propertyName,
+ const QString &propertyType = QString("string"),
+ const QString &objectType = QString())
+ { return addIndex(propertyName, propertyName, propertyType,
+ objectType.isEmpty() ? QStringList() : (QStringList() << objectType)); }
+ bool removeIndex(const QString &indexName);
+ void reindexObjects(const QString &indexName, const QStringList &path, quint32 stateNumber);
+ void indexObject(const ObjectKey &objectKey, JsonDbObject object, quint32 stateNumber);
+ void deindexObject(const ObjectKey &objectKey, JsonDbObject object, quint32 stateNumber);
+ void updateIndex(JsonDbIndex *index);
+
+ bool get(const ObjectKey &objectKey, QJsonObject *object, bool includeDeleted=false);
+ bool put(const ObjectKey &objectKey, const JsonDbObject &object);
+ bool remove(const ObjectKey &objectKey);
+
+ QString errorMessage() const;
+
+ GetObjectsResult getObjects(const QString &keyName, const QJsonValue &keyValue, const QString &objectType);
+
+private:
+ quint32 changesSince(quint32 stateNumber, QMap<ObjectKey,JsonDbUpdate> *changes);
+
+private:
+ JsonDbPartition *mPartition;
+ QString mFilename;
+ JsonDbBtree *mBdb;
+ QHash<QString,IndexSpec> mIndexes; // indexed by full path, e.g., _type or _name.first
+ QVector<JsonDbBtree::Transaction *> mBdbTransactions;
+
+ quint32 mStateNumber;
+
+ // intermediate state changes until the commit is called
+ QByteArray mStateChanges;
+ QList<JsonDbObject> mStateObjectChanges;
+};
+
+void makeStateKey(QByteArray &baStateKey, quint32 stateNumber);
+bool isStateKey(const QByteArray &baStateKey);
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(JsonDbObjectTable::SyncFlags)
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_OBJECT_TABLE_H
diff --git a/src/partition/jsondbobjecttypes_impl_p.h b/src/partition/jsondbobjecttypes_impl_p.h
new file mode 100644
index 00000000..f2833c9b
--- /dev/null
+++ b/src/partition/jsondbobjecttypes_impl_p.h
@@ -0,0 +1,352 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_OBJECTTYPES_IMPL_P_H
+#define JSONDB_OBJECTTYPES_IMPL_P_H
+
+#include "jsondbpartitionglobal.h"
+#include "jsondbobjecttypes_p.h"
+#include "jsondbschemamanager_p.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+inline QJsonObjectTypes::ValueList::ValueList(const QJsonArray &list) : QJsonArray(list)
+{}
+
+inline uint QJsonObjectTypes::ValueList::size() const
+{
+ return QJsonArray::size();
+}
+
+inline QJsonObjectTypes::ValueList::const_iterator QJsonObjectTypes::ValueList::constBegin() const
+{
+ return const_iterator(0, this);
+}
+
+inline QJsonObjectTypes::ValueList::const_iterator QJsonObjectTypes::ValueList::constEnd() const
+{
+ return const_iterator(size(), this);
+}
+
+inline QJsonObjectTypes::ValueList::const_iterator::const_iterator()
+ : m_index(-1)
+ , m_list(0)
+{}
+
+inline QJsonObjectTypes::Value QJsonObjectTypes::ValueList::const_iterator::operator *() const
+{
+ Q_ASSERT(isValid());
+ return Value(m_index, *m_list);
+}
+
+inline bool QJsonObjectTypes::ValueList::const_iterator::operator !=(const const_iterator &other) const
+{
+ return m_index != other.m_index || m_list != other.m_list;
+}
+
+inline QJsonObjectTypes::ValueList::const_iterator& QJsonObjectTypes::ValueList::const_iterator::operator ++()
+{
+ m_index++;
+ return *this;
+}
+
+inline QJsonObjectTypes::ValueList::const_iterator::const_iterator(int begin, const QJsonArray *list)
+ : m_index(begin)
+ , m_list(list)
+{}
+
+inline bool QJsonObjectTypes::ValueList::const_iterator::isValid() const
+{
+ return m_index != -1 && m_index < m_list->size();
+}
+
+inline QJsonObjectTypes::Value::Value(Key propertyName, const QJsonObject &map)
+ : m_index(-1)
+ , m_property(propertyName)
+ , m_value(map)
+ , m_type(propertyName.isEmpty() ? RootMap : Map)
+{}
+
+inline QJsonObjectTypes::Value::Value(const int index, const QJsonArray &list)
+ : m_index(index)
+ , m_value(list)
+ , m_type(List)
+{}
+
+inline int QJsonObjectTypes::Value::toInt(bool *ok) const
+{
+ if (m_intCache.isValid()) {
+ *ok = true;
+ return m_intCache.value();
+ }
+ int result = 0;
+ QJsonValue::Type type;
+ switch (m_type) {
+ case Map:
+ type = typeMap();
+ if (type == QJsonValue::Double) {
+ QJsonValue v = map().value(m_property);
+ double doubleResult = v.toDouble();
+ int intResult = (int)doubleResult;
+ if ((double)intResult == doubleResult) {
+ *ok = true;
+ result = intResult;
+ } else {
+ *ok = false;
+ }
+ } else {
+ *ok = false;
+ }
+ m_intCache.set(*ok, result);
+ return result;
+ case List:
+ type = typeList();
+ if (type == QJsonValue::Double) {
+ QJsonValue v = list().at(m_index);
+ double doubleResult = v.toDouble();
+ int intResult = (int)doubleResult;
+ if ((double)intResult == doubleResult) {
+ *ok = true;
+ result = intResult;
+ } else {
+ *ok = false;
+ }
+ } else {
+ *ok = false;
+ }
+ m_intCache.set(*ok, result);
+ return result;
+ case RootMap:
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ *ok = false;
+ return -1;
+}
+
+inline double QJsonObjectTypes::Value::toDouble(bool *ok) const
+{
+ if (m_doubleCache.isValid()) {
+ *ok = true;
+ return m_doubleCache.value();
+ }
+ double result;
+ QJsonValue::Type type;
+ switch (m_type) {
+ case Map:
+ type = typeMap();
+ *ok = type == QJsonValue::Double;
+ result = map().value(m_property).toDouble();
+ m_doubleCache.set(*ok, result);
+ return result;
+ case List:
+ type = typeList();
+ *ok = type == QJsonValue::Double;
+ result = list().at(m_index).toDouble();
+ m_doubleCache.set(*ok, result);
+ return result;
+ case RootMap:
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ *ok = false;
+ return -1;
+}
+
+inline QJsonObjectTypes::ValueList QJsonObjectTypes::Value::toList(bool *ok) const
+{
+ *ok = true;
+ switch (m_type) {
+ case Map:
+ *ok = typeMap() == QJsonValue::Array;
+ return map().value(m_property).toArray();
+ case List:
+ *ok = typeList() == QJsonValue::Array;
+ return list().at(m_index).toArray();
+ case RootMap:
+ return m_value.toArray();
+ default:
+ Q_ASSERT(false);
+ }
+ *ok = false;
+ return ValueList(QJsonArray());
+}
+
+inline QString QJsonObjectTypes::Value::toString(bool *ok) const
+{
+ switch (m_type) {
+ case Map:
+ *ok = typeMap() == QJsonValue::String;
+ return map().value(m_property).toString();
+ case List:
+ *ok = typeList() == QJsonValue::String;
+ return list().at(m_index).toString();
+ case RootMap:
+ *ok = true;
+ return QString(); // useful for debugging
+ default:
+ Q_ASSERT(false);
+ }
+ *ok = false;
+ return QString();
+}
+
+inline bool QJsonObjectTypes::Value::toBool(bool *ok) const
+{
+ switch (m_type) {
+ case Map:
+ *ok = typeMap() == QJsonValue::Bool;
+ return map().value(m_property).toBool();
+ case List:
+ *ok = typeList() == QJsonValue::Bool;
+ return list().at(m_index).toBool();
+ case RootMap:
+ default:
+ Q_ASSERT(false);
+ }
+ *ok = false;
+ return false;
+}
+
+inline void QJsonObjectTypes::Value::toNull(bool *ok) const
+{
+ switch (m_type) {
+ case Map:
+ *ok = typeMap() == QJsonValue::Null;
+ case List:
+ *ok = typeList() == QJsonValue::Null;
+ case RootMap:
+ default:
+ Q_ASSERT(false);
+ }
+ *ok = false;
+}
+
+inline QJsonObjectTypes::Object QJsonObjectTypes::Value::toObject(bool *ok) const
+{
+ switch (m_type) {
+ case Map:
+ *ok = typeMap() == QJsonValue::Object;
+ return map().value(m_property).toObject();
+ case List:
+ *ok = typeList() == QJsonValue::Object;
+ return list().at(m_index).toObject();
+ case RootMap:
+ *ok = true;
+ return static_cast<Object>(m_value.toObject());
+ default:
+ Q_ASSERT(false);
+ }
+ *ok = false;
+ return Object(QJsonObject());
+}
+
+inline const QJsonObject QJsonObjectTypes::Value::map() const
+{
+ Q_ASSERT(m_type == Map);
+ Q_ASSERT(!m_property.isEmpty());
+ return m_value.toObject();
+}
+
+inline const QJsonArray QJsonObjectTypes::Value::list() const
+{
+ Q_ASSERT(m_type == List);
+ Q_ASSERT(m_index >= 0);
+ return m_value.toArray();
+}
+
+inline QJsonValue::Type QJsonObjectTypes::Value::typeMap() const
+{
+ if (m_qsonTypeCache.isValid())
+ return m_qsonTypeCache.value();
+ QJsonValue::Type result = map().value(m_property).type();
+ m_qsonTypeCache.set(true, result);
+ return result;
+}
+
+inline QJsonValue::Type QJsonObjectTypes::Value::typeList() const
+{
+ if (m_qsonTypeCache.isValid())
+ return m_qsonTypeCache.value();
+ QJsonValue::Type result = list().at(m_index).type();
+ m_qsonTypeCache.set(true, result);
+ return result;
+}
+
+inline QJsonObjectTypes::Object::Object()
+{}
+
+inline QJsonObjectTypes::Object::Object(const QJsonObject &map)
+ : QJsonObject(map)
+{}
+
+inline QJsonObjectTypes::Value QJsonObjectTypes::Object::property(const QJsonObjectTypes::Key& name) const
+{
+ return Value(name, *this);
+}
+
+inline QList<QJsonObjectTypes::Key> QJsonObjectTypes::Object::propertyNames() const { return QJsonObject::keys(); }
+
+inline QJsonObjectTypes::Service::Service(JsonDbSchemaManager *schemas)
+ : m_schemas(schemas)
+{}
+
+inline QJsonObject QJsonObjectTypes::Service::error() const
+{
+ return m_errorMap;
+}
+
+inline void QJsonObjectTypes::Service::setError(const QString &message)
+{
+ m_errorMap.insert(JsonDbString::kCodeStr, JsonDbError::FailedSchemaValidation);
+ m_errorMap.insert(JsonDbString::kMessageStr, message);
+}
+
+inline SchemaValidation::Schema<QJsonObjectTypes> QJsonObjectTypes::Service::loadSchema(const QString &schemaName)
+{
+ return m_schemas->schema(schemaName, this);
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+#endif // JSONDB_OBJECTTYPES_IMPL_P_H
diff --git a/src/partition/jsondbobjecttypes_p.h b/src/partition/jsondbobjecttypes_p.h
new file mode 100644
index 00000000..f2b42f58
--- /dev/null
+++ b/src/partition/jsondbobjecttypes_p.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_OBJECTTYPES_P_H
+#define JSONDB_OBJECTTYPES_P_H
+
+#include "jsondbpartitionglobal.h"
+#include "jsondbstrings.h"
+
+#include "schema-validation/object.h"
+
+#include <QPair>
+
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbSchemaManager;
+
+/**
+ \internal
+ This is type definition for schema validation framework. It was
+ created because of planed change of data representation in jsondb
+ (Bson -> Qson -> QtJson). Essentially schema validation is
+ independent from data representation. The performance cost of this
+ indirection is about 0. We can consider removing it in future or
+ leave it and check different data representation (think about
+ QJSValue).
+
+ These types define the simplest types in JSON.
+ */
+class QJsonObjectTypes {
+public:
+ typedef QString Key;
+
+ class Value;
+ class ValueList : protected QJsonArray
+ {
+ public:
+ inline ValueList(const QJsonArray &list);
+
+ // interface
+ class const_iterator;
+ inline uint size() const;
+ inline const_iterator constBegin() const;
+ inline const_iterator constEnd() const;
+ class const_iterator
+ {
+ friend class ValueList;
+ public:
+ inline const_iterator();
+ inline Value operator *() const;
+ inline bool operator !=(const const_iterator &other) const;
+ inline const_iterator& operator ++();
+
+ private:
+ inline const_iterator(int begin, const QJsonArray *list);
+ inline bool isValid() const;
+
+ int m_index;
+ const QJsonArray *m_list;
+ };
+ };
+
+ class Object;
+ class Value
+ {
+ enum Type {List, Map, RootMap};
+
+ template<class T>
+ class Cache : protected QPair<bool, T>
+ {
+ public:
+ Cache()
+ : QPair<bool, T>(false, T())
+ {}
+ bool isValid() const { return QPair<bool, T>::first; }
+ T value() const { Q_ASSERT(isValid()); return QPair<bool, T>::second; }
+ void set(const bool ok, const T &value) { QPair<bool, T>::first = ok; QPair<bool, T>::second = value; }
+ };
+
+ public:
+ inline Value(Key propertyName, const QJsonObject &map);
+ inline Value(const int index, const QJsonArray &list);
+
+ // interface
+ inline int toInt(bool *ok) const;
+ inline double toDouble(bool *ok) const;
+ inline ValueList toList(bool *ok) const;
+ inline QString toString(bool *ok) const;
+ inline bool toBool(bool *ok) const;
+ inline void toNull(bool *ok) const;
+ inline Object toObject(bool *ok) const;
+
+ private:
+ inline const QJsonObject map() const;
+ inline const QJsonArray list() const;
+ inline QJsonValue::Type typeMap() const;
+ inline QJsonValue::Type typeList() const;
+
+ const int m_index;
+ const Key m_property;
+
+ const QJsonValue m_value;
+ const Type m_type;
+
+ mutable Cache<QJsonValue::Type> m_qsonTypeCache;
+ mutable Cache<int> m_intCache;
+ mutable Cache<double> m_doubleCache;
+ };
+
+ class Object : public QJsonObject
+ {
+ public:
+ inline Object(const QJsonObject &map);
+
+ // interface
+ inline Object();
+ inline Value property(const Key& name) const;
+ inline QList<Key> propertyNames() const;
+ };
+
+ class Service {
+ public:
+ inline Service(JsonDbSchemaManager *schemas);
+ inline QJsonObject error() const;
+
+ // interface
+ inline void setError(const QString &message);
+ inline SchemaValidation::Schema<QJsonObjectTypes> loadSchema(const QString &schemaName);
+
+ private:
+ JsonDbSchemaManager *m_schemas;
+ QJsonObject m_errorMap;
+ };
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+#endif // JSONDB_OBJECTTYPES_P_H
diff --git a/src/partition/jsondbowner.cpp b/src/partition/jsondbowner.cpp
new file mode 100644
index 00000000..0d07fe3a
--- /dev/null
+++ b/src/partition/jsondbowner.cpp
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbowner.h"
+#include "jsondbpartition.h"
+#include "jsondbsettings.h"
+#include "jsondbstrings.h"
+#include <qdebug.h>
+
+#ifdef Q_OS_UNIX
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#endif
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbOwner::JsonDbOwner( QObject *parent )
+ : QObject(parent), mStorageQuota(-1), mAllowAll(false)
+{
+}
+
+void cleanQueryList(QList<JsonDbQuery *> &queryList)
+{
+ foreach (JsonDbQuery *q, queryList)
+ delete q;
+ queryList.clear();
+}
+
+JsonDbOwner::~JsonDbOwner()
+{
+ QMap<QString, QList<JsonDbQuery *> > list;
+ foreach (list, mAllowedObjectQueries) {
+ foreach (QList<JsonDbQuery *> queryList, list)
+ cleanQueryList(queryList);
+ }
+}
+
+void JsonDbOwner::setAllowedObjects(const QString &partition, const QString &op,
+ const QList<QString> &queries)
+{
+ mAllowedObjects[partition][op] = queries;
+ cleanQueryList(mAllowedObjectQueries[partition][op]);
+ QJsonObject bindings;
+ bindings.insert(QLatin1String("domain"), domain());
+ bindings.insert(QLatin1String("owner"), ownerId());
+ foreach (const QString &query, queries) {
+ mAllowedObjectQueries[partition][op].append(JsonDbQuery::parse(query, bindings));
+ }
+}
+
+void JsonDbOwner::setCapabilities(QJsonObject &applicationCapabilities, JsonDbPartition *partition)
+{
+ QJsonObject request;
+ GetObjectsResult result = partition->getObjects(JsonDbString::kTypeStr, QString("Capability"));
+ JsonDbObjectList translations = result.data;
+ //qDebug() << "JsonDbOwner::setCapabilities" << "translations" << translations;
+
+ QMap<QString, QSet<QString> > allowedObjects;
+ const QStringList ops = (QStringList() << "read" << "write" << "setOwner");
+
+ for (int i = 0; i < translations.size(); ++i) {
+ JsonDbObject translation = translations.at(i);
+ QString name = translation.value("name").toString();
+ QString partition = translation.value("partition").toString();
+ partition = partition.replace("%owner", ownerId());
+ if (applicationCapabilities.contains(name)) {
+ QJsonObject accessRules = translation.value("accessRules").toObject();
+ QVariantList accessTypesAllowed =
+ applicationCapabilities.value(name).toArray().toVariantList();
+ foreach (QVariant accessTypeAllowed, accessTypesAllowed) {
+ QJsonObject accessTypeTranslation =
+ accessRules.value(accessTypeAllowed.toString()).toObject();
+ foreach (const QString op, ops) {
+ if (accessTypeTranslation.contains(op)) {
+ QStringList rules;
+ QJsonArray jsonRules = accessTypeTranslation.value(op).toArray();
+ for (int r = 0; r < jsonRules.size(); r++) {
+ rules.append(jsonRules[r].toString());
+ }
+ allowedObjects[op].unite(QSet<QString>::fromList(rules));
+ }
+ }
+ }
+ }
+ foreach (const QString op, ops) {
+ if (!allowedObjects.value(op).empty())
+ setAllowedObjects(partition, op, allowedObjects.value(op).toList());
+ }
+ allowedObjects.clear();
+ }
+ if (jsondbSettings->verbose()) {
+ qDebug() << "setCapabilities" << mOwnerId;
+ qDebug() << mAllowedObjects << endl;
+ }
+}
+
+bool JsonDbOwner::isAllowed(JsonDbObject &object, const QString &partition,
+ const QString &op) const
+{
+ if (mAllowAll || !jsondbSettings->enforceAccessControl())
+ return true;
+
+ QString _type = object[QLatin1String("_type")].toString();
+ QStringList domainParts = _type.split(QLatin1Char('.'), QString::SkipEmptyParts);
+ QString typeDomain;
+ // TODO: handle reverse domains like co.uk.foo, co.au.bar, co.nz.foo , .... correctly
+ if (domainParts.count() > 2)
+ typeDomain = _type.left(_type.lastIndexOf(QLatin1Char('.'))+1);
+ else
+ // Capability queries with _typeDomain should fail, if there is not long enough domain
+ typeDomain = QLatin1String("public.domain.fail.");
+ QJsonValue tdval = QJsonValue(typeDomain);
+
+ QList<JsonDbQuery *> queries = mAllowedObjectQueries[partition][op];
+ foreach (JsonDbQuery *query, queries) {
+ query->bind(QString(QLatin1String("typeDomain")), tdval);
+ if (query->match(object, NULL, NULL))
+ return true;
+ }
+ queries = mAllowedObjectQueries[QLatin1String("all")][op];
+ foreach (JsonDbQuery *query, queries) {
+ query->bind(QString(QLatin1String("typeDomain")), tdval);
+ if (query->match(object, NULL, NULL))
+ return true;
+ }
+ if (jsondbSettings->verbose()) {
+ qDebug () << "Not allowed" << ownerId() << partition << _type << op;
+ }
+ return false;
+}
+
+bool JsonDbOwner::_setOwnerCapabilities(struct passwd *pwd, JsonDbPartition *partition)
+{
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ mOwnerId = QString::fromLocal8Bit(pwd->pw_name);
+
+ // Parse domain from username
+ // TODO: handle reverse domains like co.uk.foo, co.au.bar, co.nz.foo , .... correctly
+ QStringList domainParts = mOwnerId.split(QLatin1Char('.'), QString::SkipEmptyParts);
+ if (domainParts.count() > 2)
+ mDomain = domainParts.at(0)+QLatin1Char('.')+domainParts.at(1);
+ else
+ mDomain = QStringLiteral("public");
+
+ if (jsondbSettings->debug())
+ qDebug() << "username" << mOwnerId << "uid" << pwd->pw_uid << "domain set to" << mDomain;
+
+ // Get capabilities from supplementary groups
+ if (pwd->pw_uid) {
+ int ngroups = 128;
+ gid_t groups[128];
+ bool setOwner = false;
+ QJsonObject capabilities;
+ if (::getgrouplist(pwd->pw_name, pwd->pw_gid, groups, &ngroups) != -1) {
+ struct group *gr;
+ for (int i = 0; i < ngroups; i++) {
+ gr = ::getgrgid(groups[i]);
+ if (gr && ::strcasecmp (gr->gr_name, "identity") == 0)
+ setOwner = true;
+ }
+ // Start from 1 to omit the primary group
+ for (int i = 1; i < ngroups; i++) {
+ gr = ::getgrgid(groups[i]);
+ QJsonArray value;
+ if (!gr || ::strcasecmp (gr->gr_name, "identity") == 0)
+ continue;
+ if (setOwner)
+ value.append(QJsonValue(QLatin1String("setOwner")));
+ value.append(QJsonValue(QLatin1String("rw")));
+ capabilities.insert(QString::fromLocal8Bit(gr->gr_name), value);
+ if (jsondbSettings->debug())
+ qDebug() << "Adding capability" << QString::fromLocal8Bit(gr->gr_name)
+ << "to user" << mOwnerId << "setOwner =" << setOwner;
+ }
+ if (ngroups)
+ setCapabilities(capabilities, partition);
+ } else {
+ qWarning() << Q_FUNC_INFO << mOwnerId << "belongs to too many groups (>128)";
+ }
+ } else {
+ // root can access all
+ setAllowAll(true);
+ setStorageQuota(-1);
+ }
+
+ // Read quota from security object
+ GetObjectsResult result = partition->getObjects(JsonDbString::kTypeStr, QString("Quota"));
+ JsonDbObjectList securityObjects;
+ for (int i = 0; i < result.data.size(); i++) {
+ JsonDbObject doc = result.data.at(i);
+ if (doc.value(JsonDbString::kTokenStr).toString() == mOwnerId)
+ securityObjects.append(doc);
+ }
+ if (securityObjects.size() == 1) {
+ QJsonObject securityObject = securityObjects.at(0);
+ QJsonObject capabilities = securityObject.value("capabilities").toObject();
+ QStringList keys = capabilities.keys();
+ if (keys.contains("quotas")) {
+ QJsonObject quotas = capabilities.value("quotas").toObject();
+ int storageQuota = quotas.value("storage").toDouble();
+ setStorageQuota(storageQuota);
+ }
+ } else if (!securityObjects.isEmpty()) {
+ qWarning() << Q_FUNC_INFO << "Wrong number of security objects found." << securityObjects.size();
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool JsonDbOwner::setOwnerCapabilities(QString username, JsonDbPartition *partition)
+{
+ if (!jsondbSettings->enforceAccessControl()) {
+ setAllowAll(true);
+ return true;
+ }
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ struct passwd *pwd = ::getpwnam(username.toLocal8Bit());
+ if (!pwd) {
+ qWarning() << Q_FUNC_INFO << "pwd entry for" << username <<
+ "not found" << strerror(errno);
+ return false;
+ }
+ return _setOwnerCapabilities (pwd, partition);
+#else
+ setAllowAll(true);
+ return true;
+#endif
+}
+
+bool JsonDbOwner::setOwnerCapabilities(uid_t uid, JsonDbPartition *partition)
+{
+ if (!jsondbSettings->enforceAccessControl()) {
+ setAllowAll(true);
+ return true;
+ }
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ struct passwd *pwd = ::getpwuid(uid);
+ if (!pwd) {
+ qWarning() << Q_FUNC_INFO << "pwd entry for" << uid <<
+ "not found" << strerror(errno);
+ return false;
+ }
+ return _setOwnerCapabilities (pwd, partition);
+#else
+ setAllowAll(true);
+ return true;
+#endif
+}
+
+#include "moc_jsondbowner.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbowner.h b/src/partition/jsondbowner.h
new file mode 100644
index 00000000..c3416103
--- /dev/null
+++ b/src/partition/jsondbowner.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_OWNER_H
+#define JSONDB_OWNER_H
+
+#include <QObject>
+#include <QMap>
+#include <QString>
+#include <QSet>
+
+#include "jsondbpartitionglobal.h"
+#include "jsondbquery.h"
+#include "jsondbobject.h"
+
+QT_BEGIN_HEADER
+
+class TestPartition;
+struct passwd;
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbOwner : public QObject
+{
+ Q_OBJECT
+public:
+ JsonDbOwner( QObject *parent = 0 );
+ ~JsonDbOwner();
+ QString ownerId() const { return mOwnerId; }
+ void setOwnerId(const QString &ownerId) { mOwnerId = ownerId; }
+ QString domain() const { return mDomain; }
+ void setDomain(const QString &domain) { mDomain = domain; }
+
+ QList<QString> allowedObjects(const QString &op) const;
+
+ void setAllowedObjects(const QString &partition, const QString &op,
+ const QList<QString> &queries);
+ void setCapabilities(QJsonObject &capabilities, JsonDbPartition *partition);
+ bool allowAll() const { return mAllowAll; }
+ void setAllowAll(bool allowAll) { mAllowAll = allowAll; }
+
+ bool isAllowed(JsonDbObject &object, const QString &partition, const QString &op) const;
+ int storageQuota() const { return mStorageQuota; }
+ void setStorageQuota(int storageQuota) { mStorageQuota = storageQuota; }
+ bool setOwnerCapabilities(uid_t uid, JsonDbPartition *partition);
+ bool setOwnerCapabilities(QString username, JsonDbPartition *partition);
+
+private:
+ QString mOwnerId; // from security object
+ QString mDomain; // from security object
+ QMap<QString, QMap<QString, QList<QString> > > mAllowedObjects; // list of querie strings per partition
+ QMap<QString, QMap<QString, QList<JsonDbQuery *> > > mAllowedObjectQueries;
+ int mStorageQuota;
+ bool mAllowAll;
+ friend class ::TestPartition;
+ bool _setOwnerCapabilities(struct passwd *pwd, JsonDbPartition *partition);
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_OWNER_H
diff --git a/src/partition/jsondbpartition.cpp b/src/partition/jsondbpartition.cpp
new file mode 100644
index 00000000..80500d42
--- /dev/null
+++ b/src/partition/jsondbpartition.cpp
@@ -0,0 +1,1920 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QObject>
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QJsonDocument>
+#include <QRegExp>
+#include <QString>
+#include <QElapsedTimer>
+#include <QUuid>
+#include <QtAlgorithms>
+#include <QtEndian>
+#include <QStringBuilder>
+#include <QTimerEvent>
+#include <QMap>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "jsondbstrings.h"
+#include "jsondberrors.h"
+#include "jsondbpartition.h"
+#include "jsondbindex.h"
+#include "jsondbindexquery.h"
+#include "jsondbobjecttable.h"
+#include "jsondbbtree.h"
+#include "jsondbsettings.h"
+#include "jsondbview.h"
+#include "jsondbschemamanager_impl_p.h"
+#include "jsondbobjecttypes_impl_p.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+const QString gDatabaseSchemaVersion = "0.2";
+
+JsonDbPartition::JsonDbPartition(const QString &filename, const QString &name, JsonDbOwner *owner, QObject *parent)
+ : QObject(parent)
+ , mObjectTable(0)
+ , mPartitionName(name)
+ , mFilename(filename)
+ , mTransactionDepth(0)
+ , mWildCardPrefixRegExp("([^*?\\[\\]\\\\]+).*")
+ , mMainSyncTimerId(-1)
+ , mIndexSyncTimerId(-1)
+ , mDefaultOwner(owner)
+{
+ if (!mFilename.endsWith(QLatin1String(".db")))
+ mFilename += QLatin1String(".db");
+
+ mMainSyncInterval = jsondbSettings->syncInterval();
+ if (mMainSyncInterval < 1000)
+ mMainSyncInterval = 5000;
+ mIndexSyncInterval = jsondbSettings->indexSyncInterval();
+ if (mIndexSyncInterval < 1000)
+ mIndexSyncInterval = 12000;
+}
+
+JsonDbPartition::~JsonDbPartition()
+{
+ if (mTransactionDepth) {
+ qCritical() << "JsonDbBtreePartition::~JsonDbBtreePartition"
+ << "closing while transaction open" << "mTransactionDepth" << mTransactionDepth;
+ }
+ close();
+}
+
+bool JsonDbPartition::close()
+{
+ foreach (JsonDbView *view, mViews.values()) {
+ view->close();
+ delete view;
+ }
+ mViews.clear();
+
+ delete mObjectTable;
+ mObjectTable = 0;
+
+ return true;
+}
+
+bool JsonDbPartition::open()
+{
+ if (jsondbSettings->debug())
+ qDebug() << "JsonDbBtree::open" << mPartitionName << mFilename;
+
+ mObjectTable = new JsonDbObjectTable(this);
+ mObjectTable->open(mFilename);
+
+ if (!checkStateConsistency()) {
+ qCritical() << "JsonDbBtreePartition::open()" << "Unable to recover database";
+ return false;
+ }
+
+ bool rebuildingDatabaseMetadata = false;
+
+ QString partitionId;
+ GetObjectsResult getObjectsResult = mObjectTable->getObjects(JsonDbString::kTypeStr, QJsonValue(JsonDbString::kDbidTypeStr),
+ JsonDbString::kDbidTypeStr);
+ if (getObjectsResult.data.size()) {
+ QJsonObject object = getObjectsResult.data.at(0);
+ partitionId = object.value("id").toString();
+ QString partitionName = object.value("name").toString();
+ if (partitionName != mPartitionName || !object.contains(JsonDbString::kDatabaseSchemaVersionStr)
+ || object.value(JsonDbString::kDatabaseSchemaVersionStr).toString() != gDatabaseSchemaVersion) {
+ if (jsondbSettings->verbose())
+ qDebug() << "Rebuilding database metadata";
+ rebuildingDatabaseMetadata = true;
+ }
+ }
+
+ if (partitionId.isEmpty() || rebuildingDatabaseMetadata) {
+ if (partitionId.isEmpty())
+ partitionId = QUuid::createUuid().toString();
+
+ QByteArray baKey(4, 0);
+ qToBigEndian(0, (uchar *)baKey.data());
+
+ JsonDbObject object;
+ object.insert(JsonDbString::kTypeStr, JsonDbString::kDbidTypeStr);
+ object.insert(QLatin1String("id"), partitionId);
+ object.insert(QLatin1String("name"), mPartitionName);
+ object.insert(JsonDbString::kDatabaseSchemaVersionStr, gDatabaseSchemaVersion);
+ JsonDbWriteResult result = updateObject(mDefaultOwner, object, ForcedWrite);
+ Q_ASSERT(result.code == JsonDbError::NoError);
+ }
+ if (jsondbSettings->verbose())
+ qDebug() << "partition" << mPartitionName << "id" << partitionId;
+
+ initSchemas();
+ initIndexes();
+ JsonDbView::initViews(this);
+
+ return true;
+}
+
+bool JsonDbPartition::clear()
+{
+ if (mObjectTable->bdb()) {
+ qCritical() << "Cannot clear database while it is open.";
+ return false;
+ }
+ QStringList filters;
+ QFileInfo fi(mFilename);
+ filters << QString::fromLatin1("%1*.db").arg(fi.baseName());
+ QDir dir(fi.absolutePath());
+ QStringList lst = dir.entryList(filters);
+ foreach (const QString &fileName, lst) {
+ if (jsondbSettings->verbose())
+ qDebug() << "removing" << fileName;
+ if (!dir.remove(fileName)) {
+ qCritical() << "Failed to remove" << fileName;
+ return false;
+ }
+ }
+ return true;
+}
+
+inline quint16 fieldValueSize(QJsonValue::Type vt, const QJsonValue &fieldValue)
+{
+ switch (vt) {
+ case QJsonValue::Undefined:
+ case QJsonValue::Null:
+ case QJsonValue::Array:
+ case QJsonValue::Object:
+ return 0;
+ case QJsonValue::Bool:
+ return 4;
+ case QJsonValue::Double:
+ return 8;
+ case QJsonValue::String:
+ return 2*fieldValue.toString().count();
+ }
+ return 0;
+}
+
+void memcpyFieldValue(char *data, QJsonValue::Type vt, const QJsonValue &fieldValue)
+{
+ switch (vt) {
+ case QJsonValue::Undefined:
+ case QJsonValue::Null:
+ case QJsonValue::Array:
+ case QJsonValue::Object:
+ break;
+ case QJsonValue::Bool: {
+ quint32 value = fieldValue.toBool() ? 1 : 0;
+ qToBigEndian(value, (uchar *)data);
+ } break;
+ case QJsonValue::Double: {
+ union {
+ double d;
+ quint64 ui;
+ };
+ d = fieldValue.toDouble();
+ qToBigEndian<quint64>(ui, (uchar *)data);
+ } break;
+ case QJsonValue::String: {
+ QString str = fieldValue.toString();
+ memcpy(data, (const char *)str.constData(), 2*str.count());
+ }
+ }
+}
+
+void memcpyFieldValue(QJsonValue::Type vt, QJsonValue &fieldValue, const char *data, quint16 size)
+{
+ switch (vt) {
+ case QJsonValue::Undefined:
+ case QJsonValue::Array:
+ case QJsonValue::Object:
+ break;
+ case QJsonValue::Null:
+ fieldValue = QJsonValue();
+ break;
+ case QJsonValue::Bool: {
+ fieldValue = qFromBigEndian<qint32>((const uchar *)data) == 1 ? true : false;
+ } break;
+ case QJsonValue::Double: {
+ union {
+ double d;
+ quint64 ui;
+ };
+ ui = qFromBigEndian<quint64>((const uchar *)data);
+ fieldValue = d;
+ } break;
+ case QJsonValue::String: {
+ fieldValue = QString((const QChar *)data, size/2);
+ }
+ }
+}
+
+int intcmp(const uchar *aptr, const uchar *bptr)
+{
+ qint32 a = qFromBigEndian<qint32>((const uchar *)aptr);
+ qint32 b = qFromBigEndian<qint32>((const uchar *)bptr);
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+}
+int doublecmp(const uchar *aptr, const uchar *bptr)
+{
+ union {
+ double d;
+ quint64 ui;
+ } a, b;
+ a.ui = qFromBigEndian<quint64>((const uchar *)aptr);
+ b.ui = qFromBigEndian<quint64>((const uchar *)bptr);
+ if (a.d < b.d)
+ return -1;
+ if (a.d > b.d)
+ return 1;
+ return 0;
+}
+int qstringcmp(const quint16 *achar, quint32 acount, const quint16 *bchar, quint32 bcount)
+{
+ int rv = 0;
+ quint32 minCount = qMin(acount, bcount);
+ for (quint32 i = 0; i < minCount; i++) {
+ if ((rv = (achar[i] - bchar[i])) != 0)
+ return rv;
+ }
+ return acount-bcount;
+}
+
+QJsonValue makeFieldValue(const QJsonValue &value, const QString &type)
+{
+ if (type.isEmpty() || type == QLatin1String("string")) {
+ switch (value.type()) {
+ case QJsonValue::Null: return QLatin1String("null");
+ case QJsonValue::Bool: return QLatin1String(value.toBool() ? "true" : "false");
+ case QJsonValue::Double: return QString::number(value.toDouble());
+ case QJsonValue::String: return value.toString();
+ case QJsonValue::Array: {
+ QJsonArray array = value.toArray();
+ if (array.size() == 1)
+ return makeFieldValue(array.at(0), type);
+ return QJsonValue(QJsonValue::Undefined);
+ }
+ case QJsonValue::Object: break;
+ case QJsonValue::Undefined: break;
+ }
+ } else if ((type == QLatin1String("number"))
+ || (type == QLatin1String("integer"))) {
+ switch (value.type()) {
+ case QJsonValue::Null: return 0;
+ case QJsonValue::Bool: return value.toBool() ? 1 : 0;
+ case QJsonValue::Double: return value.toDouble();
+ case QJsonValue::String: {
+ QString str = value.toString();
+ bool ok = false;
+ double dval = str.toDouble(&ok);
+ if (ok)
+ return dval;
+ int ival = str.toInt(&ok);
+ if (ok)
+ return ival;
+ break;
+ }
+ case QJsonValue::Array: {
+ QJsonArray array = value.toArray();
+ if (array.size() == 1)
+ return makeFieldValue(array.at(0), type);
+ return QJsonValue(QJsonValue::Undefined);
+ }
+ case QJsonValue::Object: break;
+ case QJsonValue::Undefined: break;
+ }
+ } else {
+ qWarning() << "qtjsondb: makeFieldValue: unsupported index type" << type;
+ }
+ return QJsonValue(QJsonValue::Undefined);
+}
+
+static const int VtLastKind = QJsonValue::Undefined;
+QByteArray makeForwardKey(const QJsonValue &fieldValue, const ObjectKey &objectKey)
+{
+ QJsonValue::Type vt = fieldValue.type();
+ Q_ASSERT(vt <= VtLastKind);
+ quint32 size = fieldValueSize(vt, fieldValue);
+
+ QByteArray forwardKey(4+size+16, 0);
+ char *data = forwardKey.data();
+ qToBigEndian<quint32>(vt, (uchar *)&data[0]);
+ memcpyFieldValue(data+4, vt, fieldValue);
+ qToBigEndian(objectKey, (uchar *)&data[4+size]);
+
+ return forwardKey;
+}
+
+void forwardKeySplit(const QByteArray &forwardKey, QJsonValue &fieldValue)
+{
+ const char *data = forwardKey.constData();
+ QJsonValue::Type vt = (QJsonValue::Type)qFromBigEndian<quint32>((const uchar *)&data[0]);
+ Q_ASSERT(vt <= VtLastKind);
+ quint32 fvSize = forwardKey.size()-4-16;
+ memcpyFieldValue(vt, fieldValue, data+4, fvSize);
+}
+void forwardKeySplit(const QByteArray &forwardKey, QJsonValue &fieldValue, ObjectKey &objectKey)
+{
+ const char *data = forwardKey.constData();
+ QJsonValue::Type vt = (QJsonValue::Type)qFromBigEndian<quint32>((const uchar *)&data[0]);
+ Q_ASSERT(vt <= VtLastKind);
+ quint32 fvSize = forwardKey.size()-4-16;
+ memcpyFieldValue(vt, fieldValue, data+4, fvSize);
+ objectKey = qFromBigEndian<ObjectKey>((const uchar *)&data[4+fvSize]);
+}
+int forwardKeyCmp(const QByteArray &ab, const QByteArray &bb)
+{
+ const char *aptr = ab.constData();
+ size_t asiz = ab.size();
+ const char *bptr = bb.constData();
+ size_t bsiz = bb.size();
+
+ if (!bsiz && !asiz)
+ return 0;
+ if (!bsiz)
+ return 1;
+ if (!asiz)
+ return -1;
+
+ int rv = 0;
+ QJsonValue::Type avt = (QJsonValue::Type)qFromBigEndian<quint32>((const uchar *)&aptr[0]);
+ QJsonValue::Type bvt = (QJsonValue::Type)qFromBigEndian<quint32>((const uchar *)&bptr[0]);
+ Q_ASSERT(avt <= VtLastKind);
+ Q_ASSERT(bvt <= VtLastKind);
+ quint32 asize = asiz - 4 - 16;
+ quint32 bsize = bsiz - 4 - 16;
+ if (avt != bvt)
+ return avt - bvt;
+
+ const char *aData = aptr + 4;
+ const char *bData = bptr + 4;
+ switch (avt) {
+ case QJsonValue::Bool:
+ rv = intcmp((const uchar *)aData, (const uchar *)bData);
+ break;
+ case QJsonValue::Double:
+ rv = doublecmp((const uchar *)aData, (const uchar *)bData);
+ break;
+ case QJsonValue::String:
+ rv = qstringcmp((const quint16 *)aData, asize/2, (const quint16 *)bData, bsize/2);
+ break;
+ case QJsonValue::Undefined:
+ case QJsonValue::Null:
+ case QJsonValue::Array:
+ case QJsonValue::Object:
+ rv = 0;
+ break;
+ }
+ if (rv != 0)
+ return rv;
+ ObjectKey aObjectKey = qFromBigEndian<ObjectKey>((const uchar *)aptr+4+asize);
+ ObjectKey bObjectKey = qFromBigEndian<ObjectKey>((const uchar *)bptr+4+bsize);
+ if (aObjectKey == bObjectKey)
+ return 0;
+ return aObjectKey < bObjectKey ? -1 : 1;
+}
+
+QByteArray makeForwardValue(const ObjectKey &objectKey)
+{
+ QByteArray forwardValue(16, 0);
+ char *data = forwardValue.data();
+ qToBigEndian(objectKey, (uchar *)&data[0]);
+ return forwardValue;
+}
+void forwardValueSplit(const QByteArray &forwardValue, ObjectKey &objectKey)
+{
+ const uchar *data = (const uchar *)forwardValue.constData();
+ objectKey = qFromBigEndian<ObjectKey>(&data[0]);
+}
+
+JsonDbView *JsonDbPartition::addView(const QString &viewType)
+{
+ JsonDbView *view = mViews.value(viewType);
+ if (view)
+ return view;
+
+ view = new JsonDbView(this, viewType, this);
+ view->open();
+ mViews.insert(viewType, view);
+ return view;
+}
+
+void JsonDbPartition::removeView(const QString &viewType)
+{
+ JsonDbView *view = mViews.value(viewType);
+ view->close();
+ view->deleteLater();
+ mViews.remove(viewType);
+}
+
+void JsonDbPartition::updateView(const QString &objectType, quint32 stateNumber)
+{
+ if (!mViews.contains(objectType))
+ return;
+ mViews[objectType]->updateView(stateNumber);
+}
+
+bool JsonDbPartition::checkCanAddSchema(const JsonDbObject &schema, const JsonDbObject &oldSchema, QString &errorMsg)
+{
+ if (!schema.contains("name") || !schema.contains("schema")) {
+ errorMsg = QLatin1String("_schemaType objects must specify both name and schema properties");
+ return false;
+ }
+
+ QString schemaName = schema.value("name").toString();
+
+ if (schemaName.isEmpty()) {
+ errorMsg = QLatin1String("name property of _schemaType object must be specified");
+ return false;
+ } else if (mSchemas.contains(schemaName) && oldSchema.value("name").toString() != schemaName) {
+ errorMsg = QString("A schema with name %1 already exists").arg(schemaName);
+ return false;
+ }
+
+ return true;
+}
+
+JsonDbView *JsonDbPartition::findView(const QString &objectType) const
+{
+ if (mViews.contains(objectType))
+ return mViews.value(objectType);
+ else
+ return 0;
+}
+
+JsonDbObjectTable *JsonDbPartition::findObjectTable(const QString &objectType) const
+{
+ if (mViews.contains(objectType))
+ return mViews.value(objectType)->objectTable();
+ else
+ return mObjectTable;
+}
+
+bool JsonDbPartition::beginTransaction()
+{
+ if (mTransactionDepth++ == 0) {
+ Q_ASSERT(mTableTransactions.isEmpty());
+ }
+ return true;
+}
+
+bool JsonDbPartition::commitTransaction(quint32 stateNumber)
+{
+ if (--mTransactionDepth == 0) {
+ bool ret = true;
+ quint32 nextStateNumber = stateNumber ? stateNumber : (mObjectTable->stateNumber() + 1);
+
+ if (jsondbSettings->debug())
+ qDebug() << "commitTransaction" << stateNumber;
+
+ if (!stateNumber && (mTableTransactions.size() == 1))
+ nextStateNumber = mTableTransactions.at(0)->stateNumber() + 1;
+
+ for (int i = 0; i < mTableTransactions.size(); i++) {
+ JsonDbObjectTable *table = mTableTransactions.at(i);
+ if (!table->commit(nextStateNumber)) {
+ qCritical() << __FILE__ << __LINE__ << "Failed to commit transaction on object table";
+ ret = false;
+ }
+ }
+ mTableTransactions.clear();
+
+ if (mMainSyncTimerId == -1)
+ mMainSyncTimerId = startTimer(mMainSyncInterval, Qt::VeryCoarseTimer);
+ if (mIndexSyncTimerId == -1)
+ mIndexSyncTimerId = startTimer(mIndexSyncInterval, Qt::VeryCoarseTimer);
+
+ return ret;
+ }
+ return true;
+}
+
+bool JsonDbPartition::abortTransaction()
+{
+ if (--mTransactionDepth == 0) {
+ if (jsondbSettings->verbose())
+ qDebug() << "JsonDbBtreePartition::abortTransaction()";
+ bool ret = true;
+
+ for (int i = 0; i < mTableTransactions.size(); i++) {
+ JsonDbObjectTable *table = mTableTransactions.at(i);
+ if (!table->abort()) {
+ qCritical() << __FILE__ << __LINE__ << "Failed to abort transaction";
+ ret = false;
+ }
+ }
+ mTableTransactions.clear();
+ return ret;
+ }
+ return true;
+}
+
+QJsonObject JsonDbPartition::flush()
+{
+ mObjectTable->sync(JsonDbObjectTable::SyncObjectTable);
+
+ QJsonObject resultmap, errormap;
+ resultmap.insert(JsonDbString::kStateNumberStr, (int)mObjectTable->stateNumber());
+ return makeResponse(resultmap, errormap);
+}
+
+
+void JsonDbPartition::timerEvent(QTimerEvent *event)
+{
+ if (mTransactionDepth)
+ return;
+
+ if (event->timerId() == mMainSyncTimerId) {
+ if (jsondbSettings->debug())
+ qDebug() << "Syncing main object table";
+
+ mObjectTable->sync(JsonDbObjectTable::SyncObjectTable);
+ killTimer(mMainSyncTimerId);
+ mMainSyncTimerId = -1;
+ } else if (event->timerId() == mIndexSyncTimerId) {
+
+ if (jsondbSettings->debug())
+ qDebug() << "Syncing indexes and views";
+
+ // sync the main object table's indexes
+ mObjectTable->sync(JsonDbObjectTable::SyncIndexes);
+
+ // sync the views
+ foreach (JsonDbView *view, mViews)
+ view->objectTable()->sync(JsonDbObjectTable::SyncObjectTable | JsonDbObjectTable::SyncIndexes);
+
+ killTimer(mIndexSyncTimerId);
+ mIndexSyncTimerId = -1;
+ }
+}
+
+bool JsonDbPartition::getObject(const QString &uuid, JsonDbObject &object, const QString &objectType) const
+{
+ ObjectKey objectKey(uuid);
+ return getObject(objectKey, object, objectType);
+}
+
+bool JsonDbPartition::getObject(const ObjectKey &objectKey, JsonDbObject &object, const QString &objectType) const
+{
+ JsonDbObjectTable *table = findObjectTable(objectType);
+
+ bool ok = table->get(objectKey, &object);
+ if (ok)
+ return ok;
+ QHash<QString,QPointer<JsonDbView> >::const_iterator it = mViews.begin();
+ for (; it != mViews.end(); ++it) {
+ JsonDbView *view = it.value();
+ if (!view)
+ qDebug() << "no view";
+ if (!view->objectTable())
+ qDebug() << "no object table for view";
+ bool ok = view->objectTable()->get(objectKey, &object);
+ if (ok)
+ return ok;
+ }
+ return false;
+}
+
+GetObjectsResult JsonDbPartition::getObjects(const QString &keyName, const QJsonValue &keyValue, const QString &_objectType, bool updateViews)
+{
+ const QString &objectType = (keyName == JsonDbString::kTypeStr) ? keyValue.toString() : _objectType;
+ JsonDbObjectTable *table = findObjectTable(objectType);
+
+ if (updateViews && (table != mObjectTable))
+ updateView(objectType);
+ return table->getObjects(keyName, keyValue, objectType);
+}
+
+QJsonObject JsonDbPartition::changesSince(quint32 stateNumber, const QSet<QString> &limitTypes)
+{
+ JsonDbObjectTable *objectTable = 0;
+ if (!limitTypes.size())
+ objectTable = mObjectTable;
+ else
+ foreach (const QString &limitType, limitTypes) {
+ JsonDbObjectTable *ot = findObjectTable(limitType);
+ if (!objectTable)
+ objectTable = ot;
+ else if (ot == objectTable)
+ continue;
+ else
+ return JsonDbPartition::makeError(JsonDbError::InvalidRequest, "limit types must be from the same object table");
+ }
+ Q_ASSERT(objectTable);
+ JsonDbUpdateList changeList;
+ quint32 currentStateNumber = objectTable->changesSince(stateNumber, limitTypes, &changeList);
+ QJsonArray changeArray;
+ foreach (const JsonDbUpdate &update, changeList) {
+ QJsonObject change;
+ change.insert("before", update.oldObject);
+ change.insert("after", update.newObject);
+ changeArray.append(change);
+ }
+
+ QJsonObject resultmap, errormap;
+ resultmap.insert("count", changeArray.size());
+ resultmap.insert("startingStateNumber", static_cast<qint32>(stateNumber));
+ resultmap.insert("currentStateNumber", static_cast<qint32>(currentStateNumber));
+ resultmap.insert("changes", changeArray);
+ QJsonObject changesSince(JsonDbPartition::makeResponse(resultmap, errormap));
+ return changesSince;
+}
+
+void JsonDbPartition::flushCaches()
+{
+ mObjectTable->flushCaches();
+ for (QHash<QString,QPointer<JsonDbView> >::const_iterator it = mViews.begin();
+ it != mViews.end();
+ ++it)
+ it.value()->reduceMemoryUsage();
+}
+
+void JsonDbPartition::initIndexes()
+{
+ QByteArray baPropertyName;
+ QByteArray baIndexObject;
+
+ mObjectTable->addIndexOnProperty(JsonDbString::kUuidStr, "string");
+ mObjectTable->addIndexOnProperty(JsonDbString::kTypeStr, "string");
+
+ GetObjectsResult getObjectsResult = mObjectTable->getObjects(JsonDbString::kTypeStr, JsonDbString::kIndexTypeStr, JsonDbString::kIndexTypeStr);
+ foreach (const QJsonObject indexObject, getObjectsResult.data) {
+ if (jsondbSettings->verbose())
+ qDebug() << "initIndexes" << "index" << indexObject;
+ QString indexObjectType = indexObject.value(JsonDbString::kTypeStr).toString();
+ if (indexObjectType == JsonDbString::kIndexTypeStr) {
+ QString indexName = JsonDbIndex::determineName(indexObject);
+ QString propertyName = indexObject.value(JsonDbString::kPropertyNameStr).toString();
+ QString propertyType = indexObject.value(JsonDbString::kPropertyTypeStr).toString();
+ QString propertyFunction = indexObject.value(JsonDbString::kPropertyFunctionStr).toString();
+ QString locale = indexObject.value(JsonDbString::kLocaleStr).toString();
+ QString collation = indexObject.value(JsonDbString::kCollationStr).toString();
+ QString casePreference = indexObject.value(JsonDbString::kCasePreferenceStr).toString();
+ QStringList objectTypes;
+ QJsonValue objectTypeValue = indexObject.value(JsonDbString::kObjectTypeStr);
+ if (objectTypeValue.isString()) {
+ objectTypes.append(objectTypeValue.toString());
+ } else if (objectTypeValue.isArray()) {
+ foreach (const QJsonValue objectType, objectTypeValue.toArray())
+ objectTypes.append(objectType.toString());
+ }
+
+ Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
+ if (indexObject.contains(JsonDbString::kCaseSensitiveStr))
+ caseSensitivity = (indexObject.value(JsonDbString::kCaseSensitiveStr).toBool() == true ? Qt::CaseSensitive : Qt::CaseInsensitive);
+
+ addIndex(indexName, propertyName, propertyType, objectTypes, propertyFunction, locale, collation, casePreference, caseSensitivity);
+ }
+ }
+}
+
+bool JsonDbPartition::addIndex(const QString &indexName, const QString &propertyName,
+ const QString &propertyType, const QStringList &objectTypes, const QString &propertyFunction,
+ const QString &locale, const QString &collation, const QString &casePreference,
+ Qt::CaseSensitivity caseSensitivity)
+{
+ Q_ASSERT(!indexName.isEmpty());
+ //qDebug() << "JsonDbBtreePartition::addIndex" << propertyName << objectType;
+ JsonDbObjectTable *table = 0;
+ if (objectTypes.isEmpty())
+ table = mainObjectTable();
+ else
+ foreach (const QString &objectType, objectTypes) {
+ JsonDbObjectTable *t = findObjectTable(objectType);
+ if (table && (t != table)) {
+ qDebug() << "addIndex" << "index on multiple tables" << objectTypes;
+ return false;
+ }
+ table = t;
+ }
+ const IndexSpec *indexSpec = table->indexSpec(indexName);
+ if (indexSpec)
+ return true;
+ //if (gVerbose) qDebug() << "JsonDbBtreePartition::addIndex" << propertyName << objectType;
+ return table->addIndex(indexName, propertyName, propertyType, objectTypes, propertyFunction, locale, collation, casePreference, caseSensitivity);
+}
+
+bool JsonDbPartition::removeIndex(const QString &indexName, const QString &objectType)
+{
+ JsonDbObjectTable *table = findObjectTable(objectType);
+ const IndexSpec *indexSpec = table->indexSpec(indexName);
+ if (!indexSpec)
+ return false;
+ return table->removeIndex(indexName);
+}
+
+bool JsonDbPartition::checkStateConsistency()
+{
+ return true;
+}
+
+void JsonDbPartition::checkIndexConsistency(JsonDbObjectTable *objectTable, JsonDbIndex *index)
+{
+ quint32 indexStateNumber = index->bdb()->tag();
+ quint32 objectStateNumber = objectTable->stateNumber();
+ if (indexStateNumber > objectTable->stateNumber()) {
+ qCritical() << "reverting index" << index->propertyName() << indexStateNumber << objectStateNumber;
+ while (indexStateNumber > objectTable->stateNumber()) {
+ int rc = index->bdb()->rollback();
+ quint32 newIndexStateNumber = index->bdb()->tag();
+ if (newIndexStateNumber == indexStateNumber) {
+ qDebug() << "failed to revert. clearing" << rc;
+ index->bdb()->clearData();
+ break;
+ }
+ qCritical() << " reverted index to state" << indexStateNumber;
+ }
+ }
+}
+
+QHash<QString, qint64> JsonDbPartition::fileSizes() const
+{
+ QList<QFileInfo> fileInfo;
+ fileInfo << mObjectTable->bdb()->fileName();
+
+ foreach (const IndexSpec &spec, mObjectTable->indexSpecs().values()) {
+ if (spec.index->bdb())
+ fileInfo << spec.index->bdb()->fileName();
+ }
+
+ foreach (JsonDbView *view, mViews) {
+ JsonDbObjectTable *objectTable = view->objectTable();
+ fileInfo << objectTable->bdb()->fileName();
+ foreach (const IndexSpec &spec, objectTable->indexSpecs().values()) {
+ if (spec.index->bdb())
+ fileInfo << spec.index->bdb()->fileName();
+ }
+ }
+
+ QHash<QString, qint64> result;
+ foreach (const QFileInfo &info, fileInfo)
+ result.insert(info.fileName(), info.size());
+ return result;
+}
+
+void JsonDbPartition::compileOrQueryTerm(JsonDbIndexQuery *indexQuery, const QueryTerm &queryTerm)
+{
+ QString op = queryTerm.op();
+ QJsonValue fieldValue = queryTerm.value();
+ if (op == ">") {
+ indexQuery->addConstraint(new QueryConstraintGt(fieldValue));
+ indexQuery->setMin(fieldValue);
+ } else if (op == ">=") {
+ indexQuery->addConstraint(new QueryConstraintGe(fieldValue));
+ indexQuery->setMin(fieldValue);
+ } else if (op == "<") {
+ indexQuery->addConstraint(new QueryConstraintLt(fieldValue));
+ indexQuery->setMax(fieldValue);
+ } else if (op == "<=") {
+ indexQuery->addConstraint(new QueryConstraintLe(fieldValue));
+ indexQuery->setMax(fieldValue);
+ } else if (op == "=") {
+ indexQuery->addConstraint(new QueryConstraintEq(fieldValue));
+ indexQuery->setMin(fieldValue);
+ indexQuery->setMax(fieldValue);
+ } else if (op == "=~") {
+ const QRegExp &re = queryTerm.regExpConst();
+ QRegExp::PatternSyntax syntax = re.patternSyntax();
+ Qt::CaseSensitivity cs = re.caseSensitivity();
+ QString pattern = re.pattern();
+ indexQuery->addConstraint(new QueryConstraintRegExp(re));
+ if (cs == Qt::CaseSensitive) {
+ QString prefix;
+ if ((syntax == QRegExp::Wildcard)
+ && mWildCardPrefixRegExp.exactMatch(pattern)) {
+ prefix = mWildCardPrefixRegExp.cap(1);
+ if (jsondbSettings->debug())
+ qDebug() << "wildcard regexp prefix" << pattern << prefix;
+ }
+ indexQuery->setMin(prefix);
+ indexQuery->setMax(prefix);
+ }
+ } else if (op == "!=") {
+ indexQuery->addConstraint(new QueryConstraintNe(fieldValue));
+ } else if (op == "exists") {
+ indexQuery->addConstraint(new QueryConstraintExists);
+ } else if (op == "notExists") {
+ indexQuery->addConstraint(new QueryConstraintNotExists);
+ } else if (op == "in") {
+ QJsonArray value = queryTerm.value().toArray();
+ //qDebug() << "in" << value << value.size();
+ if (value.size() == 1)
+ indexQuery->addConstraint(new QueryConstraintEq(value.at(0)));
+ else
+ indexQuery->addConstraint(new QueryConstraintIn(queryTerm.value()));
+ } else if (op == "notIn") {
+ indexQuery->addConstraint(new QueryConstraintNotIn(queryTerm.value()));
+ } else if (op == "in") {
+ indexQuery->addConstraint(new QueryConstraintContains(queryTerm.value()));
+ } else if (op == "startsWith") {
+ indexQuery->addConstraint(new QueryConstraintStartsWith(queryTerm.value().toString()));
+ }
+}
+
+JsonDbIndexQuery *JsonDbPartition::compileIndexQuery(const JsonDbOwner *owner, const JsonDbQuery *query)
+{
+ JsonDbIndexQuery *indexQuery = 0;
+ JsonDbQuery *residualQuery = new JsonDbQuery();
+ QString orderField;
+ QSet<QString> typeNames;
+ const QList<OrderTerm> &orderTerms = query->orderTerms;
+ const QList<OrQueryTerm> &orQueryTerms = query->queryTerms;
+ QString indexCandidate;
+ int indexedQueryTermCount = 0;
+ JsonDbObjectTable *table = mObjectTable; //TODO fix me
+ JsonDbView *view = 0;
+ QList<QString> unindexablePropertyNames; // fields for which we cannot use an index
+ if (orQueryTerms.size()) {
+ // first pass to find unindexable property names
+ for (int i = 0; i < orQueryTerms.size(); i++)
+ unindexablePropertyNames.append(orQueryTerms[i].findUnindexablePropertyNames());
+ for (int i = 0; i < orQueryTerms.size(); i++) {
+ const OrQueryTerm orQueryTerm = orQueryTerms[i];
+ const QList<QString> &querypropertyNames = orQueryTerm.propertyNames();
+ if (querypropertyNames.size() == 1) {
+ //QString fieldValue = queryTerm.value().toString();
+ QString propertyName = querypropertyNames[0];
+
+ const QList<QueryTerm> &queryTerms = orQueryTerm.terms();
+ const QueryTerm &queryTerm = queryTerms[0];
+
+ if ((typeNames.size() == 1)
+ && mViews.contains(typeNames.toList()[0])) {
+ view = mViews[typeNames.toList()[0]];
+ table = view->objectTable();
+ }
+
+ if (table->indexSpec(propertyName))
+ indexedQueryTermCount++;
+ else if (indexCandidate.isEmpty()
+ && (propertyName != JsonDbString::kTypeStr)
+ && !unindexablePropertyNames.contains(propertyName)) {
+ indexCandidate = propertyName;
+ if (!queryTerm.joinField().isEmpty())
+ indexCandidate = queryTerm.joinPaths()[0].join("->");
+ }
+
+ propertyName = queryTerm.propertyName();
+ QString fieldValue = queryTerm.value().toString();
+ QString op = queryTerm.op();
+ if (propertyName == JsonDbString::kTypeStr) {
+ if ((op == "=") || (op == "in")) {
+ QSet<QString> types;
+ if (op == "=") {
+ types << fieldValue;
+ for (int i = 1; i < queryTerms.size(); ++i) {
+ if (queryTerms[i].propertyName() == JsonDbString::kTypeStr && queryTerms[i].op() == "=")
+ types << queryTerms[i].value().toString();
+ }
+ } else {
+ QJsonArray array = queryTerm.value().toArray();
+ types.clear();
+ for (int t = 0; t < array.size(); t++)
+ types << array.at(t).toString();
+ }
+
+ if (typeNames.count()) {
+ typeNames.intersect(types);
+ if (!typeNames.count()) {
+ // make this a null query -- I really need a domain (partial order) here and not a set
+ typeNames = types;
+ }
+ } else {
+ typeNames = types;
+ }
+ } else if ((op == "!=") || (op == "notIn")) {
+ QSet<QString> types;
+ if (op == "!=")
+ types << fieldValue;
+ else {
+ QJsonArray array = queryTerm.value().toArray();
+ for (int t = 0; t < array.size(); t++)
+ types << array.at(t).toString();
+ }
+ }
+ }
+ }
+ }
+ }
+ if ((typeNames.size() == 1)
+ && mViews.contains(typeNames.toList()[0])) {
+ view = mViews[typeNames.toList()[0]];
+ table = view->objectTable();
+ }
+ if (!indexedQueryTermCount && !indexCandidate.isEmpty()) {
+ if (jsondbSettings->debug())
+ qDebug() << "adding index" << indexCandidate;
+ //TODO: remove this
+ table->addIndexOnProperty(indexCandidate);
+ }
+
+ for (int i = 0; i < orderTerms.size(); i++) {
+ const OrderTerm &orderTerm = orderTerms[i];
+ QString propertyName = orderTerm.propertyName;
+ if (!table->indexSpec(propertyName)) {
+ if (jsondbSettings->verbose() || jsondbSettings->performanceLog())
+ qDebug() << "Unindexed sort term" << propertyName << orderTerm.ascending;
+ residualQuery->orderTerms.append(orderTerm);
+ continue;
+ }
+ if (unindexablePropertyNames.contains(propertyName)) {
+ if (jsondbSettings->verbose() || jsondbSettings->performanceLog())
+ qDebug() << "Unindexable sort term uses notExists" << propertyName << orderTerm.ascending;
+ residualQuery->orderTerms.append(orderTerm);
+ continue;
+ }
+ if (!indexQuery) {
+ orderField = propertyName;
+ const IndexSpec *indexSpec = table->indexSpec(propertyName);
+ if (view)
+ view->updateView();
+
+ indexQuery = JsonDbIndexQuery::indexQuery(this, table, propertyName, indexSpec->propertyType,
+ owner, orderTerm.ascending);
+ } else if (orderField != propertyName) {
+ qCritical() << QString("unimplemented: multiple order terms. Sorting on '%1'").arg(orderField);
+ residualQuery->orderTerms.append(orderTerm);
+ }
+ }
+
+ for (int i = 0; i < orQueryTerms.size(); i++) {
+ const OrQueryTerm &orQueryTerm = orQueryTerms[i];
+ const QList<QueryTerm> &queryTerms = orQueryTerm.terms();
+ if (queryTerms.size() == 1) {
+ QueryTerm queryTerm = queryTerms[0];
+ QString propertyName = queryTerm.propertyName();
+ QString fieldValue = queryTerm.value().toString();
+ QString op = queryTerm.op();
+
+ if (!queryTerm.joinField().isEmpty()) {
+ residualQuery->queryTerms.append(queryTerm);
+ propertyName = queryTerm.joinField();
+ op = "exists";
+ queryTerm.setPropertyName(propertyName);
+ queryTerm.setOp(op);
+ queryTerm.setJoinField(QString());
+ }
+ if (!table->indexSpec(propertyName)
+ || (indexQuery
+ && (propertyName != orderField))) {
+ if (jsondbSettings->verbose() || jsondbSettings->debug())
+ qDebug() << "residual query term" << propertyName << "orderField" << orderField;
+ residualQuery->queryTerms.append(queryTerm);
+ continue;
+ }
+
+ if (!indexQuery
+ && (propertyName != JsonDbString::kTypeStr)
+ && table->indexSpec(propertyName)
+ && !unindexablePropertyNames.contains(propertyName)) {
+ orderField = propertyName;
+ const IndexSpec *indexSpec = table->indexSpec(propertyName);
+ if (view)
+ view->updateView();
+ indexQuery = JsonDbIndexQuery::indexQuery(this, table, propertyName, indexSpec->propertyType, owner);
+ }
+
+ if (propertyName == orderField) {
+ compileOrQueryTerm(indexQuery, queryTerm);
+ } else {
+ residualQuery->queryTerms.append(orQueryTerm);
+ }
+ } else {
+ residualQuery->queryTerms.append(orQueryTerm);
+ }
+ }
+
+ if (!indexQuery) {
+ QString defaultIndex = JsonDbString::kUuidStr;
+ if (typeNames.size()) {
+ if ((typeNames.size() == 1)
+ && mViews.contains(typeNames.toList()[0])) {
+ view = mViews[typeNames.toList()[0]];
+ table = view->objectTable();
+ } else
+ defaultIndex = JsonDbString::kTypeStr;
+ }
+ const IndexSpec *indexSpec = table->indexSpec(defaultIndex);
+
+ //qDebug() << "defaultIndex" << defaultIndex << "on table" << indexSpec->objectType;
+
+ if (view)
+ view->updateView();
+ indexQuery = JsonDbIndexQuery::indexQuery(this, table, defaultIndex, indexSpec->propertyType, owner);
+ if (typeNames.size() == 0)
+ qCritical() << "searching all objects" << query->query;
+
+ if (defaultIndex == JsonDbString::kTypeStr) {
+ foreach (const OrQueryTerm &term, orQueryTerms) {
+ QList<QueryTerm> terms = term.terms();
+ if (terms.size() == 1 && terms[0].propertyName() == JsonDbString::kTypeStr) {
+ compileOrQueryTerm(indexQuery, terms[0]);
+ break;
+ }
+ }
+ }
+ }
+ if (typeNames.count() > 0)
+ indexQuery->setTypeNames(typeNames);
+ if (residualQuery->queryTerms.size() || residualQuery->orderTerms.size())
+ indexQuery->setResidualQuery(residualQuery);
+ else
+ delete residualQuery;
+ indexQuery->setAggregateOperation(query->mAggregateOperation);
+ indexQuery->setResultExpressionList(query->mapExpressionList);
+ indexQuery->setResultKeyList(query->mapKeyList);
+ return indexQuery;
+}
+
+void JsonDbPartition::doIndexQuery(const JsonDbOwner *owner, JsonDbObjectList &results, int &limit, int &offset,
+ JsonDbIndexQuery *indexQuery)
+{
+ if (jsondbSettings->debugQuery())
+ qDebug() << "doIndexQuery" << "limit" << limit << "offset" << offset;
+
+ bool countOnly = (indexQuery->aggregateOperation() == "count");
+ int count = 0;
+ for (JsonDbObject object = indexQuery->first();
+ !object.isEmpty();
+ object = indexQuery->next()) {
+ if (!owner->isAllowed(object, indexQuery->partition(), QString("read")))
+ continue;
+ if (limit && (offset <= 0)) {
+ if (!countOnly) {
+ if (jsondbSettings->debugQuery())
+ qDebug() << "appending result" << object << endl;
+ JsonDbObject result = indexQuery->resultObject(object);
+ results.append(result);
+ }
+ limit--;
+ count++;
+ }
+ offset--;
+ if (limit == 0)
+ break;
+ }
+ if (countOnly) {
+ QJsonObject countObject;
+ countObject.insert(QLatin1String("count"), count);
+ results.append(countObject);
+ }
+}
+
+bool JsonDbPartition::checkQuota(const JsonDbOwner *owner, int size) const
+{
+ Q_UNUSED(owner);
+ Q_UNUSED(size);
+ return true;
+}
+
+bool JsonDbPartition::addToQuota(const JsonDbOwner *owner, int size)
+{
+ Q_UNUSED(owner);
+ Q_UNUSED(size);
+ return true;
+}
+
+JsonDbQueryResult JsonDbPartition::queryObjects(const JsonDbOwner *owner, const JsonDbQuery *query, int limit, int offset)
+{
+ JsonDbQueryResult result;
+ JsonDbObjectList results;
+ JsonDbObjectList joinedResults;
+
+ if (!(query->queryTerms.size() || query->orderTerms.size())) {
+ QJsonObject error;
+ error.insert(JsonDbString::kCodeStr, JsonDbError::MissingQuery);
+ error.insert(JsonDbString::kMessageStr, QString("Missing query: %1").arg(query->queryExplanation.join("\n")));
+ result.error = error;
+ return result;
+ }
+
+ QElapsedTimer time;
+ time.start();
+ JsonDbIndexQuery *indexQuery = compileIndexQuery(owner, query);
+
+ int elapsedToCompile = time.elapsed();
+ doIndexQuery(owner, results, limit, offset, indexQuery);
+ int elapsedToQuery = time.elapsed();
+ quint32 stateNumber = indexQuery->stateNumber();
+ int length = results.size();
+ JsonDbQuery *residualQuery = indexQuery->residualQuery();
+ if (residualQuery && residualQuery->orderTerms.size()) {
+ if (jsondbSettings->verbose())
+ qDebug() << "queryPersistentObjects" << "sorting";
+ sortValues(residualQuery, results, joinedResults);
+ }
+
+ QJsonArray sortKeys;
+ sortKeys.append(indexQuery->propertyName());
+ sortKeys.append(indexQuery->objectTable()->filename());
+
+ delete indexQuery;
+
+ QStringList mapExpressions = query->mapExpressionList;
+ QStringList mapKeys = query->mapKeyList;
+
+ result.data = results;
+ result.length = length;
+ result.offset = offset;
+ result.state = (qint32)stateNumber;
+ result.sortKeys = sortKeys;
+ int elapsedToDone = time.elapsed();
+ if (jsondbSettings->verbose())
+ qDebug() << "elapsed" << elapsedToCompile << elapsedToQuery << elapsedToDone << query->query;
+ return result;
+}
+
+JsonDbWriteResult JsonDbPartition::updateObjects(const JsonDbOwner *owner, const JsonDbObjectList &objects, JsonDbPartition::WriteMode mode,
+ JsonDbUpdateList *changeList)
+{
+ JsonDbWriteResult result;
+ WithTransaction transaction(this);
+ QList<JsonDbUpdate> updated;
+ QString errorMsg;
+
+ foreach (const JsonDbObject &toUpdate, objects) {
+ JsonDbObject object = toUpdate;
+
+ bool forRemoval = object.value(JsonDbString::kDeletedStr).toBool();
+
+ if (!object.contains(JsonDbString::kUuidStr) || object.value(JsonDbString::kUuidStr).isNull()) {
+ if (forRemoval) {
+ result.code = JsonDbError::MissingUUID;
+ result.message = QLatin1String("Missing '_uuid' field in object");
+ return result;
+ }
+
+ if (!object.contains(JsonDbString::kOwnerStr)
+ || ((object.value(JsonDbString::kOwnerStr).toString() != owner->ownerId())
+ && !owner->isAllowed(object, mPartitionName, "setOwner")))
+ object.insert(JsonDbString::kOwnerStr, owner->ownerId());
+ object.generateUuid();
+ }
+
+ if (!forRemoval && object.value(JsonDbString::kTypeStr).toString().isEmpty()) {
+ result.code = JsonDbError::MissingType;
+ result.message = QLatin1String("Missing '_type' field in object");
+ return result;
+ }
+
+ if (!(mode == ViewObject || checkNaturalObjectType(object, errorMsg))) {
+ result.code = JsonDbError::MissingType;
+ result.message = errorMsg;
+ }
+ if (!(forRemoval || validateSchema(object.type(), object, errorMsg))) {
+ result.code = JsonDbError::FailedSchemaValidation;
+ result.message = errorMsg;
+ return result;
+ }
+
+ JsonDbObjectTable *objectTable = findObjectTable(object.type());
+
+ if (mode != ViewObject) {
+ if (mViewTypes.contains(object.type())) {
+ result.code = JsonDbError::InvalidType;
+ result.message = QString("Cannot write object of view type '%1'").arg(object.value(JsonDbString::kTypeStr).toString());
+ return result;
+ }
+
+ transaction.addObjectTable(objectTable);
+ }
+
+ JsonDbObject master;
+ bool forCreation = !getObject(object.uuid(), master, object.type());
+
+ // FIXME: explicity disallow changing _type
+
+ if (mode != ReplicatedWrite && forCreation && forRemoval) {
+ result.code = JsonDbError::MissingObject;
+ result.message = QLatin1String("Cannot remove non-existing object");
+ return result;
+ }
+
+ if (!(forCreation || owner->isAllowed(master, mPartitionName, "write"))) {
+ result.code = JsonDbError::OperationNotPermitted;
+ result.message = QLatin1String("Access denied");
+ return result;
+ }
+
+ if (!master.value(JsonDbString::kOwnerStr).toString().isEmpty())
+ object.insert(JsonDbString::kOwnerStr, master.value(JsonDbString::kOwnerStr));
+ else if (object.value(JsonDbString::kOwnerStr).toString().isEmpty())
+ object.insert(JsonDbString::kOwnerStr, owner->ownerId());
+
+ if (!(forRemoval || owner->isAllowed(object, mPartitionName, "write"))) {
+ result.code = JsonDbError::OperationNotPermitted;
+ result.message = QLatin1String("Access denied");
+ return result;
+ }
+
+ bool validWrite;
+ QString versionWritten;
+ JsonDbObject oldMaster = master;
+
+ switch (mode) {
+ case OptimisticWrite:
+ validWrite = master.updateVersionOptimistic(object, &versionWritten);
+ break;
+ case ViewObject:
+ case ForcedWrite:
+ master = object;
+ versionWritten = master.computeVersion();
+ validWrite = true;
+ break;
+ case ReplicatedWrite:
+ validWrite = master.updateVersionReplicating(object);
+ versionWritten = master.version();
+ break;
+ default:
+ result.code = JsonDbError::InvalidRequest;
+ result.message = QLatin1String("Missing writeMode implementation.");
+ return result;
+ }
+
+ if (!validWrite) {
+ if (mode == ReplicatedWrite) {
+ result.code = JsonDbError::InvalidRequest;
+ result.message = QLatin1String("Replication has reject your update for sanity reasons");
+ } else {
+ if (jsondbSettings->debug())
+ qDebug() << "Stale update detected - expected version:" << oldMaster.version() << object;
+ result.code = JsonDbError::UpdatingStaleVersion;
+ result.message = QString("Updating stale version of object. Expected version %1, received %2").arg(oldMaster.version()).arg(versionWritten);
+ }
+ return result;
+ }
+
+ // recheck, it might just be a conflict removal
+ forRemoval = master.isDeleted();
+
+ if (master.type().isNull())
+ master.insert(JsonDbString::kTypeStr, oldMaster.type());
+
+ bool isVisibleWrite = oldMaster.version() != master.version();
+
+ if (isVisibleWrite) {
+ JsonDbError::ErrorCode errorCode;
+ if (!(forRemoval || (errorCode = checkBuiltInTypeValidity(master, oldMaster, errorMsg)) == JsonDbError::NoError)) {
+ result.code = errorCode;
+ result.message = errorMsg;
+ return result;
+ } else if (oldMaster.type() == JsonDbString::kSchemaTypeStr &&
+ !checkCanRemoveSchema(oldMaster, errorMsg)) {
+ result.code = JsonDbError::InvalidSchemaOperation;
+ result.message = errorMsg;
+ return result;
+ } else if ((errorCode = checkBuiltInTypeAccessControl(forCreation, owner, master, oldMaster, errorMsg)) != JsonDbError::NoError) {
+ result.code = errorCode;
+ result.message = errorMsg;
+ return result;
+ }
+ }
+
+ ObjectKey objectKey(master.uuid());
+
+ if (!forCreation)
+ objectTable->deindexObject(objectKey, oldMaster, objectTable->stateNumber());
+
+ if (!objectTable->put(objectKey, master)) {
+ result.code = JsonDbError::DatabaseError;
+ result.message = objectTable->errorMessage();
+ }
+
+ if (jsondbSettings->debug())
+ qDebug() << "Wrote object" << objectKey << endl << master << endl << oldMaster;
+
+ JsonDbNotification::Action action = JsonDbNotification::Update;
+ if (forRemoval)
+ action = JsonDbNotification::Delete;
+ else if (forCreation)
+ action = JsonDbNotification::Create;
+
+ JsonDbUpdate change(oldMaster, master, action);
+ quint32 stateNumber = objectTable->storeStateChange(objectKey, change);
+ if (changeList)
+ changeList->append(change);
+
+ if (!forRemoval)
+ objectTable->indexObject(objectKey, master, objectTable->stateNumber());
+
+ updateBuiltInTypes(master, oldMaster);
+
+ result.state = stateNumber;
+
+
+ master.insert(JsonDbString::kVersionStr, versionWritten);
+ result.objectsWritten.append(master);
+ updated.append(change);
+ }
+
+ transaction.commit();
+
+ emit objectsUpdated(updated);
+ return result;
+}
+
+JsonDbWriteResult JsonDbPartition::updateObject(const JsonDbOwner *owner, const JsonDbObject &object, JsonDbPartition::WriteMode mode, JsonDbUpdateList *changeList)
+{
+ return updateObjects(owner, JsonDbObjectList() << object, mode, changeList);
+}
+
+void JsonDbPartition::checkIndex(const QString &propertyName)
+{
+// TODO
+ if (mObjectTable->indexSpec(propertyName))
+ mObjectTable->indexSpec(propertyName)->index->checkIndex();
+}
+
+bool JsonDbPartition::compact()
+{
+ for (QHash<QString,QPointer<JsonDbView> >::const_iterator it = mViews.begin();
+ it != mViews.end();
+ ++it) {
+ it.value()->objectTable()->compact();
+ }
+ bool result = true;
+ result &= mObjectTable->compact();
+ return result;
+}
+
+JsonDbStat JsonDbPartition::stat() const
+{
+ JsonDbStat result;
+ for (QHash<QString,QPointer<JsonDbView> >::const_iterator it = mViews.begin();
+ it != mViews.end();
+ ++it) {
+ result += it.value()->objectTable()->stat();
+ }
+ result += mObjectTable->stat();
+ return result;
+}
+
+struct QJsonSortable {
+ QJsonValue key;
+ QJsonObject result;
+ QJsonObject joinedResult;
+};
+
+bool sortableLessThan(const QJsonSortable &a, const QJsonSortable &b)
+{
+ return JsonDbIndexQuery::lessThan(a.key, b.key);
+}
+bool sortableGreaterThan(const QJsonSortable &a, const QJsonSortable &b)
+{
+ return JsonDbIndexQuery::greaterThan(a.key, b.key);
+}
+
+void JsonDbPartition::sortValues(const JsonDbQuery *parsedQuery, JsonDbObjectList &results, JsonDbObjectList &joinedResults)
+{
+ const QList<OrderTerm> &orderTerms = parsedQuery->orderTerms;
+ if (!orderTerms.size() || (results.size() < 2))
+ return;
+ const OrderTerm &orderTerm0 = orderTerms[0];
+ QString field0 = orderTerm0.propertyName;
+ bool ascending = orderTerm0.ascending;
+ QStringList path0 = field0.split('.');
+
+ if (orderTerms.size() == 1) {
+ QVector<QJsonSortable> valuesToSort(results.size());
+ int resultsSize = results.size();
+ int joinedResultsSize = joinedResults.size();
+
+ for (int i = 0; i < resultsSize; i++) {
+ QJsonSortable *p = &valuesToSort[i];
+ JsonDbObject r = results.at(i);
+ p->key = r.propertyLookup(path0);
+ p->result = r;
+ if (joinedResultsSize > i)
+ p->joinedResult = joinedResults.at(i);
+ }
+
+ if (ascending)
+ qStableSort(valuesToSort.begin(), valuesToSort.end(), sortableLessThan);
+ else
+ qStableSort(valuesToSort.begin(), valuesToSort.end(), sortableGreaterThan);
+
+ results = JsonDbObjectList();
+ joinedResults = JsonDbObjectList();
+ for (int i = 0; i < resultsSize; i++) {
+ QJsonSortable *p = &valuesToSort[i];
+ results.append(p->result);
+ if (joinedResultsSize > i)
+ joinedResults.append(p->joinedResult);
+ }
+ } else {
+ qCritical() << "Unimplemented: sorting on multiple keys or non-string keys";
+ }
+}
+
+bool JsonDbPartition::checkCanRemoveSchema(const JsonDbObject &schema, QString &message)
+{
+ QString schemaName = schema.value("name").toString();
+
+ // for View types, make sure no Maps or Reduces point at this type
+ // the call to getObject will have updated the view, so pending Map or Reduce object removes will be forced
+ JsonDbView *view = findView(schemaName);
+ if (view && view->isActive()) {
+ message = QString("An active view with targetType of %2 exists. You cannot remove the schema").arg(schemaName);
+ return false;
+ }
+
+ // check if any objects exist
+ GetObjectsResult getObjectResponse = getObjects(JsonDbString::kTypeStr, schemaName);
+
+ // for non-View types, if objects exist the schema cannot be removed
+ if (!mViewTypes.contains(schemaName)) {
+ if (getObjectResponse.data.size() != 0) {
+ message = QString("%1 object(s) of type %2 exist. You cannot remove the schema")
+ .arg(getObjectResponse.data.size())
+ .arg(schemaName);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool JsonDbPartition::validateSchema(const QString &schemaName, const JsonDbObject &object, QString &errorMsg)
+{
+ errorMsg.clear();
+
+ if (!jsondbSettings->validateSchemas()) {
+ if (jsondbSettings->debug())
+ qDebug() << "Not validating schemas";
+ return true;
+ }
+
+ QJsonObject result = mSchemas.validate(schemaName, object);
+ if (!result.value(JsonDbString::kCodeStr).isNull()) {
+ errorMsg = result.value(JsonDbString::kMessageStr).toString();
+ if (jsondbSettings->debug())
+ qDebug() << "Schema validation error: " << errorMsg << object;
+ return false;
+ }
+
+ return true;
+}
+
+bool JsonDbPartition::checkNaturalObjectType(const JsonDbObject &object, QString &errorMsg)
+{
+ QString type = object.value(JsonDbString::kTypeStr).toString();
+ if (mViewTypes.contains(type)) {
+ QByteArray str = QJsonDocument(object).toJson();
+ errorMsg = QString("Cannot create/remove object of view type '%1': '%2'").arg(type).arg(QString::fromUtf8(str));
+ return false;
+ }
+
+ return true;
+}
+
+JsonDbError::ErrorCode JsonDbPartition::checkBuiltInTypeAccessControl(bool forCreation, const JsonDbOwner *owner, const JsonDbObject &object, const JsonDbObject &oldObject, QString &errorMsg)
+{
+ if (!jsondbSettings->enforceAccessControl())
+ return JsonDbError::NoError;
+
+ QString objectType = object.value(JsonDbString::kTypeStr).toString();
+ errorMsg.clear();
+
+ // Access control checks
+ if (objectType == JsonDbString::kMapTypeStr ||
+ objectType == JsonDbString::kReduceTypeStr) {
+ // Check that owner can write targetType
+ QJsonValue targetType = object.value(QLatin1String("targetType"));
+ JsonDbObject fake; // Just for access control
+ fake.insert (JsonDbString::kOwnerStr, object.value(JsonDbString::kOwnerStr));
+ fake.insert (JsonDbString::kTypeStr, targetType);
+ if (!owner->isAllowed(fake, mPartitionName, "write")) {
+ errorMsg = QString::fromLatin1("Access denied %1").arg(targetType.toString());
+ return JsonDbError::OperationNotPermitted;
+ }
+ bool forRemoval = object.isDeleted();
+
+ // For removal it is enough to be able to write to targetType
+ if (!forRemoval) {
+ if (!forCreation) {
+ // In update we want to check also the old targetType
+ QJsonValue oldTargetType = oldObject.value(QLatin1String("targetType"));
+ fake.insert (JsonDbString::kTypeStr, oldTargetType);
+ if (!owner->isAllowed(fake, mPartitionName, "write")) {
+ errorMsg = QString::fromLatin1("Access denied %1").arg(oldTargetType.toString());
+ return JsonDbError::OperationNotPermitted;
+ }
+ }
+ // For create/update we need to check the read acces to sourceType(s) also
+ if (objectType == JsonDbString::kMapTypeStr) {
+ QScopedPointer<JsonDbMapDefinition> def(new JsonDbMapDefinition(owner, this, object));
+ QStringList sourceTypes = def->sourceTypes();
+ for (int i = 0; i < sourceTypes.size(); i++) {
+ fake.insert (JsonDbString::kTypeStr, sourceTypes[i]);
+ if (!owner->isAllowed(fake, mPartitionName, "read")) {
+ errorMsg = QString::fromLatin1("Access denied %1").arg(sourceTypes[i]);
+ return JsonDbError::OperationNotPermitted;
+ }
+ }
+ } else if (objectType == JsonDbString::kReduceTypeStr) {
+ QJsonValue sourceType = object.value(QLatin1String("sourceType"));
+ fake.insert (JsonDbString::kTypeStr, sourceType);
+ if (!owner->isAllowed(fake, mPartitionName, "read")) {
+ errorMsg = QString::fromLatin1("Access denied %1").arg(sourceType.toString());
+ return JsonDbError::OperationNotPermitted;
+ }
+ }
+ }
+ } else if (objectType == JsonDbString::kSchemaTypeStr) {
+ // Check that owner can write name
+ QJsonValue name = object.value(JsonDbString::kNameStr);
+ JsonDbObject fake; // Just for access control
+ fake.insert (JsonDbString::kOwnerStr, object.value(JsonDbString::kOwnerStr));
+ fake.insert (JsonDbString::kTypeStr, name);
+ if (!owner->isAllowed(fake, mPartitionName, "write")) {
+ errorMsg = QString::fromLatin1("Access denied %1").arg(name.toString());
+ return JsonDbError::OperationNotPermitted;
+ }
+ } else if (objectType == JsonDbString::kIndexTypeStr) {
+
+ // Only the owner or admin can update Index
+ if (!forCreation) {
+ QJsonValue oldOwner = oldObject.value(JsonDbString::kOwnerStr);
+ if (owner->ownerId() != oldOwner.toString() && !owner->allowAll()) {
+ // Only admin (allowAll = true) can update Index:s owned by somebody else
+ errorMsg = QString::fromLatin1("Only admin can update Index:s not owned by itself");
+ return JsonDbError::OperationNotPermitted;
+ }
+ }
+
+ // Check that owner can read all objectTypes
+ QJsonValue objectTypeProperty = object.value(QLatin1String("objectType"));
+ JsonDbObject fake; // Just for access control
+ if (objectTypeProperty.isUndefined() || objectTypeProperty.isNull()) {
+ if (!owner->allowAll()) {
+ // Only admin (allowAll = true) can do Index:s without objectType
+ errorMsg = QString::fromLatin1("Only admin can do Index:s without objectType");
+ return JsonDbError::OperationNotPermitted;
+ }
+ } else if (objectTypeProperty.isArray()) {
+ QJsonArray arr = objectTypeProperty.toArray();
+ foreach (QJsonValue val, arr) {
+ fake.insert (JsonDbString::kOwnerStr, object.value(JsonDbString::kOwnerStr));
+ fake.insert (JsonDbString::kTypeStr, val.toString());
+ if (!owner->isAllowed(fake, mPartitionName, "read")) {
+ errorMsg = QString::fromLatin1("Access denied %1 in Index %2").arg(val.toString()).
+ arg(JsonDbIndex::determineName(object));
+ return JsonDbError::OperationNotPermitted;
+ }
+ }
+ } else if (objectTypeProperty.isString()) {
+ fake.insert (JsonDbString::kOwnerStr, object.value(JsonDbString::kOwnerStr));
+ fake.insert (JsonDbString::kTypeStr, objectTypeProperty.toString());
+ if (!owner->isAllowed(fake, mPartitionName, "read")) {
+ errorMsg = QString::fromLatin1("Access denied %1 in Index %2").arg(objectTypeProperty.toString()).
+ arg(JsonDbIndex::determineName(object));
+ return JsonDbError::OperationNotPermitted;
+ }
+ } else {
+ errorMsg = QString::fromLatin1("Invalid objectType in Index %1").arg(JsonDbIndex::determineName(object));
+ return JsonDbError::InvalidIndexOperation;
+ }
+ }
+ return JsonDbError::NoError;
+}
+
+JsonDbError::ErrorCode JsonDbPartition::checkBuiltInTypeValidity(const JsonDbObject &object, const JsonDbObject &oldObject, QString &errorMsg)
+{
+ QString objectType = object.value(JsonDbString::kTypeStr).toString();
+ errorMsg.clear();
+
+ if (objectType == JsonDbString::kSchemaTypeStr &&
+ !checkCanAddSchema(object, oldObject, errorMsg))
+ return JsonDbError::InvalidSchemaOperation;
+ else if (objectType == JsonDbString::kMapTypeStr &&
+ !JsonDbMapDefinition::validateDefinition(object, this, errorMsg))
+ return JsonDbError::InvalidMap;
+ else if (objectType == JsonDbString::kReduceTypeStr &&
+ !JsonDbReduceDefinition::validateDefinition(object, this, errorMsg))
+ return JsonDbError::InvalidReduce;
+ else if (objectType == JsonDbString::kIndexTypeStr && !JsonDbIndex::validateIndex(object, oldObject, errorMsg))
+ return JsonDbError::InvalidIndexOperation;
+
+ return JsonDbError::NoError;
+}
+
+void JsonDbPartition::updateBuiltInTypes(const JsonDbObject &object, const JsonDbObject &oldObject)
+{
+ if (oldObject.type() == JsonDbString::kIndexTypeStr) {
+ QString indexName = JsonDbIndex::determineName(oldObject);
+ removeIndex(indexName, oldObject.value(JsonDbString::kObjectTypeStr).toString());
+ }
+
+ if (object.type() == JsonDbString::kIndexTypeStr && !object.isDeleted()) {
+ QString indexName = JsonDbIndex::determineName(object);
+
+ bool caseSensitivity = true;
+ if (object.contains(JsonDbString::kCaseSensitiveStr))
+ caseSensitivity = object.value(JsonDbString::kCaseSensitiveStr).toBool();
+
+ QStringList objectTypes;
+ QJsonValue v = object.value(JsonDbString::kObjectTypeStr);
+ if (v.isString()) {
+ objectTypes = (QStringList() << v.toString());
+ } else if (v.isArray()) {
+ QJsonArray array = v.toArray();
+ foreach (const QJsonValue objectType, array)
+ objectTypes.append(objectType.toString());
+ }
+
+ addIndex(indexName,
+ object.value(JsonDbString::kPropertyNameStr).toString(),
+ object.value(JsonDbString::kPropertyTypeStr).toString(),
+ objectTypes,
+ object.value(JsonDbString::kPropertyFunctionStr).toString(),
+ object.value(JsonDbString::kLocaleStr).toString(),
+ object.value(JsonDbString::kCollationStr).toString(),
+ object.value(JsonDbString::kCasePreferenceStr).toString(),
+ caseSensitivity == true ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ }
+
+ if (oldObject.type() == JsonDbString::kSchemaTypeStr)
+ removeSchema(oldObject.value(JsonDbString::kNameStr).toString());
+
+ if (object.type() == JsonDbString::kSchemaTypeStr &&
+ object.value(JsonDbString::kSchemaStr).type() == QJsonValue::Object
+ && !object.isDeleted())
+ setSchema(object.value(JsonDbString::kNameStr).toString(), object.value(JsonDbString::kSchemaStr).toObject());
+
+ if (!oldObject.isEmpty()
+ && (oldObject.type() == JsonDbString::kMapTypeStr || oldObject.type() == JsonDbString::kReduceTypeStr))
+ JsonDbView::removeDefinition(this, oldObject);
+
+ if (!object.isDeleted()
+ && (object.type() == JsonDbString::kMapTypeStr || object.type() == JsonDbString::kReduceTypeStr)
+ && !(object.contains(JsonDbString::kActiveStr) && !object.value(JsonDbString::kActiveStr).toBool()))
+ JsonDbView::createDefinition(this, object);
+}
+
+void JsonDbPartition::setSchema(const QString &schemaName, const QJsonObject &schema)
+{
+ if (jsondbSettings->verbose())
+ qDebug() << "setSchema" << schemaName << schema;
+
+ QJsonObject errors = mSchemas.insert(schemaName, schema);
+
+ if (!errors.isEmpty()) {
+ qWarning() << "setSchema failed because of errors" << schemaName << schema;
+ qWarning() << errors;
+ // FIXME should we accept broken schemas?
+ }
+
+ if (schema.contains("extends")) {
+ QJsonValue extendsValue = schema.value("extends");
+ QString extendedSchemaName;
+ if (extendsValue.type() == QJsonValue::String)
+ extendedSchemaName = extendsValue.toString();
+ else if ((extendsValue.type() == QJsonValue::Object)
+ && extendsValue.toObject().contains("$ref"))
+ extendedSchemaName = extendsValue.toObject().value("$ref").toString();
+ if (extendedSchemaName == JsonDbString::kViewTypeStr) {
+ mViewTypes.insert(schemaName);
+ addView(schemaName);
+ if (jsondbSettings->verbose())
+ qDebug() << "viewTypes" << mViewTypes;
+ }
+ }
+ if (schema.contains("properties"))
+ updateSchemaIndexes(schemaName, schema);
+}
+
+void JsonDbPartition::removeSchema(const QString &schemaName)
+{
+ if (jsondbSettings->verbose())
+ qDebug() << "removeSchema" << schemaName;
+
+ if (mSchemas.contains(schemaName)) {
+ QJsonObject schema = mSchemas.take(schemaName);
+
+ if (schema.contains("extends")) {
+ QJsonValue extendsValue = schema.value("extends");
+ QString extendedSchemaName;
+ if (extendsValue.type() == QJsonValue::String)
+ extendedSchemaName = extendsValue.toString();
+ else if ((extendsValue.type() == QJsonValue::Object)
+ && extendsValue.toObject().contains("$ref"))
+ extendedSchemaName = extendsValue.toObject().value("$ref").toString();
+
+ if (extendedSchemaName == JsonDbString::kViewTypeStr) {
+ mViewTypes.remove(schemaName);
+ removeView(schemaName);
+ }
+ }
+ }
+}
+
+void JsonDbPartition::updateSchemaIndexes(const QString &schemaName, QJsonObject object, const QStringList &path)
+{
+ QJsonObject properties = object.value("properties").toObject();
+ const QList<QString> keys = properties.keys();
+ for (int i = 0; i < keys.size(); i++) {
+ const QString &k = keys[i];
+ QJsonObject propertyInfo = properties.value(k).toObject();
+ if (propertyInfo.contains("indexed")) {
+ QString propertyType = (propertyInfo.contains("type") ? propertyInfo.value("type").toString() : "string");
+ QStringList kpath = path;
+ kpath << k;
+ QString propertyName = kpath.join(".");
+ addIndex(propertyName, propertyName, propertyType);
+ }
+ if (propertyInfo.contains("properties"))
+ updateSchemaIndexes(schemaName, propertyInfo, path + (QStringList() << k));
+ }
+}
+
+void JsonDbPartition::setError(QJsonObject &map, int code, const QString &message)
+{
+ map.insert(JsonDbString::kCodeStr, code);
+ map.insert(JsonDbString::kMessageStr, message);
+}
+
+QJsonObject JsonDbPartition::makeError(int code, const QString &message)
+{
+ QJsonObject map;
+ setError(map, code, message);
+ return map;
+}
+
+QJsonObject JsonDbPartition::makeResponse(const QJsonObject &resultmap, const QJsonObject &errormap, bool silent)
+{
+ QJsonObject map;
+ if (jsondbSettings->verbose() && !silent && !errormap.isEmpty())
+ qCritical() << errormap;
+
+ if (!resultmap.isEmpty())
+ map.insert( JsonDbString::kResultStr, resultmap);
+ else
+ map.insert( JsonDbString::kResultStr, QJsonValue());
+
+ if (!errormap.isEmpty())
+ map.insert( JsonDbString::kErrorStr, errormap );
+ else
+ map.insert( JsonDbString::kErrorStr, QJsonValue());
+ return map;
+}
+
+QJsonObject JsonDbPartition::makeErrorResponse(QJsonObject &resultmap, int code, const QString &message, bool silent)
+{
+ QJsonObject errormap;
+ setError(errormap, code, message);
+ return makeResponse(resultmap, errormap, silent);
+}
+
+bool JsonDbPartition::responseIsError(const QJsonObject &responseMap)
+{
+ return responseMap.contains(JsonDbString::kErrorStr)
+ && responseMap.value(JsonDbString::kErrorStr).isObject();
+}
+
+bool WithTransaction::addObjectTable(JsonDbObjectTable *table)
+{
+ if (!mPartition)
+ return false;
+ if (!mPartition->mTableTransactions.contains(table)) {
+ bool ok = table->begin();
+ mPartition->mTableTransactions.append(table);
+ return ok;
+ }
+ return true;
+}
+
+void JsonDbPartition::initSchemas()
+{
+ if (jsondbSettings->verbose())
+ qDebug() << "initSchemas";
+ {
+ JsonDbObjectList schemas = getObjects(JsonDbString::kTypeStr, JsonDbString::kSchemaTypeStr,
+ QString()).data;
+ for (int i = 0; i < schemas.size(); ++i) {
+ JsonDbObject schemaObject = schemas.at(i);
+ QString schemaName = schemaObject.value("name").toString();
+ QJsonObject schema = schemaObject.value("schema").toObject();
+ setSchema(schemaName, schema);
+ }
+ }
+
+ foreach (const QString &schemaName, (QStringList() << JsonDbString::kNotificationTypeStr << JsonDbString::kViewTypeStr
+ << "Capability" << JsonDbString::kIndexTypeStr)) {
+ if (!mSchemas.contains(schemaName)) {
+ QFile schemaFile(QString(":schema/%1.json").arg(schemaName));
+ schemaFile.open(QIODevice::ReadOnly);
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(schemaFile.readAll(), &error);
+ if (doc.isNull()) {
+ qWarning() << "Parsing " << schemaName << " schema" << error.error;
+ return;
+ }
+ QJsonObject schema = doc.object();
+ JsonDbObject schemaObject;
+ schemaObject.insert(JsonDbString::kTypeStr, JsonDbString::kSchemaTypeStr);
+ schemaObject.insert("name", schemaName);
+ schemaObject.insert("schema", schema);
+ updateObject(mDefaultOwner, schemaObject, ForcedWrite);
+ }
+ }
+ {
+ JsonDbObject nameIndex;
+ nameIndex.insert(JsonDbString::kTypeStr, JsonDbString::kIndexTypeStr);
+ nameIndex.insert(JsonDbString::kNameStr, QLatin1String("capabilityName"));
+ nameIndex.insert(JsonDbString::kPropertyNameStr, QLatin1String("name"));
+ nameIndex.insert(JsonDbString::kPropertyTypeStr, QLatin1String("string"));
+ nameIndex.insert(JsonDbString::kObjectTypeStr, QLatin1String("Capability"));
+ updateObject(mDefaultOwner, nameIndex, ForcedWrite);
+
+ const QString capabilityName("RootCapability");
+ QFile capabilityFile(QString(":schema/%1.json").arg(capabilityName));
+ capabilityFile.open(QIODevice::ReadOnly);
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(capabilityFile.readAll(), &error);
+ if (doc.isNull()) {
+ qWarning() << "Parsing " << capabilityName << " capability" << error.error;
+ return;
+ }
+ JsonDbObject capability = doc.object();
+ QString name = capability.value("name").toString();
+ GetObjectsResult getObjectResponse = getObjects("capabilityName", name, "Capability");
+ int count = getObjectResponse.data.size();
+ if (!count) {
+ if (jsondbSettings->verbose())
+ qDebug() << "Creating capability" << capability;
+ updateObject(mDefaultOwner, capability);
+ } else {
+ JsonDbObject currentCapability = getObjectResponse.data.at(0);
+ if (currentCapability.value("accessRules") != capability.value("accessRules"))
+ updateObject(mDefaultOwner, capability);
+ }
+ }
+}
+
+#include "moc_jsondbpartition.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbpartition.h b/src/partition/jsondbpartition.h
new file mode 100644
index 00000000..eae6f16d
--- /dev/null
+++ b/src/partition/jsondbpartition.h
@@ -0,0 +1,288 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_PARTITION_H
+#define JSONDB_PARTITION_H
+
+#include <QStringList>
+#include <QRegExp>
+#include <QSet>
+#include <QTimer>
+#include <QVector>
+#include <QPointer>
+
+#include "jsondberrors.h"
+#include "jsondbobjectkey.h"
+#include "jsondbnotification.h"
+#include "jsondbowner.h"
+#include "jsondbpartitionglobal.h"
+#include "jsondbstat.h"
+#include "jsondbschemamanager_p.h"
+
+QT_BEGIN_HEADER
+
+class TestJsonDb;
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbBtree;
+class JsonDbOwner;
+class JsonDbObjectTable;
+class JsonDbIndex;
+class JsonDbIndexQuery;
+class JsonDbView;
+
+struct JsonDbUpdate {
+ JsonDbUpdate(const JsonDbObject &oldObj, const JsonDbObject &newObj, JsonDbNotification::Action act) :
+ oldObject(oldObj), newObject(newObj), action(act) { }
+ JsonDbUpdate() : action(JsonDbNotification::None) {}
+ JsonDbObject oldObject;
+ JsonDbObject newObject;
+ JsonDbNotification::Action action;
+};
+
+typedef QList<JsonDbUpdate> JsonDbUpdateList;
+
+struct JsonDbWriteResult {
+ JsonDbWriteResult() : state(0), code(JsonDbError::NoError) { }
+ JsonDbObjectList objectsWritten;
+ quint32 state;
+ JsonDbError::ErrorCode code;
+ QString message;
+};
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbPartition : public QObject
+{
+ Q_OBJECT
+public:
+
+ enum WriteMode {
+ OptimisticWrite, // write must not introduce a conflict
+ ForcedWrite, // accept write as is (almost no matter what)
+ ReplicatedWrite, // master/master replication, may create obj._meta.conflicts
+ ViewObject // internal for view object
+ };
+
+ JsonDbPartition(const QString &filename, const QString &name, JsonDbOwner *owner, QObject *parent = 0);
+ ~JsonDbPartition();
+ QString filename() const { return mFilename; }
+ bool open();
+ bool close();
+ bool clear();
+
+ bool beginTransaction();
+ bool commitTransaction(quint32 stateNumber = 0);
+ bool abortTransaction();
+
+ void initIndexes();
+ void flushCaches();
+ bool addIndex(const QString &indexName,
+ const QString &propertyName,
+ const QString &propertyType = QString("string"),
+ const QStringList &objectTypes = QStringList(),
+ const QString &propertyFunction = QString(),
+ const QString &locale = QString(),
+ const QString &collation = QString(),
+ const QString &casePreference = QString(),
+ Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive);
+ bool removeIndex(const QString &indexName, const QString &objectType = QString());
+
+ bool checkQuota(const JsonDbOwner *owner, int size) const;
+ bool addToQuota(const JsonDbOwner *owner, int size);
+
+ JsonDbQueryResult queryObjects(const JsonDbOwner *owner, const JsonDbQuery *query, int limit=-1, int offset=0);
+ JsonDbWriteResult updateObjects(const JsonDbOwner *owner, const JsonDbObjectList &objects, WriteMode mode = OptimisticWrite, JsonDbUpdateList *changeList = 0);
+ JsonDbWriteResult updateObject(const JsonDbOwner *owner, const JsonDbObject &object, WriteMode mode = OptimisticWrite, JsonDbUpdateList *changeList = 0);
+
+ QJsonObject flush();
+
+ JsonDbView *addView(const QString &viewType);
+ void removeView(const QString &viewType);
+ JsonDbObjectTable *mainObjectTable() const { return mObjectTable; }
+ JsonDbObjectTable *findObjectTable(const QString &objectType) const;
+ JsonDbView *findView(const QString &objectType) const;
+
+ bool getObject(const QString &uuid, JsonDbObject &object, const QString &objectType = QString()) const;
+ bool getObject(const ObjectKey & objectKey, JsonDbObject &object, const QString &objectType = QString()) const;
+
+ GetObjectsResult getObjects(const QString &keyName, const QJsonValue &key, const QString &type = QString(),
+ bool updateViews = true);
+
+ QJsonObject changesSince(quint32 stateNumber, const QSet<QString> &limitTypes = QSet<QString>());
+
+ inline QString name() const { return mPartitionName; }
+ inline void setName(const QString &name) { mPartitionName = name; }
+ inline JsonDbOwner *defaultOwner() { return mDefaultOwner; }
+
+ void checkIndex(const QString &propertyName);
+ bool compact();
+ JsonDbStat stat() const;
+
+ QHash<QString, qint64> fileSizes() const;
+
+ // FIXME: copied from JsonDb class.
+ // This is the protocol leaking into the lower layers and should be removed
+ static void setError(QJsonObject &map, int code, const QString &message);
+ static QJsonObject makeError(int code, const QString &message);
+ static QJsonObject makeResponse(const QJsonObject &resultmap, const QJsonObject &errormap, bool silent = false);
+ static QJsonObject makeErrorResponse(QJsonObject &resultmap, int code, const QString &message, bool silent = false);
+ static bool responseIsError(const QJsonObject &responseMap);
+
+public Q_SLOTS:
+ void updateView(const QString &objectType, quint32 stateNumber=0);
+
+Q_SIGNALS:
+ void objectsUpdated(const JsonDbUpdateList &objects);
+
+protected:
+ void initSchemas();
+
+ void timerEvent(QTimerEvent *event);
+
+ bool checkStateConsistency();
+ void checkIndexConsistency(JsonDbObjectTable *table, JsonDbIndex *index);
+
+ JsonDbIndexQuery *compileIndexQuery(const JsonDbOwner *owner, const JsonDbQuery *query);
+ void compileOrQueryTerm(JsonDbIndexQuery *indexQuery, const QueryTerm &queryTerm);
+
+ void doIndexQuery(const JsonDbOwner *owner, JsonDbObjectList &results, int &limit, int &offset,
+ JsonDbIndexQuery *indexQuery);
+
+ static void sortValues(const JsonDbQuery *query, JsonDbObjectList &results, JsonDbObjectList &joinedResults);
+
+ bool checkCanAddSchema(const JsonDbObject &schema, const JsonDbObject &oldSchema, QString &errorMsg);
+ bool checkCanRemoveSchema(const JsonDbObject &schema, QString &errorMsg);
+ bool validateSchema(const QString &schemaName, const JsonDbObject &object, QString &errorMsg);
+ bool checkNaturalObjectType(const JsonDbObject &object, QString &errorMsg);
+
+ JsonDbError::ErrorCode checkBuiltInTypeValidity(const JsonDbObject &object, const JsonDbObject &oldObject, QString &errorMsg);
+ JsonDbError::ErrorCode checkBuiltInTypeAccessControl(bool forCreation, const JsonDbOwner *owner, const JsonDbObject &object,
+ const JsonDbObject &oldObject, QString &errorMsg);
+ void updateBuiltInTypes(const JsonDbObject &object, const JsonDbObject &oldObject);
+ void setSchema(const QString &schemaName, const QJsonObject &schema);
+ void removeSchema(const QString &schemaName);
+ void updateSchemaIndexes(const QString &schemaName, QJsonObject object, const QStringList &path=QStringList());
+
+private:
+ JsonDbObjectTable *mObjectTable;
+ QVector<JsonDbObjectTable *> mTableTransactions;
+
+ QString mPartitionName;
+ QString mFilename;
+ int mTransactionDepth;
+ bool mTransactionOk;
+ QHash<QString,QPointer<JsonDbView> > mViews;
+ QSet<QString> mViewTypes;
+ JsonDbSchemaManager mSchemas;
+ QRegExp mWildCardPrefixRegExp;
+ int mMainSyncTimerId;
+ int mIndexSyncTimerId;
+ int mMainSyncInterval;
+ int mIndexSyncInterval;
+ JsonDbOwner *mDefaultOwner;
+
+ friend class JsonDbIndexQuery;
+ friend class JsonDbObjectTable;
+ friend class JsonDbMapDefinition;
+ friend class WithTransaction;
+ friend class ::TestPartition;
+ friend class ::TestJsonDb;
+};
+
+class WithTransaction {
+public:
+ WithTransaction(JsonDbPartition *partition=0, QString name=QString())
+ : mPartition(0)
+ {
+ Q_UNUSED(name)
+ if (partition)
+ setPartition(partition);
+ }
+
+ ~WithTransaction()
+ {
+ if (mPartition)
+ mPartition->commitTransaction();
+ }
+
+ void setPartition(JsonDbPartition *partition)
+ {
+ Q_ASSERT(!mPartition);
+ mPartition = partition;
+ if (!mPartition->beginTransaction())
+ mPartition = 0;
+ }
+ bool hasBegin() { return mPartition; }
+ bool addObjectTable(JsonDbObjectTable *table);
+
+ void abort()
+ {
+ if (mPartition)
+ mPartition->abortTransaction();
+ mPartition = 0;
+ }
+
+ void commit(quint32 stateNumber = 0)
+ {
+ if (mPartition)
+ mPartition->commitTransaction(stateNumber);
+ mPartition = 0;
+ }
+
+private:
+ JsonDbPartition *mPartition;
+};
+
+QJsonValue makeFieldValue(const QJsonValue &value, const QString &type);
+Q_JSONDB_PARTITION_EXPORT QByteArray makeForwardKey(const QJsonValue &fieldValue, const ObjectKey &objectKey);
+Q_JSONDB_PARTITION_EXPORT int forwardKeyCmp(const QByteArray &ab, const QByteArray &bb);
+void forwardKeySplit(const QByteArray &forwardKey, QJsonValue &fieldValue);
+void forwardKeySplit(const QByteArray &forwardKey, QJsonValue &fieldValue, ObjectKey &objectKey);
+QByteArray makeForwardValue(const ObjectKey &);
+void forwardValueSplit(const QByteArray &forwardValue, ObjectKey &objectKey);
+
+QDebug &operator<<(QDebug &, const ObjectKey &);
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_PARTITION_H
diff --git a/src/partition/jsondbpartitionglobal.h b/src/partition/jsondbpartitionglobal.h
new file mode 100644
index 00000000..9b88409b
--- /dev/null
+++ b/src/partition/jsondbpartitionglobal.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_PARTITION_GLOBAL_H
+#define JSONDB_PARTITION_GLOBAL_H
+
+#include "QtCore/qglobal.h"
+
+#if defined(QT_JSONDB_PARTITION_LIB)
+# define Q_JSONDB_PARTITION_EXPORT Q_DECL_EXPORT
+#else
+# define Q_JSONDB_PARTITION_EXPORT Q_DECL_IMPORT
+#endif
+
+#if defined(QT_NAMESPACE)
+# define QT_BEGIN_NAMESPACE_JSONDB_PARTITION namespace QT_NAMESPACE { namespace QtJsonDb { namespace Partition {
+# define QT_END_NAMESPACE_JSONDB_PARTITION } } }
+# define QT_USE_NAMESPACE_JSONDB_PARTITION using namespace QT_NAMESPACE::QtJsonDb::Partition;
+# define QT_PREPEND_NAMESPACE_JSONDB_PARTITION(name) ::QT_NAMESPACE::QtJsonDb::Partition::name
+#else
+# define QT_BEGIN_NAMESPACE_JSONDB_PARTITION namespace QtJsonDb { namespace Partition {
+# define QT_END_NAMESPACE_JSONDB_PARTITION } }
+# define QT_USE_NAMESPACE_JSONDB_PARTITION using namespace QtJsonDb::Partition;
+# define QT_PREPEND_NAMESPACE_JSONDB_PARTITION(name) ::QtJsonDb::Partition::name
+#endif
+
+// a workaround for moc - if there is a header file that doesn't use jsondb
+// namespace, we still force moc to do "using namespace" but the namespace have to
+// be defined, so let's define an empty namespace here
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+#endif // JSONDB_PARTITION_GLOBAL_H
diff --git a/src/partition/jsondbproxy.cpp b/src/partition/jsondbproxy.cpp
new file mode 100644
index 00000000..d6040717
--- /dev/null
+++ b/src/partition/jsondbproxy.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbproxy.h"
+#include "jsondbstrings.h"
+#include "jsondbobject.h"
+#include "jsondbsettings.h"
+
+#include <QDebug>
+#include <QJSEngine>
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbMapProxy::JsonDbMapProxy(const JsonDbOwner *owner, JsonDbPartition *partition, QObject *parent)
+ : QObject(parent)
+ , mOwner(owner)
+ , mPartition(partition)
+{
+}
+JsonDbMapProxy::~JsonDbMapProxy()
+{
+}
+
+void JsonDbMapProxy::emitViewObject(const QString &key, const QJSValue &v)
+{
+ QJSValue object = v.engine()->newObject();
+ object.setProperty("key", key);
+ object.setProperty("value", v);
+ emit viewObjectEmitted(object);
+}
+
+void JsonDbMapProxy::lookup(const QString &key, const QJSValue &value, const QJSValue &context)
+{
+ QJSValue query = value.engine()->newObject();
+ query.setProperty("index", key);
+ query.setProperty("value", value);
+
+ emit lookupRequested(query, context);
+}
+
+void JsonDbMapProxy::lookupWithType(const QString &key, const QJSValue &value, const QJSValue &objectType, const QJSValue &context)
+{
+ QJSValue query = value.engine()->newObject();
+ query.setProperty("index", key);
+ query.setProperty("value", value);
+ query.setProperty("objectType", objectType);
+ emit lookupRequested(query, context);
+}
+
+JsonDbJoinProxy::JsonDbJoinProxy(const JsonDbOwner *owner, JsonDbPartition *partition, QObject *parent)
+ : QObject(parent)
+ , mOwner(owner)
+ , mPartition(partition)
+{
+}
+JsonDbJoinProxy::~JsonDbJoinProxy()
+{
+}
+
+void JsonDbJoinProxy::create(const QJSValue &v)
+{
+ emit viewObjectEmitted(v);
+}
+
+void JsonDbJoinProxy::lookup(const QJSValue &spec, const QJSValue &context)
+{
+ emit lookupRequested(spec, context);
+}
+
+QString JsonDbJoinProxy::createUuidFromString(const QString &id)
+{
+ JsonDbObject o;
+ o.insert(QLatin1String("_id"), id);
+ o.generateUuid();
+ return o.value(JsonDbString::kUuidStr).toString();
+}
+
+Console::Console()
+{
+}
+
+void Console::log(const QString &s)
+{
+ if (jsondbSettings->debug())
+ qDebug() << s;
+}
+void Console::debug(const QString &s)
+{
+ if (jsondbSettings->debug())
+ qDebug() << s;
+}
+void Console::info(const QString &s)
+{
+ qDebug() << s;
+}
+void Console::warn(const QString &s)
+{
+ qWarning() << s;
+}
+void Console::error(const QString &s)
+{
+ qCritical() << s;
+}
+
+#include "moc_jsondbproxy.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbproxy.h b/src/partition/jsondbproxy.h
new file mode 100644
index 00000000..886a6ba1
--- /dev/null
+++ b/src/partition/jsondbproxy.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_PROXY_H
+#define JSONDB_PROXY_H
+
+#include <QObject>
+#include <QMultiMap>
+#include <QJSValue>
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbOwner;
+class JsonDbPartition;
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbMapProxy : public QObject {
+ Q_OBJECT
+public:
+ JsonDbMapProxy(const JsonDbOwner *owner, JsonDbPartition *partition, QObject *parent=0);
+ ~JsonDbMapProxy();
+
+ Q_SCRIPTABLE void emitViewObject(const QString &key, const QJSValue &value );
+ Q_SCRIPTABLE void lookup(const QString &key, const QJSValue &value, const QJSValue &context );
+ Q_SCRIPTABLE void lookupWithType(const QString &key, const QJSValue &value, const QJSValue &objectType, const QJSValue &context);
+
+ void setOwner(const JsonDbOwner *owner) { mOwner = owner; }
+
+ signals:
+ void viewObjectEmitted(const QJSValue &);
+ // to be removed when all map/lookup are converted to join/lookup
+ void lookupRequested(const QJSValue &, const QJSValue &);
+private:
+ const JsonDbOwner *mOwner;
+ JsonDbPartition *mPartition;
+};
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbJoinProxy : public QObject {
+ Q_OBJECT
+public:
+ JsonDbJoinProxy( const JsonDbOwner *owner, JsonDbPartition *partition, QObject *parent=0 );
+ ~JsonDbJoinProxy();
+
+ Q_SCRIPTABLE void create(const QJSValue &value );
+ Q_SCRIPTABLE void lookup(const QJSValue &spec, const QJSValue &context );
+ Q_SCRIPTABLE QString createUuidFromString(const QString &id);
+
+ void setOwner(const JsonDbOwner *owner) { mOwner = owner; }
+
+ signals:
+ void viewObjectEmitted(const QJSValue &);
+ void lookupRequested(const QJSValue &, const QJSValue &);
+private:
+ const JsonDbOwner *mOwner;
+ JsonDbPartition *mPartition;
+};
+
+class Console : public QObject {
+ Q_OBJECT
+public:
+ Console();
+ Q_SCRIPTABLE void log(const QString &string);
+ Q_SCRIPTABLE void debug(const QString &string);
+ Q_SCRIPTABLE void info(const QString &string);
+ Q_SCRIPTABLE void warn(const QString &string);
+ Q_SCRIPTABLE void error(const QString &string);
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_PROXY_H
diff --git a/src/partition/jsondbquery.cpp b/src/partition/jsondbquery.cpp
new file mode 100644
index 00000000..393ec1f4
--- /dev/null
+++ b/src/partition/jsondbquery.cpp
@@ -0,0 +1,688 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QDebug>
+#include <QFile>
+#include <QJsonDocument>
+#include <QStack>
+#include <QString>
+
+#include "jsondbstrings.h"
+#include "jsondbindexquery.h"
+#include "jsondbpartition.h"
+#include "jsondbquery.h"
+#include "jsondbsettings.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+const char* JsonDbQueryTokenizer::sTokens[] = {
+"[", "]", "{", "}", "/", "?", ",", ":", "|", "\\"
+//operators are ordered by precedence
+, "!=", "<=", ">=", "=~", "->", "=", ">", "<"
+, ""//end of the token list
+};
+
+JsonDbQueryTokenizer::JsonDbQueryTokenizer(QString input)
+ : mInput(input), mPos(0)
+{
+}
+
+QString JsonDbQueryTokenizer::pop()
+{
+ QString token;
+ if (!mNextToken.isEmpty()) {
+ token = mNextToken;
+ mNextToken.clear();
+ } else {
+ token = getNextToken();
+ }
+ return token;
+}
+
+QString JsonDbQueryTokenizer::popIdentifier()
+{
+ QString identifier = pop();
+ if (identifier.startsWith('\"')
+ && identifier.endsWith('\"'))
+ identifier = identifier.mid(1, identifier.size()-2);
+ return identifier;
+}
+
+QString JsonDbQueryTokenizer::peek()
+{
+ if (mNextToken.isEmpty()) {
+ mNextToken = getNextToken();
+ }
+ return mNextToken;
+}
+
+QString JsonDbQueryTokenizer::getNextToken()
+{
+ QString result;
+
+ while (mPos < mInput.size()) {
+ QChar c = mInput[mPos++];
+ if (c == '"') {
+ // match string
+ result.append(c);
+ bool escaped = false;
+ QChar sc;
+ int size = mInput.size();
+ int i;
+ //qDebug() << "start of string";
+ for (i = mPos; (i < size); i++) {
+ sc = mInput[i];
+ //qDebug() << i << sc << escaped;
+ if (!escaped && (sc == '"'))
+ break;
+ escaped = (sc == '\\');
+ }
+ //qDebug() << "end" << i << sc << escaped;
+ //qDebug() << mInput.mid(mPos, i-mPos+1);
+ if ((i < size) && (sc == '"')) {
+ //qDebug() << mPos << i-mPos << "string is" << mInput.mid(mPos, i-mPos);
+ result.append(mInput.mid(mPos, i-mPos+1));
+ mPos = i+1;
+ } else {
+ mPos = i;
+ result = QString();
+ }
+ return result;
+ } else if (c.isSpace()) {
+ if (result.size())
+ return result;
+ else
+ continue;
+ } else if (result.size() && mPos+1 < mInput.size()) {
+ //index expression?[n],[*]
+ if (c == '[' && mInput[mPos+1] == ']') {
+ result.append(mInput.mid(mPos-1,3));
+ mPos += 2;
+ continue;
+ }
+ }
+ //operators
+ int i = 0;
+ while (sTokens[i][0] != 0) {
+ if (mInput.midRef(mPos - 1,2).startsWith(sTokens[i])) {
+ if (!result.isEmpty()) {
+ mPos --;
+ return result;
+ }
+ result.append(sTokens[i]);
+ mPos += strlen(sTokens[i]) - 1;
+ return result;
+ }
+ i++;
+ }
+ result.append(mInput[mPos-1]);
+ }//while
+ return QString();
+}
+
+QJsonValue QueryTerm::value() const
+{
+ if (mVariable.size())
+ return mQuery->binding(mVariable);
+ else
+ return mValue;
+}
+
+JsonDbQuery::JsonDbQuery(const QList<OrQueryTerm> &qt, const QList<OrderTerm> &ot) :
+ queryTerms(qt)
+ , orderTerms(ot)
+{
+}
+
+JsonDbQuery::~JsonDbQuery()
+{
+ queryTerms.clear();
+ orderTerms.clear();
+}
+
+QJsonValue JsonDbQuery::parseJsonLiteral(const QString &json, QueryTerm *term, const QJsonObject &bindings, bool *ok)
+{
+ const ushort trueLiteral[] = {'t','r','u','e', 0};
+ const ushort falseLiteral[] = {'f','a','l','s','e', 0};
+ const ushort *literal = json.utf16();
+ Q_ASSERT(ok != NULL);
+ *ok = true;
+ switch (literal[0]) {
+ case '"':
+ term->setValue(json.mid(1, json.size()-2));
+ break;
+ case 't':
+ // we will interpret "true0something" as true is it a real problem ?
+ for (int i = 1; i < 5 /* 'true0' length */; ++i) {
+ if (trueLiteral[i] != literal[i]) {
+ *ok = false;
+ return term->value();
+ }
+ }
+ term->setValue(true);
+ break;
+ case 'f':
+ // we will interpret "false0something" as false is it a real problem ?
+ for (int i = 1; i < 6 /* 'false0' length */; ++i) {
+ if (falseLiteral[i] != literal[i]) {
+ *ok = false;
+ return term->value();
+ }
+ }
+ term->setValue(false);
+ break;
+ case '%':
+ {
+ const QString name = json.mid(1);
+ if (bindings.contains(name))
+ term->setValue(bindings.value(name));
+ else
+ term->setVariable(name);
+ break;
+ }
+ case 0:
+ // This can happen if json.length() == 0
+ *ok = false;
+ return term->value();
+ default:
+ int result = json.toInt(ok);
+ if (*ok) {
+ term->setValue(result);
+ } else {
+ // bad luck, it can be only a double
+ term->setValue(json.toDouble(ok));
+ }
+ }
+ return term->value();
+}
+
+JsonDbQuery *JsonDbQuery::parse(const QString &query, const QJsonObject &bindings)
+{
+ JsonDbQuery *parsedQuery = new JsonDbQuery;
+ parsedQuery->query = query;
+
+ if (!query.startsWith('['))
+ return parsedQuery;
+
+ bool parseError = false;
+ JsonDbQueryTokenizer tokenizer(query);
+ QString token;
+ while (!parseError
+ && !(token = tokenizer.pop()).isEmpty()) {
+ if (token != "[") {
+ qCritical() << "unexpected token" << token;
+ break;
+ }
+ token = tokenizer.pop();
+ if (token == "?") {
+ OrQueryTerm oqt;
+ do {
+ QString fieldSpec = tokenizer.popIdentifier();
+ if (fieldSpec == "|")
+ fieldSpec = tokenizer.popIdentifier();
+
+ QString opOrJoin = tokenizer.pop();
+ QString op;
+ QStringList joinFields;
+ QString joinField;
+ while (opOrJoin == "->") {
+ joinFields.append(fieldSpec);
+ fieldSpec = tokenizer.popIdentifier();
+ opOrJoin = tokenizer.pop();
+ }
+ if (joinFields.size())
+ joinField = joinFields.join("->");
+ op = opOrJoin;
+
+
+ QueryTerm term(parsedQuery);
+ if (!joinField.isEmpty())
+ term.setJoinField(joinField);
+ if (fieldSpec.startsWith(QChar('%'))) {
+ const QString name = fieldSpec.mid(1);
+ if (bindings.contains(name)) {
+ QJsonValue val = bindings.value(name);
+ parsedQuery->bind(name, val);
+ }
+ term.setPropertyVariable(name);
+ }
+ else
+ term.setPropertyName(fieldSpec);
+ term.setOp(op);
+ if (op == "=~") {
+ QString tvs = tokenizer.pop();
+ int sepPos = 1; // assuming it's a literal "/regexp/modifiers"
+ if (tvs.startsWith(QChar('%'))) {
+ const QString name = tvs.mid(1);
+ if (bindings.contains(name)) {
+ tvs = bindings.value(name).toString();
+ sepPos = 0;
+ }
+ } else if (!tvs.startsWith("\"")) {
+ parsedQuery->queryExplanation.append(QString("Failed to parse query regular expression '%1' in query '%2' %3 op %4")
+ .arg(tvs)
+ .arg(parsedQuery->query)
+ .arg(fieldSpec)
+ .arg(op));
+ parseError = true;
+ break;
+ }
+ QChar sep = tvs[sepPos];
+ int eor = sepPos;
+ do {
+ eor = tvs.indexOf(sep, eor+1); // end of regexp;
+ //qDebug() << "tvs" << tvs << "eor" << eor << "tvs[eor-1]" << ((eor > 0) ? tvs[eor-1] : QChar('*'));
+ if (eor <= sepPos) {
+ parseError = true;
+ break;
+ }
+ } while ((eor > 0) && (tvs[eor-1] == '\\'));
+ QString modifiers = tvs.mid(eor+1,tvs.size()-eor-2*sepPos);
+ if (jsondbSettings->debug()) {
+ qDebug() << "modifiers" << modifiers;
+ qDebug() << "regexp" << tvs.mid(sepPos + 1, eor-sepPos-1);
+ }
+ if (modifiers.contains('w'))
+ term.regExp().setPatternSyntax(QRegExp::Wildcard);
+ if (modifiers.contains('i'))
+ term.regExp().setCaseSensitivity(Qt::CaseInsensitive);
+ //qDebug() << "pattern" << tvs.mid(2, eor-2);
+ term.regExp().setPattern(tvs.mid(sepPos + 1, eor-sepPos-1));
+ } else if (op == "contains") {
+ bool ok = true;;
+
+ QString value = tokenizer.pop();
+ if (value == "[" || value == "{") {
+ QStack<QString> tokenStack;
+ tokenStack.push(value);
+ QString tkn = value;
+
+ while (ok && !tkn.isEmpty()) {
+ tkn = tokenizer.pop();
+ if (tkn == "]" && tokenStack.isEmpty()) {
+ tokenizer.push(tkn);
+ break;
+ } else {
+ value += tkn;
+ if (tkn == "]") {
+ if (tokenStack.pop() != "[")
+ ok = false;
+ } else if (tkn == "}") {
+ if (tokenStack.pop() != "{")
+ ok = false;
+ }
+ }
+ }
+
+ if (ok) {
+ QJsonParseError parserError;
+ QJsonDocument parsedValue = QJsonDocument::fromJson(value.toUtf8(), &parserError);
+ if (parserError.error != QJsonParseError::NoError) {
+ ok = false;
+ } else {
+ if (parsedValue.isArray())
+ term.setValue(parsedValue.array());
+ else
+ term.setValue(parsedValue.object());
+ }
+ }
+ } else {
+ parseJsonLiteral(value, &term, bindings, &ok);
+ }
+
+ if (!ok) {
+ parsedQuery->queryExplanation.append(QString("Failed to parse query value '%1' in query '%2' %3 op %4")
+ .arg(value)
+ .arg(parsedQuery->query)
+ .arg(fieldSpec)
+ .arg(op));
+ parseError = true;
+ break;
+ }
+ } else if ((op != "exists") && (op != "notExists")) {
+ QString value = tokenizer.pop();
+ bool ok = true;;
+ if (value == "[") {
+ QJsonArray values;
+ while (1) {
+ value = tokenizer.pop();
+ if (value == "]")
+ break;
+ parseJsonLiteral(value, &term, bindings, &ok);
+ if (!ok)
+ break;
+ values.append(term.value());
+ if (tokenizer.peek() == ",")
+ tokenizer.pop();
+ }
+ term.setValue(values);
+ } else {
+ parseJsonLiteral(value, &term, bindings, &ok);
+ }
+ if (!ok) {
+ parsedQuery->queryExplanation.append(QString("Failed to parse query value '%1' in query '%2' %3 op %4")
+ .arg(value)
+ .arg(parsedQuery->query)
+ .arg(fieldSpec)
+ .arg(op));
+ parseError = true;
+ break;
+ }
+ }
+
+ oqt.addTerm(term);
+ } while (tokenizer.peek() != "]");
+ parsedQuery->queryTerms.append(oqt);
+ } else if (token == "=") {
+ QString curlyBraceToken = tokenizer.pop();
+ if (curlyBraceToken != "{") {
+ parsedQuery->queryExplanation.append(QString("Parse error: expecting '{' but got '%1'")
+ .arg(curlyBraceToken));
+ parseError = true;
+ break;
+ }
+ QString nextToken;
+ while (!(nextToken = tokenizer.popIdentifier()).isEmpty()) {
+ if (nextToken == "}") {
+ break;
+ } else {
+
+ //qDebug() << "isMapObject" << nextToken << tokenizer.peek();
+ parsedQuery->mapKeyList.append(nextToken);
+ QString colon = tokenizer.pop();
+ if (colon != ":") {
+ parsedQuery->queryExplanation.append(QString("Parse error: expecting ':' but got '%1'").arg(colon));
+ parseError = true;
+ break;
+ }
+ nextToken = tokenizer.popIdentifier();
+
+ while (tokenizer.peek() == "->") {
+ QString op = tokenizer.pop();
+ nextToken.append(op);
+ nextToken.append(tokenizer.popIdentifier());
+ }
+ parsedQuery->mapExpressionList.append(nextToken);
+ QString maybeComma = tokenizer.pop();
+ if (maybeComma == "}") {
+ tokenizer.push(maybeComma);
+ continue;
+ } else if (maybeComma != ",") {
+ parsedQuery->queryExplanation.append(QString("Parse error: expecting ',', or '}' but got '%1'")
+ .arg(maybeComma));
+ parseError = true;
+ break;
+ }
+ }
+ }
+ } else if ((token == "/") || (token == "\\") || (token == ">") || (token == "<")) {
+ QString ordering = token;
+ OrderTerm term;
+ term.propertyName = tokenizer.popIdentifier();
+ term.ascending = ((ordering == "/") || (ordering == ">"));
+ parsedQuery->orderTerms.append(term);
+ } else if (token == "count") {
+ parsedQuery->mAggregateOperation = "count";
+ } else if (token == "*") {
+ // match all objects
+ } else {
+ qCritical() << QString("Parse error: expecting '?', '/', '\\', or 'count' but got '%1'").arg(token);
+ parseError = true;
+ break;
+ }
+ QString closeBracket = tokenizer.pop();
+ if (closeBracket != "]") {
+ qCritical() << QString("Parse error: expecting ']' but got '%1'").arg(closeBracket);
+ parseError = true;
+ break;
+ }
+ }
+
+ if (parseError) {
+ QStringList explanation = parsedQuery->queryExplanation;
+ delete parsedQuery;
+ parsedQuery = new JsonDbQuery;
+ parsedQuery->queryExplanation = explanation;
+ qCritical() << "Parser error: query" << query << explanation;
+ return parsedQuery;
+ }
+
+ foreach (const OrQueryTerm &oqt, parsedQuery->queryTerms) {
+ foreach (const QueryTerm &term, oqt.terms()) {
+ if (term.propertyName() == JsonDbString::kTypeStr) {
+ if (term.op() == "=") {
+ parsedQuery->mMatchedTypes.clear();
+ parsedQuery->mMatchedTypes.insert(term.value().toString());
+ } else if (term.op() == "!=") {
+ parsedQuery->mMatchedTypes.insert(term.value().toString());
+ }
+ }
+ }
+ }
+
+ if (!parsedQuery->queryTerms.size() && !parsedQuery->orderTerms.size()) {
+ // match everything -- sort on type
+ OrderTerm term;
+ term.propertyName = JsonDbString::kTypeStr;
+ term.ascending = true;
+ parsedQuery->orderTerms.append(term);
+ }
+
+ //qDebug() << "queryTerms.size()" << parsedQuery->queryTerms.size();
+ //qDebug() << "orderTerms.size()" << parsedQuery->orderTerms.size();
+ return parsedQuery;
+}
+
+bool JsonDbQuery::match(const JsonDbObject &object, QHash<QString, JsonDbObject> *objectCache, JsonDbPartition *partition) const
+{
+ for (int i = 0; i < queryTerms.size(); i++) {
+ const OrQueryTerm &orQueryTerm = queryTerms[i];
+ bool matches = false;
+ foreach (const QueryTerm &term, orQueryTerm.terms()) {
+ const QString &joinPropertyName = term.joinField();
+ const QString &op = term.op();
+ const QJsonValue &termValue = term.value();
+
+ QJsonValue objectFieldValue;
+ if (!joinPropertyName.isEmpty()) {
+ JsonDbObject joinedObject = object;
+ const QVector<QStringList> &joinPaths = term.joinPaths();
+ for (int j = 0; j < joinPaths.size(); j++) {
+ if (!joinPaths[j].size()) {
+ if (jsondbSettings->debug())
+ qDebug() << term.joinField() << term.joinPaths();
+ }
+ QString uuidValue = joinedObject.propertyLookup(joinPaths[j]).toString();
+ if (objectCache && objectCache->contains(uuidValue))
+ joinedObject = objectCache->value(uuidValue);
+ else if (partition) {
+ ObjectKey objectKey(uuidValue);
+ partition->getObject(objectKey, joinedObject);
+ if (objectCache) objectCache->insert(uuidValue, joinedObject);
+ }
+ }
+ objectFieldValue = joinedObject.propertyLookup(term.fieldPath());
+ } else {
+ if (term.propertyName().isEmpty())
+ objectFieldValue = binding(term.propertyVariable());
+ else
+ objectFieldValue = object.propertyLookup(term.fieldPath());
+ }
+ if ((op == "=") || (op == "==")) {
+ if (objectFieldValue == termValue)
+ matches = true;
+ } else if ((op == "<>") || (op == "!=")) {
+ if (objectFieldValue != termValue)
+ matches = true;
+ } else if (op == "=~") {
+ if (jsondbSettings->debug())
+ qDebug() << objectFieldValue.toString() << term.regExpConst().exactMatch(objectFieldValue.toString());
+ if (term.regExpConst().exactMatch(objectFieldValue.toString()))
+ matches = true;
+ } else if (op == "<=") {
+ matches = JsonDbIndexQuery::lessThan(objectFieldValue, termValue) || (objectFieldValue == termValue);
+ } else if (op == "<") {
+ matches = JsonDbIndexQuery::lessThan(objectFieldValue, termValue);
+ } else if (op == ">=") {
+ matches = JsonDbIndexQuery::greaterThan(objectFieldValue, termValue) || (objectFieldValue == termValue);
+ } else if (op == ">") {
+ matches = JsonDbIndexQuery::greaterThan(objectFieldValue, termValue);
+ } else if (op == "exists") {
+ if (objectFieldValue.type() != QJsonValue::Undefined)
+ matches = true;
+ } else if (op == "notExists") {
+ if (objectFieldValue.type() == QJsonValue::Undefined)
+ matches = true;
+ } else if (op == "in") {
+ if (0) qDebug() << __FUNCTION__ << __LINE__ << objectFieldValue << "in" << termValue
+ << termValue.toArray().contains(objectFieldValue);
+ if (termValue.toArray().contains(objectFieldValue))
+ matches = true;
+ } else if (op == "notIn") {
+ if (0) qDebug() << __FUNCTION__ << __LINE__ << objectFieldValue << "notIn" << termValue
+ << !termValue.toArray().contains(objectFieldValue);
+ if (!termValue.toArray().contains(objectFieldValue))
+ matches = true;
+ } else if (op == "contains") {
+ if (0) qDebug() << __FUNCTION__ << __LINE__ << objectFieldValue << "contains" << termValue
+ << objectFieldValue.toArray().contains(termValue);
+ if (objectFieldValue.toArray().contains(termValue))
+ matches = true;
+ } else if (op == "startsWith") {
+ if ((objectFieldValue.type() == QJsonValue::String)
+ && objectFieldValue.toString().startsWith(termValue.toString()))
+ matches = true;
+ } else {
+ qCritical() << "match" << "unhandled term" << term.propertyName() << term.op() << term.value() << term.joinField();
+ }
+ }
+ if (!matches)
+ return false;
+ }
+ return true;
+}
+
+
+
+QueryTerm::QueryTerm(const JsonDbQuery *query)
+ : mQuery(query), mJoinPaths()
+{
+}
+
+QueryTerm::~QueryTerm()
+{
+ mValue = QJsonValue();
+ mJoinPaths.clear();
+}
+
+OrQueryTerm::OrQueryTerm()
+{
+}
+
+OrQueryTerm::OrQueryTerm(const QueryTerm &term)
+{
+ mTerms.append(term);
+}
+
+OrQueryTerm::~OrQueryTerm()
+{
+}
+
+QList<QString> OrQueryTerm::propertyNames() const
+{
+ QList<QString> propertyNames;
+ foreach (const QueryTerm &term, mTerms) {
+ QString propertyName = term.propertyName();
+ if (!propertyNames.contains(propertyName))
+ propertyNames.append(propertyName);
+ }
+ return propertyNames;
+}
+
+QList<QString> OrQueryTerm::findUnindexablePropertyNames() const
+{
+ QList<QString> unindexablePropertyNames;
+ foreach (const QueryTerm &term, mTerms) {
+ const QString propertyName = term.propertyName();
+ const QString op = term.op();
+ if (op == QLatin1String("notExists") && !unindexablePropertyNames.contains(propertyName))
+ unindexablePropertyNames.append(propertyName);
+ }
+ return unindexablePropertyNames;
+}
+
+OrderTerm::OrderTerm()
+{
+}
+
+OrderTerm::~OrderTerm()
+{
+}
+
+QVariantMap JsonDbQueryResult::toVariantMap() const
+{
+ QJsonObject resultmap, errormap;
+ QJsonArray variantList;
+ for (int i = 0; i < data.size(); i++)
+ variantList.append(data.at(i));
+ resultmap.insert(JsonDbString::kDataStr, variantList);
+ resultmap.insert(JsonDbString::kLengthStr, data.size());
+ resultmap.insert(JsonDbString::kOffsetStr, offset);
+ resultmap.insert(JsonDbString::kExplanationStr, explanation);
+ resultmap.insert(QString("sortKeys"), sortKeys);
+ if (error.isObject())
+ errormap = error.toObject();
+ return JsonDbPartition::makeResponse(resultmap, errormap).toVariantMap();
+}
+
+JsonDbQueryResult JsonDbQueryResult::makeErrorResponse(JsonDbError::ErrorCode code, const QString &message, bool silent)
+{
+ JsonDbQueryResult result;
+ QJsonObject errormap;
+ errormap.insert(JsonDbString::kCodeStr, code);
+ errormap.insert(JsonDbString::kMessageStr, message);
+ result.error = errormap;
+ if (jsondbSettings->verbose() && !silent && !errormap.isEmpty())
+ qCritical() << errormap;
+ return result;
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbquery.h b/src/partition/jsondbquery.h
new file mode 100644
index 00000000..324f9ee1
--- /dev/null
+++ b/src/partition/jsondbquery.h
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_QUERY_H
+#define JSONDB_QUERY_H
+
+#include <QDebug>
+#include <QObject>
+#include <QHash>
+#include <QStringList>
+#include <QVariant>
+#include "jsondberrors.h"
+
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+#include "jsondbobject.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbPartition;
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbQueryTokenizer {
+public:
+ JsonDbQueryTokenizer(QString input);
+ QString pop();
+ QString popIdentifier();
+ QString peek();
+ void push(QString token) {
+ if (!mNextToken.isEmpty())
+ qCritical() << Q_FUNC_INFO << "Cannot push multiple tokens";
+ mNextToken = token;
+ }
+protected:
+ QString getNextToken();
+ static const char* sTokens[];
+private:
+ QString mInput;
+ int mPos;
+ QString mNextToken;
+};
+
+class JsonDbQuery;
+class Q_JSONDB_PARTITION_EXPORT QueryTerm {
+public:
+ QueryTerm(const JsonDbQuery *query);
+ ~QueryTerm();
+ QString propertyName() const { return mPropertyName; }
+ void setPropertyName(QString propertyName) { mPropertyName = propertyName; mFieldPath = propertyName.split('.'); }
+ const QStringList &fieldPath() const { return mFieldPath; }
+
+ QString op() const { return mOp; }
+ void setOp(QString op) { mOp = op; }
+
+ QString joinField() const { return mJoinField; }
+ void setJoinField(QString joinField) {
+ mJoinField = joinField;
+ if (!joinField.isEmpty()) {
+ QStringList joinFields = joinField.split("->");
+ mJoinPaths.resize(joinFields.size());
+ for (int j = 0; j < joinFields.size(); j++)
+ mJoinPaths[j] = joinFields[j].split('.');
+ }
+ }
+ const QVector<QStringList> &joinPaths() const { return mJoinPaths; }
+
+ QString variable() const { return mVariable; }
+ void setVariable(const QString variable) { mVariable = variable; }
+
+ QString propertyVariable() const { return mPropertyVariable; }
+ void setPropertyVariable(const QString variable) { mPropertyVariable = variable; }
+
+ QJsonValue value() const;
+ void setValue(const QJsonValue &v) { mValue = v; }
+ QRegExp &regExp() { return mRegExp; }
+ void setRegExp(const QRegExp &regExp) { mRegExp = regExp; }
+ const QRegExp &regExpConst() const { return mRegExp; }
+
+ private:
+ const JsonDbQuery *mQuery;
+ QString mVariable;
+ QString mPropertyName;
+ QString mPropertyVariable;
+ QStringList mFieldPath;
+ QString mOp;
+ QString mJoinField;
+ QVector<QStringList> mJoinPaths;
+ QJsonValue mValue;
+ QRegExp mRegExp;
+};
+
+class OrQueryTerm {
+public:
+ OrQueryTerm();
+ OrQueryTerm(const QueryTerm &term);
+ ~OrQueryTerm();
+ const QList<QueryTerm> &terms() const { return mTerms; }
+ void addTerm(const QueryTerm &term) { mTerms.append(term); }
+ QList<QString> propertyNames() const;
+ QList<QString> findUnindexablePropertyNames() const;
+private:
+ QList<QueryTerm> mTerms;
+};
+
+class Q_JSONDB_PARTITION_EXPORT OrderTerm {
+public:
+ OrderTerm();
+ ~OrderTerm();
+ bool ascending;
+ QString propertyName;
+};
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbQuery {
+public:
+ JsonDbQuery() { }
+ JsonDbQuery(const QList<OrQueryTerm> &qt, const QList<OrderTerm> &ot);
+ ~JsonDbQuery();
+ QList<OrQueryTerm> queryTerms;
+ QList<OrderTerm> orderTerms;
+ QString query;
+ QStringList mapExpressionList;
+ QStringList mapKeyList;
+ QStringList queryExplanation;
+ QString mAggregateOperation;
+
+ QSet<QString> matchedTypes() const { return mMatchedTypes; }
+ QJsonValue binding(const QString variable) const { return mBindings.value(variable); }
+ void bind(QString variable, QJsonValue &binding) { mBindings[variable] = binding; }
+ bool match(const JsonDbObject &object, QHash<QString, JsonDbObject> *objectCache, JsonDbPartition *partition = 0) const;
+
+ static QJsonValue parseJsonLiteral(const QString &json, QueryTerm *term, const QJsonObject &bindings, bool *ok);
+ static JsonDbQuery *parse(const QString &query, const QJsonObject &bindings = QJsonObject());
+
+private:
+ QSet<QString> mMatchedTypes;
+ QMap<QString,QJsonValue> mBindings;
+ Q_DISABLE_COPY(JsonDbQuery);
+};
+
+typedef QList<QJsonValue> QJsonValueList;
+typedef QList<QJsonObject> QJsonObjectList;
+typedef QList<JsonDbObject> JsonDbObjectList;
+class Q_JSONDB_PARTITION_EXPORT JsonDbQueryResult {
+public:
+ JsonDbObjectList data;
+ QJsonValue length;
+ QJsonValue offset;
+ QJsonValue explanation;
+ QJsonValue sortKeys;
+ QJsonValue state;
+ QJsonValue error; // { code: int, message: string }
+ QVariantMap toVariantMap() const;
+ static JsonDbQueryResult makeErrorResponse(JsonDbError::ErrorCode, const QString&, bool silent=false);
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_QUERY_H
diff --git a/src/partition/jsondbreducedefinition.cpp b/src/partition/jsondbreducedefinition.cpp
new file mode 100644
index 00000000..ded9357e
--- /dev/null
+++ b/src/partition/jsondbreducedefinition.cpp
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QDebug>
+#include <QElapsedTimer>
+#include <QJSValue>
+#include <QJSValueIterator>
+#include <QStringList>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "jsondbpartition.h"
+#include "jsondbstrings.h"
+#include "jsondberrors.h"
+
+#include "jsondbproxy.h"
+#include "jsondbsettings.h"
+#include "jsondbobjecttable.h"
+#include "jsondbreducedefinition.h"
+#include "jsondbscriptengine.h"
+#include "jsondbview.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbReduceDefinition::JsonDbReduceDefinition(const JsonDbOwner *owner, JsonDbPartition *partition,
+ QJsonObject definition, QObject *parent) :
+ QObject(parent)
+ , mOwner(owner)
+ , mPartition(partition)
+ , mDefinition(definition)
+ , mScriptEngine(0)
+ , mUuid(mDefinition.value(JsonDbString::kUuidStr).toString())
+ , mTargetType(mDefinition.value("targetType").toString())
+ , mTargetTable(mPartition->findObjectTable(mTargetType))
+ , mSourceType(mDefinition.value("sourceType").toString())
+{
+ if (mDefinition.contains("targetKeyName"))
+ mTargetKeyName = mDefinition.value("targetKeyName").toString();
+ else
+ mTargetKeyName = QLatin1String("key");
+ if (mDefinition.contains("sourceKeyName"))
+ mSourceKeyName = mDefinition.value("sourceKeyName").toString();
+ mSourceKeyNameList = mSourceKeyName.split(".");
+ if (mDefinition.contains("targetValueName")) {
+ if (mDefinition.value("targetValueName").isString())
+ mTargetValueName = mDefinition.value("targetValueName").toString();
+ } else
+ mTargetValueName = QLatin1String("value");
+
+}
+
+void JsonDbReduceDefinition::definitionCreated()
+{
+ initScriptEngine();
+ initIndexes();
+
+ GetObjectsResult getObjectResponse = mPartition->getObjects(JsonDbString::kTypeStr, mSourceType);
+ if (!getObjectResponse.error.isNull()) {
+ if (jsondbSettings->verbose())
+ qDebug() << "createReduceDefinition" << mTargetType << getObjectResponse.error.toString();
+ setError(getObjectResponse.error.toString());
+ }
+ JsonDbObjectList objects = getObjectResponse.data;
+ for (int i = 0; i < objects.size(); i++)
+ updateObject(QJsonObject(), objects.at(i));
+}
+
+void JsonDbReduceDefinition::definitionRemoved(JsonDbPartition *partition, JsonDbObjectTable *table, const QString targetType, const QString &definitionUuid)
+{
+ if (jsondbSettings->verbose())
+ qDebug() << "Removing Reduce view objects" << targetType;
+ // remove the output objects
+ GetObjectsResult getObjectResponse = table->getObjects(QLatin1String("_reduceUuid"), definitionUuid, targetType);
+ JsonDbObjectList objects = getObjectResponse.data;
+ for (int i = 0; i < objects.size(); i++) {
+ JsonDbObject o = objects[i];
+ o.markDeleted();
+ partition->updateObject(partition->defaultOwner(), o, JsonDbPartition::ViewObject);
+ }
+}
+
+void JsonDbReduceDefinition::initScriptEngine()
+{
+ if (mScriptEngine)
+ return;
+
+ mScriptEngine = JsonDbScriptEngine::scriptEngine();
+ QString message;
+ bool status = compileFunctions(mScriptEngine, mDefinition, mFunctions, message);
+ if (!status)
+ setError(message);
+
+ Q_ASSERT(!mDefinition.value("add").toString().isEmpty());
+ Q_ASSERT(!mDefinition.value("subtract").toString().isEmpty());
+}
+
+void JsonDbReduceDefinition::releaseScriptEngine()
+{
+ mFunctions.clear();
+ mScriptEngine = 0;
+}
+
+void JsonDbReduceDefinition::initIndexes()
+{
+ // TODO: this index should not be automatic
+ if (!mSourceKeyName.isEmpty()) {
+ JsonDbObjectTable *sourceTable = mPartition->findObjectTable(mSourceType);
+ sourceTable->addIndexOnProperty(mSourceKeyName, QLatin1String("string"));
+ }
+ // TODO: this index should not be automatic
+ mTargetTable->addIndexOnProperty(mTargetKeyName, QLatin1String("string"), mTargetType);
+ mTargetTable->addIndexOnProperty(QLatin1String("_reduceUuid"), QLatin1String("string"), mTargetType);
+}
+
+void JsonDbReduceDefinition::updateObject(JsonDbObject before, JsonDbObject after, JsonDbUpdateList *changeList)
+{
+ initScriptEngine();
+
+ QJsonValue beforeKeyValue = sourceKeyValue(before);
+ QJsonValue afterKeyValue = sourceKeyValue(after);
+
+ if (!after.isEmpty() && !before.isEmpty() && (beforeKeyValue != afterKeyValue)) {
+ // do a subtract only on the before key
+ if (!beforeKeyValue.isUndefined())
+ updateObject(before, QJsonObject(), changeList);
+
+ // and then continue here with the add with the after key
+ before = QJsonObject();
+ }
+
+ const QJsonValue keyValue(after.isDeleted() ? beforeKeyValue : afterKeyValue);
+ if (keyValue.isUndefined())
+ return;
+
+ GetObjectsResult getObjectResponse = mTargetTable->getObjects(mTargetKeyName, keyValue, mTargetType);
+ if (!getObjectResponse.error.isNull())
+ setError(getObjectResponse.error.toString());
+
+ JsonDbObject previousObject;
+ QJsonValue previousValue(QJsonValue::Undefined);
+
+ JsonDbObjectList previousResults = getObjectResponse.data;
+ for (int k = 0; k < previousResults.size(); ++k) {
+ JsonDbObject previous = previousResults.at(k);
+ if (previous.value("_reduceUuid").toString() == mUuid) {
+ previousObject = previous;
+ previousValue = previousObject;
+ break;
+ }
+ }
+
+ QJsonValue value = previousValue;
+ if (!before.isEmpty())
+ value = addObject(JsonDbReduceDefinition::Subtract, keyValue, value, before);
+ if (!after.isDeleted())
+ value = addObject(JsonDbReduceDefinition::Add, keyValue, value, after);
+
+ JsonDbObjectList objectsToUpdate;
+ // if we had a previous object to reduce
+ if (previousObject.contains(JsonDbString::kUuidStr)) {
+ // and now the value is undefined
+ if (value.isUndefined()) {
+ // then remove it
+ previousObject.markDeleted();
+ objectsToUpdate.append(previousObject);
+ } else {
+ //otherwise update it
+ JsonDbObject reduced(value.toObject());
+ reduced.insert(JsonDbString::kTypeStr, mTargetType);
+ reduced.insert(JsonDbString::kUuidStr,
+ previousObject.value(JsonDbString::kUuidStr));
+ reduced.insert(JsonDbString::kVersionStr,
+ previousObject.value(JsonDbString::kVersionStr));
+ reduced.insert(mTargetKeyName, keyValue);
+ reduced.insert("_reduceUuid", mUuid);
+ objectsToUpdate.append(reduced);
+ }
+ } else if (!value.isUndefined()) {
+ // otherwise create the new object
+ JsonDbObject reduced(value.toObject());
+ reduced.insert(JsonDbString::kTypeStr, mTargetType);
+ reduced.insert(mTargetKeyName, keyValue);
+ reduced.insert("_reduceUuid", mUuid);
+
+ objectsToUpdate.append(reduced);
+ }
+
+ JsonDbWriteResult res = mPartition->updateObjects(mOwner, objectsToUpdate, JsonDbPartition::ViewObject, changeList);
+ if (res.code != JsonDbError::NoError)
+ setError(QString("Error executing add function: %1").arg(res.message));
+}
+
+QJsonValue JsonDbReduceDefinition::addObject(JsonDbReduceDefinition::FunctionNumber functionNumber,
+ const QJsonValue &keyValue, QJsonValue previousValue, JsonDbObject object)
+{
+ initScriptEngine();
+ QJSValue svKeyValue = JsonDbScriptEngine::toJSValue(keyValue, mScriptEngine);
+ if (!mTargetValueName.isEmpty())
+ previousValue = previousValue.toObject().value(mTargetValueName);
+ QJSValue svPreviousValue = JsonDbScriptEngine::toJSValue(previousValue, mScriptEngine);
+ QJSValue svObject = JsonDbScriptEngine::toJSValue(object, mScriptEngine);
+
+ QJSValueList reduceArgs;
+ reduceArgs << svKeyValue << svPreviousValue << svObject;
+ QJSValue reduced = mFunctions[functionNumber].call(reduceArgs);
+
+ if (!reduced.isUndefined() && !reduced.isError()) {
+ QJsonValue jsonReduced = JsonDbScriptEngine::fromJSValue(reduced);
+ QJsonObject jsonReducedObject;
+ if (!mTargetValueName.isEmpty())
+ jsonReducedObject.insert(mTargetValueName, jsonReduced);
+ else
+ jsonReducedObject = jsonReduced.toObject();
+ return jsonReducedObject;
+ } else {
+
+ if (reduced.isError())
+ setError("Error executing add function: " + reduced.toString());
+
+ return QJsonValue(QJsonValue::Undefined);
+ }
+}
+
+bool JsonDbReduceDefinition::isActive() const
+{
+ return !mDefinition.contains(JsonDbString::kActiveStr) || mDefinition.value(JsonDbString::kActiveStr).toBool();
+}
+
+void JsonDbReduceDefinition::setError(const QString &errorMsg)
+{
+ mDefinition.insert(JsonDbString::kActiveStr, false);
+ mDefinition.insert(JsonDbString::kErrorStr, errorMsg);
+ if (mPartition)
+ mPartition->updateObject(mOwner, mDefinition, JsonDbPartition::ForcedWrite);
+}
+
+bool JsonDbReduceDefinition::validateDefinition(const JsonDbObject &reduce, JsonDbPartition *partition, QString &message)
+{
+ message.clear();
+ QString targetType = reduce.value("targetType").toString();
+ QString sourceType = reduce.value("sourceType").toString();
+ QString uuid = reduce.value(JsonDbString::kUuidStr).toString();
+ JsonDbView *view = partition->findView(targetType);
+
+ if (targetType.isEmpty())
+ message = QLatin1Literal("targetType property for Reduce not specified");
+ else if (!view)
+ message = QLatin1Literal("targetType must be of a type that extends View");
+ else if (sourceType.isEmpty())
+ message = QLatin1Literal("sourceType property for Reduce not specified");
+ else if (view->mReduceDefinitionsBySource.contains(sourceType)
+ && view->mReduceDefinitionsBySource.value(sourceType)->uuid() != uuid)
+ message = QString("duplicate Reduce definition on source %1 and target %2")
+ .arg(sourceType).arg(targetType);
+ else if (reduce.value("sourceKeyName").toString().isEmpty() && reduce.value("sourceKeyFunction").toString().isEmpty())
+ message = QLatin1Literal("sourceKeyName or sourceKeyFunction must be provided for Reduce");
+ else if (!reduce.value("sourceKeyName").toString().isEmpty() && !reduce.value("sourceKeyFunction").toString().isEmpty())
+ message = QLatin1Literal("Only one of sourceKeyName and sourceKeyFunction may be provided for Reduce");
+ else if (reduce.value("add").toString().isEmpty())
+ message = QLatin1Literal("add function for Reduce not specified");
+ else if (reduce.value("subtract").toString().isEmpty())
+ message = QLatin1Literal("subtract function for Reduce not specified");
+ else if (reduce.contains("targetValueName")
+ && !(reduce.value("targetValueName").isString() || reduce.value("targetValueName").isNull()))
+ message = QLatin1Literal("targetValueName for Reduce must be a string or null");
+ else {
+ QJSEngine *scriptEngine = JsonDbScriptEngine::scriptEngine();
+ QVector<QJSValue> functions;
+ // check for script errors
+ compileFunctions(scriptEngine, reduce, functions, message);
+ scriptEngine->collectGarbage();
+ }
+ return message.isEmpty();
+}
+
+bool JsonDbReduceDefinition::compileFunctions(QJSEngine *scriptEngine, QJsonObject definition,
+ QVector<QJSValue> &functions, QString &message)
+{
+ bool status = true;
+ QStringList functionNames = (QStringList()
+ << QLatin1String("add")
+ << QLatin1String("subtract")
+ << QLatin1String("sourceKeyFunction"));
+ int i = 0;
+ functions.resize(3);
+ foreach (const QString &functionName, functionNames) {
+ int functionNumber = i++;
+ if (!definition.contains(functionName))
+ continue;
+ QString script = definition.value(functionName).toString();
+ QJSValue result = scriptEngine->evaluate(QString("(%1)").arg(script));
+
+ if (result.isError() || !result.isCallable()) {
+ message = QString("Unable to parse add function: %1").arg(result.toString());
+ status = false;
+ continue;
+ }
+ functions[functionNumber] = result;
+ }
+ return status;
+}
+
+QJsonValue JsonDbReduceDefinition::sourceKeyValue(const JsonDbObject &object)
+{
+ if (object.isEmpty() || object.isDeleted()) {
+ return QJsonValue(QJsonValue::Undefined);
+ } else if (mFunctions[JsonDbReduceDefinition::SourceKeyValue].isCallable()) {
+ QJSValueList args;
+ args << JsonDbScriptEngine::toJSValue(object, mScriptEngine);
+ QJsonValue keyValue = JsonDbScriptEngine::fromJSValue(mFunctions[JsonDbReduceDefinition::SourceKeyValue].call(args));
+ return keyValue;
+ } else
+ return mSourceKeyName.contains(".") ? JsonDbObject(object).propertyLookup(mSourceKeyNameList) : object.value(mSourceKeyName);
+
+}
+
+#include "moc_jsondbreducedefinition.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbreducedefinition.h b/src/partition/jsondbreducedefinition.h
new file mode 100644
index 00000000..8b063338
--- /dev/null
+++ b/src/partition/jsondbreducedefinition.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_REDUCE_DEFINITION_H
+#define JSONDB_REDUCE_DEFINITION_H
+
+#include <QJSEngine>
+#include <QStringList>
+
+#include "jsondbpartitionglobal.h"
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+#include <jsondbobject.h>
+#include <jsondbpartition.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbOwner;
+class JsonDbJoinProxy;
+class JsonDbMapProxy;
+class JsonDbPartition;
+class JsonDbObjectTable;
+
+class JsonDbReduceDefinition : public QObject
+{
+ Q_OBJECT
+public:
+ JsonDbReduceDefinition(const JsonDbOwner *mOwner, JsonDbPartition *partition, QJsonObject reduceDefinition, QObject *parent = 0);
+ QString uuid() const { return mUuid; }
+ QString targetType() const { return mTargetType; }
+ QString sourceType() const { return mSourceType; }
+ QString sourceKeyName() const { return mSourceKeyName; }
+ QString targetKeyName() const { return mTargetKeyName; }
+ QString targetValueName() const { return mTargetValueName; }
+ // sourceKeyName split on .
+ QStringList sourceKeyNameList() const { return mSourceKeyNameList; }
+ bool isActive() const;
+ QJsonObject definition() const { return mDefinition; }
+ const JsonDbOwner *owner() const { return mOwner; }
+
+ static void definitionRemoved(JsonDbPartition *partition, JsonDbObjectTable *table, const QString targetType, const QString &definitionUuid);
+ void definitionCreated();
+
+ void initScriptEngine();
+ void releaseScriptEngine();
+ void initIndexes();
+
+ void updateObject(JsonDbObject before, JsonDbObject after, JsonDbUpdateList *changeList = 0);
+
+ void setError(const QString &errorMsg);
+
+ static bool validateDefinition(const JsonDbObject &reduce, JsonDbPartition *partition, QString &message);
+
+private:
+ enum FunctionNumber {
+ Add = 0,
+ Subtract = 1,
+ SourceKeyValue = 2
+ };
+ static bool compileFunctions(QJSEngine *scriptEngine, QJsonObject definition, QVector<QJSValue> &mFunctions, QString &message);
+ QJsonValue sourceKeyValue(const JsonDbObject &object);
+ QJsonValue addObject(FunctionNumber fn, const QJsonValue &keyValue, QJsonValue previousResult, JsonDbObject object);
+
+private:
+ const JsonDbOwner *mOwner;
+ JsonDbPartition *mPartition;
+ QJsonObject mDefinition;
+ QJSEngine *mScriptEngine;
+ QVector<QJSValue> mFunctions;
+ QString mUuid;
+ QString mTargetType;
+ JsonDbObjectTable *mTargetTable;
+ QString mSourceType;
+ QString mTargetKeyName;
+ QString mTargetValueName;
+ QString mSourceKeyName;
+ // mSourceKeyName split on .
+ QStringList mSourceKeyNameList;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_REDUCE_DEFINITION_H
diff --git a/src/partition/jsondbschemamanager_impl_p.h b/src/partition/jsondbschemamanager_impl_p.h
new file mode 100644
index 00000000..21d03746
--- /dev/null
+++ b/src/partition/jsondbschemamanager_impl_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_SCHEMA_MANAGER_IMPL_P_H
+#define JSONDB_SCHEMA_MANAGER_IMPL_P_H
+
+#include "jsondbschemamanager_p.h"
+#include "schema-validation/object.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+bool JsonDbSchemaManager::contains(const QString &name) const
+{
+ return m_schemas.contains(name);
+}
+
+QJsonObject JsonDbSchemaManager::value(const QString &name) const
+{
+ return m_schemas.value(name).first;
+}
+
+SchemaValidation::Schema<QJsonObjectTypes> JsonDbSchemaManager::schema(const QString &schemaName, QJsonObjectTypes::Service *service)
+{
+ QJsonObjectSchemaPair schemaPair = m_schemas.value(schemaName);
+ ensureCompiled(schemaName, &schemaPair, service);
+ return schemaPair.second;
+}
+
+QJsonObject JsonDbSchemaManager::take(const QString &name)
+{
+ return m_schemas.take(name).first;
+}
+
+QJsonObject JsonDbSchemaManager::insert(const QString &name, const QJsonObject &schema)
+{
+ m_schemas.insert(name, qMakePair(schema, SchemaValidation::Schema<QJsonObjectTypes>()));
+ return QJsonObject();
+}
+
+inline QJsonObject JsonDbSchemaManager::ensureCompiled(const QString &schemaName, QJsonObjectSchemaPair *pair, QJsonObjectTypes::Service *callbacks)
+{
+ SchemaValidation::Schema<QJsonObjectTypes> schema(pair->second);
+ if (!schema.isValid()) {
+ // Try to compile schema
+ QJsonObjectTypes::Object schemaObject(pair->first);
+ SchemaValidation::Schema<QJsonObjectTypes> compiledSchema(schemaObject, callbacks);
+ pair->second = compiledSchema;
+ m_schemas.insert(schemaName, *pair);
+ return callbacks->error();
+ }
+ return QJsonObject();
+}
+
+inline QJsonObject JsonDbSchemaManager::validate(const QString &schemaName, JsonDbObject object)
+{
+ if (!contains(schemaName))
+ return QJsonObject();
+
+ QJsonObjectTypes::Service callbacks(this);
+ QJsonObjectSchemaPair schemaPair = m_schemas.value(schemaName);
+ ensureCompiled(schemaName, &schemaPair, &callbacks);
+ SchemaValidation::Schema<QJsonObjectTypes> schema(schemaPair.second);
+ QJsonObjectTypes::Value rootObject(QString(), object);
+ /*bool result = */ schema.check(rootObject, &callbacks);
+ return callbacks.error();
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+#endif // JSONDB_SCHEMA_MANAGER_IMPL_P_H
diff --git a/src/partition/jsondbschemamanager_p.h b/src/partition/jsondbschemamanager_p.h
new file mode 100644
index 00000000..18958c27
--- /dev/null
+++ b/src/partition/jsondbschemamanager_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_SCHEMA_MANAGER_P_H
+#define JSONDB_SCHEMA_MANAGER_P_H
+
+#include "jsondbpartitionglobal.h"
+
+#include <QtCore/qstring.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qmap.h>
+
+#include "schema-validation/object.h"
+#include "jsondbobjecttypes_p.h"
+#include "jsondbobject.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+//FIXME This can have better performance
+class JsonDbSchemaManager
+{
+public:
+ inline bool contains(const QString &name) const;
+ inline QJsonObject value(const QString &name) const;
+ inline SchemaValidation::Schema<QJsonObjectTypes> schema(const QString &name, QJsonObjectTypes::Service *service);
+ inline QJsonObject take(const QString &name);
+ inline QJsonObject insert(const QString &name, const QJsonObject &schema);
+
+ inline QJsonObject validate(const QString &schemaName, JsonDbObject object);
+
+private:
+ typedef QPair<QJsonObject, SchemaValidation::Schema<QJsonObjectTypes> > QJsonObjectSchemaPair;
+ inline QJsonObject ensureCompiled(const QString &schemaName, QJsonObjectSchemaPair *pair, QJsonObjectTypes::Service *service);
+
+ QMap<QString, QJsonObjectSchemaPair> m_schemas;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+#endif // JSONDB_SCHEMA_MANAGER_P_H
diff --git a/src/partition/jsondbscriptengine.cpp b/src/partition/jsondbscriptengine.cpp
new file mode 100644
index 00000000..e4c80967
--- /dev/null
+++ b/src/partition/jsondbscriptengine.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbobject.h"
+
+#include <QJSValue>
+#include <QJSValueIterator>
+#include <QStringBuilder>
+#include <QStringList>
+#include <QCryptographicHash>
+
+#include <qjsondocument.h>
+
+#include "jsondbstrings.h"
+#include "jsondbproxy.h"
+#include "jsondbscriptengine.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+QJsonValue JsonDbScriptEngine::fromJSValue(const QJSValue &v)
+{
+ if (v.isNull())
+ return QJsonValue(QJsonValue::Null);
+ if (v.isNumber())
+ return QJsonValue(v.toNumber());
+ if (v.isString())
+ return QJsonValue(v.toString());
+ if (v.isBool())
+ return QJsonValue(v.toBool());
+ if (v.isArray()) {
+ QJsonArray a;
+ int size = v.property("length").toInt();
+ for (int i = 0; i < size; i++) {
+ a.append(fromJSValue(v.property(i)));
+ }
+ return a;
+ }
+ if (v.isObject()) {
+ QJSValueIterator it(v);
+ QJsonObject o;
+ while (it.hasNext()) {
+ it.next();
+ QString name = it.name();
+ QJSValue value = it.value();
+ o.insert(name, fromJSValue(value));
+ }
+ return o;
+ }
+ return QJsonValue(QJsonValue::Undefined);
+}
+
+QJSValue JsonDbScriptEngine::toJSValue(const QJsonValue &v, QJSEngine *scriptEngine)
+{
+ switch (v.type()) {
+ case QJsonValue::Null:
+ return QJSValue(QJSValue::NullValue);
+ case QJsonValue::Undefined:
+ return QJSValue(QJSValue::UndefinedValue);
+ case QJsonValue::Double:
+ return QJSValue(v.toDouble());
+ case QJsonValue::String:
+ return QJSValue(v.toString());
+ case QJsonValue::Bool:
+ return QJSValue(v.toBool());
+ case QJsonValue::Array: {
+ QJSValue jsArray = scriptEngine->newArray();
+ QJsonArray array = v.toArray();
+ for (int i = 0; i < array.size(); i++)
+ jsArray.setProperty(i, toJSValue(array.at(i), scriptEngine));
+ return jsArray;
+ }
+ case QJsonValue::Object:
+ return toJSValue(v.toObject(), scriptEngine);
+ }
+ return QJSValue(QJSValue::UndefinedValue);
+}
+
+QJSValue JsonDbScriptEngine::toJSValue(const QJsonObject &object, QJSEngine *scriptEngine)
+{
+ QJSValue jsObject = scriptEngine->newObject();
+ for (QJsonObject::const_iterator it = object.begin(); it != object.end(); ++it)
+ jsObject.setProperty(it.key(), toJSValue(it.value(), scriptEngine));
+ return jsObject;
+}
+
+static QJSEngine *sScriptEngine;
+
+QJSEngine *JsonDbScriptEngine::scriptEngine()
+{
+ if (!sScriptEngine) {
+ sScriptEngine = new QJSEngine();
+ QJSValue globalObject = sScriptEngine->globalObject();
+ globalObject.setProperty("console", sScriptEngine->newQObject(new Console()));
+ }
+ return sScriptEngine;
+}
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbscriptengine.h b/src/partition/jsondbscriptengine.h
new file mode 100644
index 00000000..663a38fc
--- /dev/null
+++ b/src/partition/jsondbscriptengine.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_SCRIPT_ENGINE_H
+#define JSONDB_SCRIPT_ENGINE_H
+
+#include <QJSEngine>
+#include <QUuid>
+
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbScriptEngine : public QObject
+{
+public:
+ JsonDbScriptEngine();
+ JsonDbScriptEngine(const QJsonObject &object);
+ ~JsonDbScriptEngine();
+
+ static QJsonValue fromJSValue(const QJSValue &v);
+ static QJSValue toJSValue(const QJsonValue &v, QJSEngine *scriptEngine);
+ static QJSValue toJSValue(const QJsonObject &object, QJSEngine *mScriptEngine);
+ static QJSEngine *scriptEngine();
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_OBJECT_H
diff --git a/src/partition/jsondbsettings.cpp b/src/partition/jsondbsettings.cpp
new file mode 100644
index 00000000..44343cea
--- /dev/null
+++ b/src/partition/jsondbsettings.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbsettings.h"
+
+#include <QDebug>
+#include <QMetaObject>
+#include <QMetaProperty>
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+Q_GLOBAL_STATIC(JsonDbSettings, staticInstance)
+
+JsonDbSettings::JsonDbSettings() :
+ mRejectStaleUpdates(false)
+ , mDebug(false)
+ , mVerbose(false)
+ , mPerformanceLog(false)
+ , mCacheSize(128)
+ , mCompactRate(1000)
+ , mEnforceAccessControl(false)
+ , mTransactionSize(100)
+ , mValidateSchemas(false)
+ , mSyncInterval(5000)
+ , mIndexSyncInterval(12000)
+ , mDebugQuery(false)
+{
+ loadEnvironment();
+}
+
+void JsonDbSettings::loadEnvironment()
+{
+ // loop through all the properties, converting from the property
+ // name to the environment variable name
+ const QMetaObject *meta = metaObject();
+
+ for (int i = 0; i < meta->propertyCount(); i++) {
+ QMetaProperty property(meta->property(i));
+ QString propertyName = QString::fromLatin1(property.name());
+ QString envVariable = QLatin1String("JSONDB_");
+
+ for (int j = 0; j < propertyName.count(); j++) {
+ if (j > 0 && propertyName.at(j).isUpper() && !propertyName.at(j - 1).isUpper())
+ envVariable += QString("_%1").arg(propertyName.at(j));
+ else
+ envVariable += propertyName.at(j).toUpper();
+ }
+
+ if (qgetenv(envVariable.toLatin1()).size()) {
+ if (property.type() == QVariant::Bool)
+ property.write(this, QLatin1String(qgetenv(envVariable.toLatin1())) == QLatin1String("true"));
+ else if (property.type() == QVariant::Int)
+ property.write(this, qgetenv(envVariable.toLatin1()).toInt());
+ else if (property.type() == QVariant::String)
+ property.write(this, qgetenv(envVariable.toLatin1()));
+ else if (property.type() == QVariant::StringList)
+ property.write(this, QString::fromLatin1(qgetenv(envVariable.toLatin1())).split(':'));
+ else
+ qWarning() << "JsonDbSettings: unknown property type" << property.name() << property.type();
+ }
+ }
+}
+
+JsonDbSettings *JsonDbSettings::instance()
+{
+ return staticInstance();
+}
+
+#include "moc_jsondbsettings.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbsettings.h b/src/partition/jsondbsettings.h
new file mode 100644
index 00000000..19ee5422
--- /dev/null
+++ b/src/partition/jsondbsettings.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_SETTINGS_H
+#define JSONDB_SETTINGS_H
+
+#include "jsondbpartitionglobal.h"
+
+#include <QObject>
+#include <QStringList>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+#define jsondbSettings JsonDbSettings::instance()
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbSettings : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool rejectStaleUpdates READ rejectStaleUpdates WRITE setRejectStaleUpdates)
+ Q_PROPERTY(bool debug READ debug WRITE setDebug)
+ Q_PROPERTY(bool verbose READ verbose WRITE setVerbose)
+ Q_PROPERTY(bool performanceLog READ performanceLog WRITE setPerformanceLog)
+ Q_PROPERTY(int cacheSize READ cacheSize WRITE setCacheSize)
+ Q_PROPERTY(int compactRate READ compactRate WRITE setCompactRate)
+ Q_PROPERTY(bool enforceAccessControl READ enforceAccessControl WRITE setEnforceAccessControl)
+ Q_PROPERTY(int transactionSize READ transactionSize WRITE setTransactionSize)
+ Q_PROPERTY(bool validateSchemas READ validateSchemas WRITE setValidateSchemas)
+ Q_PROPERTY(int syncInterval READ syncInterval WRITE setSyncInterval)
+ Q_PROPERTY(int indexSyncInterval READ indexSyncInterval WRITE setIndexSyncInterval)
+ Q_PROPERTY(bool debugQuery READ debugQuery WRITE setDebugQuery)
+ Q_PROPERTY(QStringList configSearchPath READ configSearchPath WRITE setConfigSearchPath)
+
+public:
+ static JsonDbSettings *instance();
+
+ inline void reload() { loadEnvironment(); }
+
+ inline bool rejectStaleUpdates() const { return mRejectStaleUpdates; }
+ inline void setRejectStaleUpdates(bool reject) { mRejectStaleUpdates = reject; }
+
+ inline bool debug() const { return mDebug; }
+ inline void setDebug(bool debug) { mDebug = debug; }
+
+ inline bool verbose() const { return mVerbose; }
+ inline void setVerbose(bool verbose) { mVerbose = verbose; }
+
+ inline bool performanceLog() const { return mPerformanceLog; }
+ inline void setPerformanceLog(bool performanceLog) { mPerformanceLog = performanceLog; }
+
+ inline int cacheSize() const { return mCacheSize; }
+ inline void setCacheSize(int cacheSize) { mCacheSize = cacheSize; }
+
+ inline int compactRate() const { return mCompactRate; }
+ inline void setCompactRate(int compactRate) { mCompactRate = compactRate; }
+
+ inline bool enforceAccessControl() const { return mEnforceAccessControl; }
+ inline void setEnforceAccessControl(bool enforce) { mEnforceAccessControl = enforce; }
+
+ inline int transactionSize() const { return mTransactionSize; }
+ inline void setTransactionSize(int transactionSize) { mTransactionSize = transactionSize; }
+
+ inline bool validateSchemas() const { return mValidateSchemas; }
+ inline void setValidateSchemas(bool validate) { mValidateSchemas = validate; }
+
+ inline int syncInterval() const { return mSyncInterval; }
+ inline void setSyncInterval(int interval) { mSyncInterval = interval; }
+
+ inline int indexSyncInterval() const { return mIndexSyncInterval; }
+ inline void setIndexSyncInterval(int interval) { mIndexSyncInterval = interval; }
+
+ inline bool debugQuery() const { return mDebugQuery; }
+ inline void setDebugQuery(bool debug) { mDebugQuery = debug; }
+
+ inline QStringList configSearchPath() const { return mConfigSearchPath; }
+ inline void setConfigSearchPath(const QStringList &searchPath) { mConfigSearchPath = searchPath; }
+
+ JsonDbSettings();
+
+private:
+ void loadEnvironment();
+
+ bool mRejectStaleUpdates;
+ bool mDebug;
+ bool mVerbose;
+ bool mPerformanceLog;
+ int mCacheSize;
+ int mCompactRate;
+ bool mEnforceAccessControl;
+ int mTransactionSize;
+ bool mValidateSchemas;
+ int mSyncInterval;
+ int mIndexSyncInterval;
+ bool mDebugQuery;
+ QStringList mConfigSearchPath;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_SETTINGS_H
diff --git a/src/partition/jsondbstat.h b/src/partition/jsondbstat.h
new file mode 100644
index 00000000..f2d6f7ac
--- /dev/null
+++ b/src/partition/jsondbstat.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_STAT_H
+#define JSONDB_STAT_H
+
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbStat
+{
+public:
+ qint32 reads;
+ qint32 hits;
+ qint32 writes;
+ JsonDbStat() : reads(0), hits(0), writes(0) {};
+ JsonDbStat(qint32 reads, qint32 hits, qint32 writes) : reads(reads), hits(hits), writes(writes) {};
+ JsonDbStat &operator += (const JsonDbStat &o)
+ {
+ reads += o.reads;
+ hits += o.hits;
+ writes += o.writes;
+ return *this;
+ };
+ JsonDbStat &operator -= (const JsonDbStat &o)
+ {
+ reads = qMax((qint32)0, reads - o.reads);
+ hits = qMax((qint32)0, hits - o.hits);
+ writes = qMax((qint32)0, writes - o.writes);
+ return *this;
+ };
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_STAT_H
diff --git a/src/partition/jsondbstrings.cpp b/src/partition/jsondbstrings.cpp
new file mode 100644
index 00000000..661b941b
--- /dev/null
+++ b/src/partition/jsondbstrings.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 "jsondbstrings.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+const QString JsonDbString::kUuidStr = QString::fromLatin1("_uuid");
+const QString JsonDbString::kVersionStr = QString::fromLatin1("_version");
+const QString JsonDbString::kIdStr = QString::fromLatin1("id");
+const QString JsonDbString::kResultStr = QString::fromLatin1("result");
+const QString JsonDbString::kErrorStr = QString::fromLatin1("error");
+const QString JsonDbString::kFieldNameStr = QString::fromLatin1("fieldName");
+const QString JsonDbString::kCodeStr = QString::fromLatin1("code");
+const QString JsonDbString::kMessageStr = QString::fromLatin1("message");
+const QString JsonDbString::kNameStr = QString::fromLatin1("name");
+const QString JsonDbString::kCountStr = QString::fromLatin1("count");
+const QString JsonDbString::kCurrentStr = QString::fromLatin1("_current");
+const QString JsonDbString::kDomainStr = QString::fromLatin1("_domain");
+const QString JsonDbString::kIndexValueStr = QString::fromLatin1("_indexValue");
+const QString JsonDbString::kOwnerStr = QString::fromLatin1("_owner");
+const QString JsonDbString::kTypeStr = QString::fromLatin1("_type");
+const QString JsonDbString::kTypesStr = QString::fromLatin1("types");
+const QString JsonDbString::kParentStr = QString::fromLatin1("_parent");
+const QString JsonDbString::kSchemaTypeStr = QString::fromLatin1("_schemaType");
+
+const QString JsonDbString::kActionStr = QString::fromLatin1("action");
+const QString JsonDbString::kActionsStr = QString::fromLatin1("actions");
+const QString JsonDbString::kActiveStr = QString::fromLatin1("active");
+const QString JsonDbString::kAddIndexStr = QString::fromLatin1("addIndex");
+const QString JsonDbString::kCreateStr = QString::fromLatin1("create");
+const QString JsonDbString::kDropStr = QString::fromLatin1("drop");
+const QString JsonDbString::kConflictsStr = QString::fromLatin1("conflicts");
+const QString JsonDbString::kConnectStr = QString::fromLatin1("connect");
+const QString JsonDbString::kDataStr = QString::fromLatin1("data");
+const QString JsonDbString::kDeletedStr = QString::fromLatin1("_deleted");
+const QString JsonDbString::kDisconnectStr = QString::fromLatin1("disconnect");
+const QString JsonDbString::kExplanationStr = QString::fromLatin1("explanation");
+const QString JsonDbString::kFindStr = QString::fromLatin1("find");
+const QString JsonDbString::kLengthStr = QString::fromLatin1("length");
+const QString JsonDbString::kLimitStr = QString::fromLatin1("limit");
+const QString JsonDbString::kMapTypeStr = QString::fromLatin1("Map");
+const QString JsonDbString::kMetaStr = QString::fromLatin1("_meta");
+const QString JsonDbString::kNotifyStr = QString::fromLatin1("notify");
+const QString JsonDbString::kNotificationTypeStr = QString::fromLatin1("notification");
+const QString JsonDbString::kObjectStr = QString::fromLatin1("object");
+const QString JsonDbString::kOffsetStr = QString::fromLatin1("offset");
+const QString JsonDbString::kQueryStr = QString::fromLatin1("query");
+const QString JsonDbString::kReduceTypeStr = QString::fromLatin1("Reduce");
+const QString JsonDbString::kRemoveStr = QString::fromLatin1("remove");
+const QString JsonDbString::kSchemaStr = QString::fromLatin1("schema");
+const QString JsonDbString::kUpdateStr = QString::fromLatin1("update");
+const QString JsonDbString::kTokenStr = QString::fromLatin1("token");
+const QString JsonDbString::kFlushStr = QString::fromLatin1("flush");
+const QString JsonDbString::kSettingsStr = QString::fromLatin1("settings");
+const QString JsonDbString::kViewTypeStr = QString::fromLatin1("View");
+const QString JsonDbString::kChangesSinceStr = QString::fromLatin1("changesSince");
+const QString JsonDbString::kStateNumberStr = QString::fromLatin1("stateNumber");
+const QString JsonDbString::kCollapsedStr = QString::fromLatin1("collapsed");
+const QString JsonDbString::kCurrentStateNumberStr = QString::fromLatin1("currentStateNumber");
+const QString JsonDbString::kStartingStateNumberStr = QString::fromLatin1("startingStateNumber");
+const QString JsonDbString::kTombstoneStr = QString::fromLatin1("Tombstone");
+const QString JsonDbString::kPartitionTypeStr = QString::fromLatin1("Partition");
+const QString JsonDbString::kPartitionStr = QString::fromLatin1("partition");
+const QString JsonDbString::kLogStr = QString::fromLatin1("log");
+const QString JsonDbString::kPropertyNameStr = QString::fromLatin1("propertyName");
+const QString JsonDbString::kPropertyTypeStr = QString::fromLatin1("propertyType");
+const QString JsonDbString::kPropertyFunctionStr = QString::fromLatin1("propertyFunction");
+const QString JsonDbString::kObjectTypeStr = QString::fromLatin1("objectType");
+const QString JsonDbString::kDbidTypeStr = QString::fromLatin1("DatabaseId");
+const QString JsonDbString::kIndexTypeStr = QString::fromLatin1("Index");
+const QString JsonDbString::kLocaleStr = QString::fromLatin1("locale");
+const QString JsonDbString::kCollationStr = QString::fromLatin1("collation");
+const QString JsonDbString::kCaseSensitiveStr = QString::fromLatin1("caseSensitive");
+const QString JsonDbString::kCasePreferenceStr = QString::fromLatin1("casePreference");
+const QString JsonDbString::kDatabaseSchemaVersionStr = QString::fromLatin1("databaseSchemaVersion");
+const QString JsonDbString::kPathStr = QString::fromLatin1("path");
+const QString JsonDbString::kDefaultStr = QString::fromLatin1("default");
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbstrings.h b/src/partition/jsondbstrings.h
new file mode 100644
index 00000000..60fb7c23
--- /dev/null
+++ b/src/partition/jsondbstrings.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_STRINGS_H
+#define JSONDB_STRINGS_H
+
+#include <QString>
+#include "jsondbpartitionglobal.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbString {
+public:
+ static const QString kActionStr;
+ static const QString kActionsStr;
+ static const QString kActiveStr;
+ static const QString kAddIndexStr;
+ static const QString kCodeStr;
+ static const QString kConflictsStr;
+ static const QString kConnectStr;
+ static const QString kCountStr;
+ static const QString kCreateStr;
+ static const QString kDropStr;
+ static const QString kCurrentStr;
+ static const QString kDataStr;
+ static const QString kDeletedStr;
+ static const QString kDisconnectStr;
+ static const QString kDomainStr;
+ static const QString kErrorStr;
+ static const QString kExplanationStr;
+ static const QString kFieldNameStr;
+ static const QString kFindStr;
+ static const QString kNameStr;
+ static const QString kIdStr;
+ static const QString kIndexValueStr;
+ static const QString kLengthStr;
+ static const QString kLimitStr;
+ static const QString kMapTypeStr;
+ static const QString kMessageStr;
+ static const QString kMetaStr;
+ static const QString kNotifyStr;
+ static const QString kNotificationTypeStr;
+ static const QString kObjectStr;
+ static const QString kParentStr;
+ static const QString kOffsetStr;
+ static const QString kOwnerStr;
+ static const QString kQueryStr;
+ static const QString kReduceTypeStr;
+ static const QString kRemoveStr;
+ static const QString kResultStr;
+ static const QString kSchemaStr;
+ static const QString kSchemaTypeStr;
+ static const QString kTypeStr;
+ static const QString kTypesStr;
+ static const QString kUpdateStr;
+ static const QString kUuidStr;
+ static const QString kVersionStr;
+ static const QString kViewTypeStr;
+ static const QString kTokenStr;
+ static const QString kFlushStr;
+ static const QString kSettingsStr;
+ static const QString kChangesSinceStr;
+ static const QString kStateNumberStr;
+ static const QString kCollapsedStr;
+ static const QString kCurrentStateNumberStr;
+ static const QString kStartingStateNumberStr;
+ static const QString kTombstoneStr;
+ static const QString kPartitionTypeStr;
+ static const QString kPartitionStr;
+ static const QString kLogStr;
+ static const QString kPropertyNameStr;
+ static const QString kPropertyTypeStr;
+ static const QString kPropertyFunctionStr;
+ static const QString kObjectTypeStr;
+ static const QString kDbidTypeStr;
+ static const QString kIndexTypeStr;
+ static const QString kLocaleStr;
+ static const QString kCollationStr;
+ static const QString kCaseSensitiveStr;
+ static const QString kCasePreferenceStr;
+ static const QString kDatabaseSchemaVersionStr;
+ static const QString kPathStr;
+ static const QString kDefaultStr;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+#endif /* JSONDB-STRINGS_H */
diff --git a/src/partition/jsondbview.cpp b/src/partition/jsondbview.cpp
new file mode 100644
index 00000000..0c862b5d
--- /dev/null
+++ b/src/partition/jsondbview.cpp
@@ -0,0 +1,538 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QObject>
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QString>
+#include <QElapsedTimer>
+
+#include "jsondbpartition.h"
+#include "jsondbobject.h"
+#include "jsondbview.h"
+#include "jsondbmapdefinition.h"
+#include "jsondbobjecttable.h"
+#include "jsondbreducedefinition.h"
+#include "jsondbsettings.h"
+#include "jsondbscriptengine.h"
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+JsonDbView::JsonDbView(JsonDbPartition *partition, const QString &viewType, QObject *parent) :
+ QObject(parent)
+ , mPartition(partition)
+ , mViewObjectTable(0)
+ , mMainObjectTable(mPartition->mainObjectTable())
+ , mViewType(viewType)
+ , mUpdating(false)
+{
+ mViewObjectTable = new JsonDbObjectTable(mPartition);
+ mViewStateNumber = 0; // FIXME: should be able to read it from the object table
+}
+
+JsonDbView::~JsonDbView()
+{
+ close();
+ delete mViewObjectTable;
+}
+
+void JsonDbView::open()
+{
+ QFileInfo fi(mPartition->filename());
+ QString dirName = fi.dir().path();
+ QString baseName = fi.fileName();
+ baseName.replace(".db", "");
+ if (!mViewObjectTable->open(QString("%1/%2-%3-View.db")
+ .arg(dirName)
+ .arg(baseName)
+ .arg(mViewType))) {
+ qCritical() << "viewDb->open" << mViewObjectTable->errorMessage();
+ return;
+ }
+}
+
+void JsonDbView::close()
+{
+ if (mViewObjectTable)
+ mViewObjectTable->close();
+}
+
+void JsonDbView::initViews(JsonDbPartition *partition)
+{
+ if (jsondbSettings->verbose())
+ qDebug() << "Initializing views on partition" << partition->name();
+ {
+ JsonDbObjectList mrdList = partition->getObjects(JsonDbString::kTypeStr, JsonDbString::kMapTypeStr).data;
+
+ for (int i = 0; i < mrdList.size(); ++i) {
+ JsonDbObject mrd = mrdList.at(i);
+ JsonDbView *view = partition->addView(mrd.value("targetType").toString());
+ view->createMapDefinition(mrd);
+ }
+ }
+ {
+ JsonDbObjectList mrdList = partition->getObjects(JsonDbString::kTypeStr, JsonDbString::kReduceTypeStr).data;
+
+ for (int i = 0; i < mrdList.size(); ++i) {
+ JsonDbObject mrd = mrdList.at(i);
+ JsonDbView *view = partition->addView(mrd.value("targetType").toString());
+ view->createReduceDefinition(mrd);
+ }
+ }
+}
+
+void JsonDbView::createDefinition(JsonDbPartition *partition, QJsonObject definition)
+{
+ QString definitionType = definition.value(JsonDbString::kTypeStr).toString();
+ QString targetType = definition.value("targetType").toString();
+ JsonDbView *view = partition->findView(targetType);
+ if (!view)
+ return;
+ if (jsondbSettings->verbose())
+ qDebug() << "createDefinition" << targetType;
+ if (definitionType == JsonDbString::kMapTypeStr)
+ view->createMapDefinition(definition);
+ else
+ view->createReduceDefinition(definition);
+}
+void JsonDbView::removeDefinition(JsonDbPartition *partition, QJsonObject definition)
+{
+ QString definitionType = definition.value(JsonDbString::kTypeStr).toString();
+ QString targetType = definition.value("targetType").toString();
+ JsonDbView *view = partition->findView(targetType);
+ if (!view)
+ return;
+ if (jsondbSettings->verbose())
+ qDebug() << "removeDefinition" << targetType;
+
+ if (definitionType == JsonDbString::kMapTypeStr)
+ view->removeMapDefinition(definition);
+ else
+ view->removeReduceDefinition(definition);
+}
+
+
+void JsonDbView::createMapDefinition(QJsonObject mapDefinition)
+{
+ QString targetType = mapDefinition.value("targetType").toString();
+ QString uuid = mapDefinition.value(JsonDbString::kUuidStr).toString();
+ if (jsondbSettings->verbose())
+ qDebug() << "createMapDefinition" << uuid << targetType << "{";
+
+ JsonDbOwner *owner = new JsonDbOwner(this);
+ owner->setAllowAll(true);
+ JsonDbMapDefinition *def = new JsonDbMapDefinition(owner, mPartition, mapDefinition, this);
+ def->initIndexes();
+
+ QStringList sourceTypes = def->sourceTypes();
+ for (int i = 0; i < sourceTypes.size(); i++) {
+ const QString sourceType = sourceTypes[i];
+ mMapDefinitionsBySource.insert(sourceType, def);
+ }
+ mMapDefinitions.insert(def->uuid(), def);
+ updateSourceTypesList();
+ if (jsondbSettings->verbose())
+ qDebug() << "createMapDefinition" << uuid << targetType << "}";
+}
+
+void JsonDbView::removeMapDefinition(QJsonObject mapDefinition)
+{
+ QString targetType = mapDefinition.value("targetType").toString();
+ QString uuid = mapDefinition.value(JsonDbString::kUuidStr).toString();
+ if (jsondbSettings->verbose())
+ qDebug() << "removeMapDefinition" << uuid << targetType << "{";
+ foreach (JsonDbMapDefinition *d, mMapDefinitions) {
+ if (d->uuid() == uuid) {
+ JsonDbMapDefinition *def = d;
+ mMapDefinitions.remove(def->uuid());
+ const QStringList &sourceTypes = def->sourceTypes();
+ for (int i = 0; i < sourceTypes.size(); i++)
+ mMapDefinitionsBySource.remove(sourceTypes[i]);
+ break;
+ }
+ }
+
+ updateSourceTypesList();
+ if (jsondbSettings->verbose())
+ qDebug() << "removeMapDefinition" << uuid << targetType << "}";
+}
+
+void JsonDbView::createReduceDefinition(QJsonObject reduceDefinition)
+{
+ QString targetType = reduceDefinition.value("targetType").toString();
+ QString sourceType = reduceDefinition.value("sourceType").toString();
+ if (jsondbSettings->debug())
+ qDebug() << "createReduceDefinition" << sourceType << targetType << sourceType << "{";
+
+ JsonDbOwner *owner = new JsonDbOwner(this);
+ owner->setAllowAll(true);
+ JsonDbReduceDefinition *def = new JsonDbReduceDefinition(owner, mPartition, reduceDefinition, this);
+ def->initIndexes();
+ mReduceDefinitionsBySource.insert(sourceType, def);
+ mReduceDefinitions.insert(def->uuid(), def);
+
+ updateSourceTypesList();
+ if (jsondbSettings->verbose())
+ qDebug() << "createReduceDefinition" << sourceType << targetType << "}";
+}
+
+void JsonDbView::removeReduceDefinition(QJsonObject reduceDefinition)
+{
+ QString targetType = reduceDefinition.value("targetType").toString();
+ QString sourceType = reduceDefinition.value("sourceType").toString();
+ QString uuid = reduceDefinition.value(JsonDbString::kUuidStr).toString();
+
+ if (jsondbSettings->verbose())
+ qDebug() << "removeReduceDefinition" << sourceType << targetType << "{";
+
+ foreach (JsonDbReduceDefinition *d, mReduceDefinitionsBySource.values(sourceType)) {
+ if (d->uuid() == uuid) {
+ JsonDbReduceDefinition *def = d;
+ mReduceDefinitionsBySource.remove(def->sourceType());
+ mReduceDefinitions.remove(def->uuid());
+ break;
+ }
+ }
+
+ updateSourceTypesList();
+ //TODO: actually remove the table
+ if (jsondbSettings->verbose())
+ qDebug() << "removeReduceDefinition" << sourceType << targetType << "}";
+}
+
+void JsonDbView::updateSourceTypesList()
+{
+ QSet<QString> sourceTypes;
+ foreach (const JsonDbMapDefinition *d, mMapDefinitions) {
+ foreach (const QString sourceType, d->sourceTypes())
+ sourceTypes.insert(sourceType);
+ }
+ foreach (const JsonDbReduceDefinition *d, mReduceDefinitions)
+ sourceTypes.insert(d->sourceType());
+
+ ObjectTableSourceTypeMap objectTableSourceTypeMap;
+ for (QSet<QString>::const_iterator it = sourceTypes.begin();
+ it != sourceTypes.end();
+ ++it) {
+ QString sourceType = *it;
+ JsonDbObjectTable *objectTable = mPartition->findObjectTable(sourceType);
+ objectTableSourceTypeMap[objectTable].insert(sourceType);
+ }
+ mSourceTypes = sourceTypes.toList();
+ mObjectTableSourceTypeMap = objectTableSourceTypeMap;
+}
+
+void JsonDbView::updateView(quint32 desiredStateNumber, JsonDbUpdateList *resultingChanges)
+{
+ QElapsedTimer timer;
+ if (jsondbSettings->performanceLog())
+ timer.start();
+ if (jsondbSettings->verbose())
+ qDebug() << "updateView" << mViewType << "{";
+ // current state of the main object table of the partition
+ quint32 partitionStateNumber = mMainObjectTable->stateNumber();
+ quint32 viewStateNumber = mViewStateNumber;
+
+ // if the view is up to date, then return
+ if ((desiredStateNumber && viewStateNumber >= desiredStateNumber)
+ || viewStateNumber == partitionStateNumber) {
+ if (jsondbSettings->verbose())
+ qDebug() << "updateView" << mViewType << "}";
+ return;
+ }
+ if (mUpdating) {
+ if (jsondbSettings->verbose())
+ qDebug() << "Update already in progess"
+ << "updateView" << mViewType << "}";
+ return;
+ }
+ mUpdating = true;
+
+ // update the source types in case they are views
+ for (JsonDbView::ObjectTableSourceTypeMap::const_iterator it = mObjectTableSourceTypeMap.begin();
+ it != mObjectTableSourceTypeMap.end();
+ ++it) {
+ const QSet<QString> &sourceTypes = it.value();
+ QString sourceType = *sourceTypes.begin();
+ // update the source tables (as indicated by one of the source types)
+ mPartition->updateView(sourceType);
+ }
+
+ // The Uuids of the definitions processed for the first time by processUpdatedDefinitions.
+ QSet<QString> processedDefinitionUuids;
+
+ // find any Map or Reduce definitions that have been updated from
+ // states viewStateNumber to partitionStateNumber and process them.
+ bool inTransaction =
+ processUpdatedDefinitions(mViewType, viewStateNumber, processedDefinitionUuids);
+ // Do not process them again during this update of the view.
+
+ if (jsondbSettings->verbose())
+ qDebug() << "processedDefinitionUuids" << processedDefinitionUuids;
+ // if there were any updated definitions, then it would have
+ // already begun the transaction on the view's mObjectTable.
+ if (!inTransaction)
+ mViewObjectTable->begin();
+ inTransaction = true;
+
+ // now process the changes on each of the source tables
+ for (ObjectTableSourceTypeMap::const_iterator it = mObjectTableSourceTypeMap.begin();
+ it != mObjectTableSourceTypeMap.end();
+ ++it) {
+ JsonDbObjectTable *sourceTable = it.key();
+ const QSet<QString> &sourceTypes = it.value();
+ if (sourceTable->stateNumber() == viewStateNumber)
+ // up-to-date with respect this source table
+ continue;
+
+ QList<JsonDbUpdate> changeList;
+ sourceTable->changesSince(viewStateNumber, sourceTypes, &changeList, JsonDbObjectTable::SplitTypeChanges);
+ updateViewOnChanges(changeList, processedDefinitionUuids, resultingChanges);
+ }
+ mViewStateNumber = partitionStateNumber;
+ mUpdating = false;
+ JsonDbScriptEngine::scriptEngine()->collectGarbage();
+ if (inTransaction)
+ mViewObjectTable->commit(partitionStateNumber);
+ if (jsondbSettings->verbose())
+ qDebug() << "}" << "updateView" << mViewType << partitionStateNumber;
+ if (jsondbSettings->performanceLog())
+ qDebug() << "updateView" << "stateNumber" << mViewStateNumber << mViewType << timer.elapsed() << "ms";
+
+ emit updated(mViewType);
+}
+
+void JsonDbView::updateEagerView(const JsonDbUpdateList &objectsUpdated, JsonDbUpdateList *resultingChanges)
+{
+ quint32 partitionStateNumber = mMainObjectTable->stateNumber();
+ quint32 viewStateNumber = mViewStateNumber;
+
+ // make sure we can run this set of updates
+ if (mViewStateNumber != (partitionStateNumber - 1)
+ || viewDefinitionUpdated(objectsUpdated)) {
+ // otherwise do a full update
+ if (jsondbSettings->verbose())
+ qDebug() << "updateEagerView" << mViewType << "full update"
+ << "viewStateNumber" << mViewStateNumber << "partitionStateNumber" << partitionStateNumber
+ << (viewDefinitionUpdated(objectsUpdated) ? "definition updated" : "");
+ updateView(partitionStateNumber, resultingChanges);
+ return;
+ }
+
+ QElapsedTimer timer;
+ if (jsondbSettings->performanceLog())
+ timer.start();
+ if (jsondbSettings->verbose())
+ qDebug() << "updateEagerView" << mViewType << "{";
+
+ // begin transaction
+ mViewObjectTable->begin();
+
+ // then do the update
+ QSet<QString> processedDefinitionUuids;
+ updateViewOnChanges(objectsUpdated, processedDefinitionUuids, resultingChanges);
+
+ // end transaction
+ JsonDbScriptEngine::scriptEngine()->collectGarbage();
+ mViewObjectTable->commit(partitionStateNumber);
+ mViewStateNumber = partitionStateNumber;
+
+ if (jsondbSettings->verbose())
+ qDebug() << "updateEagerView" << mViewType << viewStateNumber << "}";
+ if (jsondbSettings->performanceLog())
+ qDebug() << "updateEagerView" << "stateNumber" << mViewStateNumber << mViewType << timer.elapsed() << "ms";
+}
+
+// Updates the in-memory state numbers on the view so that we know it
+// has seen all relevant updates from this transaction
+void JsonDbView::updateViewStateNumber(quint32 partitionStateNumber)
+{
+ // If the change is not zero or one it's an error
+ if (jsondbSettings->verbose() || (mViewStateNumber != (partitionStateNumber - 1) && mViewStateNumber != partitionStateNumber))
+ qCritical() << "updateViewStateNumber" << mViewType << "viewStateNumber" << mViewStateNumber << "partitionStateNumber" << partitionStateNumber;
+ mViewStateNumber = partitionStateNumber;
+}
+
+bool JsonDbView::viewDefinitionUpdated(const JsonDbUpdateList &objectsUpdated) const
+{
+ foreach (const JsonDbUpdate &update, objectsUpdated) {
+ QJsonObject beforeObject = update.oldObject;
+ QJsonObject afterObject = update.newObject;
+ QString beforeUuid = beforeObject.value(JsonDbString::kUuidStr).toString();
+ QString afterUuid = afterObject.value(JsonDbString::kUuidStr).toString();
+
+ if ((!beforeObject.isEmpty()
+ && (mMapDefinitions.contains(beforeUuid) || mReduceDefinitions.contains(beforeUuid)))
+ || (!afterObject.isEmpty()
+ && (mMapDefinitions.contains(afterUuid) || mReduceDefinitions.contains(afterUuid))))
+ return false;
+ }
+ return false;
+}
+
+void JsonDbView::updateViewOnChanges(const JsonDbUpdateList &objectsUpdated,
+ QSet<QString> &processedDefinitionUuids,
+ JsonDbUpdateList *changeList)
+{
+ foreach (const JsonDbUpdate &update, objectsUpdated) {
+ QJsonObject beforeObject = update.oldObject;
+ QJsonObject afterObject = update.newObject;
+ QString beforeType = beforeObject.value(JsonDbString::kTypeStr).toString();
+ QString afterType = afterObject.value(JsonDbString::kTypeStr).toString();
+
+ if (mMapDefinitionsBySource.contains(beforeType)) {
+ JsonDbMapDefinition *def = mMapDefinitionsBySource.value(beforeType);
+ if (processedDefinitionUuids.contains(def->uuid()))
+ continue;
+ def->updateObject(beforeObject, afterObject, changeList);
+ } else if (mMapDefinitionsBySource.contains(afterType)) {
+ JsonDbMapDefinition *def = mMapDefinitionsBySource.value(afterType);
+ if (processedDefinitionUuids.contains(def->uuid()))
+ continue;
+ def->updateObject(beforeObject, afterObject, changeList);
+ }
+
+ if (mReduceDefinitionsBySource.contains(beforeType)) {
+ JsonDbReduceDefinition *def = mReduceDefinitionsBySource.value(beforeType);
+ if (processedDefinitionUuids.contains(def->uuid()))
+ continue;
+ def->updateObject(beforeObject, afterObject, changeList);
+ } else if (mReduceDefinitionsBySource.contains(afterType)) {
+ JsonDbReduceDefinition *def = mReduceDefinitionsBySource.value(afterType);
+ if (processedDefinitionUuids.contains(def->uuid()))
+ continue;
+ def->updateObject(beforeObject, afterObject, changeList);
+ }
+ }
+}
+
+bool JsonDbView::processUpdatedDefinitions(const QString &viewType, quint32 targetStateNumber,
+ QSet<QString> &processedDefinitionUuids)
+{
+ bool inTransaction = false;
+ quint32 stateNumber = mMainObjectTable->stateNumber();
+ if (stateNumber == targetStateNumber)
+ return inTransaction;
+ QSet<QString> limitTypes;
+ limitTypes << JsonDbString::kMapTypeStr << JsonDbString::kReduceTypeStr;
+ QList<JsonDbUpdate> changeList;
+ mMainObjectTable->changesSince(targetStateNumber, limitTypes, &changeList, JsonDbObjectTable::SplitTypeChanges);
+ foreach (const JsonDbUpdate &change, changeList) {
+ QString definitionUuid;
+ JsonDbNotification::Action action = change.action;
+ JsonDbObject before = change.oldObject;
+ JsonDbObject after = change.newObject;
+ QString beforeType = before.value(JsonDbString::kTypeStr).toString();
+ QString afterType = after.value(JsonDbString::kTypeStr).toString();
+ if (jsondbSettings->verbose())
+ qDebug() << "definition change" << change;
+ if (action != JsonDbNotification::Create) {
+ if (limitTypes.contains(beforeType)
+ && (before.value("targetType").toString() == viewType)) {
+ if (!inTransaction) {
+ mViewObjectTable->begin();
+ inTransaction = true;
+ }
+ definitionUuid = before.value(JsonDbString::kUuidStr).toString();
+ QString definitionType = before.value(JsonDbString::kTypeStr).toString();
+ QString targetType = before.value("targetType").toString();
+ if (definitionType == JsonDbString::kMapTypeStr)
+ JsonDbMapDefinition::definitionRemoved(mPartition, mViewObjectTable, targetType, definitionUuid);
+ else
+ JsonDbReduceDefinition::definitionRemoved(mPartition, mViewObjectTable, targetType, definitionUuid);
+ }
+ }
+ if (action != JsonDbNotification::Delete) {
+ if ((limitTypes.contains(afterType))
+ && (after.value("targetType").toString() == viewType)) {
+ if (!inTransaction) {
+ mViewObjectTable->begin();
+ inTransaction = true;
+ }
+ definitionUuid = after.value(JsonDbString::kUuidStr).toString();
+ QString definitionType = after.value(JsonDbString::kTypeStr).toString();
+ if (!after.contains(JsonDbString::kActiveStr) || after.value(JsonDbString::kActiveStr).toBool()) {
+ if (definitionType == JsonDbString::kMapTypeStr)
+ mMapDefinitions.value(definitionUuid)->definitionCreated();
+ else
+ mReduceDefinitions.value(definitionUuid)->definitionCreated();
+ }
+ }
+ }
+ if (!definitionUuid.isEmpty())
+ processedDefinitionUuids.insert(definitionUuid);
+ }
+ return inTransaction;
+}
+
+void JsonDbView::reduceMemoryUsage()
+{
+ mViewObjectTable->flushCaches();
+
+ for (QMap<QString,JsonDbMapDefinition*>::iterator it = mMapDefinitions.begin();
+ it != mMapDefinitions.end();
+ ++it)
+ it.value()->releaseScriptEngine();
+ for (QMap<QString,JsonDbReduceDefinition*>::iterator it = mReduceDefinitions.begin();
+ it != mReduceDefinitions.end();
+ ++it)
+ it.value()->releaseScriptEngine();
+}
+
+bool JsonDbView::isActive() const
+{
+ foreach (JsonDbMapDefinition *mapDef, mMapDefinitions) {
+ if (mapDef->isActive())
+ return true;
+ }
+
+ foreach (JsonDbReduceDefinition *reduceDef, mReduceDefinitions) {
+ if (reduceDef->isActive())
+ return true;
+ }
+
+ return false;
+}
+
+#include "moc_jsondbview.cpp"
+
+QT_END_NAMESPACE_JSONDB_PARTITION
diff --git a/src/partition/jsondbview.h b/src/partition/jsondbview.h
new file mode 100644
index 00000000..70a1bda4
--- /dev/null
+++ b/src/partition/jsondbview.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 JSONDB_VIEW_H
+#define JSONDB_VIEW_H
+
+#include <QObject>
+#include <QString>
+
+#include "jsondbpartitionglobal.h"
+#include "jsondbmapdefinition.h"
+#include "jsondbreducedefinition.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE_JSONDB_PARTITION
+
+class JsonDbOwner;
+class JsonDbPartition;
+class JsonDbObjectTable;
+
+class Q_JSONDB_PARTITION_EXPORT JsonDbView : public QObject
+{
+ Q_OBJECT
+public:
+ JsonDbView(JsonDbPartition *partition, const QString &viewType,
+ QObject *parent = 0);
+ ~JsonDbView();
+ JsonDbPartition *partition() const { return mPartition; }
+ JsonDbObjectTable *objectTable() const { return mViewObjectTable; }
+ QStringList sourceTypes() const { return mSourceTypes; }
+ QSet<QString> sourceTypeSet() const { return QSet<QString>::fromList(mSourceTypes); }
+
+ void open();
+ void close();
+
+ static void initViews(JsonDbPartition *partition);
+ static void createDefinition(JsonDbPartition *partition, QJsonObject definition);
+ static void removeDefinition(JsonDbPartition *partition, QJsonObject definition);
+
+ void updateView(quint32 stateNumber=0, JsonDbUpdateList *resultingChanges=0);
+ void updateEagerView(const JsonDbUpdateList &objectsUpdated, JsonDbUpdateList *resultingChanges);
+ void updateViewStateNumber(quint32 partitionStateNumber);
+ void reduceMemoryUsage();
+
+ bool isActive() const;
+
+Q_SIGNALS:
+ void updated(const QString &type);
+
+private:
+ void createMapDefinition(QJsonObject mapDefinition);
+ void removeMapDefinition(QJsonObject mapDefinition);
+ void createReduceDefinition(QJsonObject reduceDefinition);
+ void removeReduceDefinition(QJsonObject reduceDefinition);
+ bool processUpdatedDefinitions(const QString &viewType, quint32 targetStateNumber,
+ QSet<QString> &processedDefinitions);
+ void updateSourceTypesList();
+ bool viewDefinitionUpdated(const JsonDbUpdateList &objectsUpdated) const;
+ void updateViewOnChanges(const JsonDbUpdateList &objectsUpdated, QSet<QString> &processedDefinitionUuids, JsonDbUpdateList *changeList);
+
+private:
+ JsonDbPartition *mPartition;
+ JsonDbObjectTable *mViewObjectTable; // view object table
+ JsonDbObjectTable *mMainObjectTable; // partition's main object table
+ quint32 mViewStateNumber;
+ QString mViewType;
+ QStringList mSourceTypes;
+ typedef QMap<JsonDbObjectTable*,QSet<QString> > ObjectTableSourceTypeMap;
+ ObjectTableSourceTypeMap mObjectTableSourceTypeMap;
+ QMap<QString,JsonDbMapDefinition*> mMapDefinitions; // maps uuid to view definition
+ QMap<QString,JsonDbMapDefinition*> mMapDefinitionsBySource; // maps map source type to view definition
+ QMap<QString,JsonDbReduceDefinition*> mReduceDefinitions; // maps uuid to view definition
+ QMap<QString,JsonDbReduceDefinition*> mReduceDefinitionsBySource; // maps reduce source type to view definition
+ bool mUpdating;
+
+ friend class JsonDbMapDefinition;
+ friend class JsonDbReduceDefinition;
+};
+
+QT_END_NAMESPACE_JSONDB_PARTITION
+
+QT_END_HEADER
+
+#endif // JSONDB_VIEW_H
diff --git a/src/partition/partition.pro b/src/partition/partition.pro
new file mode 100644
index 00000000..063e5d48
--- /dev/null
+++ b/src/partition/partition.pro
@@ -0,0 +1,78 @@
+TEMPLATE = lib
+TARGET = $$QT.jsondbpartition.name
+MODULE = jsondbpartition
+
+load(qt_module)
+load(qt_module_config)
+
+DESTDIR = $$QT.jsondbpartition.libs
+VERSION = $$QT.jsondbpartition.VERSION
+DEFINES += QT_JSONDB_PARTITION_LIB
+
+QT = core network qml
+
+CONFIG += module create_prl
+MODULE_PRI = ../../modules/qt_jsondbpartition.pri
+
+include(../3rdparty/btree/btree.pri)
+include(../hbtree/hbtree.pri)
+
+RESOURCES = jsondb.qrc
+
+HEADERS += \
+ jsondbowner.h \
+ jsondbproxy.h \
+ jsondbindex.h \
+ jsondbobject.h \
+ jsondbpartition.h \
+ jsondbquery.h \
+ jsondbstat.h \
+ jsondbview.h \
+ jsondbmapdefinition.h \
+ jsondbnotification.h \
+ jsondbobjectkey.h \
+ jsondbobjecttable.h \
+ jsondbbtree.h \
+ jsondbobjecttypes_impl_p.h \
+ jsondbobjecttypes_p.h \
+ jsondbreducedefinition.h \
+ schema-validation/checkpoints.h \
+ schema-validation/object.h \
+ jsondbschemamanager_impl_p.h \
+ jsondbschemamanager_p.h \
+ jsondbscriptengine.h \
+ jsondbsettings.h \
+ jsondbindexquery.h \
+ jsondberrors.h \
+ jsondbstrings.h \
+ jsondbpartitionglobal.h \
+ jsondbcollator.h \
+ jsondbcollator_p.h
+
+SOURCES += \
+ jsondbowner.cpp \
+ jsondbproxy.cpp \
+ jsondbindex.cpp \
+ jsondbobject.cpp \
+ jsondbpartition.cpp \
+ jsondbquery.cpp \
+ jsondbview.cpp \
+ jsondbmapdefinition.cpp \
+ jsondbnotification.cpp \
+ jsondbobjecttable.cpp \
+ jsondbbtree.cpp \
+ jsondbreducedefinition.cpp \
+ jsondbscriptengine.cpp \
+ jsondbsettings.cpp \
+ jsondbindexquery.cpp \
+ jsondberrors.cpp \
+ jsondbstrings.cpp \
+ jsondbcollator.cpp
+
+mac:QMAKE_FRAMEWORK_BUNDLE_NAME = $$QT.jsondbpartition.name
+
+contains(config_test_icu, yes) {
+ LIBS += -licuuc -licui18n
+} else {
+ DEFINES += NO_COLLATION_SUPPORT
+}
diff --git a/src/partition/schema-validation/checkpoints.h b/src/partition/schema-validation/checkpoints.h
new file mode 100644
index 00000000..34e2e164
--- /dev/null
+++ b/src/partition/schema-validation/checkpoints.h
@@ -0,0 +1,837 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qregexp.h>
+#include "object.h"
+
+#ifndef CHECKPOINTS_H
+#define CHECKPOINTS_H
+
+QT_BEGIN_HEADER
+
+namespace SchemaValidation {
+
+/**
+ \internal
+ This template is used for hash computation for static latin1 strings.
+ */
+template<ushort C1 = 0, ushort C2 = 0, ushort C3 = 0, ushort C4 = 0, ushort C5 = 0,
+ ushort C6 = 0, ushort C7 = 0, ushort C8 = 0, ushort C9 = 0, ushort C10 = 0,
+ ushort C11 = 0, ushort C12 = 0, ushort C13 = 0, ushort C14 = 0, ushort C15 = 0,
+ ushort C16 = 0, ushort C17 = 0, ushort C18 = 0>
+struct QStaticStringHash
+{
+ typedef QStaticStringHash<C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C18> Suffix;
+
+ const static int Hash = (C1 ^ Suffix::Hash) + 5;
+ //(C1 ^ ( (C2 ^ (...)) +5 )) +5
+};
+
+template<>
+struct QStaticStringHash<>
+{
+ typedef QStaticStringHash<> Suffix;
+ const static int Hash = 0;
+
+ /**
+ \internal
+ This function has to be align with qStringHash::Hash value
+ */
+ inline static int hash(const QString &string)
+ {
+ const ushort *str = reinterpret_cast<const ushort*>(string.constData());
+ return hash(str, 0, string.length());
+ }
+private:
+ inline static int hash(const ushort *str, const int index, const int length)
+ {
+ return index != length ? (str[index] ^ hash(str, index + 1, length)) + 5
+ : 0;
+ }
+};
+
+template<class T>
+class SchemaPrivate<T>::NullCheck : public Check {
+public:
+ NullCheck(SchemaPrivate *schema)
+ : Check(schema, "") // TODO
+ {}
+protected:
+ virtual bool doCheck(const Value&) { return true; }
+};
+
+// 5.1
+template<class T>
+class SchemaPrivate<T>::CheckType : public Check {
+ enum Type {StringType = 0x0001,
+ NumberType = 0x0002,
+ IntegerType = 0x0004,
+ BooleanType = 0x0008,
+ ObjectType = 0x0010,
+ ArrayType = 0x0020,
+ NullType = 0x0040,
+ AnyType = 0x0080,
+ UnknownType = 0};
+public:
+ CheckType(SchemaPrivate *schema, const Value& type)
+ : Check(schema, "Type check failed for %1")
+ , m_type(UnknownType)
+ {
+ bool ok;
+ QStringList typesName;
+
+ QString typeName = type.toString(&ok);
+ if (!ok) {
+ ValueList typesList = type.toList(&ok);
+ Q_ASSERT_X(ok, Q_FUNC_INFO, "Type is neither a string nor an array");
+ typename ValueList::const_iterator i;
+ for (i = typesList.constBegin(); i != typesList.constEnd(); ++i) {
+ typeName = (*i).toString(&ok);
+ if (ok) {
+ typesName << typeName;
+ }
+ }
+ } else {
+ typesName << typeName;
+ }
+ foreach (const QString &name, typesName) {
+ const int hash = QStaticStringHash<>::hash(name.toLower());
+
+ // FIXME there are chances of conflicts, do we care? What is chance for new types in JSON?
+ // FIXME we need to check only 2 chars. That would be faster.
+ // FIXME probably we want to support schemas here too.
+ switch (hash) {
+ case QStaticStringHash<'s','t','r','i','n','g'>::Hash:
+ m_type |= StringType;
+ break;
+ case QStaticStringHash<'n','u','m','b','e','r'>::Hash:
+ m_type |= NumberType;
+ m_type |= IntegerType; // an integer is also a number
+ break;
+ case QStaticStringHash<'i','n','t','e','g','e','r'>::Hash:
+ m_type |= IntegerType;
+ break;
+ case QStaticStringHash<'b','o','o','l','e','a','n'>::Hash:
+ m_type |= BooleanType;
+ break;
+ case QStaticStringHash<'o','b','j','e','c','t'>::Hash:
+ m_type |= ObjectType;
+ break;
+ case QStaticStringHash<'a','r','r','a','y'>::Hash:
+ m_type |= ArrayType;
+ break;
+ case QStaticStringHash<'a','n','y'>::Hash:
+ m_type |= AnyType;
+ break;
+ case QStaticStringHash<'n','u','l','l'>::Hash:
+ m_type |= NullType;
+ break;
+ default:
+ m_type |= UnknownType;
+ }
+ }
+
+// qDebug() << Q_FUNC_INFO << m_type << type.toString(&ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ if (m_type == UnknownType)
+ return true;
+
+ bool result = findType(value) & m_type;
+// bool ok;
+// qDebug() << Q_FUNC_INFO << findType(value) << m_type << value.toString(&ok) << result;
+ return result;
+ }
+
+private:
+ inline Type findType(const Value &value) const
+ {
+ bool ok;
+ // Lets assume that the value is valid.
+ switch (m_type) {
+ case StringType:
+ value.toString(&ok);
+ if (ok)
+ return StringType;
+ break;
+ case NumberType:
+ value.toDouble(&ok);
+ if (ok)
+ return NumberType;
+ break;
+ case IntegerType:
+ value.toInt(&ok);
+ if (ok)
+ return IntegerType;
+ break;
+ case BooleanType:
+ value.toBool(&ok);
+ if (ok)
+ return BooleanType;
+ break;
+ case ObjectType:
+ value.toObject(&ok);
+ if (ok)
+ return ObjectType;
+ break;
+ case NullType:
+ value.toNull(&ok);
+ if (ok)
+ return NullType;
+ break;
+ case AnyType:
+ return AnyType;
+ case UnknownType:
+ break;
+ default:
+ break;
+ };
+
+ //TODO FIXME it can be better
+ value.toInt(&ok);
+ if (ok)
+ return IntegerType;
+ value.toDouble(&ok);
+ if (ok)
+ return NumberType;
+ value.toObject(&ok);
+ if (ok)
+ return ObjectType;
+ value.toString(&ok);
+ if (ok)
+ return StringType;
+ value.toBool(&ok);
+ if (ok)
+ return BooleanType;
+ value.toList(&ok);
+ if (ok)
+ return ArrayType;
+ return AnyType;
+ }
+
+ uint m_type;
+};
+
+// 5.2
+template<class T>
+class SchemaPrivate<T>::CheckProperties : public Check {
+public:
+ CheckProperties(SchemaPrivate *schema, const Value &properties)
+ : Check(schema, "Properties check failed for %1")
+ {
+ bool ok;
+ const Object obj = properties.toObject(&ok);
+ Q_ASSERT(ok);
+
+ QList<Key> propertyNames = obj.propertyNames();
+// qDebug() << " propertyNames: " << propertyNames <<this;
+
+ m_checks.reserve(propertyNames.count());
+ foreach (const Key &propertyName, propertyNames) {
+ QVarLengthArray<Check *, 4> checks;
+ const Object propertyChecks = obj.property(propertyName).toObject(&ok);
+// qDebug() << " propertyChecks:" << propertyChecks;
+
+ Q_ASSERT(ok);
+ foreach (const Key &key, propertyChecks.propertyNames()) {
+// bool ok;
+// qDebug() << " key:" << key << this << propertyChecks.property(key).toString(&ok)<< propertyChecks.property(key).toInt(&ok);
+ checks.append(schema->createCheckPoint(key, propertyChecks.property(key)));
+ }
+ m_checks.insert(propertyName, checks);
+ }
+ }
+
+ ~CheckProperties()
+ {
+ typename QHash<const Key, QVarLengthArray<Check *, 4> >::const_iterator i;
+ for (i = m_checks.constBegin(); i != m_checks.constEnd(); ++i) {
+ typename QVarLengthArray<Check *, 4>::const_iterator j;
+ for (j = i.value().constBegin(); j != i.value().constEnd(); ++j){
+ delete *j;
+ }
+ }
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ Object object = value.toObject(&ok);
+ if (!ok)
+ return false;
+
+ //qDebug() << Q_FUNC_INFO;
+ foreach (const Key &key, object.propertyNames()) {
+ QVarLengthArray<Check *, 4> empty;
+ QVarLengthArray<Check *, 4> checks = m_checks.value(key, empty);
+ Value property = object.property(key);
+ foreach (Check *check, checks) {
+ //qDebug() <<"CHECKING:" << check;
+ if (!check->check(property)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+private:
+ QHash<const Key, QVarLengthArray<Check *, 4> > m_checks;
+};
+
+// 5.5
+template<class T>
+class SchemaPrivate<T>::CheckItems : public Check {
+public:
+ CheckItems(SchemaPrivate *schemap, const Value &schema)
+ : Check(schemap, "Items check failed for %1")
+ {
+ // qDebug() << Q_FUNC_INFO << this;
+ bool ok;
+ Object obj = schema.toObject(&ok);
+ Q_ASSERT(ok);
+ m_schema = Schema<T>(obj, schemap->m_callbacks);
+ }
+
+ virtual bool doCheck(const Value& value)
+ {
+ //qDebug() << Q_FUNC_INFO << this;
+ bool ok;
+ ValueList array = value.toList(&ok);
+ if (!ok)
+ return false;
+
+ typename ValueList::const_iterator i;
+ for (i = array.constBegin(); i != array.constEnd(); ++i) {
+ if (!m_schema.check(*i, Check::m_schema->m_callbacks)) {
+ return false;
+ }
+ }
+ return true;
+ }
+private:
+ Schema<T> m_schema;
+};
+
+// 5.7
+template<class T>
+class SchemaPrivate<T>::CheckRequired : public Check {
+public:
+ CheckRequired(SchemaPrivate *schema, const Value &required)
+ : Check(schema, "Check required field") // TODO what to do about Required ?
+ {
+ bool ok;
+ m_req = required.toBool(&ok);
+ if (!ok) {
+ // maybe someone used string instead of bool
+ QString value = required.toString(&ok).toLower();
+ if (value == QString::fromLatin1("false"))
+ m_req = false;
+ else if (value == QString::fromLatin1("true"))
+ m_req = true;
+ else
+ Q_ASSERT(false);
+
+ qWarning() << QString::fromLatin1("Wrong 'required' syntax found, instead of boolean type a string was used");
+ }
+ Q_ASSERT(ok);
+ if (m_req)
+ Check::m_schema->m_maxRequired++;
+ }
+
+ virtual bool doCheck(const Value&)
+ {
+ //qDebug() << Q_FUNC_INFO << m_schema << this;
+ if (m_req)
+ Check::m_schema->m_requiredCount++;
+ return true;
+ }
+private:
+ bool m_req;
+};
+
+// 5.9
+template<class T>
+class SchemaPrivate<T>::CheckMinimum : public Check {
+public:
+ CheckMinimum(SchemaPrivate *schema, const Value &minimum)
+ : Check(schema, "Minimum check failed for %1")
+ {
+ bool ok;
+ m_min = minimum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ return value.toDouble(&ok) >= m_min && ok;
+ }
+private:
+ double m_min;
+};
+
+// 5.10
+template<class T>
+class SchemaPrivate<T>::CheckMaximum : public Check {
+public:
+ CheckMaximum(SchemaPrivate *schema, const Value &maximum)
+ : Check(schema, "Maximum check failed for %1")
+ {
+ // qDebug() << Q_FUNC_INFO << this;
+ bool ok;
+ m_max = maximum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ //qDebug() << Q_FUNC_INFO << value << m_max << this;
+ bool ok;
+ return value.toDouble(&ok) <= m_max && ok;
+ }
+private:
+ double m_max;
+};
+
+
+// 5.11
+template<class T>
+class SchemaPrivate<T>::CheckExclusiveMinimum : public Check {
+public:
+ CheckExclusiveMinimum(SchemaPrivate *schema, const Value &minimum)
+ : Check(schema, "Exclusive minimum check failed for %1")
+ {
+ bool ok;
+ m_min = minimum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ return value.toDouble(&ok) > m_min && ok;
+ }
+private:
+ double m_min;
+};
+
+// 5.12
+template<class T>
+class SchemaPrivate<T>::CheckExclusiveMaximum : public Check {
+public:
+ CheckExclusiveMaximum(SchemaPrivate *schema, const Value &maximum)
+ : Check(schema, "Exclusive minimum check failed for %1")
+ {
+ bool ok;
+ m_max = maximum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ return value.toDouble(&ok) < m_max && ok;
+ }
+private:
+ double m_max;
+};
+
+// 5.13
+template<class T>
+class SchemaPrivate<T>::CheckMinItems : public Check {
+public:
+ CheckMinItems(SchemaPrivate *schema, const Value& minimum)
+ : Check(schema, "Minimum item count check failed for %1")
+ {
+ bool ok;
+ m_min = minimum.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ int count = value.toList(&ok).size();
+ return count >= m_min && ok;
+ }
+private:
+ int m_min;
+};
+
+// 5.14
+template<class T>
+class SchemaPrivate<T>::CheckMaxItems : public Check {
+public:
+ CheckMaxItems(SchemaPrivate *schema, const Value& maximum)
+ : Check(schema, "Maximum item count check failed for %1")
+ {
+ bool ok;
+ m_max = maximum.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ int count = value.toList(&ok).size();
+ return count <= m_max && ok;
+ }
+
+private:
+ int m_max;
+};
+
+// 5.16
+template<class T>
+class SchemaPrivate<T>::CheckPattern : public Check {
+public:
+ CheckPattern(SchemaPrivate *schema, const Value& patternValue)
+ : Check(schema, "Pattern check failed for %1")
+ {
+ bool ok;
+ QString patternString = patternValue.toString(&ok);
+ m_regexp.setPattern(patternString);
+ Q_ASSERT(ok && m_regexp.isValid());
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ QString str = value.toString(&ok);
+ if (!ok) {
+ // According to spec (5.15) we should check the value only when it exist and it is a string.
+ // It is a bit strange, but I think we have to return true here.
+ return true;
+ }
+ return m_regexp.exactMatch(str);
+ }
+private:
+ QRegExp m_regexp;
+};
+
+// 5.17
+template<class T>
+class SchemaPrivate<T>::CheckMinLength : public Check {
+public:
+ CheckMinLength(SchemaPrivate *schema, const Value& min)
+ : Check(schema, "Minimal string length check failed for %1")
+ {
+ bool ok;
+ m_min = min.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ QString str = value.toString(&ok);
+// qDebug() << Q_FUNC_INFO << str << ok;
+ if (!ok) {
+ // According to spec (5.16) we should check the value only when it exist and it is a string.
+ // It is a bit strange, but I think we have to return true here.
+ return true;
+ }
+ return str.length() >= m_min;
+ }
+private:
+ int m_min;
+};
+
+// 5.18
+template<class T>
+class SchemaPrivate<T>::CheckMaxLength : public Check {
+public:
+ CheckMaxLength(SchemaPrivate *schema, const Value& max)
+ : Check(schema, "Maximal string length check failed for %1")
+ {
+ bool ok;
+ m_max = max.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ QString str = value.toString(&ok);
+// qDebug() << Q_FUNC_INFO << str << ok;
+ if (!ok) {
+ // According to spec (5.16) we should check the value only when it exist and it is a string.
+ // It is a bit strange, but I think we have to return true here.
+ return true;
+ }
+ return str.length() <= m_max;
+ }
+private:
+ int m_max;
+};
+
+// 5.26
+template<class T>
+class SchemaPrivate<T>::CheckExtends : public Check {
+public:
+ CheckExtends(SchemaPrivate *schema, const Value &value)
+ : Check(schema, "Extends check failed for %1")
+ {
+ // FIXME
+ // Keep in mind that there is a bug in spec. (internet draft 3).
+ // We should search for a schema not for a string here.
+ // Tests are using "string" syntax, so we need to support it for a while
+ bool ok;
+ Object obj = value.toObject(&ok);
+ if (!ok) {
+ QString schemaName = value.toString(&ok);
+ if (!ok) {
+ ValueList array = value.toList(&ok);
+ Q_ASSERT(ok);
+ typename ValueList::const_iterator i;
+ for (i = array.constBegin(); i != array.constEnd(); ++i) {
+ Object obj = (*i).toObject(&ok);
+ Q_ASSERT(ok);
+ m_extendedSchema.append(Schema<T>(obj, schema->m_callbacks));
+ }
+ } else {
+ qWarning() << QString::fromLatin1("Wrong 'extends' syntax found, instead of \"%1\" should be \"%2\"")
+ .arg(schemaName, QString::fromLatin1("{\"$ref\":\"%1\"}").arg(schemaName));
+ m_extendedSchema.append(schema->m_callbacks->loadSchema(schemaName));
+ }
+ } else {
+ m_extendedSchema.append(Schema<T>(obj, schema->m_callbacks));
+ }
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ for (int i = 0; i < m_extendedSchema.count(); ++i) {
+ if (!m_extendedSchema[i].check(value, Check::m_schema->m_callbacks))
+ return false;
+ }
+ return true;
+ }
+private:
+ QVarLengthArray<Schema<T>, 4> m_extendedSchema;
+};
+
+// 5.28
+template<class T>
+class SchemaPrivate<T>::CheckRef : public Check {
+public:
+ CheckRef(SchemaPrivate *schema, const Value &value)
+ : Check(schema, "$Ref check failed for %1")
+ {
+ // TODO according to spec we should replace existing check by this one
+ // I'm not sure what does it mean. Should we remove other checks?
+ // What if we have two $ref? Can it happen? For now, lets use magic of
+ // undefined bahaviour (without crashing of course).
+ bool ok;
+ QString schemaName = value.toString(&ok);
+ Q_ASSERT(ok);
+
+ m_newSchema = schema->m_callbacks->loadSchema(schemaName);
+ if (!m_newSchema.isValid()) {
+ // FIXME should we have current schema name?
+ const QString msg = QString::fromLatin1("Schema extends %1 but it is unknown.")
+ .arg(schemaName);
+ qWarning() << msg;
+ schema->m_callbacks->setError(msg);
+ }
+ }
+ virtual bool doCheck(const Value &value)
+ {
+ bool result = m_newSchema.check(value, Check::m_schema->m_callbacks);
+// qDebug() << Q_FUNC_INFO << result;
+ return result;
+ }
+private:
+ Schema<T> m_newSchema;
+};
+
+template<class T>
+class SchemaPrivate<T>::CheckDescription : public NullCheck {
+public:
+ CheckDescription(SchemaPrivate *schema)
+ : NullCheck(schema)
+ {}
+};
+
+template<class T>
+class SchemaPrivate<T>::CheckTitle : public NullCheck {
+public:
+ CheckTitle(SchemaPrivate *schema)
+ : NullCheck(schema)
+ {}
+};
+
+template<class T>
+typename SchemaPrivate<T>::Check *SchemaPrivate<T>::createCheckPoint(const Key &key, const Value &value)
+{
+ QString keyName = key;
+ keyName = keyName.toLower();
+
+ // This is a perfect hash. BUT spec, in future, can be enriched by new values, that we should just ignore.
+ // As we do not know about them we can't be sure that our perfect hash will be still perfect, therefore
+ // we have to do additional string comparison that confirm result hash function result.
+ int hash = QStaticStringHash<>::hash(keyName);
+ switch (hash) {
+ case QStaticStringHash<'r','e','q','u','i','r','e','d'>::Hash:
+ if (QString::fromLatin1("required") == keyName)
+ return new CheckRequired(this, value);
+ break;
+ case QStaticStringHash<'m','a','x','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("maximum") == keyName)
+ return new CheckMaximum(this, value);
+ break;
+ case QStaticStringHash<'e','x','c','l','u','s','i','v','e','m','a','x','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("exclusivemaximum") == keyName)
+ return new CheckExclusiveMaximum(this, value);
+ break;
+ case QStaticStringHash<'m','i','n','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("minimum") == keyName)
+ return new CheckMinimum(this, value);
+ break;
+ case QStaticStringHash<'e','x','c','l','u','s','i','v','e','m','i','n','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("exclusiveminimum") == keyName)
+ return new CheckExclusiveMinimum(this, value);
+ break;
+ case QStaticStringHash<'p','r','o','p','e','r','t','i','e','s'>::Hash:
+ if (QString::fromLatin1("properties") == keyName)
+ return new CheckProperties(this, value);
+ break;
+ case QStaticStringHash<'d','e','s','c','r','i','p','t','i','o','n'>::Hash:
+ if (QString::fromLatin1("description") == keyName)
+ return new CheckDescription(this);
+ break;
+ case QStaticStringHash<'t','i','t','l','e'>::Hash:
+ if (QString::fromLatin1("title") == keyName)
+ return new CheckTitle(this);
+ break;
+ case QStaticStringHash<'m','a','x','i','t','e','m','s'>::Hash:
+ if (QString::fromLatin1("maxitems") == keyName)
+ return new CheckMaxItems(this,value);
+ break;
+ case QStaticStringHash<'m','i','n','i','t','e','m','s'>::Hash:
+ if (QString::fromLatin1("minitems") == keyName)
+ return new CheckMinItems(this,value);
+ break;
+ case QStaticStringHash<'i','t','e','m','s'>::Hash:
+ if (QString::fromLatin1("items") == keyName)
+ return new CheckItems(this,value);
+ break;
+ case QStaticStringHash<'e','x','t','e','n','d','s'>::Hash:
+ if (QString::fromLatin1("extends") == keyName)
+ return new CheckExtends(this,value);
+ break;
+ case QStaticStringHash<'p','a','t','t','e','r','n'>::Hash:
+ if (QString::fromLatin1("pattern") == keyName)
+ return new CheckPattern(this, value);
+ break;
+ case QStaticStringHash<'m','i','n','l','e','n','g','t','h'>::Hash:
+ if (QString::fromLatin1("minlength") == keyName)
+ return new CheckMinLength(this, value);
+ break;
+ case QStaticStringHash<'m','a','x','l','e','n','g','t','h'>::Hash:
+ if (QString::fromLatin1("maxlength") == keyName)
+ return new CheckMaxLength(this, value);
+ break;
+ case QStaticStringHash<'$','r','e','f'>::Hash:
+ if (QString::fromLatin1("$ref") == keyName)
+ return new CheckRef(this, value);
+ break;
+ case QStaticStringHash<'t','y','p','e'>::Hash:
+ if (QString::fromLatin1("type") == keyName)
+ return new CheckType(this, value);
+ break;
+ default:
+// qDebug() << "NOT FOUND" << keyName;
+ return new NullCheck(this);
+ }
+
+// qDebug() << "FALLBACK" << keyName;
+// bool ok;
+// qCritical() << keyName << value.toString(&ok);
+ return new NullCheck(this);
+}
+
+template<class T>
+bool Schema<T>::check(const Value &value, Service *callbackToUseForCheck) const
+{
+ return d_ptr->check(value, callbackToUseForCheck);
+}
+
+template<class T>
+bool SchemaPrivate<T>::check(const Value &value, Service *callbackToUseForCheck) const
+{
+ //qDebug() << Q_FUNC_INFO << m_checks.count() << this;
+ Q_ASSERT(callbackToUseForCheck);
+ Q_ASSERT(!m_callbacks);
+
+ m_callbacks = callbackToUseForCheck;
+ bool result = check(value);
+ m_callbacks = 0;
+ return result;
+}
+
+template<class T>
+bool SchemaPrivate<T>::check(const Value &value) const
+{
+ Q_ASSERT(m_callbacks);
+
+ m_requiredCount = 0;
+ foreach (Check *check, m_checks) {
+ if (!check->check(value)) {
+ return false;
+ }
+ }
+ if (m_requiredCount != m_maxRequired) {
+ m_callbacks->setError(QString::fromLatin1("Schema validation error: Required field is missing"));
+ return false;
+ }
+ return true;
+}
+
+} // namespace SchemaValidation
+
+QT_END_HEADER
+
+#endif // CHECKPOINTS_H
diff --git a/src/partition/schema-validation/object.h b/src/partition/schema-validation/object.h
new file mode 100644
index 00000000..2725647e
--- /dev/null
+++ b/src/partition/schema-validation/object.h
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb 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 <QtCore/qlist.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qshareddata.h>
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+QT_BEGIN_HEADER
+
+namespace SchemaValidation {
+
+///**
+// Interface for object like classes. Instances of specialization for this class would be
+// used as input data for schema validation
+// \internal
+//*/
+//template<class T>
+//class Object
+//{
+//public:
+// /**
+// Should return value of a property with the given \a name
+// */
+// Value<T> property(const Key<T>& name) const;
+
+// /**
+// Should return list of all properties name
+// \todo replace to iterator syntax
+// */
+// QList<Key<T> > propertyNames() const;
+
+//};
+
+///**
+// Interface for value like classes. A value can be one of types defined types in JSON
+// \internal
+//*/
+//template<class T>
+//class Value {
+//public:
+// int toInt(bool *ok) const;
+// double toDouble(bool *ok) const;
+// ValueList toList(bool *ok) const;
+// QString toString(bool *ok) const;
+// bool toBool(bool *ok) const;
+// void toNull(bool *ok) const;
+// Object<T> toObject(bool *ok) const;
+//};
+// ValueList::count()
+// ValueList::constBegin()
+// ValueList::constEnd()
+// ValueList::const_iterator()
+//
+// void Service::setError(const QString &message)
+// Schema<T> Service::loadSchema(const QString &name)
+
+template<class T>
+class SchemaPrivate;
+
+template<class T>
+class Schema {
+ typedef typename T::Value Value;
+ typedef typename T::Key Key;
+ typedef typename T::Object Object;
+ typedef typename T::ValueList ValueList;
+ typedef typename T::Service Service;
+
+public:
+ inline bool check(const Value &value, Service *callbackToUseForCheck) const;
+
+ Schema()
+ : d_ptr(new SchemaPrivate<T>())
+ {}
+
+ Schema(const Object& schema, Service *callbacksToUseForCompilation)
+ : d_ptr(new SchemaPrivate<T>())
+ {
+ d_ptr->compile(schema, callbacksToUseForCompilation);
+ }
+
+ bool isValid() const
+ {
+ return d_ptr->isValid();
+ }
+private:
+
+ friend class SchemaPrivate<T>;
+ QExplicitlySharedDataPointer<SchemaPrivate<T> > d_ptr;
+};
+
+template<class T>
+class SchemaPrivate : public QSharedData
+{
+ typedef typename Schema<T>::Value Value;
+ typedef typename Schema<T>::Key Key;
+ typedef typename Schema<T>::Object Object;
+ typedef typename Schema<T>::ValueList ValueList;
+ typedef typename Schema<T>::Service Service;
+
+ class Check {
+ public:
+ Check(SchemaPrivate *schema, const char* errorMessage)
+ : m_schema(schema)
+ , m_errorMessage(errorMessage)
+ {
+ Q_ASSERT(schema);
+ Q_ASSERT(errorMessage);
+ }
+ virtual ~Check() {}
+ bool check(const Value& value)
+ {
+ bool result = doCheck(value);
+ if (!result) {
+ bool ok;
+ // TODO it is tricky, as we do not have access to source code of this schema.
+ // maybe we can set "additional, hidden " source property in each schema property, or some nice hash?
+ m_schema->m_callbacks->setError("Schema validation error: " + QString::fromLatin1(m_errorMessage).arg(value.toString(&ok)));
+ }
+ return result;
+ }
+
+ protected:
+ SchemaPrivate *m_schema;
+ // return true if it is ok
+ virtual bool doCheck(const Value&) = 0;
+ private:
+ const char *m_errorMessage;
+ };
+
+ // empty check
+ class NullCheck;
+ // 5.1
+ class CheckType;
+ // 5.2
+ class CheckProperties;
+ // 5.5
+ class CheckItems;
+ // 5.7
+ class CheckRequired;
+ // 5.9
+ class CheckMinimum;
+ // 5.10
+ class CheckMaximum;
+ // 5.11
+ class CheckExclusiveMinimum;
+ // 5.12
+ class CheckExclusiveMaximum;
+ // 5.13
+ class CheckMinItems;
+ // 5.14
+ class CheckMaxItems;
+ // 5.16
+ class CheckPattern;
+ // 5.17
+ class CheckMinLength;
+ // 5.18
+ class CheckMaxLength;
+ // 5.21
+ class CheckTitle;
+ // 5.26
+ class CheckExtends;
+ // 5.28
+ class CheckRef;
+ class CheckDescription;
+
+ inline Check *createCheckPoint(const Key &key, const Value &value);
+ inline bool check(const Value &value) const;
+
+public:
+ SchemaPrivate()
+ : m_maxRequired(0)
+ , m_requiredCount(0)
+ , m_callbacks(0)
+ {}
+ ~SchemaPrivate()
+ {
+ for (int i = 0; i < m_checks.count(); ++i)
+ delete m_checks.at(i);
+ }
+
+ inline bool check(const Value &value, Service *callbackToUseForCheck) const;
+ inline void compile(const Object &schema, Service *callbackToUseForCompile)
+ {
+ Q_ASSERT(callbackToUseForCompile);
+ m_callbacks = callbackToUseForCompile;
+ const QList<Key> checksKeys = schema.propertyNames();
+ m_checks.reserve(checksKeys.count());
+ foreach (const Key &key, checksKeys) {
+ m_checks.append(createCheckPoint(key, schema.property(key)));
+ }
+ m_callbacks = 0;
+ }
+
+ bool isValid() const
+ {
+ // we have some checks so it means that something got compiled.
+ return m_checks.size();
+ }
+
+private:
+ QVarLengthArray<Check *, 4> m_checks;
+ qint32 m_maxRequired;
+ mutable qint32 m_requiredCount;
+ mutable Service *m_callbacks;
+};
+
+}
+
+#include "checkpoints.h"
+
+QT_END_HEADER
+
+#endif // OBJECT_H
diff --git a/src/partition/schema/Capability.json b/src/partition/schema/Capability.json
new file mode 100644
index 00000000..27f49859
--- /dev/null
+++ b/src/partition/schema/Capability.json
@@ -0,0 +1,33 @@
+{
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the capability"
+ },
+ "partition": {
+ "type":"string",
+ "description": "partition where this capability should be used"
+ },
+
+ "accessRules": {
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "description": "A JsonDb query string. Objects matching this query will be allowed for this access type."
+ }
+ }
+ }
+ },
+ "quotas": {
+ "type": "object",
+ "storage": {
+ "type": "number",
+ "description": "Current only one in quotas."
+ }
+ }
+ }
+}
diff --git a/src/partition/schema/Index.json b/src/partition/schema/Index.json
new file mode 100644
index 00000000..7c60d153
--- /dev/null
+++ b/src/partition/schema/Index.json
@@ -0,0 +1,24 @@
+{
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the index. Default to the value of propertyName, if non-empty. Required if propertyFunction is specified."
+ },
+ "propertyName": {
+ "type": "string",
+ "description": "Property to index."
+ },
+ "propertyFunction": {
+ "type": "string",
+ "description": "String that evaluates to a function that emits the index values. Mutually exclusive with propertyName."
+ },
+ "propertyType": {
+ "type": "string",
+ "description": "Type of values stored in that property."
+ },
+ "objectType": {
+ "description": "Object type to index. Optional."
+ }
+ }
+}
diff --git a/src/partition/schema/RootCapability.json b/src/partition/schema/RootCapability.json
new file mode 100644
index 00000000..9d4869fa
--- /dev/null
+++ b/src/partition/schema/RootCapability.json
@@ -0,0 +1,17 @@
+{
+ "_type": "Capability",
+ "name": "root",
+ "partition": "all",
+ "accessRules": {
+ "rw": {
+ "read": [".*"],
+ "write": [".*"]
+ },
+ "setOwner": {
+ "setOwner": [".*"]
+ }
+ },
+ "quotas": {
+ "storage": -1
+ }
+}
diff --git a/src/partition/schema/View.json b/src/partition/schema/View.json
new file mode 100644
index 00000000..6130fa33
--- /dev/null
+++ b/src/partition/schema/View.json
@@ -0,0 +1,10 @@
+{
+ "type": "object",
+ "properties": {
+ "sourceUuids": {
+ "type": "array"
+ },
+ "key": {
+ }
+ }
+}
diff --git a/src/partition/schema/notification.json b/src/partition/schema/notification.json
new file mode 100644
index 00000000..360be3b2
--- /dev/null
+++ b/src/partition/schema/notification.json
@@ -0,0 +1,12 @@
+{
+ "type": "object",
+ "ephemeral": "true",
+ "properties": {
+ "actions": {
+ "type": "array"
+ },
+ "query": {
+ "type": "string"
+ }
+ }
+}