summaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/common
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/common')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/bihash.h302
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h362
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/column.cpp38
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/column.h26
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/global.h66
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/memoryusage.cpp83
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/memoryusage.h6
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/nulldevice.cpp19
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/nulldevice.h15
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/objectpool.h84
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp103
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.h65
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/sortedhash.h164
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/strhash.h182
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/table.cpp52
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/table.h32
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/unused.h6
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/utils.cpp855
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/utils.h244
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp552
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h71
21 files changed, 3327 insertions, 0 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/bihash.h b/SQLiteStudio3/coreSQLiteStudio/common/bihash.h
new file mode 100644
index 0000000..cde93d1
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/bihash.h
@@ -0,0 +1,302 @@
+#ifndef BIHASH_H
+#define BIHASH_H
+
+#include <QHash>
+
+/**
+ * @brief Bi-directional QHash
+ *
+ * Bi-directional hash treats both inserted values as keys to each other.
+ * Bi-directional hash built on the <tt>left</tt> and <tt>right</tt> values concept.
+ *
+ * It's not multi-value hash, so when you try to insert existing value to any
+ * of sides (left or right), it will replace the whole conflicting entry.
+ *
+ * It doesn't provide operator[], because returning reference to an object,
+ * which then can be changed outside would desynchronize internals of this hash
+ * (internal inverted map could not be synchronized properly according to changes
+ * to the external reference).
+ */
+template <class L, class R>
+class BiHash
+{
+ public:
+ /**
+ * @brief Creates empty hash.
+ */
+ BiHash() {}
+
+ /**
+ * @brief Creates hash initialized with given values.
+ * @param list C++11 style initializer list, like: <tt>{{x=y}, {a=b}}</tt>
+ */
+ BiHash(std::initializer_list<std::pair<L, R>> list)
+ {
+ hash = QHash<L,R>(list);
+ initInverted();
+ }
+
+ /**
+ * @brief Creates bi-hash basing on QHash.
+ * @param other QHash to copy data from.
+ *
+ * If multiple keys in the QHash refer to the same value,
+ * this is not defined which one of those values will remain,
+ * but there will definitely be copied only one of them.
+ */
+ BiHash(const QHash<L, R> & other)
+ {
+ unite(other);
+ }
+
+ /**
+ * @brief Creates copy of BiHash.
+ * @param other BiHash to copy.
+ */
+ BiHash(const BiHash<L, R> & other) : hash(other.hash), inverted(other.inverted) {}
+
+ /**
+ * @brief Inserts new values to the hash.
+ * @param left Left value to be inserted.
+ * @param right Right value to be inserted.
+ *
+ * Note that if the \p left is already in left values, then the current entry
+ * for the left will be replaced with this new one.
+ * The same applies for the \p right.
+ *
+ * If it happens, that both \p left and \p right have already entries in the
+ * hash, but those are 2 different entries, then the insert operation will
+ * remove both conflicting records and insert the new one.
+ */
+ void insert(const L& left, const R& right)
+ {
+ if (hash.contains(left))
+ removeLeft(left);
+
+ if (inverted.contains(right))
+ removeRight(right);
+
+ inverted.insert(right, left);
+ hash.insert(left, right);
+ }
+
+ /**
+ * @brief Tests if left values contain given value.
+ * @param left Value to test.
+ * @return true if left values containe the \p left value.
+ */
+ bool containsLeft(const L& left) const
+ {
+ return hash.contains(left);
+ }
+
+ /**
+ * @brief Tests if right values contain given value.
+ * @param right Value to test.
+ * @return true if right values containe the \p right value.
+ */
+ bool containsRight(const R& right) const
+ {
+ return inverted.contains(right);
+ }
+
+ /**
+ * @brief Removes entry with left value equal to given value.
+ * @param left Value to remove by.
+ * @return Number of removed entries.
+ */
+ int removeLeft(const L& left)
+ {
+ if (!hash.contains(left))
+ return 0;
+
+ inverted.remove(hash.value(left));
+ hash.remove(left);
+
+ return 1;
+ }
+
+ /**
+ * @brief Removes entry with right value equal to given value.
+ * @param right Value to remove by.
+ * @return Number of removed entries.
+ */
+ int removeRight(const R& right)
+ {
+ if (!inverted.contains(right))
+ return 0;
+
+ hash.remove(inverted.value(right));
+ inverted.remove(right);
+
+ return 1;
+ }
+
+ /**
+ * @brief Pops entry with left value equal to given value.
+ * @param left Value to pop by.
+ * @return Right value assigned to given left value.
+ */
+ R takeLeft(const L& left)
+ {
+ R right = hash.take(left);
+ inverted.remove(right);
+ return right;
+ }
+
+ /**
+ * @brief Pops entry with right value equal to given value.
+ * @param right Value to pop by.
+ * @return Left value assigned to given right value.
+ */
+ L takeRight(const R& right)
+ {
+ R left = inverted.take(right);
+ hash.remove(left);
+ return left;
+ }
+
+ /**
+ * @brief Copies all entries from other BiHash to this hash.
+ * @param other BiHash to copy from.
+ * @return Reference to this hash after values are copied.
+ *
+ * Any entries from the \p other hash will overwrite current
+ * entries in case of conflict (by either left or right value).
+ */
+ BiHash<L,R>& unite(const BiHash<L,R>& other)
+ {
+ unite(other.hash);
+ return *this;
+ }
+
+ /**
+ * @overload
+ */
+ BiHash<L,R>& unite(const QHash<L,R>& other)
+ {
+ QHashIterator<L, R> it(other);
+ while (it.hasNext())
+ insert(it.next().key(), it.value());
+
+ return *this;
+ }
+
+ /**
+ * @brief Finds right value associated with given left value.
+ * @param left Left value to match.
+ * @return Right value if found, or default constructed value of right type.
+ */
+ R valueByLeft(const L& left) const
+ {
+ return hash.value(left);
+ }
+
+ /**
+ * @brief Finds left value associated with given right value.
+ * @param right Right value to match.
+ * @return Left value if found, or default constructed value of left type.
+ */
+ L valueByRight(const R& right) const
+ {
+ return inverted.value(right);
+ }
+
+ /**
+ * @brief Provides all left values.
+ * @return List of values from left side.
+ */
+ QList<L> leftValues() const
+ {
+ return hash.keys();
+ }
+
+ /**
+ * @brief Provides all right values.
+ * @return List of values from right side.
+ */
+ QList<R> rightValues() const
+ {
+ return inverted.keys();
+ }
+
+ /**
+ * @brief Provides java-like iterator for the hash.
+ * @return Iterator object for this hash.
+ */
+ QHashIterator<L,R> iterator() const
+ {
+ return QHashIterator<L,R>(hash);
+ }
+
+ /**
+ * @brief Removes all entries from the hash.
+ */
+ void clear()
+ {
+ hash.clear();
+ inverted.clear();
+ }
+
+ /**
+ * @brief Counts all entries in the hash.
+ * @return Number of entries.
+ */
+ int count() const
+ {
+ return hash.count();
+ }
+
+ /**
+ * @brief Tests whether the hash is empty or not.
+ * @return true if the hash is empty, false otherwise.
+ */
+ bool isEmpty() const
+ {
+ return hash.isEmpty();
+ }
+
+ /**
+ * @brief Provides QHash from this BiHash.
+ * @return QHash with left values as keys.
+ */
+ const QHash<L,R>& toQHash() const
+ {
+ return hash;
+ }
+
+ /**
+ * @brief Provides QHash with inverted values (right-to-left)
+ * @return QHash with right values as keys.
+ */
+ const QHash<R,L>& toInvertedQHash() const
+ {
+ return inverted;
+ }
+
+ private:
+ /**
+ * @brief Fills inverted internal hash basing on values from hash class member.
+ */
+ void initInverted()
+ {
+ QHashIterator<L,R> it(hash);
+ while (it.hasNext())
+ {
+ it.next();
+ inverted[it.value()] = it.key();
+ }
+ }
+
+ /**
+ * @brief Hash containing left-to-right mapping.
+ */
+ QHash<L,R> hash;
+
+ /**
+ * @brief Hash containing right-to-left mapping.
+ */
+ QHash<R,L> inverted;
+};
+
+#endif // BIHASH_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h b/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h
new file mode 100644
index 0000000..65c907b
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h
@@ -0,0 +1,362 @@
+#ifndef BISTRHASH_H
+#define BISTRHASH_H
+
+#include "bihash.h"
+#include <QHash>
+#include <QString>
+
+/**
+ * @brief Bi-directional string-oriented hash.
+ *
+ * This hash is very similar to BiHash, except it always uses QString
+ * for both left and right values. Given that, it also provides Qt::CaseSensitivity support
+ * for any operations accepting values in parameters.
+ *
+ * Just like BiHash, the BiStrHash doesn't provide operator[]. For more details see BiHash.
+ */
+class BiStrHash
+{
+ public:
+ /**
+ * @brief Creates empty hash.
+ */
+ BiStrHash() {}
+
+ /**
+ * @brief Creates pre-initialized hash.
+ * @param list C++11 style initializer list, like: <tt>{{"x"="y"}, {"a"="b"}}</tt>
+ */
+ BiStrHash(std::initializer_list<std::pair<QString, QString>> list)
+ {
+ hash = QHash<QString,QString>(list);
+ initInvertedAndLower();
+ }
+
+ /**
+ * @brief Creates BiStrHash basing on QHash.
+ * @param other QHash to copy values from.
+ *
+ * Any conflicting values from the \p other hash will overwrite
+ * current values in the hash.
+ */
+ BiStrHash(const QHash<QString, QString> & other)
+ {
+ unite(other);
+ }
+
+ /**
+ * @brief Copy constructor.
+ * @param other Other hash to copy.
+ */
+ BiStrHash(const BiStrHash& other) : hash(other.hash), inverted(other.inverted),
+ lowerHash(other.lowerHash), lowerInverted(other.lowerInverted) {}
+
+ /**
+ * @brief Inserts entry into the hash.
+ * @param left Left-side value to insert.
+ * @param right Right-side value to insert.
+ *
+ * Inserting to the hash is done in case-insensitive manner, hence any conflicting
+ * values matched with case insensitive method will be replaced with the new entry.
+ */
+ void insert(const QString& left, const QString& right)
+ {
+ if (lowerHash.contains(left.toLower()))
+ removeLeft(left, Qt::CaseInsensitive);
+
+ if (lowerInverted.contains(right.toLower()))
+ removeRight(right, Qt::CaseInsensitive);
+
+ inverted.insert(right, left);
+ hash.insert(left, right);
+ lowerHash.insert(left.toLower(), left);
+ lowerInverted.insert(right.toLower(), right);
+ }
+
+ /**
+ * @brief Tests if given value is in the left values of the hash.
+ * @param left Left-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return true if the key was matched in left side values, or false otherwise.
+ */
+ bool containsLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ return hash.contains(left);
+ else
+ return lowerHash.contains(left.toLower());
+ }
+
+ /**
+ * @brief Tests if given value is in the right values of the hash.
+ * @param right Right-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return true if the key was matched in right side values, or false otherwise.
+ */
+ bool containsRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ return inverted.contains(right);
+ else
+ return lowerInverted.contains(right.toLower());
+ }
+
+ /**
+ * @brief Removes entry matching given value in left-side values.
+ * @param left Left-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return Number of entries removed.
+ */
+ int removeLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ {
+ if (!hash.contains(left))
+ return 0;
+
+ inverted.remove(hash.value(left));
+ hash.remove(left);
+
+ return 1;
+ }
+ else
+ {
+ QString lowerLeft = left.toLower();
+ if (!lowerHash.contains(lowerLeft))
+ return 0;
+
+ QString right = hash.value(lowerHash.value(lowerLeft));
+
+ hash.remove(inverted.value(right));
+ inverted.remove(right);
+ lowerHash.remove(lowerLeft);
+ lowerInverted.remove(right.toLower());
+
+ return 1;
+ }
+ }
+
+ /**
+ * @brief Removes entry matching given value in right-side values.
+ * @param right Right-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return Number of entries removed.
+ */
+ int removeRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ {
+ if (!inverted.contains(right))
+ return 0;
+
+ hash.remove(inverted.value(right));
+ inverted.remove(right);
+
+ return 1;
+ }
+ else
+ {
+ QString lowerRight = right.toLower();
+ if (!lowerInverted.contains(lowerRight))
+ return 0;
+
+ QString left = inverted.value(lowerInverted.value(lowerRight));
+
+ inverted.remove(hash.value(left));
+ hash.remove(left);
+ lowerHash.remove(left.toLower());
+ lowerInverted.remove(lowerRight);
+
+ return 1;
+ }
+ }
+
+ /**
+ * @brief Removes entry from hash and returns it.
+ * @param left Left-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return Right side value, or null string if the \p left was not matched.
+ */
+ QString takeLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ {
+ QString right = hash.take(left);
+ inverted.remove(right);
+ return right;
+ }
+ else
+ {
+ QString right = hash.take(lowerHash.take(left.toLower()));
+ inverted.remove(lowerInverted.take(right.toLower()));
+ return right;
+ }
+ }
+
+ /**
+ * @brief Removes entry from hash and returns it.
+ * @param right Right-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return Left side value, or null string if the \p left was not matched.
+ */
+ QString takeRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ {
+ QString left = inverted.take(right);
+ hash.remove(left);
+ return left;
+ }
+ else
+ {
+ QString left = inverted.take(lowerInverted.take(right.toLower()));
+ hash.remove(lowerHash.take(left.toLower()));
+ return left;
+ }
+ }
+
+ /**
+ * @brief Copies all entries from the other hash to this hash.
+ * @param other Other hash to copy values from.
+ * @return Reference to this hash, after update.
+ */
+ BiStrHash& unite(const BiStrHash& other)
+ {
+ unite(other.hash);
+ return *this;
+ }
+
+ /**
+ * @overload
+ */
+ BiStrHash& unite(const QHash<QString,QString>& other)
+ {
+ QHashIterator<QString, QString> it(other);
+ while (it.hasNext())
+ insert(it.next().key(), it.value());
+
+ return *this;
+ }
+
+ /**
+ * @brief Finds right-side value by matching the left-side value.
+ * @param left Left-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return Right-side value, or null string if left-side value was not matched.
+ */
+ QString valueByLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
+ {
+ if (cs == Qt::CaseSensitive)
+ return hash.value(left);
+ else
+ return hash.value(lowerHash.value(left.toLower()));
+ }
+
+ /**
+ * @brief Finds left-side value by matching the right-side value.
+ * @param right Right-side value to match.
+ * @param cs Case sensitivity flag.
+ * @return Left-side value, or null string if right-side value was not matched.
+ */
+ QString valueByRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
+ {
+ if (cs == Qt::CaseSensitive)
+ return inverted.value(right);
+ else
+ return inverted.value(lowerInverted.value(right.toLower()));
+ }
+
+ /**
+ * @brief Gives all left-side values.
+ * @return List of values.
+ */
+ QStringList leftValues() const
+ {
+ return hash.keys();
+ }
+
+ /**
+ * @brief Gives all right-side values.
+ * @return List of values.
+ */
+ QStringList rightValues() const
+ {
+ return inverted.keys();
+ }
+
+ /**
+ * @brief Provides java-style iterator for this hash.
+ * @return Iterator object.
+ */
+ QHashIterator<QString,QString> iterator() const
+ {
+ return QHashIterator<QString,QString>(hash);
+ }
+
+ /**
+ * @brief Removes all entries from the hash.
+ */
+ void clear()
+ {
+ hash.clear();
+ inverted.clear();
+ lowerHash.clear();
+ lowerInverted.clear();
+ }
+
+ /**
+ * @brief Counts all entries in the hash.
+ * @return Number of entries in the hash.
+ */
+ int count() const
+ {
+ return hash.count();
+ }
+
+ /**
+ * @brief Tests whether the hash is empty or not.
+ * @return true if the hash is empty, false otherwise.
+ */
+ bool isEmpty() const
+ {
+ return hash.isEmpty();
+ }
+
+ private:
+ /**
+ * @brief Fills inverted and lower internal hashes basing on the main hash class member.
+ */
+ void initInvertedAndLower()
+ {
+ QHashIterator<QString,QString> it(hash);
+ while (it.hasNext())
+ {
+ it.next();
+ inverted[it.value()] = it.key();
+ lowerHash[it.key().toLower()] = it.key();
+ lowerInverted[it.value().toLower()] = it.value();
+ }
+ }
+
+ /**
+ * @brief Standard mapping - left to right.
+ */
+ QHash<QString,QString> hash;
+
+ /**
+ * @brief Right to left mapping.
+ */
+ QHash<QString,QString> inverted;
+
+ /**
+ * @brief Lower left to true left key mapping.
+ */
+ QHash<QString,QString> lowerHash;
+
+ /**
+ * @brief Lower right to true right key mapping.
+ */
+ QHash<QString,QString> lowerInverted;
+};
+
+#endif // BISTRHASH_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/column.cpp b/SQLiteStudio3/coreSQLiteStudio/common/column.cpp
new file mode 100644
index 0000000..cc282e6
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/column.cpp
@@ -0,0 +1,38 @@
+#include "column.h"
+#include <QHash>
+
+Column::Column() : Table()
+{
+}
+
+Column::Column(const QString& database, const QString& table, const QString& column) :
+ Table(database, table)
+{
+ setColumn(column);
+}
+
+Column::Column(const Column& other) :
+ Table(other.database, other.table)
+{
+ column = other.column;
+}
+
+int Column::operator ==(const Column& other) const
+{
+ return Table::operator==(other) && column == other.column;
+}
+
+QString Column::getColumn() const
+{
+ return column;
+}
+
+void Column::setColumn(const QString& value)
+{
+ column = value;
+}
+
+int qHash(Column column)
+{
+ return qHash(column.getDatabase() + "." + column.getTable() + "." + column.getColumn());
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/column.h b/SQLiteStudio3/coreSQLiteStudio/common/column.h
new file mode 100644
index 0000000..18e5edd
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/column.h
@@ -0,0 +1,26 @@
+#ifndef COLUMN_H
+#define COLUMN_H
+
+#include "table.h"
+#include "coreSQLiteStudio_global.h"
+#include <QString>
+
+struct API_EXPORT Column : public Table
+{
+ public:
+ Column();
+ Column(const QString& database, const QString& table, const QString& column);
+ Column(const Column& other);
+
+ int operator ==(const Column& other) const;
+
+ QString getColumn() const;
+ void setColumn(const QString& value);
+
+ private:
+ QString column;
+};
+
+int API_EXPORT qHash(Column column);
+
+#endif // COLUMN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/global.h b/SQLiteStudio3/coreSQLiteStudio/common/global.h
new file mode 100644
index 0000000..bc228ca
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/global.h
@@ -0,0 +1,66 @@
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+/** @file */
+
+#define DEEP_COPY_FIELD(T, F) \
+ if (other.F) \
+ { \
+ F = new T(*other.F); \
+ F->setParent(this); \
+ }
+
+#define DEEP_COPY_COLLECTION(T, F) \
+ T* _new##T; \
+ foreach (T* _element, other.F) \
+ { \
+ _new##T = new T(*_element); \
+ _new##T->setParent(this); \
+ F << _new##T; \
+ }
+
+/**
+ * @brief Deletes object and sets the pointer to null.
+ *
+ * Deletes object under given pointer, but only if the pointer is not null.
+ * Also sets the pointer to the null after deleting is done.
+ */
+#define safe_delete(var) \
+ if (var) \
+ { \
+ delete var; \
+ var = nullptr; \
+ }
+
+#define static_char static constexpr const char
+
+#define static_qstring(N,V) const static QString N = QStringLiteral(V)
+
+#define DECLARE_SINGLETON(Cls) \
+ public: \
+ static Cls* getInstance(); \
+ static void destroy(); \
+ \
+ private: \
+ static Cls* _instance;
+
+#define DEFINE_SINGLETON(Cls) \
+ Cls* Cls::_instance = nullptr; \
+ \
+ Cls* Cls::getInstance() \
+ { \
+ if (!_instance) \
+ _instance = new Cls(); \
+ \
+ return _instance; \
+ } \
+ \
+ void Cls::destroy() \
+ { \
+ safe_delete(_instance); \
+ }
+
+#define STRINGIFY(s) _STRINGIFY(s)
+#define _STRINGIFY(s) #s
+
+#endif // GLOBAL_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/memoryusage.cpp b/SQLiteStudio3/coreSQLiteStudio/common/memoryusage.cpp
new file mode 100644
index 0000000..386c65c
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/memoryusage.cpp
@@ -0,0 +1,83 @@
+#include "memoryusage.h"
+#include <QtGlobal>
+
+#ifdef Q_OS_LINUX
+#include <QFile>
+#include <QRegularExpression>
+#else
+
+#ifdef Q_OS_WIN32
+#include "windows.h"
+#include "psapi.h"
+#else
+
+#ifdef Q_OS_MAC
+#include <mach/mach.h>
+#endif // Q_OS_MAC
+
+#endif // Q_OS_WIN32
+#endif // Q_OS_LINUX
+
+#ifdef Q_OS_LINUX
+
+int getMemoryUsage()
+{
+ static const QRegularExpression re("VmSize\\:\\s+(\\d+)\\s+(\\w+)");
+
+ QFile file("/proc/self/status");
+ if (!file.open(QIODevice::ReadOnly))
+ return -1;
+
+ QString contents = file.readAll();
+ QRegularExpressionMatch match = re.match(contents);
+ if (!match.hasMatch())
+ return -1;
+
+ bool ok;
+ int result = match.captured(1).toInt(&ok);
+ if (!ok)
+ return -1;
+
+ QString unit = match.captured(2).toLower();
+ if (unit == "mb")
+ return result * 1024 * 1024;
+
+ if (unit == "kb")
+ return result * 1024;
+
+ return result;
+}
+
+#else
+#ifdef Q_OS_WIN32
+
+int getMemoryUsage()
+{
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc));
+ return pmc.PrivateUsage;
+}
+
+#else
+#ifdef Q_OS_MAC
+
+int getMemoryUsage()
+{
+ struct task_basic_info t_info;
+ mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
+
+ if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count))
+ return -1;
+
+ return t_info.virtual_size;
+}
+
+#else
+int getMemoryUsage()
+{
+ return -1;
+}
+
+#endif // Q_OS_MAC
+#endif // Q_OS_WIN32
+#endif // Q_OS_LINUX
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/memoryusage.h b/SQLiteStudio3/coreSQLiteStudio/common/memoryusage.h
new file mode 100644
index 0000000..55edfdb
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/memoryusage.h
@@ -0,0 +1,6 @@
+#ifndef MEMORYUSAGE_H
+#define MEMORYUSAGE_H
+
+int getMemoryUsage();
+
+#endif // MEMORYUSAGE_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/nulldevice.cpp b/SQLiteStudio3/coreSQLiteStudio/common/nulldevice.cpp
new file mode 100644
index 0000000..304aa9d
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/nulldevice.cpp
@@ -0,0 +1,19 @@
+#include "nulldevice.h"
+
+NullDevice::NullDevice(QObject *parent) :
+ QIODevice(parent)
+{
+}
+
+qint64 NullDevice::readData(char *data, qint64 maxSize)
+{
+ (void)(data); // slicence unused var
+ (void)(maxSize); // slicence unused var
+ return 0;
+}
+
+qint64 NullDevice::writeData(const char *data, qint64 maxSize)
+{
+ (void)(data); // slicence unused var
+ return maxSize;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/nulldevice.h b/SQLiteStudio3/coreSQLiteStudio/common/nulldevice.h
new file mode 100644
index 0000000..d714114
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/nulldevice.h
@@ -0,0 +1,15 @@
+#ifndef NULLDEVICE_H
+#define NULLDEVICE_H
+
+#include <QIODevice>
+
+class NullDevice : public QIODevice
+{
+ public:
+ explicit NullDevice(QObject *parent = 0);
+
+ qint64 readData(char * data, qint64 maxSize);
+ qint64 writeData(const char * data, qint64 maxSize);
+};
+
+#endif // NULLDEVICE_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/objectpool.h b/SQLiteStudio3/coreSQLiteStudio/common/objectpool.h
new file mode 100644
index 0000000..b9f6b9f
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/objectpool.h
@@ -0,0 +1,84 @@
+#ifndef OBJECTPOOL_H
+#define OBJECTPOOL_H
+
+#include <QHash>
+#include <QHashIterator>
+#include <QMutex>
+#include <QWaitCondition>
+
+template <class T>
+class ObjectPool
+{
+ public:
+ ObjectPool(quint32 min, quint32 max);
+
+ T* reserve();
+ void release(T* obj);
+
+ private:
+ QHash<T*, bool> pool;
+ QMutex mutex;
+ QWaitCondition waitCond;
+ int min;
+ int max;
+};
+
+template <class T>
+ObjectPool::ObjectPool(quint32 min, quint32 max)
+ : min(min), max(max)
+{
+ Q_ASSERT(min > 0);
+ T* obj = nullptr;
+ for (int i = 0; i < min; i++)
+ {
+ obj = new T();
+ pool[obj] = false;
+ }
+}
+
+T* ObjectPool::reserve()
+{
+ mutex.lock();
+
+ forever
+ {
+ QHashIterator<T*, bool> i(pool);
+ while (i.hasNext())
+ {
+ i.next();
+ if (!i.value())
+ {
+ pool[i.key()] = true;
+ T* obj = i.key();
+ mutex.unlock();
+ return obj;
+ }
+ }
+
+ // Check if we can enlarge the pool
+ if (pool.size() < max)
+ {
+ T* obj = new T();
+ pool[i.key()] = true;
+ mutex.unlock();
+ return obj;
+ }
+
+ // Wait for release
+ waitCond.wait(&mutex);
+ }
+
+ // no need to unlock, because the loop will repeat
+ // until the free obj is found and then mutex is unlocked.
+}
+
+template <class T>
+void ObjectPool::release(T* obj)
+{
+ mutex.lock();
+ pool[obj] = false;
+ mutex.unlock();
+ waitCond.wakeOne();
+}
+
+#endif // OBJECTPOOL_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp b/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp
new file mode 100644
index 0000000..0eaca75
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp
@@ -0,0 +1,103 @@
+#include "readwritelocker.h"
+#include "parser/lexer.h"
+#include <QReadWriteLock>
+#include <QReadLocker>
+#include <QWriteLocker>
+
+ReadWriteLocker::ReadWriteLocker(QReadWriteLock* lock, Mode mode)
+{
+ init(lock, mode);
+}
+
+ReadWriteLocker::ReadWriteLocker(QReadWriteLock* lock, const QString& query, Dialect dialect, bool noLock)
+{
+ init(lock, getMode(query, dialect, noLock));
+}
+
+ReadWriteLocker::~ReadWriteLocker()
+{
+ if (readLocker)
+ {
+ delete readLocker;
+ readLocker = nullptr;
+ }
+
+ if (writeLocker)
+ {
+ delete writeLocker;
+ writeLocker = nullptr;
+ }
+}
+
+void ReadWriteLocker::init(QReadWriteLock* lock, ReadWriteLocker::Mode mode)
+{
+ switch (mode)
+ {
+ case ReadWriteLocker::READ:
+ readLocker = new QReadLocker(lock);
+ break;
+ case ReadWriteLocker::WRITE:
+ writeLocker = new QWriteLocker(lock);
+ break;
+ case ReadWriteLocker::NONE:
+ // Nothing to lock.
+ break;
+ }
+}
+
+ReadWriteLocker::Mode ReadWriteLocker::getMode(const QString &query, Dialect dialect, bool noLock)
+{
+ static QStringList readOnlyCommands = {"ANALYZE", "EXPLAIN", "PRAGMA"};
+
+ if (noLock)
+ return ReadWriteLocker::NONE;
+
+ TokenList tokens = Lexer::tokenize(query, dialect);
+ int keywordIdx = tokens.indexOf(Token::KEYWORD);
+
+ if (keywordIdx > -1 && readOnlyCommands.contains(tokens[keywordIdx]->value.toUpper()))
+ return ReadWriteLocker::READ;
+
+ if (keywordIdx > -1 && tokens[keywordIdx]->value.toUpper() == "WITH")
+ {
+ bool matched = false;
+ bool isSelect = false;
+ int depth = 0;
+ for (TokenPtr token : tokens)
+ {
+ switch (token->type)
+ {
+ case Token::PAR_LEFT:
+ depth++;
+ break;
+ case Token::PAR_RIGHT:
+ depth--;
+ break;
+ case Token::KEYWORD:
+ if (depth == 0)
+ {
+ QString val = token->value.toUpper();
+ if (val == "SELECT")
+ {
+ matched = true;
+ isSelect = true;
+ }
+ else if (val == "DELETE" || val == "UPDATE" || val == "INSERT")
+ {
+ matched = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (matched)
+ break;
+ }
+ if (isSelect)
+ return ReadWriteLocker::READ;
+ }
+
+ return ReadWriteLocker::WRITE;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.h b/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.h
new file mode 100644
index 0000000..cac9368
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.h
@@ -0,0 +1,65 @@
+#ifndef READWRITELOCKER_H
+#define READWRITELOCKER_H
+
+#include "coreSQLiteStudio_global.h"
+#include "dialect.h"
+
+class QReadLocker;
+class QWriteLocker;
+class QReadWriteLock;
+
+/**
+ * @brief The ReadWriteLocker class
+ *
+ * This class behaves pretty much like QReadLocker or QWriteLocker
+ * (it actually uses those internally), except it can be either
+ * of those and this is to be decided at the moment of creation.
+ * Therefore the locker can work as read or write locker depending
+ * on an external condition.
+ * There's also a possibility to not lock anything as a third
+ * choice of working mode, so this also can be decided
+ * at construction moment.
+ */
+class API_EXPORT ReadWriteLocker
+{
+ public:
+ enum Mode
+ {
+ READ,
+ WRITE,
+ NONE
+ };
+
+ ReadWriteLocker(QReadWriteLock* lock, Mode mode);
+ ReadWriteLocker(QReadWriteLock* lock, const QString& query, Dialect dialect, bool noLock);
+ virtual ~ReadWriteLocker();
+
+ /**
+ * @brief Provides required locking mode for given query.
+ * @param query Query to be executed.
+ * @return Locking mode: READ or WRITE.
+ *
+ * Given the query this method analyzes what is the query and provides information if the query
+ * will do some changes on the database, or not. Then it returns proper locking mode that should
+ * be used for this query execution.
+ *
+ * Query execution methods from this class check if lock mode of the query to be executed isn't
+ * in conflict with the lock being currently applied on the dbOperLock (if any is applied at the moment).
+ *
+ * This method works on a very simple rule. It assumes that queries: SELECT, ANALYZE, EXPLAIN,
+ * and PRAGMA - are read-only, while all other queries are read-write.
+ * In case of PRAGMA this is not entirely true, but it's not like using PRAGMA for changing
+ * some setting would cause database state inconsistency. At least not from perspective of SQLiteStudio.
+ *
+ * In case of WITH statement it filters out the "WITH clause" and then checks for SELECT keyword.
+ */
+ static ReadWriteLocker::Mode getMode(const QString& query, Dialect dialect, bool noLock);
+
+ private:
+ void init(QReadWriteLock* lock, Mode mode);
+
+ QReadLocker* readLocker = nullptr;
+ QWriteLocker* writeLocker = nullptr;
+};
+
+#endif // READWRITELOCKER_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/sortedhash.h b/SQLiteStudio3/coreSQLiteStudio/common/sortedhash.h
new file mode 100644
index 0000000..9ca6ade
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/sortedhash.h
@@ -0,0 +1,164 @@
+#ifndef SORTEDHASH_H
+#define SORTEDHASH_H
+
+#include <QHash>
+
+/**
+ * @brief Partially implemented sorted hash.
+ *
+ * This is kind of a sorted QHash, except it doesn't act as sorted with iterators,
+ * just with keys. It also doesn't work with multiple values for single key.
+ *
+ * The complete sorted hash might be implemented later on.
+ */
+template <class Key, class T>
+class SortedHash : public QHash<Key, T>
+{
+ public:
+ SortedHash(std::initializer_list<std::pair<Key, T>> list) : QHash<Key, T>(list)
+ {
+ sortedKeys = keys();
+ }
+
+ SortedHash(const QHash<Key, T>& other) : QHash<Key, T>(other)
+ {
+ sortedKeys = keys();
+ }
+
+ SortedHash(QHash<Key, T>&& other) : QHash<Key, T>(other)
+ {
+ sortedKeys = keys();
+ }
+
+ SortedHash() : QHash<Key, T>()
+ {
+ }
+
+ typename QHash<Key, T>::iterator insert(const Key& key, const T& value)
+ {
+ if (!sortedKeys.contains(key))
+ sortedKeys << key;
+
+ return QHash<Key, T>::insert(key, value);
+ }
+
+ int remove(const Key& key)
+ {
+ sortedKeys.removeOne(key);
+ return QHash<Key, T>::remove(key);
+ }
+
+ void swap(QHash<Key, T>& other)
+ {
+ QHash<Key, T>::swap(other);
+ sortedKeys = keys();
+ }
+
+ void swap(SortedHash<Key, T>& other)
+ {
+ QHash<Key, T>::swap(other);
+ sortedKeys = other.sortedKeys;
+ }
+
+ T take(const Key& key)
+ {
+ sortedKeys.removeOne(key);
+ return QHash<Key, T>::take(key);
+ }
+
+ QList<Key> keys() const
+ {
+ return sortedKeys;
+ }
+
+ QList<Key> keys(const T& value) const
+ {
+ QList<Key> results;
+ foreach (const Key& k, sortedKeys)
+ if (value(k) == value)
+ results << k;
+
+ return results;
+ }
+
+ SortedHash<Key, T>& unite(const QHash<Key, T>& other)
+ {
+ QHash<Key, T>::unite(other);
+ sortedKeys += other.keys();
+ return *this;
+ }
+
+ SortedHash<Key, T>& unite(const SortedHash<Key, T>& other)
+ {
+ QHash<Key, T>::unite(other);
+ sortedKeys += other.sortedKeys;
+ return *this;
+ }
+
+ QList<T> values() const
+ {
+ QList<T> results;
+ foreach (const Key& k, sortedKeys)
+ results << value(k);
+
+ return results;
+ }
+
+ bool operator!=(const SortedHash<Key, T>& other) const
+ {
+ return !operator==(other);
+ }
+
+ SortedHash<Key, T>& operator=(const QHash<Key, T>& other)
+ {
+ QHash<Key, T>::operator=(other);
+ sortedKeys = other.keys();
+ return *this;
+ }
+
+ SortedHash<Key, T>& operator=(const SortedHash<Key, T>& other)
+ {
+ QHash<Key, T>::operator=(other);
+ sortedKeys = other.sortedKeys;
+ return *this;
+ }
+
+ bool operator==(const SortedHash<Key, T>& other) const
+ {
+ return QHash<Key, T>::operator==(other) && sortedKeys == other.sortedKeys;
+ }
+
+ T & operator[](const Key& key)
+ {
+ if (!sortedKeys.contains(key))
+ sortedKeys << key;
+
+ return QHash<Key, T>::operator[](key);
+ }
+
+ const T operator[](const Key& key) const
+ {
+ return QHash<Key, T>::operator[](key);
+ }
+
+ Key firstKey() const
+ {
+ if (sortedKeys.size() == 0)
+ return Key();
+
+ return sortedKeys.first();
+ }
+
+ Key lastKey() const
+ {
+ if (sortedKeys.size() == 0)
+ return Key();
+
+ return sortedKeys.last();
+ }
+
+ private:
+ QList<Key> sortedKeys;
+};
+
+#endif // SORTEDHASH_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/strhash.h b/SQLiteStudio3/coreSQLiteStudio/common/strhash.h
new file mode 100644
index 0000000..4fb7bb3
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/strhash.h
@@ -0,0 +1,182 @@
+#ifndef STRHASH_H
+#define STRHASH_H
+
+#include <QHash>
+#include <QString>
+#include <QStringList>
+#include <QDebug>
+
+template <class T>
+class StrHash
+{
+ public:
+ StrHash() {}
+ StrHash(std::initializer_list<std::pair<QString,T>> list) : hash(QHash<QString,T>(list))
+ {
+ initLower();
+ }
+
+ StrHash(const QHash<QString,T>& other) : hash(QHash<QString,T>(other))
+ {
+ initLower();
+ }
+
+ void insert(const QString& key, const T& value)
+ {
+ if (lowerCaseHash.contains(key.toLower()))
+ remove(key, Qt::CaseInsensitive);
+
+ hash.insert(key, value);
+ lowerCaseHash.insert(key.toLower(), key);
+ }
+
+ bool contains(const QString& key, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
+ {
+ if (cs == Qt::CaseSensitive)
+ return hash.contains(key);
+
+ return lowerCaseHash.contains(key.toLower());
+ }
+
+ int remove(const QString& key, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ {
+ int res = hash.remove(key);
+ if (res > 0)
+ lowerCaseHash.remove(key.toLower());
+
+ return res;
+ }
+
+ // Case insensitive
+ QString lowerKey = key.toLower();
+ if (lowerCaseHash.contains(lowerKey))
+ {
+ int res = hash.remove(lowerCaseHash.value(lowerKey));
+ lowerCaseHash.remove(lowerKey);
+ return res;
+ }
+
+ return 0;
+ }
+
+ T take(const QString& key, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+ {
+ if (cs == Qt::CaseSensitive)
+ {
+ lowerCaseHash.remove(key.toLower());
+ return hash.take(key);
+ }
+
+ // Case insensitive
+ QString lowerKey = key.toLower();
+ if (lowerCaseHash.contains(lowerKey))
+ {
+ QString theKey = lowerCaseHash.value(lowerKey);
+ lowerCaseHash.remove(lowerKey);
+ return hash.take(theKey);
+ }
+
+ return QString();
+ }
+
+ StrHash<T>& unite(const StrHash<T>& other)
+ {
+ unite(other.hash);
+ return *this;
+ }
+
+ StrHash<T>& unite(const QHash<QString,T>& other)
+ {
+ QHashIterator<QString,T> it(other);
+ while (it.hasNext())
+ {
+ it.next();
+ insert(it.key(), it.value());
+ }
+
+ return *this;
+ }
+
+ T value(const QString& key, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
+ {
+ if (cs == Qt::CaseSensitive)
+ return hash.value(key);
+
+ return hash.value(lowerCaseHash.value(key.toLower()));
+ }
+
+ QList<T> values() const
+ {
+ return hash.values();
+ }
+
+ QStringList keys() const
+ {
+ return hash.keys();
+ }
+
+ QHashIterator<QString,T> iterator() const
+ {
+ return QHashIterator<QString,T>(hash);
+ }
+
+ void clear()
+ {
+ hash.clear();
+ lowerCaseHash.clear();
+ }
+
+ int count() const
+ {
+ return hash.count();
+ }
+
+ int count(const QString& key, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
+ {
+ if (cs == Qt::CaseSensitive)
+ return hash.count(key);
+
+ return lowerCaseHash.count(key.toLower());
+ }
+
+ bool isEmpty() const
+ {
+ return hash.isEmpty();
+ }
+
+ T& operator[](const QString& key)
+ {
+ if (lowerCaseHash.contains(key.toLower()) && !hash.contains(key))
+ {
+ T value = hash[lowerCaseHash[key.toLower()]];
+ remove(key, Qt::CaseInsensitive);
+ hash[key] = value;
+ }
+
+ lowerCaseHash[key.toLower()] = key;
+ return hash[key];
+ }
+
+ const T operator[](const QString& key) const
+ {
+ return hash[lowerCaseHash[key.toLower()]];
+ }
+
+ private:
+ void initLower()
+ {
+ QHashIterator<QString,T> it(hash);
+ while (it.hasNext())
+ {
+ it.next();
+ lowerCaseHash[it.key().toLower()] = it.key();
+ }
+ }
+
+ QHash<QString,QString> lowerCaseHash;
+ QHash<QString,T> hash;
+};
+
+#endif // STRHASH_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/table.cpp b/SQLiteStudio3/coreSQLiteStudio/common/table.cpp
new file mode 100644
index 0000000..c590995
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/table.cpp
@@ -0,0 +1,52 @@
+#include "table.h"
+#include <QHash>
+
+Table::Table()
+{
+}
+
+Table::Table(const QString& database, const QString& table)
+{
+ setDatabase(database);
+ setTable(table);
+}
+
+Table::Table(const Table& other)
+{
+ database = other.database;
+ table = other.table;
+}
+
+Table::~Table()
+{
+}
+
+int Table::operator ==(const Table &other) const
+{
+ return other.database == this->database && other.table == this->table;
+}
+
+QString Table::getTable() const
+{
+ return table;
+}
+
+void Table::setTable(const QString& value)
+{
+ table = value;
+}
+
+QString Table::getDatabase() const
+{
+ return database;
+}
+
+void Table::setDatabase(const QString& value)
+{
+ database = value.isEmpty() ? "main" : value;
+}
+
+int qHash(Table table)
+{
+ return qHash(table.getDatabase() + "." + table.getTable());
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/table.h b/SQLiteStudio3/coreSQLiteStudio/common/table.h
new file mode 100644
index 0000000..d17a729
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/table.h
@@ -0,0 +1,32 @@
+#ifndef TABLE_H
+#define TABLE_H
+
+#include "coreSQLiteStudio_global.h"
+#include <QString>
+
+class API_EXPORT Table
+{
+ public:
+ Table();
+ Table(const QString& database, const QString& table);
+ Table(const Table& other);
+ virtual ~Table();
+
+ int operator ==(const Table& other) const;
+
+ QString getDatabase() const;
+ void setDatabase(const QString& value);
+
+ QString getTable() const;
+ void setTable(const QString& value);
+
+ protected:
+ QString database;
+ QString table;
+
+};
+
+int API_EXPORT qHash(Table table);
+
+
+#endif // TABLE_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/unused.h b/SQLiteStudio3/coreSQLiteStudio/common/unused.h
new file mode 100644
index 0000000..090a8a2
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/unused.h
@@ -0,0 +1,6 @@
+#ifndef UNUSED_H
+#define UNUSED_H
+
+#define UNUSED(X) (void)(X)
+
+#endif // UNUSED_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp b/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp
new file mode 100644
index 0000000..d56d838
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp
@@ -0,0 +1,855 @@
+#include "common/utils.h"
+#include "common/global.h"
+#include "dbobjecttype.h"
+#include "rsa/RSA.h"
+#include <QTextCodec>
+#include <QString>
+#include <QSet>
+#include <QVariant>
+#include <QDateTime>
+#include <QSysInfo>
+#include <QDebug>
+#include <QRegularExpression>
+#include <QDir>
+
+#ifdef Q_OS_LINUX
+#include <sys/utsname.h>
+
+#include <QFileInfo>
+#endif
+
+void initUtils()
+{
+ qRegisterMetaType<QList<int>>("QList<int>");
+ qRegisterMetaType<DbObjectType>("DbObjectType");
+}
+
+bool isXDigit(const QChar& c)
+{
+ return c.isDigit() || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+
+QChar charAt(const QString& str, int pos)
+{
+ if (pos < 0 || pos >= str.size())
+ return QChar(0);
+
+ return str[pos];
+}
+
+int rand(int min, int max)
+{
+ return qrand() % (max-min) + min;
+}
+
+QString randStr(int length, bool numChars, bool whiteSpaces)
+{
+ static const char* alphaNumChars = " abcdefghijklmnopqrstuvwxyz1234567890";
+ int start = 1;
+ int range = start + (numChars ? 36 : 26);
+
+ if (whiteSpaces)
+ {
+ start--;
+ range++;
+ }
+
+ QString output = "";
+ for (int i = 0; i < length; i++)
+ output += alphaNumChars[rand(start, range)];
+
+ return output;
+}
+
+QString randStr(int length, const QString& charCollection)
+{
+ int range = charCollection.size();
+ QString output = "";
+ for (int i = 0; i < length; i++)
+ output += charCollection[rand(0, range)];
+
+ return output;
+}
+
+QString randBinStr(int length)
+{
+ char* output = new char[length];
+ for (int i =0; i < length; i++)
+ output[i] = rand(0, 256);
+
+ return QString::fromLatin1(output, length);
+}
+
+QString randStrNotIn(int length, const QSet<QString> set, bool numChars, bool whiteSpaces)
+{
+ if (length == 0)
+ return "";
+
+ QString outStr;
+ do
+ {
+ outStr = randStr(length, numChars, whiteSpaces);
+ }
+ while (set.contains(outStr));
+
+ return outStr;
+}
+
+
+Range::Range() :
+ from(0), to(0)
+{
+}
+
+Range::Range(qint64 from, qint64 to)
+ :from(from), to(to)
+{
+ fromValid = true;
+ toValid = true;
+}
+
+void Range::setFrom(qint64 from)
+{
+ this->from = from;
+ fromValid = true;
+}
+
+void Range::setTo(qint64 to)
+{
+ this->to = to;
+ toValid = true;
+}
+
+qint64 Range::getFrom() const
+{
+ return from;
+}
+
+qint64 Range::getTo() const
+{
+ return to;
+}
+
+bool Range::isValid() const
+{
+ return fromValid && toValid && from <= to;
+}
+
+bool Range::contains(qint64 position) const
+{
+ return position >= from && position <= to;
+}
+
+bool Range::overlaps(const Range& other) const
+{
+ return overlaps(other.from, other.to);
+}
+
+bool Range::overlaps(qint64 from, qint64 to) const
+{
+ return (this->from >= from && this->from <= to) || (this->to >= from && this->to <= to);
+}
+
+Range Range::common(const Range& other) const
+{
+ return common(other.from, other.to);
+}
+
+Range Range::common(qint64 from, qint64 to) const
+{
+ if (!isValid() || from > to)
+ return Range();
+
+ if (this->from >= from)
+ {
+ if (this->from > to)
+ return Range();
+
+ if (this->to < to)
+ return Range(this->from, this->to);
+ else
+ return Range(this->from, to);
+ }
+ else
+ {
+ if (from > this->to)
+ return Range();
+
+ if (to < this->to)
+ return Range(from, to);
+ else
+ return Range(from, this->to);
+ }
+}
+
+qint64 Range::length() const
+{
+ return to - from + 1;
+}
+
+QString generateUniqueName(const QString &baseName, const QStringList &existingNames)
+{
+ QString name = baseName;
+ int i = 0;
+ while (existingNames.contains(name))
+ name = baseName+QString::number(i++);
+
+ return name;
+}
+
+bool isNumeric(const QVariant& value)
+{
+ bool ok;
+ value.toLongLong(&ok);
+ if (ok)
+ return true;
+
+ value.toDouble(&ok);
+ return ok;
+}
+
+QString rStrip(const QString& str)
+{
+ if (str.isNull())
+ return str;
+
+ for (int n = str.size() - 1; n >= 0; n--)
+ {
+ if (!str.at(n).isSpace())
+ return str.left(n + 1);
+ }
+ return "";
+}
+
+QStringList tokenizeArgs(const QString& str)
+{
+ QStringList results;
+ QString token;
+ bool quote = false;
+ bool escape = false;
+ QChar c;
+ for (int i = 0; i < str.length(); i++)
+ {
+ c = str[i];
+ if (escape)
+ {
+ token += c;
+ }
+ else if (c == '\\')
+ {
+ escape = true;
+ }
+ else if (quote)
+ {
+ if (c == '"')
+ {
+ results << token;
+ token.clear();
+ quote = false;
+ }
+ else
+ {
+ token += c;
+ }
+ }
+ else if (c == '"')
+ {
+ if (token.length() > 0)
+ token += c;
+ else
+ quote = true;
+ }
+ else if (c.isSpace())
+ {
+ if (token.length() > 0)
+ {
+ results << token;
+ token.clear();
+ }
+ }
+ else
+ {
+ token += c;
+ }
+ }
+
+ if (token.length() > 0)
+ results << token;
+
+ return results;
+}
+
+QStringList prefixEach(const QString& prefix, const QStringList& list)
+{
+ QStringList result;
+ foreach (const QString& item, list)
+ result << (prefix + item);
+
+ return result;
+}
+
+QByteArray hashToBytes(const QHash<QString,QVariant>& hash)
+{
+ QByteArray bytes;
+ QDataStream stream(&bytes, QIODevice::WriteOnly);
+ stream << QVariant(hash);
+ return bytes;
+}
+
+QHash<QString,QVariant> bytesToHash(const QByteArray& bytes)
+{
+ if (bytes.isNull())
+ return QHash<QString,QVariant>();
+
+ QVariant deserializedValue;
+ QDataStream stream(bytes);
+ stream >> deserializedValue;
+ return deserializedValue.toHash();
+}
+
+int indexOf(const QStringList& list, const QString& value, Qt::CaseSensitivity cs)
+{
+ return indexOf(list, value, 0, cs);
+}
+
+int indexOf(const QStringList& list, const QString& value, int from, Qt::CaseSensitivity cs)
+{
+ if (cs == Qt::CaseSensitive)
+ return list.indexOf(value, from);
+
+ int cnt = list.size();
+ for (int i = from; i < cnt; i++)
+ if (list[i].compare(value, cs) == 0)
+ return i;
+
+ return -1;
+}
+
+QString pad(const QString& str, int length, const QChar& fillChar)
+{
+ if (str.length() >= abs(length))
+ return str;
+
+ QString result = str;
+ QString fill = QString(fillChar).repeated(abs(length) - str.length());
+ if (length >= 0)
+ return result.append(fill);
+ else
+ return result.prepend(fill);
+}
+
+QString center(const QString& str, int length, const QChar& fillChar)
+{
+ if (str.length() >= length)
+ return str;
+
+ QString result = str;
+ QString fillLeft = QString(fillChar).repeated((length - str.length()) / 2);
+ QString fillRight = fillLeft;
+ if ((fillLeft.length() + fillRight.length() + str.length()) < length)
+ fillLeft += fillChar;
+
+ return result.prepend(fillLeft).append(fillRight);
+}
+
+QString longest(const QStringList& strList)
+{
+ int max = 0;
+ QString result;
+ foreach (const QString str, strList)
+ {
+ if (str.size() > max)
+ {
+ result = str;
+ max = str.size();
+ }
+ }
+ return result;
+}
+
+QString shortest(const QStringList& strList)
+{
+ int max = INT_MAX;
+ QString result;
+ foreach (const QString str, strList)
+ {
+ if (str.size() < max)
+ {
+ result = str;
+ max = str.size();
+ }
+ }
+ return result;
+}
+
+QString longestCommonPart(const QStringList& strList)
+{
+ if (strList.size() == 0)
+ return QString::null;
+
+ QString common;
+ QString first = strList.first();
+ for (int i = 0; i < first.length(); i++)
+ {
+ common += first[i];
+ foreach (const QString& str, strList)
+ {
+ if (!str.startsWith(common))
+ return common.left(i);
+ }
+ }
+ return common;
+}
+
+QStringList applyMargin(const QString& str, int margin)
+{
+ QStringList lines;
+ QString line;
+ foreach (QString word, str.split(" "))
+ {
+ if (((line + word).length() + 1) > margin)
+ {
+ if (!line.isEmpty())
+ {
+ lines << line;
+ line.clear();
+ }
+
+ while ((line + word).length() > margin)
+ {
+ line += word.left(margin);
+ lines << line;
+ word = word.mid(margin);
+ }
+ }
+
+ if (!line.isEmpty())
+ line += " ";
+
+ line += word;
+
+ if (line.endsWith("\n"))
+ {
+ lines << line.trimmed();
+ line.clear();
+ }
+ }
+
+ if (!line.isEmpty())
+ lines << line;
+
+ if (lines.size() == 0)
+ lines << QString();
+
+ return lines;
+}
+
+QDateTime toGregorian(double julianDateTime)
+{
+ int Z = (int)julianDateTime;
+ double F = julianDateTime - Z;
+
+ int A;
+ if (Z < 2299161)
+ {
+ A = Z;
+ }
+ else
+ {
+ int alpha = (int)((Z - 1867216.25)/36524.25);
+ A = Z + 1 + alpha - (int)(alpha / 4);
+ }
+
+ int B = A + 1524;
+ int C = (int)((B - 122.1) / 365.25);
+ int D = (int)(365.25 * C);
+ int E = (int)((B-D) / 30.6001);
+ int DD = B - D - (int)(30.6001 * E) + F;
+ int MM = (E <= 13) ? E - 1 : E - 13;
+ int YYYY = (MM <= 2) ? C - 4715 : C - 4716;
+
+ int mmmBase = qRound(F * 86400000.0);
+ int mmm = mmmBase % 1000;
+ int ssBase = mmmBase / 1000;
+ int ss = ssBase % 60;
+ int mmBase = ssBase / 60;
+ int mm = mmBase % 60;
+ int hh = (mmBase / 60) + 12;
+ if (hh >= 24)
+ {
+ hh -= 24;
+ DD++;
+ }
+
+ QDateTime dateTime;
+ dateTime.setDate(QDate(YYYY, MM, DD));
+ dateTime.setTime(QTime(hh, mm, ss, mmm));
+ return dateTime;
+}
+
+double toJulian(const QDateTime& gregDateTime)
+{
+ QDate date = gregDateTime.date();
+ QTime time = gregDateTime.time();
+ return toJulian(date.year(), date.month(), date.day(), time.hour(), time.minute(), time.second(), time.msec());
+}
+
+double toJulian(int year, int month, int day, int hour, int minute, int second, int msec)
+{
+ int a = (14 - month) / 12;
+ int y = year + 4800 + a;
+ int m = month + 12 * a - 3;
+
+ // Julian Day
+ int jnd = day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
+
+ // Julian Day + Julian Time
+ double jndt = jnd + (hour - 12.0) / 24.0 + minute / 1440.0 + second / 86400.0 + msec / 86400000.0;
+
+ return jndt;
+}
+
+QString formatFileSize(quint64 size)
+{
+ quint64 bytes = size;
+ quint64 kb = 0;
+ quint64 mb = 0;
+ quint64 gb = 0;
+
+ QStringList words;
+ if (bytes > (1024*1024*1024))
+ {
+ gb = bytes / (1024*1024*1024);
+ bytes %= (1024*1024*1024);
+ words << QString("%1GB").arg(gb);
+ }
+
+ if (bytes > (1024*1024))
+ {
+ mb = bytes / (1024*1024);
+ bytes %= (1024*1024);
+ words << QString("%1MB").arg(mb);
+ }
+
+ if (bytes > 1024)
+ {
+ kb = bytes / 1024;
+ bytes %= 1024;
+ words << QString("%1KB").arg(kb);
+ }
+
+ if (bytes > 0)
+ words << QString("%1B").arg(bytes);
+
+ return words.join(" ");
+}
+
+QString formatTimePeriod(int msecs)
+{
+ int hours = 0;
+ int minutes = 0;
+ int seconds = 0;
+
+ QStringList words;
+ if (msecs > (1000*60*60))
+ {
+ hours = msecs / (1000*60*60);
+ msecs %= (1000*60*60);
+ words << QString("%1h").arg(hours);
+ }
+
+ if (msecs > (1000*60))
+ {
+ minutes = msecs / (1000*60);
+ msecs %= (1000*60);
+ words << QString("%1m").arg(minutes);
+ }
+
+ if (msecs > (1000))
+ {
+ seconds = msecs / 1000;
+ msecs %= 1000;
+ words << QString("%1s").arg(seconds);
+ }
+
+ if (msecs > 0)
+ words << QString("%1ms").arg(msecs);
+
+ return words.join(" ");
+}
+
+QStringList common(const QStringList& list1, const QStringList& list2, Qt::CaseSensitivity cs)
+{
+ QStringList newList;
+ for (const QString& str : list1)
+ {
+ if (list2.contains(str, cs))
+ newList << str;
+ }
+ return newList;
+}
+
+QStringList textCodecNames()
+{
+ QList<QByteArray> codecs = QTextCodec::availableCodecs();
+ QStringList names;
+ QSet<QString> nameSet;
+ for (const QByteArray& codec : codecs)
+ nameSet << QString::fromLatin1(codec.constData());
+
+ names = nameSet.toList();
+ qSort(names);
+ return names;
+}
+
+QTextCodec* codecForName(const QString& name)
+{
+ return QTextCodec::codecForName(name.toLatin1());
+}
+
+QTextCodec* defaultCodec()
+{
+ return QTextCodec::codecForLocale();
+}
+
+QString defaultCodecName()
+{
+ return QString::fromLatin1(QTextCodec::codecForLocale()->name());
+}
+
+QStringList splitByLines(const QString& str)
+{
+ return str.split(QRegExp("\r?\n"));
+}
+
+QString joinLines(const QStringList& lines)
+{
+#ifdef Q_OS_WIN
+ static_char* newLine = "\r\n";
+#else
+ static_char* newLine = "\n";
+#endif
+ return lines.join(newLine);
+}
+
+int sum(const QList<int>& integers)
+{
+ int res = 0;
+ for (int i : integers)
+ res += i;
+
+ return res;
+}
+
+QString getOsString()
+{
+#if defined(Q_OS_WIN)
+ QString os = "Windows";
+ switch (QSysInfo::WindowsVersion)
+ {
+ case QSysInfo::WV_XP:
+ os += " XP";
+ break;
+ case QSysInfo::WV_2003:
+ os += " 2003";
+ break;
+ case QSysInfo::WV_VISTA:
+ os += " Vista";
+ break;
+ case QSysInfo::WV_WINDOWS7:
+ os += " 7";
+ break;
+ case QSysInfo::WV_WINDOWS8:
+ os += " 8";
+ break;
+ case QSysInfo::WV_WINDOWS8_1:
+ os += " 8.1";
+ break;
+ case QSysInfo::WV_32s:
+ case QSysInfo::WV_95:
+ case QSysInfo::WV_98:
+ case QSysInfo::WV_Me:
+ case QSysInfo::WV_DOS_based:
+ case QSysInfo::WV_NT:
+ case QSysInfo::WV_2000:
+ case QSysInfo::WV_NT_based:
+ case QSysInfo::WV_CE:
+ case QSysInfo::WV_CENET:
+ case QSysInfo::WV_CE_5:
+ case QSysInfo::WV_CE_6:
+ case QSysInfo::WV_CE_based:
+ break;
+ }
+#elif defined(Q_OS_LINUX)
+ QString os = "Linux";
+ utsname uts;
+ if (uname(&uts) != 0)
+ {
+ qWarning() << "Error while calling uname() for OS version. Error code: " << errno;
+ }
+ else
+ {
+ os += " " + QString::fromLatin1(uts.release);
+ }
+#elif defined(Q_OS_OSX)
+ QString os = "MacOS X";
+ switch (QSysInfo::MacintoshVersion)
+ {
+ case QSysInfo::MV_10_4:
+ os += " 10.4 Tiger";
+ break;
+ case QSysInfo::MV_10_5:
+ os += " 10.5 Leopard";
+ break;
+ case QSysInfo::MV_10_6:
+ os += " 10.6 Snow Leopard";
+ break;
+ case QSysInfo::MV_10_7:
+ os += " 10.7 Lion";
+ break;
+ case QSysInfo::MV_10_8:
+ os += " 10.8 Mountain Lion";
+ break;
+ case QSysInfo::MV_10_9:
+ os += " 10.9 Mavericks";
+ break;
+ case QSysInfo::MV_9:
+ case QSysInfo::MV_10_0:
+ case QSysInfo::MV_10_1:
+ case QSysInfo::MV_10_2:
+ case QSysInfo::MV_10_3:
+ case QSysInfo::MV_IOS:
+ case QSysInfo::MV_IOS_4_3:
+ case QSysInfo::MV_IOS_5_0:
+ case QSysInfo::MV_IOS_5_1:
+ case QSysInfo::MV_IOS_6_0:
+ case QSysInfo::MV_IOS_6_1:
+ case QSysInfo::MV_IOS_7_0:
+ case QSysInfo::MV_IOS_7_1:
+ case QSysInfo::MV_Unknown:
+ break;
+ }
+#elif defined(Q_OS_UNIX)
+ QString os = "Unix";
+#else
+ QString os = "Unknown";
+#endif
+
+ os += ", " + QString::number(QSysInfo::WordSize) + "bit";
+ return os;
+}
+
+DistributionType getDistributionType()
+{
+#if defined(Q_OS_OSX)
+ return DistributionType::OSX_BOUNDLE;
+#elif defined(PORTABLE_CONFIG)
+ return DistributionType::PORTABLE;
+#else
+ return DistributionType::OS_MANAGED;
+#endif
+}
+
+bool validateEmail(const QString& email)
+{
+ static const QRegularExpression re("^[a-zA-Z0-9_\\.-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-\\.]+$");
+ return re.match(email).hasMatch();
+}
+
+bool isHex(const QString& str)
+{
+ bool ok;
+ str.toLongLong(&ok, 16);
+ return ok;
+}
+
+QString formatVersion(int version)
+{
+ int majorVer = version / 10000;
+ int minorVer = version % 10000 / 100;
+ int patchVer = version % 100;
+ return QString::number(majorVer) + "." + QString::number(minorVer) + "." + QString::number(patchVer);
+}
+
+bool copyRecursively(const QString& src, const QString& dst)
+{
+ // Code taken from QtCreator:
+ // https://qt.gitorious.org/qt-creator/qt-creator/source/1a37da73abb60ad06b7e33983ca51b266be5910e:src/app/main.cpp#L13-189
+ QFileInfo srcFileInfo(src);
+ if (srcFileInfo.isDir())
+ {
+ QDir targetDir(dst);
+ targetDir.cdUp();
+ if (!targetDir.mkdir(QFileInfo(dst).fileName()))
+ return false;
+
+ QDir sourceDir(src);
+ QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
+ for (const QString &fileName : fileNames)
+ {
+ const QString newSrcFilePath = src + QLatin1Char('/') + fileName;
+ const QString newTgtFilePath = dst + QLatin1Char('/') + fileName;
+ if (!copyRecursively(newSrcFilePath, newTgtFilePath))
+ return false;
+ }
+ }
+ else if (srcFileInfo.isSymLink())
+ {
+ QString trg = QFile(src).symLinkTarget();
+ QFile::link(trg, dst);
+ }
+ else
+ {
+ if (!QFile::copy(src, dst))
+ return false;
+ }
+ return true;
+}
+
+bool renameBetweenPartitions(const QString& src, const QString& dst)
+{
+ if (QDir(dst).exists())
+ return false;
+
+ int res = copyRecursively(src, dst);
+ if (res)
+ QDir(src).removeRecursively();
+ else
+ QDir(dst).removeRecursively();
+
+ return res;
+}
+
+bool isWritableRecursively(const QString& dir)
+{
+ QFileInfo fi(dir);
+ if (!fi.isWritable())
+ return false;
+
+ if (fi.isDir())
+ {
+ QStringList fileNames = QDir(dir).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
+ for (const QString &fileName : fileNames)
+ {
+ if (!isWritableRecursively(dir + QLatin1Char('/') + fileName))
+ return false;
+ }
+ }
+ return true;
+}
+
+QString encryptRsa(const QString& input, const QString& modulus, const QString& exponent)
+{
+ std::string inputStdStr = input.toStdString();
+ Key key = Key(BigInt(modulus.toStdString()), BigInt(exponent.toStdString()));
+ std::string result = RSA::Encrypt(inputStdStr, key);
+ return QString::fromStdString(result);
+}
+
+QString decryptRsa(const QString& input, const QString& modulus, const QString& exponent)
+{
+ std::string inputStdStr = input.toStdString();
+ Key key = Key(BigInt(modulus.toStdString()), BigInt(exponent.toStdString()));
+ std::string result = RSA::Decrypt(inputStdStr, key);
+ return QString::fromStdString(result);
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils.h b/SQLiteStudio3/coreSQLiteStudio/common/utils.h
new file mode 100644
index 0000000..4ded52b
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/utils.h
@@ -0,0 +1,244 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include "coreSQLiteStudio_global.h"
+#include <QList>
+#include <QMutableListIterator>
+#include <QSet>
+#include <QChar>
+#include <QStringList>
+#include <QFileInfo>
+
+class QTextCodec;
+
+API_EXPORT void initUtils();
+
+class API_EXPORT Range
+{
+ public:
+ Range();
+ Range(qint64 from, qint64 to);
+
+ void setFrom(qint64 from);
+ void setTo(qint64 to);
+ qint64 getFrom() const;
+ qint64 getTo() const;
+ bool isValid() const;
+ bool contains(qint64 position) const;
+ bool overlaps(const Range& other) const;
+ bool overlaps(qint64 from, qint64 to) const;
+ Range common(const Range& other) const;
+ Range common(qint64 from, qint64 to) const;
+ qint64 length() const;
+
+ private:
+ qint64 from;
+ qint64 to;
+ bool fromValid = false;
+ bool toValid = false;
+};
+
+API_EXPORT bool isXDigit(const QChar& c);
+
+/**
+ * @brief Get character from string.
+ * @param str String to get char from.
+ * @param pos Character position.
+ * @return Requested character or null character.
+ *
+ * This is safe getter for a character of the string,
+ * thish returns null if pos index is out of range.
+ */
+QChar API_EXPORT charAt(const QString& str, int pos);
+
+API_EXPORT int rand(int min = 0, int max = RAND_MAX);
+API_EXPORT QString randStr(int length, bool numChars = true, bool whiteSpaces = false);
+API_EXPORT QString randStr(int length, const QString& charCollection);
+API_EXPORT QString randBinStr(int length);
+API_EXPORT QString randStrNotIn(int length, const QSet<QString> set, bool numChars = true, bool whiteSpaces = false);
+API_EXPORT QString generateUniqueName(const QString& prefix, const QStringList& existingNames);
+API_EXPORT bool isNumeric(const QVariant& value);
+API_EXPORT QString rStrip(const QString& str);
+API_EXPORT QStringList tokenizeArgs(const QString& str);
+API_EXPORT QStringList prefixEach(const QString& prefix, const QStringList& list);
+API_EXPORT QByteArray hashToBytes(const QHash<QString,QVariant>& hash);
+API_EXPORT QHash<QString,QVariant> bytesToHash(const QByteArray& bytes);
+/**
+ * @brief indexOf Extension to QStringList::indexOf().
+ *
+ * This method does pretty much the same as QStringList::indexOf(), except it supports
+ * case sensitivity flag, unlike the original method.
+ */
+API_EXPORT int indexOf(const QStringList& list, const QString& value, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive);
+API_EXPORT int indexOf(const QStringList& list, const QString& value, Qt::CaseSensitivity cs = Qt::CaseSensitive);
+
+/**
+ * @brief Returns only those elements from the list, which passed the filter.
+ * @tparam T type for which the filter will be applied for. It should match the type in the list and in the function argument.
+ * @param list List to filter elements from.
+ * @param filterFunction Function that accepts elements from the list and returns true for elements that should be returned by the filter.
+ * @return List of elements that passed custom function validation.
+ */
+template <class T>
+QList<T> filter(const QList<T>& list, std::function<bool(const T& value)> filterFunction)
+{
+ QList<T> results;
+ for (const T& value : list)
+ {
+ if (filterFunction(value))
+ results << value;
+ }
+ return results;
+}
+
+template <class T>
+bool contains(const QList<T>& list, std::function<bool(const T& value)> testFunction)
+{
+ for (const T& value : list)
+ {
+ if (testFunction(value))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief Appends or prepends characters to the string to make it of specified length.
+ * @param str Input string to work with.
+ * @param length Desired length of output string.
+ * @param fillChar Character to use to fill missing part of string.
+ * @param String which is at least \p length characters long, using \p str as an initial value.
+ *
+ * It appends or prepends as many \p fillChar characters to the \p str, so the \p str becomes \p length characters long.
+ * In case the \p str is already \p length characters long, or even longer, then the original string is returned.
+ *
+ * If \p length is positive value, characters are appended to string. It it's negative, then values are prepended to the string,
+ * using an absolute value of the \p length for calculating output length.
+ */
+API_EXPORT QString pad(const QString& str, int length, const QChar& fillChar);
+
+API_EXPORT QString center(const QString& str, int length, const QChar& fillChar);
+
+/**
+ * @brief Picks the longest string from the list.
+ * @param strList List to pick from.
+ * @return Longest value from the list, or empty string if the \p strList was empty as well.
+ *
+ * If there are many values with the same, longest length, then the first one is picked.
+ */
+API_EXPORT QString longest(const QStringList& strList);
+
+/**
+ * @brief Picks the shortest string from the list.
+ * @param strList List to pick from.
+ * @return Shortest value from the list, or empty string if the \p strList was empty as well.
+ *
+ * If there are many values with the same, shortest length, then the first one is picked.
+ */
+API_EXPORT QString shortest(const QStringList& strList);
+
+/**
+ * @brief Finds the longest common part of all strings.
+ * @param strList List to compare strings from.
+ * @return Longest common string (looking from the begining of each string) from the list.
+ */
+API_EXPORT QString longestCommonPart(const QStringList& strList);
+
+/**
+ * @brief Applies margin of given number of characters to the string, splitting it into lines.
+ * @param str String to apply the margin to.
+ * @param margin Number of characters allows in single line.
+ * @return List of lines produced by applying the margin.
+ *
+ * If \p str is longer than \p margin number of characters, this method splits \p str into several lines
+ * in order to respect the \p margin. White spaces are taken as points of splitting, but if there is
+ * a single word longer than \p margin, then this word gets splitted.
+ */
+API_EXPORT QStringList applyMargin(const QString& str, int margin);
+
+/**
+ * @brief toGregorian Converts Julian Date to Gregorian Date.
+ * @param julianDateTime Floating point representing Julian Day and Julian time.
+ * @return Gregorian date.
+ *
+ * Converts Julian Calendar date into Gregorian Calendar date.
+ * See Wikipedia for details.
+ */
+API_EXPORT QDateTime toGregorian(double julianDateTime);
+
+/**
+ * @brief toJulian Converts Gregorian Date to Julian Date.
+ * @param gregDateTime Gregorian calendar date and time.
+ * @return Julian calendar date and time.
+ *
+ * Converts the usually used Gregorian date and time into Julian Date format,
+ * which is floating point, where integral part is the Julian Day and fraction part is time of the day.
+ */
+API_EXPORT double toJulian(const QDateTime& gregDateTime);
+
+/**
+ * @brief toJulian Converts Gregorian Date to Julian Date.
+ * @overload
+ */
+API_EXPORT double toJulian(int year, int month, int day, int hour, int minute, int second, int msec);
+
+API_EXPORT QString formatFileSize(quint64 size);
+
+API_EXPORT QString formatTimePeriod(int msecs);
+
+API_EXPORT QStringList common(const QStringList& list1, const QStringList& list2, Qt::CaseSensitivity cs = Qt::CaseSensitive);
+
+API_EXPORT QStringList textCodecNames();
+API_EXPORT QString defaultCodecName();
+API_EXPORT QTextCodec* defaultCodec();
+API_EXPORT QTextCodec* codecForName(const QString& name);
+API_EXPORT QStringList splitByLines(const QString& str);
+API_EXPORT QString joinLines(const QStringList& lines);
+API_EXPORT int sum(const QList<int>& integers);
+API_EXPORT QString getOsString();
+API_EXPORT bool validateEmail(const QString& email);
+API_EXPORT bool isHex(const QString& str);
+API_EXPORT QString formatVersion(int version);
+API_EXPORT bool copyRecursively(const QString& src, const QString& dst);
+API_EXPORT bool renameBetweenPartitions(const QString& src, const QString& dst);
+API_EXPORT bool isWritableRecursively(const QString& dir);
+API_EXPORT QString encryptRsa(const QString& input, const QString& modulus, const QString& exponent);
+API_EXPORT QString decryptRsa(const QString& input, const QString& modulus, const QString& exponent);
+
+enum class DistributionType
+{
+ PORTABLE,
+ OSX_BOUNDLE,
+ OS_MANAGED
+};
+
+API_EXPORT DistributionType getDistributionType();
+
+template <class T>
+QList<T> reverse(const QList<T>& list)
+{
+ QList<T> result;
+ for (const T& el : list)
+ result.prepend(el);
+
+ return result;
+}
+
+template <class T>
+void removeDuplicates(QList<T>& list)
+{
+ QSet<T> set;
+ QMutableListIterator<T> i(list);
+ while (i.hasNext())
+ {
+ i.next();
+ if (set.contains(i.value()))
+ i.remove();
+ else
+ set << i.value();
+ }
+}
+
+Q_DECLARE_METATYPE(QList<int>)
+
+#endif // UTILS_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp
new file mode 100644
index 0000000..fcf49af
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp
@@ -0,0 +1,552 @@
+#include "common/utils_sql.h"
+#include "common/utils.h"
+#include "db/sqlquery.h"
+#include "parser/token.h"
+#include "parser/lexer.h"
+#include "parser/keywords.h"
+#include <QHash>
+#include <QPair>
+#include <QString>
+#include <QDebug>
+#include <QMetaType>
+
+QString invalidIdCharacters = "[]()$\"'@*.,+-=/%&|:; \t\n<>";
+QHash<NameWrapper,QPair<QChar,QChar> > wrapperChars;
+QList<NameWrapper> sqlite3Wrappers;
+QList<NameWrapper> sqlite2Wrappers;
+
+void initUtilsSql()
+{
+ wrapperChars[NameWrapper::BRACKET] = QPair<QChar,QChar>('[', ']');
+ wrapperChars[NameWrapper::QUOTE] = QPair<QChar,QChar>('\'', '\'');
+ wrapperChars[NameWrapper::BACK_QUOTE] = QPair<QChar,QChar>('`', '`');
+ wrapperChars[NameWrapper::DOUBLE_QUOTE] = QPair<QChar,QChar>('"', '"');
+
+ sqlite3Wrappers << NameWrapper::DOUBLE_QUOTE
+ << NameWrapper::BRACKET
+ << NameWrapper::QUOTE
+ << NameWrapper::BACK_QUOTE;
+ sqlite2Wrappers << NameWrapper::DOUBLE_QUOTE
+ << NameWrapper::BRACKET
+ << NameWrapper::QUOTE;
+
+ qRegisterMetaType<SqlQueryPtr>("SqlQueryPtr");
+}
+
+bool doesObjectNeedWrapping(const QString& str, Dialect dialect)
+{
+ if (str.isEmpty())
+ return true;
+
+ if (isObjWrapped(str, dialect))
+ return false;
+
+ if (isKeyword(str, dialect))
+ return true;
+
+ for (int i = 0; i < str.size(); i++)
+ if (doesObjectNeedWrapping(str[i]))
+ return true;
+
+ if (str[0].isDigit())
+ return true;
+
+ return false;
+}
+
+bool doesObjectNeedWrapping(const QChar& c)
+{
+ return invalidIdCharacters.indexOf(c) >= 0;
+}
+
+bool isObjectWrapped(const QChar& c, Dialect dialect)
+{
+ return !doesObjectNeedWrapping(c, dialect);
+}
+
+bool isObjectWrapped(const QChar& c)
+{
+ return !doesObjectNeedWrapping(c);
+}
+
+QString wrapObjIfNeeded(const QString& obj, Dialect dialect, NameWrapper favWrapper)
+{
+ if (doesObjectNeedWrapping(obj, dialect))
+ return wrapObjName(obj, dialect, favWrapper);
+
+ return obj;
+}
+
+QString wrapObjName(const QString& obj, Dialect dialect, NameWrapper favWrapper)
+{
+ QString result = obj;
+ if (result.isNull())
+ result = "";
+
+ QPair<QChar,QChar> wrapChars = getQuoteCharacter(result, dialect, favWrapper);
+
+ if (wrapChars.first.isNull() || wrapChars.second.isNull())
+ {
+ qDebug() << "No quote character possible for object name: " << result;
+ return result;
+ }
+ result.prepend(wrapChars.first);
+ result.append(wrapChars.second);
+ return result;
+}
+
+QString wrapObjName(const QString& obj, NameWrapper wrapper)
+{
+ QString result = obj;
+ if (wrapper == NameWrapper::null)
+ return result;
+
+ result.prepend(wrapperChars[wrapper].first);
+ result.append(wrapperChars[wrapper].second);
+ return result;
+}
+
+QPair<QChar,QChar> getQuoteCharacter(QString& obj, Dialect dialect, NameWrapper favWrapper)
+{
+ QList<NameWrapper> wrappers = (dialect == Dialect::Sqlite3) ? sqlite3Wrappers : sqlite2Wrappers;
+
+ // Move favourite wrapper to front of list
+ if (wrappers.contains(favWrapper))
+ {
+ wrappers.removeOne(favWrapper);
+ wrappers.insert(0, favWrapper);
+ }
+
+ QPair<QChar,QChar> wrapChars;
+ foreach (NameWrapper wrapper, wrappers)
+ {
+ wrapChars = wrapperChars[wrapper];
+ if (obj.indexOf(wrapChars.first) > -1)
+ continue;
+
+ if (obj.indexOf(wrapChars.second) > -1)
+ continue;
+
+ return wrapChars;
+ }
+
+ return QPair<QChar,QChar>();
+}
+
+QList<QString> wrapObjNames(const QList<QString>& objList, Dialect dialect, NameWrapper favWrapper)
+{
+ QList<QString> results;
+ for (int i = 0; i < objList.size(); i++)
+ results << wrapObjName(objList[i], dialect, favWrapper);
+
+ return results;
+}
+
+QList<QString> wrapObjNamesIfNeeded(const QList<QString>& objList, Dialect dialect, NameWrapper favWrapper)
+{
+ QList<QString> results;
+ for (int i = 0; i < objList.size(); i++)
+ results << wrapObjIfNeeded(objList[i], dialect, favWrapper);
+
+ return results;
+}
+
+QList<NameWrapper> getAllNameWrappers(Dialect dialect)
+{
+ if (dialect == Dialect::Sqlite3)
+ return {NameWrapper::DOUBLE_QUOTE, NameWrapper::BRACKET, NameWrapper::BACK_QUOTE, NameWrapper::QUOTE};
+ else
+ return {NameWrapper::DOUBLE_QUOTE, NameWrapper::BRACKET, NameWrapper::QUOTE};
+}
+
+QString wrapString(const QString& str)
+{
+ QString result = str;
+ result.prepend("'");
+ result.append("'");
+ return result;
+}
+
+bool doesStringNeedWrapping(const QString& str)
+{
+ return str[0] == '\'' && str[str.length()-1] == '\'';
+}
+
+bool isStringWrapped(const QString& str)
+{
+ return !doesStringNeedWrapping(str);
+}
+
+QString wrapStringIfNeeded(const QString& str)
+{
+ if (isStringWrapped(str))
+ return wrapString(str);
+
+ return str;
+}
+
+QString escapeString(QString& str)
+{
+ return str.replace('\'', "''");
+}
+
+QString escapeString(const QString& str)
+{
+ QString newStr = str;
+ return newStr.replace('\'', "''");
+}
+
+QString stripString(QString& str)
+{
+ if (str.length() <= 1)
+ return str;
+
+ if (str[0] == '\'' && str[str.length()-1] == '\'')
+ return str.mid(1, str.length()-2);
+
+ return str;
+}
+
+QString stripString(const QString& str)
+{
+ QString newStr = str;
+ return stripString(newStr);
+}
+
+QString stripEndingSemicolon(const QString& str)
+{
+ QString newStr = rStrip(str);
+ if (newStr.size() == 0)
+ return str;
+
+ if (newStr[newStr.size()-1] == ';')
+ {
+ newStr.chop(1);
+ return newStr;
+ }
+ else
+ return str;
+}
+
+QString stripObjName(const QString &str, Dialect dialect)
+{
+ QString newStr = str;
+ return stripObjName(newStr, dialect);
+}
+
+QString stripObjName(QString &str, Dialect dialect)
+{
+ if (str.isNull())
+ return str;
+
+ if (str.length() <= 1)
+ return str;
+
+ if (!isObjWrapped(str, dialect))
+ return str;
+
+ return str.mid(1, str.length()-2);
+}
+
+bool isObjWrapped(const QString& str, Dialect dialect)
+{
+ return getObjWrapper(str, dialect) != NameWrapper::null;
+}
+
+NameWrapper getObjWrapper(const QString& str, Dialect dialect)
+{
+ if (str.isEmpty())
+ return NameWrapper::null;
+
+ QList<NameWrapper> wrappers;
+
+ if (dialect == Dialect::Sqlite2)
+ wrappers = sqlite2Wrappers;
+ else
+ wrappers = sqlite3Wrappers;
+
+ foreach (NameWrapper wrapper, wrappers)
+ {
+ QPair<QChar,QChar> chars = wrapperChars[wrapper];
+ if (str[0] == chars.first && str[str.length()-1] == chars.second)
+ return wrapper;
+ }
+ return NameWrapper::null;
+}
+
+bool isWrapperChar(const QChar& c, Dialect dialect)
+{
+ QList<NameWrapper> wrappers;
+ if (dialect == Dialect::Sqlite2)
+ wrappers = sqlite2Wrappers;
+ else
+ wrappers = sqlite3Wrappers;
+
+ foreach (NameWrapper wrapper, wrappers)
+ {
+ QPair<QChar,QChar> chars = wrapperChars[wrapper];
+ if (c == chars.first || c == chars.second)
+ return true;
+ }
+ return false;
+}
+
+int qHash(NameWrapper wrapper)
+{
+ return (uint)wrapper;
+}
+
+QString getPrefixDb(const QString& origDbName, Dialect dialect)
+{
+ if (origDbName.isEmpty())
+ return "main";
+ else
+ return wrapObjIfNeeded(origDbName, dialect);
+}
+
+bool isSystemTable(const QString &name)
+{
+ return name.startsWith("sqlite_");
+}
+
+bool isSystemIndex(const QString &name, Dialect dialect)
+{
+ switch (dialect)
+ {
+ case Dialect::Sqlite3:
+ return name.startsWith("sqlite_autoindex_");
+ case Dialect::Sqlite2:
+ {
+ QRegExp re("*(*autoindex*)*");
+ re.setPatternSyntax(QRegExp::Wildcard);
+ return re.exactMatch(name);
+ }
+ }
+ return false;
+}
+
+
+TokenPtr stripObjName(TokenPtr token, Dialect dialect)
+{
+ if (!token)
+ return token;
+
+ token->value = stripObjName(token->value, dialect);
+ return token;
+}
+
+QString removeComments(const QString& value)
+{
+ Lexer lexer(Dialect::Sqlite3);
+ TokenList tokens = lexer.tokenize(value);
+ while (tokens.remove(Token::COMMENT))
+ continue;
+
+ return tokens.detokenize();
+}
+
+QList<TokenList> splitQueries(const TokenList& tokenizedQuery, bool* complete)
+{
+ QList<TokenList> queries;
+ TokenList currentQueryTokens;
+ QString value;
+ int createTriggerMeter = 0;
+ bool insideTrigger = false;
+ bool completeQuery = false;
+ foreach (const TokenPtr& token, tokenizedQuery)
+ {
+ value = token->value.toUpper();
+ if (!token->isWhitespace())
+ completeQuery = false;
+
+ if (insideTrigger)
+ {
+ if (token->type == Token::KEYWORD && value == "END")
+ {
+ insideTrigger = false;
+ completeQuery = true;
+ }
+
+ currentQueryTokens << token;
+ continue;
+ }
+
+ if (token->type == Token::KEYWORD)
+ {
+ if (value == "CREATE" || value == "TRIGGER" || value == "BEGIN")
+ createTriggerMeter++;
+
+ if (createTriggerMeter == 3)
+ insideTrigger = true;
+
+ currentQueryTokens << token;
+ }
+ else if (token->type == Token::OPERATOR && value == ";")
+ {
+ createTriggerMeter = 0;
+ currentQueryTokens << token;
+ queries << currentQueryTokens;
+ currentQueryTokens.clear();
+ completeQuery = true;
+ }
+ else
+ {
+ currentQueryTokens << token;
+ }
+ }
+
+ if (currentQueryTokens.size() > 0)
+ queries << currentQueryTokens;
+
+ if (complete)
+ *complete = completeQuery;
+
+ return queries;
+}
+
+QStringList splitQueries(const QString& sql, Dialect dialect, bool keepEmptyQueries, bool* complete)
+{
+ TokenList tokens = Lexer::tokenize(sql, dialect);
+ QList<TokenList> tokenizedQueries = splitQueries(tokens, complete);
+
+ QString query;
+ QStringList queries;
+ foreach (const TokenList& queryTokens, tokenizedQueries)
+ {
+ query = queryTokens.detokenize();
+ if (keepEmptyQueries || !query.trimmed().isEmpty())
+ queries << query;
+ }
+
+ return queries;
+}
+
+QString getQueryWithPosition(const QStringList& queries, int position, int* startPos)
+{
+ int currentPos = 0;
+ int length = 0;
+
+ if (startPos)
+ *startPos = 0;
+
+ foreach (const QString& query, queries)
+ {
+ length = query.length();
+ if (position >= currentPos && position < currentPos+length)
+ return query;
+
+ currentPos += length;
+
+ if (startPos)
+ *startPos += length;
+ }
+
+ // If we passed all queries and it happens that the cursor is just after last query - this is the query we want.
+ if (position == currentPos && queries.size() > 0)
+ {
+ if (startPos)
+ *startPos -= length;
+
+ return queries.last();
+ }
+
+ if (startPos)
+ *startPos = -1;
+
+ return QString::null;
+}
+
+QString getQueryWithPosition(const QString& queries, int position, Dialect dialect, int* startPos)
+{
+ QStringList queryList = splitQueries(queries, dialect);
+ return getQueryWithPosition(queryList, position, startPos);
+}
+
+QString trimBindParamPrefix(const QString& param)
+{
+ if (param == "?")
+ return param;
+
+ if (param.startsWith("$") || param.startsWith("@") || param.startsWith(":") || param.startsWith("?"))
+ return param.mid(1);
+
+ return param;
+}
+
+QList<QueryWithParamNames> getQueriesWithParamNames(const QString& query, Dialect dialect)
+{
+ QList<QueryWithParamNames> results;
+
+ TokenList allTokens = Lexer::tokenize(query, dialect);
+ QList<TokenList> queries = splitQueries(allTokens);
+
+ QString queryStr;
+ QStringList paramNames;
+ foreach (const TokenList& tokens, queries)
+ {
+ paramNames.clear();
+ foreach (const TokenPtr& token, tokens.filter(Token::BIND_PARAM))
+ paramNames << token->value;
+
+ queryStr = tokens.detokenize().trimmed();
+ if (!queryStr.isEmpty())
+ results << QueryWithParamNames(queryStr, paramNames);
+ }
+ return results;
+}
+
+QList<QueryWithParamCount> getQueriesWithParamCount(const QString& query, Dialect dialect)
+{
+ QList<QueryWithParamCount> results;
+
+ TokenList allTokens = Lexer::tokenize(query, dialect);
+ QList<TokenList> queries = splitQueries(allTokens);
+
+ QString queryStr;
+ foreach (const TokenList& tokens, queries)
+ {
+ queryStr = tokens.detokenize().trimmed();
+ if (!queryStr.isEmpty())
+ results << QueryWithParamCount(queryStr, tokens.filter(Token::BIND_PARAM).size());
+ }
+
+ return results;
+}
+
+QueryWithParamNames getQueryWithParamNames(const QString& query, Dialect dialect)
+{
+ TokenList allTokens = Lexer::tokenize(query, dialect);
+
+ QStringList paramNames;
+ foreach (const TokenPtr& token, allTokens.filter(Token::BIND_PARAM))
+ paramNames << token->value;
+
+ return QueryWithParamNames(query, paramNames);
+}
+
+QueryWithParamCount getQueryWithParamCount(const QString& query, Dialect dialect)
+{
+ TokenList allTokens = Lexer::tokenize(query, dialect);
+ return QueryWithParamCount(query, allTokens.filter(Token::BIND_PARAM).size());
+}
+
+QString commentAllSqlLines(const QString& sql)
+{
+ QStringList lines = splitByLines(sql);
+ QMutableStringListIterator it(lines);
+ while (it.hasNext())
+ it.next().prepend("-- ");
+
+ return joinLines(lines);
+}
+
+QString getBindTokenName(const TokenPtr& token)
+{
+ if (token->type != Token::BIND_PARAM)
+ return QString();
+
+ if (token->value == "?")
+ return token->value;
+
+ return token->value.mid(1);
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h
new file mode 100644
index 0000000..8e1f3ff
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h
@@ -0,0 +1,71 @@
+#ifndef UTILS_SQL_H
+#define UTILS_SQL_H
+
+#include "dialect.h"
+#include "parser/token.h"
+#include "coreSQLiteStudio_global.h"
+#include <QString>
+#include <QChar>
+#include <QPair>
+
+// TODO: unit tests for most of methods from this module
+
+enum class NameWrapper
+{
+ DOUBLE_QUOTE,
+ QUOTE,
+ BACK_QUOTE,
+ BRACKET,
+ null
+};
+
+typedef QPair<QString,QStringList> QueryWithParamNames;
+typedef QPair<QString,int> QueryWithParamCount;
+
+API_EXPORT void initUtilsSql();
+API_EXPORT bool doesObjectNeedWrapping(const QString& str, Dialect dialect);
+API_EXPORT bool doesObjectNeedWrapping(const QChar& c);
+API_EXPORT bool isObjectWrapped(const QChar& c, Dialect dialect);
+API_EXPORT bool isObjectWrapped(const QChar& c);
+API_EXPORT bool doesStringNeedWrapping(const QString& str);
+API_EXPORT bool isStringWrapped(const QString& str);
+API_EXPORT QString wrapObjIfNeeded(const QString& obj, Dialect dialect, NameWrapper favWrapper = NameWrapper::null);
+API_EXPORT QString wrapObjName(const QString& obj, Dialect dialect, NameWrapper favWrapper = NameWrapper::null);
+API_EXPORT QString wrapObjName(const QString& obj, NameWrapper wrapper);
+API_EXPORT TokenPtr stripObjName(TokenPtr token, Dialect dialect);
+API_EXPORT QString stripObjName(const QString &str, Dialect dialect);
+API_EXPORT QString stripObjName(QString& str, Dialect dialect);
+API_EXPORT bool isObjWrapped(const QString& str, Dialect dialect);
+API_EXPORT NameWrapper getObjWrapper(const QString& str, Dialect dialect);
+API_EXPORT bool isWrapperChar(const QChar& c, Dialect dialect);
+API_EXPORT QString wrapString(const QString& str);
+API_EXPORT QString wrapStringIfNeeded(const QString& str);
+API_EXPORT QString escapeString(QString &str);
+API_EXPORT QString escapeString(const QString& str);
+API_EXPORT QString stripString(QString& str);
+API_EXPORT QString stripString(const QString& str);
+API_EXPORT QString stripEndingSemicolon(const QString& str);
+API_EXPORT QPair<QChar,QChar> getQuoteCharacter(QString& obj, Dialect dialect,
+ NameWrapper favWrapper = NameWrapper::null);
+API_EXPORT QList<QString> wrapObjNames(const QList<QString>& objList, Dialect dialect = Dialect::Sqlite3, NameWrapper favWrapper = NameWrapper::null);
+API_EXPORT QList<QString> wrapObjNamesIfNeeded(const QList<QString>& objList, Dialect dialect, NameWrapper favWrapper = NameWrapper::null);
+API_EXPORT QList<NameWrapper> getAllNameWrappers(Dialect dialect = Dialect::Sqlite3);
+API_EXPORT int qHash(NameWrapper wrapper);
+API_EXPORT QString getPrefixDb(const QString& origDbName, Dialect dialect);
+API_EXPORT bool isSystemTable(const QString& name);
+API_EXPORT bool isSystemIndex(const QString& name, Dialect dialect);
+API_EXPORT QString removeComments(const QString& value);
+API_EXPORT QList<TokenList> splitQueries(const TokenList& tokenizedQueries, bool* complete = nullptr);
+API_EXPORT QStringList splitQueries(const QString& sql, Dialect dialect, bool keepEmptyQueries = true, bool* complete = nullptr);
+API_EXPORT QString getQueryWithPosition(const QStringList& queries, int position, int* startPos = nullptr);
+API_EXPORT QString getQueryWithPosition(const QString& queries, int position, Dialect dialect, int* startPos = nullptr);
+API_EXPORT QList<QueryWithParamNames> getQueriesWithParamNames(const QString& query, Dialect dialect);
+API_EXPORT QList<QueryWithParamCount> getQueriesWithParamCount(const QString& query, Dialect dialect);
+API_EXPORT QueryWithParamNames getQueryWithParamNames(const QString& query, Dialect dialect);
+API_EXPORT QueryWithParamCount getQueryWithParamCount(const QString& query, Dialect dialect);
+API_EXPORT QString trimBindParamPrefix(const QString& param);
+API_EXPORT QString commentAllSqlLines(const QString& sql);
+API_EXPORT QString getBindTokenName(const TokenPtr& token);
+
+
+#endif // UTILS_SQL_H