aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2015-04-19 22:30:21 -0400
committerLibravatarUnit 193 <unit193@ubuntu.com>2015-04-19 22:30:21 -0400
commita308f430f694423064ebc86fd0506c8c6fdb3d93 (patch)
treeceacd24fecf92f40980f8d8f3fd169e317c886af
parenta5b034d4a9c44f9bc1e83b01de82530f8fc63013 (diff)
downloadsqlitestudio-a308f430f694423064ebc86fd0506c8c6fdb3d93.tar.bz2
sqlitestudio-a308f430f694423064ebc86fd0506c8c6fdb3d93.tar.xz
sqlitestudio-a308f430f694423064ebc86fd0506c8c6fdb3d93.tar.zst
Imported Upstream version 3.0.5upstream/3.0.5
-rw-r--r--Plugins/DbSqlite2/dbsqlite2.json2
-rw-r--r--Plugins/SqlEnterpriseFormatter/formatcreatetrigger.cpp60
-rw-r--r--Plugins/SqlEnterpriseFormatter/formatcreatetrigger.h7
-rw-r--r--Plugins/SqlEnterpriseFormatter/formatexpr.cpp1
-rw-r--r--Plugins/SqlEnterpriseFormatter/formatstatement.cpp2
-rw-r--r--Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.json2
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/ChangeLog.txt15
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/TODO.txt9
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.cpp97
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.h44
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/expiringcache.h158
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.cpp130
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.h38
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp55
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/signalwait.cpp17
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/signalwait.h3
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.cpp17
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.h19
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/utils.cpp36
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp69
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h7
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro11
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp87
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/csvserializer.h1
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp17
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/dbversionconverter.cpp6
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/importworker.cpp1
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp4
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h9
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp7
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/populateworker.cpp9
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/populateworker.h1
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp92
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/schemaresolver.h28
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h2
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp148
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h1
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/populatemanager.cpp1
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/populatemanager.h1
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp3
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts10
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/ipvalidator.h3
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp36
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h8
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp20
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h6
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dataview.cpp39
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dataview.h6
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp38
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h3
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp29
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp22
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp13
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h2
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/iconmanager.h2
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/icons.qrc2
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/img/delete.pngbin0 -> 729 bytes
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/img/erase_table_data.pngbin0 -> 885 bytes
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts86
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/uiconfig.h6
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/uidebug.cpp8
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/uidebug.h2
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp8
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp10
-rw-r--r--SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro8
67 files changed, 1359 insertions, 241 deletions
diff --git a/Plugins/DbSqlite2/dbsqlite2.json b/Plugins/DbSqlite2/dbsqlite2.json
index 6761faf..c1e1817 100644
--- a/Plugins/DbSqlite2/dbsqlite2.json
+++ b/Plugins/DbSqlite2/dbsqlite2.json
@@ -2,6 +2,6 @@
"type": "DbPlugin",
"title": "SQLite 2",
"description": "Provides support for SQLite 2.* databases",
- "version": 10002,
+ "version": 10003,
"author": "SalSoft"
}
diff --git a/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.cpp b/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.cpp
index 01351e6..6eb8b21 100644
--- a/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.cpp
+++ b/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.cpp
@@ -9,55 +9,71 @@ FormatCreateTrigger::FormatCreateTrigger(SqliteCreateTrigger* createTrig) :
void FormatCreateTrigger::formatInternal()
{
handleExplainQuery(createTrig);
- withKeyword("CREATE");
+
+ QStringList keywords;
+
+ keywords << "CREATE";
if (createTrig->tempKw)
- withKeyword("TEMP");
+ keywords << "TEMP";
else if (createTrig->temporaryKw)
- withKeyword("TEMPORARY");
+ keywords << "TEMPORARY";
- withKeyword("TRIGGER");
+ keywords << "TRIGGER";
if (createTrig->ifNotExistsKw)
- withKeyword("IF").withKeyword("NOT").withKeyword("EXISTS");
+ keywords << "IF" << "NOT" << "EXISTS";
+
+ QString kwLineUp = keywords.join(" ");
+ markKeywordLineUp(kwLineUp, TRIGGER_MARK);
+
+ for (const QString& kw : keywords)
+ withKeyword(kw);
if (dialect == Dialect::Sqlite3 && !createTrig->database.isNull())
withId(createTrig->database).withIdDot();
- withId(createTrig->trigger);
+ withId(createTrig->trigger).withNewLine();
+
+ FormatStatementEnricher eventStmtEnricher = nullptr;
switch (createTrig->eventTime)
{
case SqliteCreateTrigger::Time::BEFORE:
- withKeyword("BEFORE");
+ withLinedUpKeyword("BEFORE", TRIGGER_MARK);
break;
case SqliteCreateTrigger::Time::AFTER:
- withKeyword("AFTER");
+ withLinedUpKeyword("AFTER", TRIGGER_MARK);
break;
case SqliteCreateTrigger::Time::INSTEAD_OF:
- withKeyword("INSTEAD").withKeyword("OF");
+ withLinedUpKeyword("INSTEAD OF", TRIGGER_MARK);
break;
case SqliteCreateTrigger::Time::null:
+ eventStmtEnricher = [kwLineUp](FormatStatement* stmt)
+ {
+ dynamic_cast<FormatCreateTriggerEvent*>(stmt)->setLineUpKeyword(kwLineUp);
+ };
break;
}
- withStatement(createTrig->event).withKeyword("ON");
+ withStatement(createTrig->event, QString(), eventStmtEnricher).withNewLine();
+ withLinedUpKeyword("ON", TRIGGER_MARK);
if (dialect == Dialect::Sqlite2 && !createTrig->database.isNull())
withId(createTrig->database).withIdDot();
- withId(createTrig->table);
+ withId(createTrig->table).withNewLine();
switch (createTrig->scope)
{
case SqliteCreateTrigger::Scope::FOR_EACH_ROW:
- withKeyword("FOR").withKeyword("EACH").withKeyword("ROW");
+ withLinedUpKeyword("FOR EACH", TRIGGER_MARK).withKeyword("ROW").withNewLine();
break;
case SqliteCreateTrigger::Scope::FOR_EACH_STATEMENT:
- withKeyword("FOR").withKeyword("EACH").withKeyword("STATEMENT");
+ withLinedUpKeyword("FOR EACH", TRIGGER_MARK).withKeyword("STATEMENT").withNewLine();
break;
case SqliteCreateTrigger::Scope::null:
break;
}
if (createTrig->precondition)
- withKeyword("WHEN").withStatement(createTrig->precondition);
+ withLinedUpKeyword("WHEN", TRIGGER_MARK).withStatement(createTrig->precondition);
withNewLine().withKeyword("BEGIN").withNewLine().withIncrIndent().withStatementList(createTrig->queries, QString(), ListSeparator::SEMICOLON).withSemicolon();
withDecrIndent().withKeyword("END").withSemicolon();
@@ -69,21 +85,29 @@ FormatCreateTriggerEvent::FormatCreateTriggerEvent(SqliteCreateTrigger::Event* e
{
}
+void FormatCreateTriggerEvent::setLineUpKeyword(const QString& lineUpKw)
+{
+ this->lineUpKw = lineUpKw;
+}
+
void FormatCreateTriggerEvent::formatInternal()
{
+ if (!lineUpKw.isNull())
+ markKeywordLineUp(lineUpKw, TRIGGER_MARK);
+
switch (ev->type)
{
case SqliteCreateTrigger::Event::INSERT:
- withKeyword("INSERT");
+ withLinedUpKeyword("INSERT", TRIGGER_MARK);
break;
case SqliteCreateTrigger::Event::UPDATE:
- withKeyword("UPDATE");
+ withLinedUpKeyword("UPDATE", TRIGGER_MARK);
break;
case SqliteCreateTrigger::Event::DELETE:
- withKeyword("DELETE");
+ withLinedUpKeyword("DELETE", TRIGGER_MARK);
break;
case SqliteCreateTrigger::Event::UPDATE_OF:
- withKeyword("UPDATE").withKeyword("OF").withIdList(ev->columnNames);
+ withLinedUpKeyword("UPDATE OF", TRIGGER_MARK).withIdList(ev->columnNames);
break;
case SqliteCreateTrigger::Event::null:
break;
diff --git a/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.h b/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.h
index 795c2c7..108a31e 100644
--- a/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.h
+++ b/Plugins/SqlEnterpriseFormatter/formatcreatetrigger.h
@@ -3,6 +3,7 @@
#include "formatstatement.h"
#include "parser/ast/sqlitecreatetrigger.h"
+#include "common/global.h"
class FormatCreateTrigger : public FormatStatement
{
@@ -14,18 +15,24 @@ class FormatCreateTrigger : public FormatStatement
private:
SqliteCreateTrigger* createTrig = nullptr;
+
+ static_char* TRIGGER_MARK = "TRIGGER";
};
class FormatCreateTriggerEvent : public FormatStatement
{
public:
FormatCreateTriggerEvent(SqliteCreateTrigger::Event* ev);
+ void setLineUpKeyword(const QString& lineUpKw);
protected:
void formatInternal();
private:
SqliteCreateTrigger::Event* ev = nullptr;
+ QString lineUpKw;
+
+ static_char* TRIGGER_MARK = "TRIGGER";
};
#endif // FORMATCREATETRIGGER_H
diff --git a/Plugins/SqlEnterpriseFormatter/formatexpr.cpp b/Plugins/SqlEnterpriseFormatter/formatexpr.cpp
index da51ae6..ec5d4d6 100644
--- a/Plugins/SqlEnterpriseFormatter/formatexpr.cpp
+++ b/Plugins/SqlEnterpriseFormatter/formatexpr.cpp
@@ -107,6 +107,7 @@ void FormatExpr::formatInternal()
break;
case SqliteExpr::Mode::NOTNULL:
{
+ withStatement(expr->expr1);
switch (expr->notNull)
{
case SqliteExpr::NotNull::ISNULL:
diff --git a/Plugins/SqlEnterpriseFormatter/formatstatement.cpp b/Plugins/SqlEnterpriseFormatter/formatstatement.cpp
index dfdbb14..e5eaed4 100644
--- a/Plugins/SqlEnterpriseFormatter/formatstatement.cpp
+++ b/Plugins/SqlEnterpriseFormatter/formatstatement.cpp
@@ -459,7 +459,7 @@ void FormatStatement::handleExplainQuery(SqliteQuery* query)
{
withKeyword("EXPLAIN");
if (query->queryPlan)
- withKeyword("QUERY").withKeyword("PLAN");
+ withKeyword("QUERY").withKeyword("PLAN").withNewLine();
}
}
diff --git a/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.json b/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.json
index 85d6414..c0ba2df 100644
--- a/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.json
+++ b/Plugins/SqlEnterpriseFormatter/sqlenterpriseformatter.json
@@ -2,6 +2,6 @@
"type": "CodeFormatterPlugin",
"title": "SQL Enterprise",
"description": "Advanced SQL formatter.",
- "version": 10005,
+ "version": 10006,
"author": "SalSoft"
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/ChangeLog.txt b/SQLiteStudio3/coreSQLiteStudio/ChangeLog.txt
index f795356..384fd8a 100644
--- a/SQLiteStudio3/coreSQLiteStudio/ChangeLog.txt
+++ b/SQLiteStudio3/coreSQLiteStudio/ChangeLog.txt
@@ -1,3 +1,18 @@
+[3.0.5]
+ * [ADDED]: #2831 Ported 'Erase table data' feature (in table's context menu) from version 2.1.5.
+ * [CHANGE]: Data view has now a 'cover' with progress bar when commiting more changes at once. This eliminates weird GUI freeze when commiting lots of new/deleted rows.
+ * [CHANGE]: Populate dialog has now progress bar when working.
+ * [CHANGE]: Enhanced SQL formatter to format CREATE TRIGGER statements in a bit more readable way.
+ * [BUGFIX]: #2838 Fixed outstanding bug causing database file to be deleted if a user tried "Test connection" on the database under Windows.
+ * [BUGFIX]: Fixed crash when closing Populate Dialog while populating was in progress.
+ * [BUGFIX]: Fixed warning messages when there was a problem with restoring windows from last session. A message contains now window name that caused problem.
+ * [BUGFIX]: #2830 Closing database list made impossible.
+ * [BUGFIX]: #2829 Fixed support for DEFERRABLE (and related) keywords in foreign keys.
+ * [BUGFIX]: #2827 Release a file after import.
+ * [BUGFIX]: #2826 Fixed bug in SQL formatter causing 'expr ISNULL' expressions to disappear.
+ * [BUGFIX]: #2834 Removed './lib' form rpath for linux non-portable compilation.
+ * [BUGFIX]: #2836 Removed termcap dependency, as it was deprecated and no longer required.
+
[3.0.4]
* [ADDED]: Import dialog has now "Ignore errors" option, to skip rows that causes problems (constraint violation, wrong column count, etc).
* [CHANGE]: Enhanced Copy&Paste capabilities of data grid, so copying and pasting internally in SQLiteStudio will not be limited by TSV format (NULL values will be distinguished, tab characters will be allowed in values), only pasting from outside will be affected by TSV limitations.
diff --git a/SQLiteStudio3/coreSQLiteStudio/TODO.txt b/SQLiteStudio3/coreSQLiteStudio/TODO.txt
index de84b10..d18c16a 100644
--- a/SQLiteStudio3/coreSQLiteStudio/TODO.txt
+++ b/SQLiteStudio3/coreSQLiteStudio/TODO.txt
@@ -1,3 +1,12 @@
+* Outstanding features for 3.1:
+- BLOB preview engine based on plugins
+- ERD plugin
+- DB compare plugin
+- Foreign Key value suggestions in GridView
+- code templates
+- executing query with bind params
+- migrate updates engine to Qt Install Framework
+
* Next versions:
- object names (columns, tables, etc) in dialogs should be validated against suffix/prefix whitespaces and if they appear, user should be asked for confirmation
- small useful features: generating template queries from context menu for table/view, from data view.
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.cpp b/SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.cpp
new file mode 100644
index 0000000..da725c8
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.cpp
@@ -0,0 +1,97 @@
+#include "blockingsocket.h"
+#include "common/global.h"
+#include "common/threadwitheventloop.h"
+#include "common/private/blockingsocketprivate.h"
+#include <QMutexLocker>
+
+BlockingSocket::BlockingSocket(QObject* parent) :
+ QObject(parent)
+{
+ socketThread = new ThreadWithEventLoop;
+ socket = new BlockingSocketPrivate;
+ socket->moveToThread(socketThread);
+
+ connect(socketThread, &QThread::finished, socket, &QObject::deleteLater);
+ connect(socketThread, &QThread::finished, socketThread, &QObject::deleteLater);
+ connect(this, SIGNAL(callForSend(QByteArray,bool&)), socket, SLOT(handleSendCall(QByteArray,bool&)), Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(callForRead(qint64,int,QByteArray&,bool&)), socket, SLOT(handleReadCall(qint64,int,QByteArray&,bool&)), Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(callForConnect(QString,int,bool&)), socket, SLOT(handleConnectCall(QString,int,bool&)), Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(callForDisconnect()), socket, SLOT(handleDisconnectCall()), Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(callForIsConnected(bool&)), socket, SLOT(handleIsConnectedCall(bool&)), Qt::BlockingQueuedConnection);
+ connect(socket, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
+
+ socketThread->start();
+}
+
+BlockingSocket::~BlockingSocket()
+{
+ QMutexLocker lock(&socketOperationMutex);
+ emit callForDisconnect();
+ socketThread->quit();
+}
+
+QAbstractSocket::SocketError BlockingSocket::getErrorCode()
+{
+ QMutexLocker lock(&socketOperationMutex);
+ return socket->getErrorCode();
+}
+
+QString BlockingSocket::getErrorText()
+{
+ QMutexLocker lock(&socketOperationMutex);
+ return socket->getErrorText();
+}
+
+bool BlockingSocket::connectToHost(const QString& host, int port)
+{
+ QMutexLocker lock(&socketOperationMutex);
+ bool res = false;
+ emit callForConnect(host, port, res);
+ return res;
+}
+
+void BlockingSocket::disconnectFromHost()
+{
+ QMutexLocker lock(&socketOperationMutex);
+ emit callForDisconnect();
+}
+
+bool BlockingSocket::isConnected()
+{
+ QMutexLocker lock(&socketOperationMutex);
+ bool res = false;
+ emit callForIsConnected(res);
+ return res;
+}
+
+bool BlockingSocket::send(const QByteArray& bytes)
+{
+ QMutexLocker lock(&socketOperationMutex);
+ bool res = false;
+ emit callForSend(bytes, res);
+ return res;
+}
+
+QByteArray BlockingSocket::read(qint64 count, int timeout, bool* ok)
+{
+ QMutexLocker lock(&socketOperationMutex);
+ bool res = false;
+ QByteArray bytes;
+ emit callForRead(count, timeout, bytes, res);
+ if (ok)
+ *ok = res;
+
+ return bytes;
+}
+
+void BlockingSocket::quit()
+{
+ QMutexLocker lock(&socketOperationMutex);
+ socketThread->quit();
+}
+
+void BlockingSocket::exit()
+{
+ QMutexLocker lock(&socketOperationMutex);
+ socketThread->quit();
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.h b/SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.h
new file mode 100644
index 0000000..2a42df7
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/blockingsocket.h
@@ -0,0 +1,44 @@
+#ifndef THREADEDSOCKET_H
+#define THREADEDSOCKET_H
+
+#include "coreSQLiteStudio_global.h"
+#include <QObject>
+#include <QAbstractSocket>
+#include <QMutex>
+
+class BlockingSocketPrivate;
+class ThreadWithEventLoop;
+
+class API_EXPORT BlockingSocket : public QObject
+{
+ Q_OBJECT
+
+ public:
+ BlockingSocket(QObject* parent = nullptr);
+ ~BlockingSocket();
+
+ QString getErrorText();
+ QAbstractSocket::SocketError getErrorCode();
+ bool connectToHost(const QString& host, int port);
+ void disconnectFromHost();
+ bool isConnected();
+ bool send(const QByteArray& bytes);
+ QByteArray read(qint64 count, int timeout = 30000, bool* ok = nullptr);
+ void quit();
+ void exit();
+
+ private:
+ ThreadWithEventLoop* socketThread = nullptr;
+ BlockingSocketPrivate* socket = nullptr;
+ QMutex socketOperationMutex;
+
+ signals:
+ void callForConnect(const QString& host, int port, bool& result);
+ void callForDisconnect();
+ void callForIsConnected(bool& connected);
+ void callForSend(const QByteArray& bytes, bool& result);
+ void callForRead(qint64 count, int timeout, QByteArray& resultBytes, bool& result);
+ void disconnected();
+};
+
+#endif // THREADEDSOCKET_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/expiringcache.h b/SQLiteStudio3/coreSQLiteStudio/common/expiringcache.h
new file mode 100644
index 0000000..d9bd483
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/expiringcache.h
@@ -0,0 +1,158 @@
+#ifndef EXPIRINGCACHE_H
+#define EXPIRINGCACHE_H
+
+#include <QCache>
+#include <QDateTime>
+#include <QTimer>
+#include <QDebug>
+
+template <class K, class V>
+class ExpiringCache : public QCache<K, V>
+{
+ public:
+ ExpiringCache(int maxCost = 100, int expireMs = 1000);
+ ~ExpiringCache();
+
+ bool insert(const K& key, V* object, int cost = 1);
+ bool contains(const K& key) const;
+ V* object(const K& key, bool noExpireCheck = false) const;
+ V* operator[](const K& key) const;
+ V* take(const K& key);
+ QList<K> keys() const;
+ bool remove(const K& key);
+ int count() const;
+ void clear();
+ bool isEmpty() const;
+ void setExpireTime(int ms);
+
+ private:
+ bool expired(const K& key) const;
+
+ mutable QHash<K, qint64> expires;
+ int expireMs;
+};
+
+template <class K, class V>
+ExpiringCache<K, V>::ExpiringCache(int maxCost, int expireMs) :
+ QCache<K, V>(maxCost), expireMs(expireMs)
+{
+}
+
+template <class K, class V>
+ExpiringCache<K, V>::~ExpiringCache()
+{
+}
+
+template <class K, class V>
+bool ExpiringCache<K, V>::insert(const K& key, V* object, int cost)
+{
+ QList<K> keysBefore = QCache<K, V>::keys();
+ bool result = QCache<K, V>::insert(key, object, cost);
+ if (!result)
+ return false;
+
+ QList<K> keysAfter = QCache<K, V>::keys();
+
+ for (const K& keyBefore : keysBefore)
+ {
+ if (!keysAfter.contains(keyBefore))
+ expires.remove(keyBefore);
+ }
+
+ expires[key] = QDateTime::currentMSecsSinceEpoch() + expireMs;
+ return true;
+}
+
+template <class K, class V>
+bool ExpiringCache<K, V>::contains(const K& key) const
+{
+ if (expired(key))
+ return false;
+
+ return QCache<K, V>::contains(key);
+}
+
+template <class K, class V>
+V* ExpiringCache<K, V>::object(const K& key, bool noExpireCheck) const
+{
+ if (!noExpireCheck && expired(key))
+ return nullptr;
+
+ return QCache<K, V>::object(key);
+}
+
+template <class K, class V>
+V* ExpiringCache<K, V>::operator[](const K& key) const
+{
+ if (expired(key))
+ return nullptr;
+
+ return QCache<K, V>::operator[](key);
+}
+
+template <class K, class V>
+V* ExpiringCache<K, V>::take(const K& key)
+{
+ if (expired(key))
+ return nullptr;
+
+ expires.remove(key);
+ return QCache<K, V>::take(key);
+}
+
+template <class K, class V>
+QList<K> ExpiringCache<K, V>::keys() const
+{
+ QList<K> keyList;
+ for (const K& key : QCache<K, V>::keys())
+ {
+ if (!expired(key))
+ keyList << key;
+ }
+ return keyList;
+}
+
+template <class K, class V>
+bool ExpiringCache<K, V>::remove(const K& key)
+{
+ expires.remove(key);
+ return QCache<K, V>::remove(key);
+}
+
+template <class K, class V>
+int ExpiringCache<K, V>::count() const
+{
+ return keys().count();
+}
+
+template <class K, class V>
+void ExpiringCache<K, V>::clear()
+{
+ expires.clear();
+ QCache<K, V>::clear();
+}
+
+template <class K, class V>
+bool ExpiringCache<K, V>::isEmpty() const
+{
+ return keys().isEmpty();
+}
+
+template <class K, class V>
+void ExpiringCache<K, V>::setExpireTime(int ms)
+{
+ expireMs = ms;
+}
+
+template <class K, class V>
+bool ExpiringCache<K, V>::expired(const K& key) const
+{
+ if (expires.contains(key) && QDateTime::currentMSecsSinceEpoch() > expires[key])
+ {
+ expires.remove(key);
+ return true;
+ }
+ return false;
+}
+
+#endif // EXPIRINGCACHE_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.cpp b/SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.cpp
new file mode 100644
index 0000000..a3d696e
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.cpp
@@ -0,0 +1,130 @@
+#include "blockingsocketprivate.h"
+#include <QTcpSocket>
+#include <QCoreApplication>
+#include <QTimer>
+#include <QThread>
+
+BlockingSocketPrivate::BlockingSocketPrivate()
+{
+}
+
+BlockingSocketPrivate::~BlockingSocketPrivate()
+{
+}
+
+void BlockingSocketPrivate::setError(QAbstractSocket::SocketError errorCode, const QString& errMsg)
+{
+ this->errorCode = errorCode;
+ this->errorText = errMsg;
+}
+
+bool BlockingSocketPrivate::isConnected()
+{
+ return (socket && socket->isOpen() && socket->state() == QAbstractSocket::ConnectedState);
+}
+
+QAbstractSocket::SocketError BlockingSocketPrivate::getErrorCode()
+{
+ return errorCode;
+}
+
+void BlockingSocketPrivate::createSocketIfNecessary()
+{
+ // This method is called only when the socket is already called,
+ // so we're sure the socket is created in the target thread.
+ if (socket)
+ return;
+
+ socket = new QTcpSocket(this);
+ connect(socket, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
+}
+
+QString BlockingSocketPrivate::getErrorText()
+{
+ return errorText;
+}
+
+void BlockingSocketPrivate::handleSendCall(const QByteArray& bytes, bool& result)
+{
+ createSocketIfNecessary();
+ result = true;
+ qint64 size = bytes.size();
+ qint64 totalBytesSent = 0;
+ qint64 bytesSent = 0;
+ while (totalBytesSent < size)
+ {
+ bytesSent = socket->write(totalBytesSent == 0 ? bytes : bytes.mid(totalBytesSent));
+ if (bytesSent < 0)
+ {
+ result = false;
+ setError(socket->error(), socket->errorString());
+ return;
+ }
+ totalBytesSent += bytesSent;
+ }
+}
+
+void BlockingSocketPrivate::handleReadCall(qint64 count, int timeout, QByteArray& resultBytes, bool& result)
+{
+ createSocketIfNecessary();
+ resultBytes.clear();
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+
+ QTimer timer;
+ timer.setSingleShot(true);
+ timer.setInterval(timeout);
+ timer.start();
+
+ while (resultBytes.size() < count && timer.isActive())
+ {
+ if (!isConnected())
+ {
+ qWarning() << "Blocking socket closed in the middle of reading.";
+ result = false;
+ setError(socket->error(), socket->errorString());
+ return;
+ }
+
+ if (socket->bytesAvailable() == 0)
+ {
+ QThread::msleep(1);
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ continue;
+ }
+
+ resultBytes += socket->read(qMin(socket->bytesAvailable(), count));
+ }
+
+ result = (resultBytes.size() >= count && timer.isActive());
+}
+
+void BlockingSocketPrivate::handleConnectCall(const QString& host, int port, bool& result)
+{
+ result = true;
+ if (isConnected())
+ return;
+
+ createSocketIfNecessary();
+ socket->connectToHost(host, port);
+ if (!socket->waitForConnected())
+ {
+ result = false;
+ setError(socket->error(), socket->errorString());
+ }
+}
+
+void BlockingSocketPrivate::handleDisconnectCall()
+{
+ if (!isConnected())
+ return;
+
+ createSocketIfNecessary();
+ socket->abort();
+ socket->close();
+
+}
+
+void BlockingSocketPrivate::handleIsConnectedCall(bool& connected)
+{
+ connected = isConnected();
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.h b/SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.h
new file mode 100644
index 0000000..8d7b994
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/private/blockingsocketprivate.h
@@ -0,0 +1,38 @@
+#ifndef BLOCKINGSOCKETPRIVATE_H
+#define BLOCKINGSOCKETPRIVATE_H
+
+#include <QObject>
+#include <QAbstractSocket>
+
+class BlockingSocketPrivate : public QObject
+{
+ Q_OBJECT
+
+ public:
+ BlockingSocketPrivate();
+ ~BlockingSocketPrivate();
+
+ QString getErrorText();
+ QAbstractSocket::SocketError getErrorCode();
+
+ private:
+ void createSocketIfNecessary();
+ void setError(QAbstractSocket::SocketError errorCode, const QString& errMsg);
+ bool isConnected();
+
+ QAbstractSocket* socket = nullptr;
+ QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError;
+ QString errorText;
+
+ private slots:
+ void handleSendCall(const QByteArray& bytes, bool& result);
+ void handleReadCall(qint64 count, int timeout, QByteArray& resultBytes, bool& result);
+ void handleConnectCall(const QString& host, int port, bool& result);
+ void handleDisconnectCall();
+ void handleIsConnectedCall(bool& connected);
+
+ signals:
+ void disconnected();
+};
+
+#endif // BLOCKINGSOCKETPRIVATE_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp b/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp
index 0eaca75..cdf8110 100644
--- a/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp
@@ -1,8 +1,10 @@
#include "readwritelocker.h"
#include "parser/lexer.h"
+#include "common/utils_sql.h"
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>
+#include <QDebug>
ReadWriteLocker::ReadWriteLocker(QReadWriteLock* lock, Mode mode)
{
@@ -47,57 +49,18 @@ void ReadWriteLocker::init(QReadWriteLock* lock, ReadWriteLocker::Mode mode)
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")
+ QueryAccessMode queryMode = getQueryAccessMode(query, dialect);
+ switch (queryMode)
{
- 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)
+ case QueryAccessMode::READ:
return ReadWriteLocker::READ;
+ case QueryAccessMode::WRITE:
+ return ReadWriteLocker::WRITE;
}
- return ReadWriteLocker::WRITE;
+ qCritical() << "Unhandled query access mode:" << static_cast<int>(queryMode);
+ return ReadWriteLocker::NONE;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/signalwait.cpp b/SQLiteStudio3/coreSQLiteStudio/common/signalwait.cpp
index 90f9075..4a22e83 100644
--- a/SQLiteStudio3/coreSQLiteStudio/common/signalwait.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/common/signalwait.cpp
@@ -12,8 +12,11 @@ bool SignalWait::wait(int msTimeout)
{
QTime timer(0, 0, 0, msTimeout);
timer.start();
- while (!called && timer.elapsed() < msTimeout)
- qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
+ while (!called && !failed && timer.elapsed() < msTimeout)
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+
+ if (failed)
+ return false;
return called;
}
@@ -23,7 +26,17 @@ void SignalWait::reset()
called = false;
}
+void SignalWait::addFailSignal(QObject* object, const char* signal)
+{
+ connect(object, signal, this, SLOT(handleFailSignal()));
+}
+
void SignalWait::handleSignal()
{
called = true;
}
+
+void SignalWait::handleFailSignal()
+{
+ failed = true;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/signalwait.h b/SQLiteStudio3/coreSQLiteStudio/common/signalwait.h
index 7b7b903..57759be 100644
--- a/SQLiteStudio3/coreSQLiteStudio/common/signalwait.h
+++ b/SQLiteStudio3/coreSQLiteStudio/common/signalwait.h
@@ -12,12 +12,15 @@ class SignalWait : public QObject
bool wait(int msTimeout);
void reset();
+ void addFailSignal(QObject *object, const char *signal);
private:
bool called = false;
+ bool failed = false;
private slots:
void handleSignal();
+ void handleFailSignal();
};
#endif // SIGNALWAIT_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.cpp b/SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.cpp
new file mode 100644
index 0000000..c05eb1e
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.cpp
@@ -0,0 +1,17 @@
+#include "threadwitheventloop.h"
+#include <QDebug>
+
+ThreadWithEventLoop::ThreadWithEventLoop(QObject* parent) :
+ QThread(parent)
+{
+}
+
+ThreadWithEventLoop::~ThreadWithEventLoop()
+{
+}
+
+void ThreadWithEventLoop::run()
+{
+ exec();
+}
+
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.h b/SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.h
new file mode 100644
index 0000000..ef0629a
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/common/threadwitheventloop.h
@@ -0,0 +1,19 @@
+#ifndef THREADWITHEVENTLOOP_H
+#define THREADWITHEVENTLOOP_H
+
+#include <QObject>
+#include <QThread>
+
+class ThreadWithEventLoop : public QThread
+{
+ Q_OBJECT
+
+ public:
+ ThreadWithEventLoop(QObject* parent = nullptr);
+ ~ThreadWithEventLoop();
+
+ protected:
+ void run() Q_DECL_OVERRIDE;
+};
+
+#endif // THREADWITHEVENTLOOP_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp b/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp
index d56d838..f2b3d1c 100644
--- a/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/common/utils.cpp
@@ -18,6 +18,33 @@
#include <QFileInfo>
#endif
+#ifdef Q_OS_WIN
+#include <windows.h>
+#include <tchar.h>
+
+typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+bool is64BitWindows()
+{
+#if defined(_WIN64)
+ return true; // 64-bit programs run only on Win64
+#elif defined(_WIN32)
+ // 32-bit programs run on both 32-bit and 64-bit Windows
+ // so must sniff
+ BOOL f64 = false;
+ LPFN_ISWOW64PROCESS fnIsWow64Process;
+
+ fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
+ if (fnIsWow64Process)
+ {
+ return fnIsWow64Process(GetCurrentProcess(), &f64) && f64;
+ }
+ return false;
+#else
+ return true; // Win64 does not support Win16
+#endif
+}
+#endif
+
void initUtils()
{
qRegisterMetaType<QList<int>>("QList<int>");
@@ -735,7 +762,14 @@ QString getOsString()
QString os = "Unknown";
#endif
- os += ", " + QString::number(QSysInfo::WordSize) + "bit";
+ os += ", ";
+#ifdef Q_OS_WIN
+ os += (is64BitWindows() ? "64" : "32");
+#else
+ os += QString::number(QSysInfo::WordSize);
+#endif
+ os += "bit";
+
return os;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp
index fcf49af..20ac736 100644
--- a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.cpp
@@ -550,3 +550,72 @@ QString getBindTokenName(const TokenPtr& token)
return token->value.mid(1);
}
+
+QueryAccessMode getQueryAccessMode(const QString& query, Dialect dialect, bool* isSelect)
+{
+ static QStringList readOnlyCommands = {"ANALYZE", "EXPLAIN", "PRAGMA", "SELECT"};
+
+ if (isSelect)
+ *isSelect = false;
+
+ TokenList tokens = Lexer::tokenize(query, dialect);
+ int keywordIdx = tokens.indexOf(Token::KEYWORD);
+
+ int cmdIdx = readOnlyCommands.indexOf(tokens[keywordIdx]->value.toUpper());
+ if (keywordIdx > -1 && cmdIdx > -1)
+ {
+ if (cmdIdx == 3 && isSelect)
+ *isSelect = true;
+
+ return QueryAccessMode::READ;
+ }
+
+ if (keywordIdx > -1 && tokens[keywordIdx]->value.toUpper() == "WITH")
+ {
+ bool matched = false;
+ bool queryIsSelect = 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;
+ queryIsSelect = true;
+ }
+ else if (val == "DELETE" || val == "UPDATE" || val == "INSERT")
+ {
+ matched = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (matched)
+ break;
+ }
+
+ if (queryIsSelect)
+ {
+ if (isSelect)
+ *isSelect = true;
+
+ return QueryAccessMode::READ;
+ }
+ }
+
+ return QueryAccessMode::WRITE;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h
index 8e1f3ff..945b7cc 100644
--- a/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h
+++ b/SQLiteStudio3/coreSQLiteStudio/common/utils_sql.h
@@ -19,6 +19,12 @@ enum class NameWrapper
null
};
+enum class QueryAccessMode
+{
+ READ,
+ WRITE
+};
+
typedef QPair<QString,QStringList> QueryWithParamNames;
typedef QPair<QString,int> QueryWithParamCount;
@@ -66,6 +72,7 @@ API_EXPORT QueryWithParamCount getQueryWithParamCount(const QString& query, Dial
API_EXPORT QString trimBindParamPrefix(const QString& param);
API_EXPORT QString commentAllSqlLines(const QString& sql);
API_EXPORT QString getBindTokenName(const TokenPtr& token);
+API_EXPORT QueryAccessMode getQueryAccessMode(const QString& query, Dialect dialect, bool* isSelect = nullptr);
#endif // UTILS_SQL_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro b/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro
index 0a7eb60..e21ee8b 100644
--- a/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro
+++ b/SQLiteStudio3/coreSQLiteStudio/coreSQLiteStudio.pro
@@ -217,7 +217,10 @@ SOURCES += sqlitestudio.cpp \
rsa/PrimeGenerator.cpp \
rsa/RSA.cpp \
translations.cpp \
- common/signalwait.cpp
+ common/signalwait.cpp \
+ common/blockingsocket.cpp \
+ common/threadwitheventloop.cpp \
+ common/private/blockingsocketprivate.cpp
HEADERS += sqlitestudio.h\
coreSQLiteStudio_global.h \
@@ -405,7 +408,11 @@ HEADERS += sqlitestudio.h\
rsa/PrimeGenerator.h \
rsa/RSA.h \
translations.h \
- common/signalwait.h
+ common/signalwait.h \
+ common/blockingsocket.h \
+ common/threadwitheventloop.h \
+ common/private/blockingsocketprivate.h \
+ common/expiringcache.h
unix: {
target.path = $$LIBDIR
diff --git a/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp b/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp
index 3672527..7d7d20b 100644
--- a/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/csvserializer.cpp
@@ -35,16 +35,17 @@ QString CsvSerializer::serialize(const QStringList& data, const CsvFormat& forma
return outputCells.join(format.columnSeparator);
}
-QList<QStringList> CsvSerializer::deserialize(const QString& data, const CsvFormat& format)
+template <class T>
+QList<QList<T>> typedDeserialize(const T& data, const CsvFormat& format)
{
- QList<QStringList> rows;
- QStringList cells;
+ QList<QList<T>> rows;
+ QList<T> cells;
int pos = 0;
int lgt = data.length();
bool quotes = false;
bool sepAsLast = false;
- QString field = "";
+ T field = "";
QChar c;
while (pos < lgt)
@@ -95,3 +96,81 @@ QList<QStringList> CsvSerializer::deserialize(const QString& data, const CsvForm
return rows;
}
+
+QList<QList<QByteArray> > CsvSerializer::deserialize(const QByteArray& data, const CsvFormat& format)
+{
+ return typedDeserialize<QByteArray>(data, format);
+}
+
+QList<QStringList> CsvSerializer::deserialize(const QString& data, const CsvFormat& format)
+{
+ QList<QList<QString>> deserialized = typedDeserialize<QString>(data, format);
+
+ QList<QStringList> finalList;
+ for (const QList<QString>& resPart : deserialized)
+ finalList << QStringList(resPart);
+
+ return finalList;
+}
+
+
+//QList<QStringList> CsvSerializer::deserialize(const QByteArray& data, const CsvFormat& format)
+//{
+// QList<QStringList> rows;
+// QStringList cells;
+
+// int pos = 0;
+// int lgt = data.length();
+// bool quotes = false;
+// bool sepAsLast = false;
+// QString field = "";
+// QChar c;
+
+// while (pos < lgt)
+// {
+// c = data[pos];
+// sepAsLast = false;
+// if (!quotes && c == '"' )
+// {
+// quotes = true;
+// }
+// else if (quotes && c == '"' )
+// {
+// if (pos + 1 < data.length() && data[pos+1] == '"' )
+// {
+// field += c;
+// pos++;
+// }
+// else
+// {
+// quotes = false;
+// }
+// }
+// else if (!quotes && format.columnSeparator.contains(c))
+// {
+// cells << field;
+// field.clear();
+// sepAsLast = true;
+// }
+// else if (!quotes && format.rowSeparator.contains(c))
+// {
+// cells << field;
+// rows << cells;
+// cells.clear();
+// field.clear();
+// }
+// else
+// {
+// field += c;
+// }
+// pos++;
+// }
+
+// if (field.size() > 0 || sepAsLast)
+// cells << field;
+
+// if (cells.size() > 0)
+// rows << cells;
+
+// return rows;
+//}
diff --git a/SQLiteStudio3/coreSQLiteStudio/csvserializer.h b/SQLiteStudio3/coreSQLiteStudio/csvserializer.h
index 3217203..1ff6ee9 100644
--- a/SQLiteStudio3/coreSQLiteStudio/csvserializer.h
+++ b/SQLiteStudio3/coreSQLiteStudio/csvserializer.h
@@ -10,6 +10,7 @@ class API_EXPORT CsvSerializer
static QString serialize(const QList<QStringList>& data, const CsvFormat& format);
static QString serialize(const QStringList& data, const CsvFormat& format);
static QList<QStringList> deserialize(const QString& data, const CsvFormat& format);
+ static QList<QList<QByteArray>> deserialize(const QByteArray& data, const CsvFormat& format);
};
#endif // CSVSERIALIZER_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
index 68c6ad7..8c08a51 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb.cpp
@@ -703,23 +703,6 @@ int AbstractDb::getErrorCode()
bool AbstractDb::initAfterCreated()
{
- bool isOpenBefore = isOpen();
- if (!isOpenBefore)
- {
- if (!openForProbing())
- {
- qWarning() << "Could not open database for initAfterCreated(). Database:" << name;
- return false;
- }
- }
-
- // SQLite version
- QVariant value = exec("SELECT sqlite_version()")->getSingleCell();
- version = value.toString().mid(0, 1).toUInt();
-
- if (!isOpenBefore)
- closeQuiet();
-
return true;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h
index 9b27dc3..0620a7d 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb2.h
@@ -52,6 +52,7 @@ class AbstractDb2 : public AbstractDb
int getErrorCodeInternal();
bool openInternal();
bool closeInternal();
+ bool initAfterCreated();
void initAfterOpen();
SqlQueryPtr prepare(const QString& query);
QString getTypeLabel();
@@ -220,6 +221,13 @@ bool AbstractDb2<T>::closeInternal()
}
template <class T>
+bool AbstractDb2<T>::initAfterCreated()
+{
+ version = 2;
+ return AbstractDb::initAfterCreated();
+}
+
+template <class T>
void AbstractDb2<T>::initAfterOpen()
{
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h
index 9cb58aa..fe37d5e 100644
--- a/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h
+++ b/SQLiteStudio3/coreSQLiteStudio/db/abstractdb3.h
@@ -51,6 +51,7 @@ class AbstractDb3 : public AbstractDb
int getErrorCodeInternal();
bool openInternal();
bool closeInternal();
+ bool initAfterCreated();
void initAfterOpen();
SqlQueryPtr prepare(const QString& query);
QString getTypeLabel();
@@ -383,6 +384,13 @@ bool AbstractDb3<T>::closeInternal()
}
template <class T>
+bool AbstractDb3<T>::initAfterCreated()
+{
+ version = 3;
+ return AbstractDb::initAfterCreated();
+}
+
+template <class T>
void AbstractDb3<T>::initAfterOpen()
{
T::enable_load_extension(dbHandle, true);
diff --git a/SQLiteStudio3/coreSQLiteStudio/dbversionconverter.cpp b/SQLiteStudio3/coreSQLiteStudio/dbversionconverter.cpp
index d31d536..676009a 100644
--- a/SQLiteStudio3/coreSQLiteStudio/dbversionconverter.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/dbversionconverter.cpp
@@ -929,10 +929,10 @@ void DbVersionConverter::fullConvertStep2()
for (DbPlugin* plugin : PLUGINS->getLoadedPlugins<DbPlugin>())
{
tmpDb = plugin->getInstance("", ":memory:", QHash<QString,QVariant>());
- if (tmpDb->initAfterCreated() && tmpDb->getDialect() == fullConversionConfig->to)
+ if (tmpDb && tmpDb->initAfterCreated() && tmpDb->getDialect() == fullConversionConfig->to)
db = plugin->getInstance(fullConversionConfig->targetName, fullConversionConfig->targetFile, QHash<QString,QVariant>());
- delete tmpDb;
+ safe_delete(tmpDb);
if (db)
break;
}
@@ -1105,7 +1105,7 @@ QList<Db*> DbVersionConverter::getAllPossibleDbInstances() const
for (DbPlugin* plugin : PLUGINS->getLoadedPlugins<DbPlugin>())
{
db = plugin->getInstance("", ":memory:", QHash<QString,QVariant>());
- if (!db->initAfterCreated())
+ if (!db || !db->initAfterCreated())
continue;
dbList << db;
diff --git a/SQLiteStudio3/coreSQLiteStudio/importworker.cpp b/SQLiteStudio3/coreSQLiteStudio/importworker.cpp
index 2cdeeb0..cf62a98 100644
--- a/SQLiteStudio3/coreSQLiteStudio/importworker.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/importworker.cpp
@@ -53,6 +53,7 @@ void ImportWorker::run()
if (tableCreated)
emit createdTable(db, table);
+ plugin->afterImport();
emit finished(true);
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp
index 454c0e3..d21578e 100644
--- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.cpp
@@ -640,8 +640,8 @@ SqliteCreateTable::Column::Column(const QString &name, SqliteColumnType *type, c
this->constraints.last()->type != SqliteCreateTable::Column::Constraint::NAME_ONLY)
{
SqliteCreateTable::Column::Constraint* last = this->constraints.last();
- last->deferrable = constr->deferrable;
- last->initially = constr->initially;
+ last->foreignKey->deferrable = constr->deferrable;
+ last->foreignKey->initially = constr->initially;
delete constr;
// We don't want deleted constr to be added to list. We finish this now.
diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h
index f3be244..877d0fa 100644
--- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h
+++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqlitecreatetable.h
@@ -21,6 +21,8 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery
public:
class API_EXPORT Constraint : public SqliteStatement
{
+ friend class Column;
+
public:
enum Type
{
@@ -70,11 +72,14 @@ class API_EXPORT SqliteCreateTable : public SqliteQuery
QString id;
QString collationName = QString::null;
SqliteForeignKey* foreignKey = nullptr;
- SqliteDeferrable deferrable = SqliteDeferrable::null;
- SqliteInitially initially = SqliteInitially::null;
protected:
TokenList rebuildTokensFromContents();
+
+ private:
+ SqliteDeferrable deferrable = SqliteDeferrable::null; // only a temporary field for parse time, before merging with actual FK
+ SqliteInitially initially = SqliteInitially::null; // only a temporary field for parse time, before merging with actual FK
+
};
typedef QSharedPointer<Constraint> ConstraintPtr;
diff --git a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp
index 1429cef..12adf80 100644
--- a/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteexpr.cpp
@@ -65,11 +65,12 @@ QString SqliteExpr::likeOp(SqliteExpr::LikeOp value)
SqliteExpr::NotNull SqliteExpr::notNullOp(const QString &value)
{
- if (value == "ISNULL")
+ QString upper = value.toUpper();
+ if (upper == "ISNULL")
return SqliteExpr::NotNull::ISNULL;
- else if (value == "NOTNULL")
+ else if (upper == "NOTNULL")
return SqliteExpr::NotNull::NOTNULL;
- else if (value == "NOT NULL")
+ else if (upper == "NOT NULL")
return SqliteExpr::NotNull::NOT_NULL;
else
return SqliteExpr::NotNull::null;
diff --git a/SQLiteStudio3/coreSQLiteStudio/populateworker.cpp b/SQLiteStudio3/coreSQLiteStudio/populateworker.cpp
index 0b08526..f25a6ac 100644
--- a/SQLiteStudio3/coreSQLiteStudio/populateworker.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/populateworker.cpp
@@ -46,6 +46,13 @@ void PopulateWorker::run()
if (i == 0 && !beforePopulating())
return;
+ if (isInterrupted())
+ {
+ db->rollback();
+ emit finished(false);
+ return;
+ }
+
args.clear();
for (PopulateEngine* engine : engines)
args << engine->nextValue(nextValueError);
@@ -58,6 +65,8 @@ void PopulateWorker::run()
emit finished(false);
return;
}
+
+ emit finishedStep(i + 1);
}
if (!db->commit())
diff --git a/SQLiteStudio3/coreSQLiteStudio/populateworker.h b/SQLiteStudio3/coreSQLiteStudio/populateworker.h
index f0a39f8..d253a79 100644
--- a/SQLiteStudio3/coreSQLiteStudio/populateworker.h
+++ b/SQLiteStudio3/coreSQLiteStudio/populateworker.h
@@ -36,6 +36,7 @@ class PopulateWorker : public QObject, public QRunnable
signals:
void finished(bool result);
+ void finishedStep(int step);
};
#endif // POPULATEWORKER_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp
index f14d23d..7988748 100644
--- a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp
@@ -14,6 +14,8 @@ const char* sqliteMasterDdl =
const char* sqliteTempMasterDdl =
"CREATE TABLE sqlite_temp_master (type text, name text, tbl_name text, rootpage integer, sql text)";
+ExpiringCache<SchemaResolver::ObjectCacheKey,QVariant> SchemaResolver::cache;
+
SchemaResolver::SchemaResolver(Db *db)
: db(db)
{
@@ -286,12 +288,19 @@ QString SchemaResolver::getObjectDdl(const QString &database, const QString &nam
// Prepare db prefix.
QString dbName = getPrefixDb(database, dialect);
+ // Cache
+ QString typeStr = objectTypeToString(type);
+ bool useCache = usesCache();
+ ObjectCacheKey key(ObjectCacheKey::OBJECT_DDL, db, dbName, lowerName, typeStr);
+ if (useCache && cache.contains(key))
+ return cache.object(key, true)->toString();
+
// Get the DDL
QVariant results;
if (type != ANY)
{
results = db->exec(QString(
- "SELECT sql FROM %1.sqlite_master WHERE lower(name) = '%2' AND type = '%3';").arg(dbName, escapeString(lowerName), objectTypeToString(type)),
+ "SELECT sql FROM %1.sqlite_master WHERE lower(name) = '%2' AND type = '%3';").arg(dbName, escapeString(lowerName), typeStr),
dbFlags
)->getSingleCell();
}
@@ -317,6 +326,9 @@ QString SchemaResolver::getObjectDdl(const QString &database, const QString &nam
if (!resStr.trimmed().endsWith(";"))
resStr += ";";
+ if (useCache)
+ cache.insert(key, new QVariant(resStr));
+
// Return the DDL
return resStr;
}
@@ -417,6 +429,11 @@ QStringList SchemaResolver::getObjects(const QString &type)
QStringList SchemaResolver::getObjects(const QString &database, const QString &type)
{
+ bool useCache = usesCache();
+ ObjectCacheKey key(ObjectCacheKey::OBJECT_NAMES, db, database, type);
+ if (useCache && cache.contains(key))
+ return cache.object(key, true)->toStringList();
+
QStringList resList;
QString dbName = getPrefixDb(database, db->getDialect());
@@ -430,6 +447,9 @@ QStringList SchemaResolver::getObjects(const QString &database, const QString &t
resList << value;
}
+ if (useCache)
+ cache.insert(key, new QVariant(resList));
+
return resList;
}
@@ -440,6 +460,11 @@ QStringList SchemaResolver::getAllObjects()
QStringList SchemaResolver::getAllObjects(const QString& database)
{
+ bool useCache = usesCache();
+ ObjectCacheKey key(ObjectCacheKey::OBJECT_NAMES, db, database);
+ if (useCache && cache.contains(key))
+ return cache.object(key, true)->toStringList();
+
QStringList resList;
QString dbName = getPrefixDb(database, db->getDialect());
@@ -455,6 +480,9 @@ QStringList SchemaResolver::getAllObjects(const QString& database)
resList << value;
}
+ if (useCache)
+ cache.insert(key, new QVariant(resList));
+
return resList;
}
@@ -605,25 +633,42 @@ StrHash<SchemaResolver::ObjectDetails> SchemaResolver::getAllObjectDetails(const
ObjectDetails detail;
QString type;
- SqlQueryPtr results = db->exec(QString("SELECT name, type, sql FROM %1.sqlite_master").arg(getPrefixDb(database, db->getDialect())), dbFlags);
- if (results->isError())
+ QList<QVariant> rows;
+ bool useCache = usesCache();
+ ObjectCacheKey key(ObjectCacheKey::OBJECT_DETAILS, db, database);
+ if (useCache && cache.contains(key))
+ {
+ rows = cache.object(key, true)->toList();
+ }
+ else
{
- qCritical() << "Error while getting all object details in SchemaResolver:" << results->getErrorCode();
- return details;
+ SqlQueryPtr results = db->exec(QString("SELECT name, type, sql FROM %1.sqlite_master").arg(getPrefixDb(database, db->getDialect())), dbFlags);
+ if (results->isError())
+ {
+ qCritical() << "Error while getting all object details in SchemaResolver:" << results->getErrorCode();
+ return details;
+ }
+
+ for (const SqlResultsRowPtr& row : results->getAll())
+ rows << row->valueMap();
+
+ if (useCache)
+ cache.insert(key, new QVariant(rows));
}
- SqlResultsRowPtr row;
- while (results->hasNext())
+ QHash<QString, QVariant> row;
+ for (const QVariant& rowVariant : rows)
{
- row = results->next();
- type = row->value("type").toString();
+ row = rowVariant.toHash();
+ type = row["type"].toString();
detail.type = stringToObjectType(type);
if (detail.type == ANY)
qCritical() << "Unhlandled db object type:" << type;
- detail.ddl = row->value("sql").toString();
- details[row->value("name").toString()] = detail;
+ detail.ddl = row["sql"].toString();
+ details[row["name"].toString()] = detail;
}
+
return details;
}
@@ -747,6 +792,16 @@ SchemaResolver::ObjectType SchemaResolver::stringToObjectType(const QString& typ
return SchemaResolver::ANY;
}
+void SchemaResolver::staticInit()
+{
+ cache.setExpireTime(3000);
+}
+
+bool SchemaResolver::usesCache()
+{
+ return db->getConnectionOptions().contains(USE_SCHEMA_CACHING) && db->getConnectionOptions()[USE_SCHEMA_CACHING].toBool();
+}
+
QList<SqliteCreateViewPtr> SchemaResolver::getParsedViewsForTable(const QString& database, const QString& table)
{
QList<SqliteCreateViewPtr> createViewList;
@@ -909,3 +964,18 @@ void SchemaResolver::setNoDbLocking(bool value)
dbFlags ^= Db::Flag::NO_LOCK;
}
+
+SchemaResolver::ObjectCacheKey::ObjectCacheKey(Type type, Db* db, const QString& value1, const QString& value2, const QString& value3) :
+ type(type), db(db), value1(value1), value2(value2), value3(value3)
+{
+}
+
+int qHash(const SchemaResolver::ObjectCacheKey& key)
+{
+ return qHash(key.type) ^ qHash(key.db) ^ qHash(key.value1) ^ qHash(key.value2) ^ qHash(key.value3);
+}
+
+int operator==(const SchemaResolver::ObjectCacheKey& k1, const SchemaResolver::ObjectCacheKey& k2)
+{
+ return (k1.type == k2.type && k1.db == k2.db && k1.value1 == k2.value1 && k1.value2 == k2.value2 && k1.value3 == k2.value3);
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h
index 5316f5a..5d325d8 100644
--- a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h
+++ b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.h
@@ -14,6 +14,7 @@
#include "db/sqlquery.h"
#include "db/db.h"
#include "common/strhash.h"
+#include "common/expiringcache.h"
#include <QStringList>
class SqliteCreateTable;
@@ -38,6 +39,24 @@ class API_EXPORT SchemaResolver
QString ddl;
};
+ struct ObjectCacheKey
+ {
+ enum Type
+ {
+ OBJECT_NAMES,
+ OBJECT_DETAILS,
+ OBJECT_DDL
+ };
+
+ ObjectCacheKey(Type type, Db* db, const QString& value1 = QString(), const QString& value2 = QString(), const QString& value3 = QString());
+
+ Type type;
+ Db* db;
+ QString value1;
+ QString value2;
+ QString value3;
+ };
+
explicit SchemaResolver(Db* db);
virtual ~SchemaResolver();
@@ -164,8 +183,12 @@ class API_EXPORT SchemaResolver
static QString objectTypeToString(ObjectType type);
static ObjectType stringToObjectType(const QString& type);
+ static void staticInit();
+
+ static_char* USE_SCHEMA_CACHING = "useSchemaCaching";
private:
+ bool usesCache();
SqliteQueryPtr getParsedDdl(const QString& ddl);
SqliteCreateTablePtr virtualTableAsRegularTable(const QString& database, const QString& table);
StrHash< QStringList> getGroupedObjects(const QString &database, const QStringList& inputList, SqliteQueryType type);
@@ -180,8 +203,13 @@ class API_EXPORT SchemaResolver
Parser* parser = nullptr;
bool ignoreSystemObjects = false;
Db::Flags dbFlags;
+
+ static ExpiringCache<ObjectCacheKey,QVariant> cache;
};
+int qHash(const SchemaResolver::ObjectCacheKey& key);
+int operator==(const SchemaResolver::ObjectCacheKey& k1, const SchemaResolver::ObjectCacheKey& k2);
+
template <class T>
StrHash<QSharedPointer<T>> SchemaResolver::getAllParsedObjectsForType(const QString& database, const QString& type)
{
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h
index 5a56151..66bbf08 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h
@@ -189,6 +189,8 @@ class API_EXPORT DbManager : public QObject
*/
virtual void notifyDatabasesAreLoaded() = 0;
+ virtual void rescanInvalidDatabasesForPlugin(DbPlugin* dbPlugin) = 0;
+
signals:
/**
* @brief Application just connected to the database.
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp
index e96e181..a7bff0d 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp
@@ -81,8 +81,12 @@ bool DbManagerImpl::updateDb(Db* db, const QString &name, const QString &path, c
return false;
}
- QDir pathDir(path);
- QString normalizedPath = pathDir.absolutePath();
+ QString normalizedPath;
+ QUrl url(path);
+ if (url.scheme().isEmpty() || url.scheme() == "file")
+ normalizedPath = QDir(path).absolutePath();
+ else
+ normalizedPath = path;
listLock.lockForWrite();
nameToDb.remove(db->getName(), Qt::CaseInsensitive);
@@ -343,6 +347,66 @@ void DbManagerImpl::scanForNewDatabasesInConfig()
}
}
+void DbManagerImpl::rescanInvalidDatabasesForPlugin(DbPlugin* dbPlugin)
+{
+ if (!dbPlugin)
+ {
+ qWarning() << "Call to DbManagerImpl::rescanInvalidDatabasesForPlugin() with null plugin.";
+ return;
+ }
+
+ Db* db = nullptr;
+
+ QUrl url;
+ QString errorMessages;
+ for (Db* invalidDb : getInvalidDatabases())
+ {
+ if (invalidDb->getConnectionOptions().contains(DB_PLUGIN) && invalidDb->getConnectionOptions()[DB_PLUGIN].toString() != dbPlugin->getName())
+ continue;
+
+ url = QUrl::fromUserInput(invalidDb->getPath());
+ if (url.isLocalFile() && !QFile::exists(invalidDb->getPath()))
+ continue;
+
+ errorMessages = QString();
+ db = createDb(invalidDb->getName(), invalidDb->getPath(), invalidDb->getConnectionOptions(), &errorMessages);
+ if (!db)
+ {
+ if (!errorMessages.isNull())
+ {
+ dynamic_cast<InvalidDb*>(invalidDb)->setError(errorMessages);
+ }
+ continue; // For this db driver was not loaded yet.
+ }
+
+ if (!dbPlugin->checkIfDbServedByPlugin(db))
+ {
+ qDebug() << "Managed to load database" << db->getPath() << " (" << db->getName() << ")"
+ << "but it doesn't use DbPlugin that was just loaded, so it will not be loaded to the db manager";
+
+ delete db;
+ continue;
+ }
+
+ removeDbInternal(invalidDb, false);
+ delete invalidDb;
+
+ addDbInternal(db, false);
+
+ if (!db->getConnectionOptions().contains(DB_PLUGIN))
+ {
+ db->getConnectionOptions()[DB_PLUGIN] = dbPlugin->getName();
+ if (!CFG->updateDb(db->getName(), db->getName(), db->getPath(), db->getConnectionOptions()))
+ qWarning() << "Could not store handling plugin in options for database" << db->getName();
+ }
+
+ if (CFG->getDbGroup(db->getName())->open)
+ db->open();
+
+ emit dbLoaded(db);
+ }
+}
+
void DbManagerImpl::addDbInternal(Db* db, bool alsoToConfig)
{
if (alsoToConfig)
@@ -391,17 +455,23 @@ Db* DbManagerImpl::tryToLoadDb(InvalidDb* invalidDb, bool emitNotifySignal)
Db* DbManagerImpl::createDb(const QString &name, const QString &path, const QHash<QString,QVariant> &options, QString* errorMessages)
{
QList<DbPlugin*> dbPlugins = PLUGINS->getLoadedPlugins<DbPlugin>();
- DbPlugin* dbPlugin = nullptr;
Db* db = nullptr;
QStringList messages;
QString message;
- QDir pathDir(path); // Using QDir to normalize separator
- foreach (dbPlugin, dbPlugins)
+
+ QString normalizedPath;
+ QUrl url(path);
+ if (url.scheme().isEmpty() || url.scheme() == "file")
+ normalizedPath = QDir(path).absolutePath();
+ else
+ normalizedPath = path;
+
+ for (DbPlugin* dbPlugin : dbPlugins)
{
if (options.contains("plugin") && options["plugin"] != dbPlugin->getName())
continue;
- db = dbPlugin->getInstance(name, pathDir.absolutePath(), options, &message);
+ db = dbPlugin->getInstance(name, normalizedPath, options, &message);
if (!db)
{
messages << message;
@@ -410,6 +480,7 @@ Db* DbManagerImpl::createDb(const QString &name, const QString &path, const QHas
if (!db->initAfterCreated())
{
+ safe_delete(db);
messages << tr("Database could not be initialized.");
continue;
}
@@ -506,46 +577,47 @@ void DbManagerImpl::loaded(Plugin* plugin, PluginType* type)
return;
DbPlugin* dbPlugin = dynamic_cast<DbPlugin*>(plugin);
- Db* db = nullptr;
+ rescanInvalidDatabasesForPlugin(dbPlugin);
+// Db* db = nullptr;
- QUrl url;
- for (Db* invalidDb : getInvalidDatabases())
- {
- if (invalidDb->getConnectionOptions().contains(DB_PLUGIN) && invalidDb->getConnectionOptions()[DB_PLUGIN].toString() != dbPlugin->getName())
- continue;
+// QUrl url;
+// for (Db* invalidDb : getInvalidDatabases())
+// {
+// if (invalidDb->getConnectionOptions().contains(DB_PLUGIN) && invalidDb->getConnectionOptions()[DB_PLUGIN].toString() != dbPlugin->getName())
+// continue;
- url = QUrl::fromUserInput(invalidDb->getPath());
- if (url.isLocalFile() && !QFile::exists(invalidDb->getPath()))
- continue;
+// url = QUrl::fromUserInput(invalidDb->getPath());
+// if (url.isLocalFile() && !QFile::exists(invalidDb->getPath()))
+// continue;
- db = createDb(invalidDb->getName(), invalidDb->getPath(), invalidDb->getConnectionOptions());
- if (!db)
- continue; // For this db driver was not loaded yet.
+// db = createDb(invalidDb->getName(), invalidDb->getPath(), invalidDb->getConnectionOptions());
+// if (!db)
+// continue; // For this db driver was not loaded yet.
- if (!dbPlugin->checkIfDbServedByPlugin(db))
- {
- qDebug() << "Managed to load database" << db->getPath() << " (" << db->getName() << ")"
- << "but it doesn't use DbPlugin that was just loaded, so it will not be loaded to the db manager";
+// if (!dbPlugin->checkIfDbServedByPlugin(db))
+// {
+// qDebug() << "Managed to load database" << db->getPath() << " (" << db->getName() << ")"
+// << "but it doesn't use DbPlugin that was just loaded, so it will not be loaded to the db manager";
- delete db;
- continue;
- }
+// delete db;
+// continue;
+// }
- removeDbInternal(invalidDb, false);
- delete invalidDb;
+// removeDbInternal(invalidDb, false);
+// delete invalidDb;
- addDbInternal(db, false);
+// addDbInternal(db, false);
- if (!db->getConnectionOptions().contains(DB_PLUGIN))
- {
- db->getConnectionOptions()[DB_PLUGIN] = dbPlugin->getName();
- if (!CFG->updateDb(db->getName(), db->getName(), db->getPath(), db->getConnectionOptions()))
- qWarning() << "Could not store handling plugin in options for database" << db->getName();
- }
+// if (!db->getConnectionOptions().contains(DB_PLUGIN))
+// {
+// db->getConnectionOptions()[DB_PLUGIN] = dbPlugin->getName();
+// if (!CFG->updateDb(db->getName(), db->getName(), db->getPath(), db->getConnectionOptions()))
+// qWarning() << "Could not store handling plugin in options for database" << db->getName();
+// }
- if (CFG->getDbGroup(db->getName())->open)
- db->open();
+// if (CFG->getDbGroup(db->getName())->open)
+// db->open();
- emit dbLoaded(db);
- }
+// emit dbLoaded(db);
+// }
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h
index 5797ac6..67a1335 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h
@@ -178,6 +178,7 @@ class API_EXPORT DbManagerImpl : public DbManager
public slots:
void notifyDatabasesAreLoaded();
void scanForNewDatabasesInConfig();
+ void rescanInvalidDatabasesForPlugin(DbPlugin* dbPlugin);
};
#endif // DBMANAGERIMPL_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.cpp b/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.cpp
index 237667d..02ba549 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.cpp
@@ -55,6 +55,7 @@ void PopulateManager::populate(Db* db, const QString& table, const QHash<QString
PopulateWorker* worker = new PopulateWorker(db, table, columns, engineList, rows);
connect(worker, SIGNAL(finished(bool)), this, SLOT(finalizePopulating(bool)));
+ connect(worker, SIGNAL(finishedStep(int)), this, SIGNAL(finishedStep(int)));
connect(this, SIGNAL(orderWorkerToInterrupt()), worker, SLOT(interrupt()));
QThreadPool::globalInstance()->start(worker);
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.h b/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.h
index 05b1f82..b445a9a 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/populatemanager.h
@@ -41,6 +41,7 @@ class API_EXPORT PopulateManager : public PluginServiceBase
void populatingSuccessful();
void populatingFailed();
void orderWorkerToInterrupt();
+ void finishedStep(int step);
};
#define POPULATE_MANAGER SQLITESTUDIO->getPopulateManager()
diff --git a/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp b/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp
index 361b2e4..963ba7d 100644
--- a/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/sqlitestudio.cpp
@@ -39,7 +39,7 @@
DEFINE_SINGLETON(SQLiteStudio)
-static const int sqlitestudioVersion = 30004;
+static const int sqlitestudioVersion = 30005;
SQLiteStudio::SQLiteStudio()
{
@@ -266,6 +266,7 @@ void SQLiteStudio::init(const QStringList& cmdListArguments, bool guiAvailable)
CfgMain::staticInit();
Db::metaInit();
initUtilsSql();
+ SchemaResolver::staticInit();
initKeywords();
Lexer::staticInit();
CompletionHelper::init();
diff --git a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts
index 24da18a..74fb84c 100644
--- a/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts
+++ b/SQLiteStudio3/coreSQLiteStudio/translations/coreSQLiteStudio_sk.ts
@@ -7,7 +7,7 @@
<location filename="../db/abstractdb.cpp" line="306"/>
<location filename="../db/abstractdb.cpp" line="323"/>
<source>Cannot execute query on closed database.</source>
- <translation type="unfinished"></translation>
+ <translation>Nemôžem spustiť dotaz na uzatvorenej databáze.</translation>
</message>
<message>
<location filename="../db/abstractdb.cpp" line="603"/>
@@ -466,22 +466,22 @@ Tables, indexes, triggers and views copied to database %3 will remain.</source>
<message>
<location filename="../services/impl/pluginmanagerimpl.cpp" line="541"/>
<source>Cannot load plugin %1, because it&apos;s in conflict with plugin %2.</source>
- <translation type="unfinished"></translation>
+ <translation>Nemôžem načítať plugin %1, pretože je v konflikte s pluginom %2.</translation>
</message>
<message>
<location filename="../services/impl/pluginmanagerimpl.cpp" line="552"/>
<source>Cannot load plugin %1, because its dependency was not loaded: %2.</source>
- <translation type="unfinished"></translation>
+ <translation>Nemôžem načítať plugin %1, pretože neboli načítané jeho závislosti %2.</translation>
</message>
<message>
<location filename="../services/impl/pluginmanagerimpl.cpp" line="561"/>
<source>Cannot load plugin %1. Error details: %2</source>
- <translation type="unfinished"></translation>
+ <translation>Nemôžem načítať plugin %1. Detaily chyby %2</translation>
</message>
<message>
<location filename="../services/impl/pluginmanagerimpl.cpp" line="577"/>
<source>Cannot load plugin %1 (error while initializing plugin).</source>
- <translation type="unfinished"></translation>
+ <translation>Nemôžem načítať plugin %1 (nastala chyba pri jeho inicializácii).</translation>
</message>
<message>
<location filename="../services/impl/pluginmanagerimpl.cpp" line="730"/>
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/ipvalidator.h b/SQLiteStudio3/guiSQLiteStudio/common/ipvalidator.h
index 1c9ca4d..2cda2d7 100644
--- a/SQLiteStudio3/guiSQLiteStudio/common/ipvalidator.h
+++ b/SQLiteStudio3/guiSQLiteStudio/common/ipvalidator.h
@@ -1,9 +1,10 @@
#ifndef IPVALIDATOR_H
#define IPVALIDATOR_H
+#include "guiSQLiteStudio_global.h"
#include <QValidator>
-class IpValidator : public QValidator
+class GUI_API_EXPORT IpValidator : public QValidator
{
public:
IpValidator(QObject* parent = 0);
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp
index 7cc6a4e..168c7f9 100644
--- a/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp
@@ -140,6 +140,11 @@ void WidgetCover::hide()
animation->start();
}
+void WidgetCover::setProgress(int value)
+{
+ busyBar->setValue(value);
+}
+
QEasingCurve WidgetCover::getEasingCurve() const
{
return easingCurve;
@@ -192,6 +197,37 @@ bool WidgetCover::eventFilter(QObject* obj, QEvent* e)
return false;
}
+void WidgetCover::displayProgress(int maxValue, const QString& format)
+{
+ if (!busyBar)
+ return;
+
+ busyBar->setRange(0, maxValue);
+ if (!format.isNull())
+ busyBar->setFormat(format);
+
+ busyBar->setTextVisible(true);
+}
+
+void WidgetCover::noDisplayProgress()
+{
+ if (!busyBar)
+ return;
+
+ busyBar->setRange(0, 0);
+ busyBar->setTextVisible(true);
+}
+
+void WidgetCover::initWithProgressBarOnly(const QString& format)
+{
+ busyBar = new QProgressBar();
+ busyBar->setRange(0, 100);
+ busyBar->setFormat(format);
+ busyBar->setTextVisible(true);
+
+ containerLayout->addWidget(busyBar, 0, 0);
+}
+
void WidgetCover::initWithInterruptContainer(const QString& interruptButtonText)
{
cancelButton = new QPushButton();
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h
index d0ccef7..0b7a2f5 100644
--- a/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h
+++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h
@@ -22,16 +22,15 @@ class GUI_API_EXPORT WidgetCover : public QWidget
QEasingCurve getEasingCurve() const;
void setEasingCurve(const QEasingCurve& value);
-
int getDuration() const;
void setDuration(int value);
-
int getTransparency() const;
void setTransparency(int value);
-
QGridLayout* getContainerLayout();
bool eventFilter(QObject* obj, QEvent* e);
-
+ void displayProgress(int maxValue, const QString& format = QString());
+ void noDisplayProgress();
+ void initWithProgressBarOnly(const QString& format);
void initWithInterruptContainer(const QString& interruptButtonText = QString());
private:
@@ -67,6 +66,7 @@ class GUI_API_EXPORT WidgetCover : public QWidget
public slots:
void show();
void hide();
+ void setProgress(int value);
};
#endif // WIDGETCOVER_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp
index 8024abf..ac97283 100644
--- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp
@@ -385,14 +385,19 @@ void SqlQueryModel::commitInternal(const QList<SqlQueryItem*>& items)
// Grouping by row and commiting
QList<QList<SqlQueryItem*>> groupedItems = groupItemsByRows(items);
+ emit aboutToCommit(groupedItems.size());
+
+ int step = 1;
+ rowsDeletedSuccessfullyInTheCommit.clear();
bool ok = true;
- foreach (const QList<SqlQueryItem*>& itemsInRow, groupedItems)
+ for (const QList<SqlQueryItem*>& itemsInRow : groupedItems)
{
if (!commitRow(itemsInRow))
{
ok = false;
break;
}
+ emit commitingStepFinished(step++);
}
// Getting current uncommited list (after rows deletion it may be different)
@@ -417,15 +422,21 @@ void SqlQueryModel::commitInternal(const QList<SqlQueryItem*>& items)
else
{
// Commited successfully
- foreach (SqlQueryItem* item, itemsLeft)
+ for (SqlQueryItem* item : itemsLeft)
{
item->setUncommited(false);
item->setNewRow(false);
}
+ qSort(rowsDeletedSuccessfullyInTheCommit);
+ int removeOffset = 0;
+ for (int row : rowsDeletedSuccessfullyInTheCommit)
+ removeRow(row - removeOffset++); // deleting row decrements all rows below
+
emit commitStatusChanged(getUncommitedItems().size() > 0);
}
}
+ rowsDeletedSuccessfullyInTheCommit.clear();
if (!ok)
{
@@ -447,6 +458,8 @@ void SqlQueryModel::commitInternal(const QList<SqlQueryItem*>& items)
int itemsAddedDeletedDelta = numberOfItemsAdded - numberOfItemsDeleted;
recalculateRowsAndPages(itemsAddedDeletedDelta);
+
+ emit commitFinished();
}
void SqlQueryModel::rollbackInternal(const QList<SqlQueryItem*>& items)
@@ -604,7 +617,8 @@ bool SqlQueryModel::commitDeletedRow(const QList<SqlQueryItem*>& itemsInRow)
}
int row = itemsInRow[0]->index().row();
- return removeRow(row);
+ rowsDeletedSuccessfullyInTheCommit << row;
+ return true;
}
void SqlQueryModel::rollbackAddedRow(const QList<SqlQueryItem*>& itemsInRow)
diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h
index 3e92bb2..062af95 100644
--- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h
+++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h
@@ -351,6 +351,8 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel
*/
QList<bool> columnEditionStatus;
+ QList<int> rowsDeletedSuccessfullyInTheCommit;
+
private slots:
void handleExecFinished(SqlQueryPtr results);
void handleExecFailed(int code, QString errorMessage);
@@ -446,6 +448,10 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel
* Emitted after columns header sorting has been changed.
*/
void sortingUpdated(const QueryExecutor::SortList& sortOrder);
+
+ void aboutToCommit(int totalSteps);
+ void commitingStepFinished(int step);
+ void commitFinished();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SqlQueryModel::Features)
diff --git a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp
index e99b9b8..32efae4 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp
@@ -11,6 +11,7 @@
#include "iconmanager.h"
#include "uiconfig.h"
#include "datagrid/sqlqueryitem.h"
+#include "common/widgetcover.h"
#include <QDebug>
#include <QHeaderView>
#include <QVBoxLayout>
@@ -42,6 +43,7 @@ void DataView::init(SqlQueryModel* model)
formViewRowCountLabel = new QLabel();
formViewCurrentRowLabel = new QLabel();
+ initWidgetCover();
initFormView();
initPageEdit();
initFilter();
@@ -139,6 +141,15 @@ void DataView::initPageEdit()
connect(pageEdit, SIGNAL(editingFinished()), this, SLOT(pageEntered()));
}
+void DataView::initWidgetCover()
+{
+ widgetCover = new WidgetCover(this);
+ widgetCover->initWithProgressBarOnly("%v / %m");
+ connect(model, SIGNAL(aboutToCommit(int)), this, SLOT(coverForGridCommit(int)));
+ connect(model, SIGNAL(commitingStepFinished(int)), this, SLOT(updateGridCommitCover(int)));
+ connect(model, SIGNAL(commitFinished()), this, SLOT(hideGridCommitCover()));
+}
+
void DataView::createActions()
{
bool rowInserting = model->features().testFlag(SqlQueryModel::INSERT_ROW);
@@ -438,6 +449,34 @@ void DataView::filterModeSelected()
actionMap[FILTER]->setIcon(modeAction->icon());
}
+void DataView::coverForGridCommit(int total)
+{
+ if (total <= 3)
+ return;
+
+ widgetCover->displayProgress(total);
+ widgetCover->show();
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+}
+
+void DataView::updateGridCommitCover(int value)
+{
+ if (!widgetCover->isVisible())
+ return;
+
+ widgetCover->setProgress(value);
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+}
+
+void DataView::hideGridCommitCover()
+{
+ if (!widgetCover->isVisible())
+ return;
+
+ widgetCover->hide();
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+}
+
void DataView::updateCommitRollbackActions(bool enabled)
{
gridView->getAction(SqlQueryView::COMMIT)->setEnabled(enabled);
diff --git a/SQLiteStudio3/guiSQLiteStudio/dataview.h b/SQLiteStudio3/guiSQLiteStudio/dataview.h
index 5207fdb..64bbb07 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dataview.h
+++ b/SQLiteStudio3/guiSQLiteStudio/dataview.h
@@ -13,6 +13,7 @@ class FormView;
class ExtLineEdit;
class QLabel;
class IntValidator;
+class WidgetCover;
CFG_KEY_LIST(DataView, QObject::tr("Data view (both grid and form)"),
CFG_KEY_ENTRY(REFRESH_DATA, Qt::Key_F5, QObject::tr("Refresh data"))
@@ -115,6 +116,7 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer
void initUpdates();
void initSlots();
void initPageEdit();
+ void initWidgetCover();
void createContents();
void goToFormRow(IndexModifier idxMod);
void setNavigationState(bool enabled);
@@ -153,6 +155,7 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer
QMutex manualPageChangeMutex;
bool uncommittedGrid = false;
bool uncommittedForm = false;
+ WidgetCover* widgetCover = nullptr;
signals:
@@ -193,6 +196,9 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer
void showFormView();
void updateTabsMode();
void filterModeSelected();
+ void coverForGridCommit(int total);
+ void updateGridCommitCover(int value);
+ void hideGridCommitCover();
};
int qHash(DataView::ActionGroup action);
diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp
index 509594d..1aeff0f 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp
@@ -136,6 +136,7 @@ void DbTree::createActions()
createAction(CLEAR_FILTER, tr("Clear filter"), ui->nameFilter, SLOT(clear()), this);
createAction(REFRESH_SCHEMAS, ICONS.DATABASE_RELOAD, tr("Refresh all database schemas"), this, SLOT(refreshSchemas()), this);
createAction(REFRESH_SCHEMA, ICONS.DATABASE_RELOAD, tr("Refresh selected database schema"), this, SLOT(refreshSchema()), this);
+ createAction(ERASE_TABLE_DATA, ICONS.ERASE_TABLE_DATA, tr("Erase table data"), this, SLOT(eraseTableData()), this);
}
void DbTree::updateActionStates(const QStandardItem *item)
@@ -189,7 +190,7 @@ void DbTree::updateActionStates(const QStandardItem *item)
break;
case DbTreeItem::Type::TABLE:
enabled << EDIT_TABLE << DEL_TABLE << EXPORT_TABLE << IMPORT_TABLE << POPULATE_TABLE << ADD_COLUMN << CREATE_SIMILAR_TABLE;
- enabled << RESET_AUTOINCREMENT << ADD_INDEX << ADD_TRIGGER;
+ enabled << RESET_AUTOINCREMENT << ADD_INDEX << ADD_TRIGGER << ERASE_TABLE_DATA;
break;
case DbTreeItem::Type::VIRTUAL_TABLE:
// TODO change below when virtual tables can be edited
@@ -393,6 +394,7 @@ void DbTree::setupActionsForMenu(DbTreeItem* currItem, QMenu* contextMenu)
actions += ActionEntry(POPULATE_TABLE);
actions += ActionEntry(CREATE_SIMILAR_TABLE);
actions += ActionEntry(RESET_AUTOINCREMENT);
+ actions += ActionEntry(ERASE_TABLE_DATA);
actions += ActionEntry(_separator);
actions += dbEntryExt;
break;
@@ -1399,6 +1401,35 @@ void DbTree::resetAutoincrement()
notifyInfo(tr("Autoincrement value for table '%1' has been reset successfly.").arg(table));
}
+void DbTree::eraseTableData()
+{
+ Db* db = getSelectedDb();
+ if (!db || !db->isValid())
+ return;
+
+ DbTreeItem* item = ui->treeView->currentItem();
+ QString table = item->getTable();
+ if (table.isNull())
+ {
+ qWarning() << "Tried to erase table data, while table wasn't selected in DbTree.";
+ return;
+ }
+
+ QMessageBox::StandardButton btn = QMessageBox::question(this, tr("Erase table data"), tr("Are you sure you want to delete all data from table '%1'?")
+ .arg(table));
+ if (btn != QMessageBox::Yes)
+ return;
+
+ SqlQueryPtr res = db->exec(QString("DELETE FROM %1;").arg(wrapObjIfNeeded(table, db->getDialect())));
+ if (res->isError())
+ {
+ notifyError(tr("An error occurred while trying to delete data from table '%1': %2").arg(table, res->getErrorText()));
+ return;
+ }
+
+ notifyInfo(tr("All data has been deleted for table '%1'.").arg(table));
+}
+
void DbTree::addColumn(DbTreeItem* item)
{
Db* db = getSelectedOpenDb();
@@ -1591,6 +1622,11 @@ void DbTree::setupDefShortcuts()
BIND_SHORTCUTS(DbTree, Action);
}
+void DbTree::closeEvent(QCloseEvent *e)
+{
+ e->ignore();
+}
+
int qHash(DbTree::Action action)
{
return static_cast<int>(action);
diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h
index 60b8dd5..2f5583e 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h
+++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h
@@ -84,6 +84,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer
REFRESH_SCHEMA,
CREATE_SIMILAR_TABLE,
RESET_AUTOINCREMENT,
+ ERASE_TABLE_DATA,
_separator // Never use it directly, it's just for menu setup
};
@@ -116,6 +117,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer
protected:
void createActions();
void setupDefShortcuts();
+ void closeEvent(QCloseEvent* e);
private:
void setActionEnabled(int action, bool enabled);
@@ -187,6 +189,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer
void integrityCheck();
void createSimilarTable();
void resetAutoincrement();
+ void eraseTableData();
void addColumn(DbTreeItem* item);
void editColumn(DbTreeItem* item);
void delColumn(DbTreeItem* item);
diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp
index a4e736f..78f0db8 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp
@@ -250,8 +250,19 @@ void DbTreeModel::restoreGroup(const Config::DbGroupPtr& group, QList<Db*>* dbLi
{
if (db)
{
- if (db->open())
+ // If the db was stored in cfg as open, it was already open by DbManager.
+ // Now the DbTreeModel didn't catch that (as it didn't exist yet), so we need to
+ // call handler for 'connected' event, instead of forcing another open call.
+ // Otherwise the database that could not be open would be requested to open twice:
+ // 1. when restoring DbManager
+ // 2. here
+ // Instead of that, we just check if the database is already open (by DbManager)
+ // and call proper handler to refresh database's schema and create tree nodes.
+ if (db->isOpen())
+ {
+ dbConnected(db);
treeView->expand(item->index());
+ }
}
else
{
@@ -401,8 +412,12 @@ QString DbTreeModel::getDbToolTip(DbTreeItem* item) const
QStringList rows;
Db* db = item->getDb();
- QFile dbFile(db->getPath());
QString iconPath = db->isValid() ? ICONS.DATABASE.toImgSrc() : ICONS.DATABASE_INVALID.toImgSrc();
+ int fileSize = -1;
+
+ QUrl url(db->getPath());
+ if (url.scheme().isEmpty() || url.scheme() == "file")
+ fileSize = QFile(db->getPath()).size();
rows << toolTipHdrRowTmp.arg(iconPath).arg(tr("Database: %1", "dbtree tooltip").arg(db->getName()));
rows << toolTipRowTmp.arg("URI:").arg(db->getPath());
@@ -410,13 +425,17 @@ QString DbTreeModel::getDbToolTip(DbTreeItem* item) const
if (db->isValid())
{
rows << toolTipRowTmp.arg(tr("Version:", "dbtree tooltip")).arg(QString("SQLite %1").arg(db->getVersion()));
- rows << toolTipRowTmp.arg(tr("File size:", "dbtree tooltip")).arg(formatFileSize(dbFile.size()));
- rows << toolTipRowTmp.arg(tr("Encoding:", "dbtree tooltip")).arg(db->getEncoding());
+
+ if (fileSize > -1)
+ rows << toolTipRowTmp.arg(tr("File size:", "dbtree tooltip")).arg(formatFileSize(fileSize));
+
+ if (db->isOpen())
+ rows << toolTipRowTmp.arg(tr("Encoding:", "dbtree tooltip")).arg(db->getEncoding());
}
else
{
InvalidDb* idb = dynamic_cast<InvalidDb*>(db);
- rows << toolTipRowTmp.arg(tr("Error details:", "dbtree tooltip")).arg(idb->getError());
+ rows << toolTipRowTmp.arg(tr("Error:", "dbtree tooltip")).arg(idb->getError());
}
return toolTipTableTmp.arg(rows.join(""));
diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp
index 05d50af..ac7cd8a 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp
@@ -455,10 +455,6 @@ bool DbDialog::testDatabase()
if (url.scheme().isEmpty())
url.setScheme("file");
- bool existed = false;
- if (url.isLocalFile() && QFile::exists(path))
- existed = QFile::exists(path);
-
QHash<QString, QVariant> options = collectOptions();
DbPlugin* plugin = dbPlugins[ui->typeCombo->currentText()];
Db* testDb = plugin->getInstance("", path, options);
@@ -466,15 +462,14 @@ bool DbDialog::testDatabase()
bool res = false;
if (testDb)
{
- res = true;
+ if (testDb->openForProbing())
+ {
+ res = !testDb->getEncoding().isEmpty();
+ testDb->closeQuiet();
+ }
delete testDb;
}
- if (!existed)
- {
- QFile file(path);
- file.remove();
- }
return res;
}
@@ -605,7 +600,12 @@ void DbDialog::browseClicked()
{
if (customBrowseHandler)
{
- customBrowseHandler(ui->fileEdit->text());
+ QString newUrl = customBrowseHandler(ui->fileEdit->text());
+ if (!newUrl.isNull())
+ {
+ ui->fileEdit->setText(newUrl);
+ updateState();
+ }
return;
}
diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp
index ca3fd31..7861ff0 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp
@@ -51,7 +51,9 @@ void PopulateDialog::init()
pluginTitles << plugin->getTitle();
widgetCover = new WidgetCover(this);
+ widgetCover->initWithInterruptContainer(tr("Abort"));
widgetCover->setVisible(false);
+ connect(widgetCover, SIGNAL(cancelClicked()), POPULATE_MANAGER, SLOT(interrupt()));
ui->scrollArea->setAutoFillBackground(false);
ui->scrollArea->viewport()->setAutoFillBackground(false);
@@ -71,6 +73,7 @@ void PopulateDialog::init()
connect(ui->databaseCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(refreshTables()));
connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(refreshColumns()));
connect(POPULATE_MANAGER, SIGNAL(populatingFinished()), widgetCover, SLOT(hide()));
+ connect(POPULATE_MANAGER, SIGNAL(finishedStep(int)), widgetCover, SLOT(setProgress(int)));
connect(POPULATE_MANAGER, SIGNAL(populatingSuccessful()), this, SLOT(finished()));
}
@@ -317,10 +320,20 @@ void PopulateDialog::accept()
QString table = ui->tableCombo->currentText();
qint64 rows = ui->rowsSpin->value();
+ started = true;
+ widgetCover->displayProgress(rows, "%v / %m");
widgetCover->show();
POPULATE_MANAGER->populate(db, table, engines, rows);
}
+void PopulateDialog::reject()
+{
+ if (started)
+ POPULATE_MANAGER->interrupt();
+
+ QDialog::reject();
+}
+
PopulateDialog::ColumnEntry::ColumnEntry(QCheckBox* check, QComboBox* combo, QToolButton* button) :
check(check), combo(combo), button(button)
{
diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h
index 0ecc318..948d6ce 100644
--- a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h
+++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h
@@ -58,6 +58,7 @@ class GUI_API_EXPORT PopulateDialog : public QDialog
QSignalMapper* buttonMapper = nullptr;
QHash<int,bool> columnsValid;
WidgetCover* widgetCover = nullptr;
+ bool started = false;
private slots:
void refreshTables();
@@ -71,6 +72,7 @@ class GUI_API_EXPORT PopulateDialog : public QDialog
public:
void accept();
+ void reject();
};
#endif // POPULATEDIALOG_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/iconmanager.h b/SQLiteStudio3/guiSQLiteStudio/iconmanager.h
index cf4f2ec..940c869 100644
--- a/SQLiteStudio3/guiSQLiteStudio/iconmanager.h
+++ b/SQLiteStudio3/guiSQLiteStudio/iconmanager.h
@@ -90,6 +90,7 @@ class GUI_API_EXPORT IconManager : public QObject
DEF_ICON(DATABASE_ONLINE, "database_online")
DEF_ICON(DATABASE_RELOAD, "database_reload")
DEF_ICON(DDL_HISTORY, "ddl_history")
+ DEF_ICON(DELETE, "delete")
DEF_ICON(DELETE_ROW, "delete_row")
DEF_ICO3(DELETE_COLLATION, DELETE_ROW)
DEF_ICO3(DELETE_DATATYPE, DELETE_ROW)
@@ -104,6 +105,7 @@ class GUI_API_EXPORT IconManager : public QObject
DEF_ICON(DIRECTORY_OPEN_WITH_DB, "directory_open_with_db")
DEF_ICON(DIRECTORY_WITH_DB, "directory_with_db")
DEF_ICON(ERASE, "erase")
+ DEF_ICON(ERASE_TABLE_DATA, "erase_table_data")
DEF_ICON(EXEC_QUERY, "exec_query")
DEF_ICON(EXPLAIN_QUERY, "explain_query")
DEF_ICON(EXPORT, "export")
diff --git a/SQLiteStudio3/guiSQLiteStudio/icons.qrc b/SQLiteStudio3/guiSQLiteStudio/icons.qrc
index 6612814..9cf981d 100644
--- a/SQLiteStudio3/guiSQLiteStudio/icons.qrc
+++ b/SQLiteStudio3/guiSQLiteStudio/icons.qrc
@@ -191,5 +191,7 @@
<file>img/go_back.png</file>
<file>img/reset_autoincrement.png</file>
<file>img/plus.png</file>
+ <file>img/erase_table_data.png</file>
+ <file>img/delete.png</file>
</qresource>
</RCC>
diff --git a/SQLiteStudio3/guiSQLiteStudio/img/delete.png b/SQLiteStudio3/guiSQLiteStudio/img/delete.png
new file mode 100644
index 0000000..20d6f5e
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/img/delete.png
Binary files differ
diff --git a/SQLiteStudio3/guiSQLiteStudio/img/erase_table_data.png b/SQLiteStudio3/guiSQLiteStudio/img/erase_table_data.png
new file mode 100644
index 0000000..a07585a
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/img/erase_table_data.png
Binary files differ
diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts
index 26e0fb1..42a735f 100644
--- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts
+++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_sk.ts
@@ -1028,7 +1028,7 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="501"/>
<source>Execute only the query under the cursor</source>
- <translation type="unfinished"></translation>
+ <translation>Vykonať len dotaz, na ktorom stojí kurzor</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="514"/>
@@ -1089,12 +1089,12 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="688"/>
<source>Sort table columns alphabetically</source>
- <translation type="unfinished"></translation>
+ <translation>Zoradiť stĺpce tabuľky abecedne</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="698"/>
<source>Expand tables node when connected to a database</source>
- <translation type="unfinished"></translation>
+ <translation>Rozbaliť zoznam tabuliek po pripojení k databáze</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="708"/>
@@ -1104,7 +1104,7 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="711"/>
<source>Display additional labels on the list</source>
- <translation type="unfinished"></translation>
+ <translation>Zobraziť doplnkové popisky v zozname</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="726"/>
@@ -1114,7 +1114,7 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="729"/>
<source>Display labels for regular tables</source>
- <translation type="unfinished"></translation>
+ <translation>Zobraziť popisky pre regulárne tabuľky</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="739"/>
@@ -1124,12 +1124,12 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="742"/>
<source>Display labels for virtual tables</source>
- <translation type="unfinished"></translation>
+ <translation>Zobraziť popisky pre virtuálne tabuľky</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="755"/>
<source>Expand views node when connected to a database</source>
- <translation type="unfinished"></translation>
+ <translation>Rozbaliť zoznam pohľadov po pripojení k databáze</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="765"/>
@@ -1139,42 +1139,42 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="768"/>
<source>Sort objects (tables, indexes, triggers and views) alphabetically</source>
- <translation type="unfinished"></translation>
+ <translation>Zoradiť objekty (tabuľky, indexy, spúšťače a pohľady) abecedne</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="778"/>
<source>Display system tables and indexes on the list</source>
- <translation type="unfinished"></translation>
+ <translation>Zobraziť systémové tabuľky a indexy v zozname</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="791"/>
<source>Table windows</source>
- <translation type="unfinished"></translation>
+ <translation>Okná tabuľky</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="797"/>
<source>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</source>
- <translation type="unfinished"></translation>
+ <translation>Ak je táto možnosť zaškrtnutá, tak sa v okne zobrazia dáta a nie štruktúra tabuľky.</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="800"/>
<source>Open Table Windows with the data tab for start</source>
- <translation type="unfinished"></translation>
+ <translation>Zobraziť dáta po otvorení tabuľky</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="813"/>
<source>View windows</source>
- <translation type="unfinished"></translation>
+ <translation>Okná pohľadov</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="819"/>
<source>When enabled, View Windows will show up with the data tab, instead of the structure tab.</source>
- <translation type="unfinished"></translation>
+ <translation>Ak je táto možnosť zaškrtnutá, tak sa v okne zobrazia dáta a nie SQL dotaz.</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="822"/>
<source>Open View Windows with the data tab for start</source>
- <translation type="unfinished"></translation>
+ <translation>Zobraziť dáta po otvorení pohľadu</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="933"/>
@@ -1184,22 +1184,22 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="962"/>
<source>Current style:</source>
- <translation type="unfinished"></translation>
+ <translation>Aktuálny štýl:</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="979"/>
<source>Preview</source>
- <translation type="unfinished"></translation>
+ <translation>Náhľad</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="989"/>
<source>Enabled</source>
- <translation type="unfinished"></translation>
+ <translation>Zapnutý</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="1162"/>
<source>Disabled</source>
- <translation type="unfinished"></translation>
+ <translation>Vypnutý</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="1211"/>
@@ -1209,17 +1209,17 @@ but it&apos;s okay to use it.</source>
<message>
<location filename="../dialogs/configdialog.ui" line="1249"/>
<source>SQL editor font</source>
- <translation type="unfinished"></translation>
+ <translation>Písmo SQL editora</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="1265"/>
<source>Database list font</source>
- <translation type="unfinished"></translation>
+ <translation>Font zoznamu databáz</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="1281"/>
<source>Database list additional label font</source>
- <translation type="unfinished"></translation>
+ <translation>Font doplnkového popisku</translation>
</message>
<message>
<location filename="../dialogs/configdialog.ui" line="1297"/>
@@ -1835,7 +1835,7 @@ Prezeranie ďalších strán bude možné až po dokončení spočítavania.</tr
<message>
<location filename="../dialogs/dbdialog.ui" line="26"/>
<source>Database type</source>
- <translation type="unfinished"></translation>
+ <translation>Typ databázy</translation>
</message>
<message>
<location filename="../dialogs/dbdialog.ui" line="32"/>
@@ -1845,22 +1845,22 @@ Prezeranie ďalších strán bude možné až po dokončení spočítavania.</tr
<message>
<location filename="../dialogs/dbdialog.ui" line="95"/>
<source>Generate automatically</source>
- <translation type="unfinished"></translation>
+ <translation>Generovať automaticky</translation>
</message>
<message>
<location filename="../dialogs/dbdialog.ui" line="108"/>
<source>Options</source>
- <translation type="unfinished">Voľby</translation>
+ <translation>Voľby</translation>
</message>
<message>
<location filename="../dialogs/dbdialog.ui" line="117"/>
<source>Permanent (keep it in configuration)</source>
- <translation type="unfinished"></translation>
+ <translation>Zapamätať si databázu</translation>
</message>
<message>
<location filename="../dialogs/dbdialog.ui" line="161"/>
<source>Test connection</source>
- <translation type="unfinished"></translation>
+ <translation>Test spojenia</translation>
</message>
<message>
<source>Name</source>
@@ -1877,7 +1877,7 @@ Prezeranie ďalších strán bude možné až po dokončení spočítavania.</tr
<message>
<location filename="../dialogs/dbdialog.ui" line="51"/>
<source>Create new database file</source>
- <translation type="unfinished"></translation>
+ <translation>Vytvoriť nový databázový súbor</translation>
</message>
<message>
<location filename="../dialogs/dbdialog.ui" line="42"/>
@@ -1888,7 +1888,7 @@ Prezeranie ďalších strán bude možné až po dokončení spočítavania.</tr
<message>
<location filename="../dialogs/dbdialog.ui" line="79"/>
<source>Name (on the list)</source>
- <translation type="unfinished"></translation>
+ <translation>Názov (v zozname)</translation>
</message>
<message>
<location filename="../dialogs/dbdialog.ui" line="92"/>
@@ -1912,7 +1912,7 @@ Prezeranie ďalších strán bude možné až po dokončení spočítavania.</tr
<message>
<location filename="../dialogs/dbdialog.cpp" line="150"/>
<source>Browse for existing database file on local computer</source>
- <translation type="unfinished"></translation>
+ <translation>Hľadať databázový súbor na lokálnom počítači</translation>
</message>
<message>
<location filename="../dialogs/dbdialog.cpp" line="283"/>
@@ -1947,7 +1947,7 @@ Prezeranie ďalších strán bude možné až po dokončení spočítavania.</tr
<message>
<location filename="../dialogs/dbdialog.cpp" line="585"/>
<source>Auto-generated</source>
- <translation type="unfinished"></translation>
+ <translation>Automaticky vygenerovaný</translation>
</message>
<message>
<source>The name will be auto-generated</source>
@@ -3325,7 +3325,7 @@ Please enter new, unique name, or press &apos;%1&apos; to abort the operation:</
<message>
<location filename="../dialogs/indexdialog.cpp" line="454"/>
<source>Cannot create unique index, because values in selected columns are not unique. Would you like to execute SELECT query to see problematic values?</source>
- <translation type="unfinished"></translation>
+ <translation>Nemôžem vytvoriť jedinečný index, pretože hodnoty vo vybraných stĺpcoch nie sú jedinečné. Chcete spustiť dotaz SELECT na zobrazenie problematických hodnôt?</translation>
</message>
<message>
<location filename="../dialogs/indexdialog.cpp" line="466"/>
@@ -4265,7 +4265,7 @@ Please enter new, unique name, or press &apos;%1&apos; to abort the operation:</
<message>
<location filename="../multieditor/multieditortext.h" line="12"/>
<source>Cell text value editor</source>
- <translation type="unfinished"></translation>
+ <translation>Úprava hodnôt v bunkách</translation>
</message>
<message>
<location filename="../multieditor/multieditortext.h" line="13"/>
@@ -4401,7 +4401,7 @@ Please enter new, unique name, or press &apos;%1&apos; to abort the operation:</
<message>
<location filename="../windows/editorwindow.h" line="26"/>
<source>SQL editor window</source>
- <translation type="unfinished"></translation>
+ <translation>Okno SQL editora</translation>
</message>
<message>
<location filename="../windows/editorwindow.h" line="27"/>
@@ -4411,42 +4411,42 @@ Please enter new, unique name, or press &apos;%1&apos; to abort the operation:</
<message>
<location filename="../windows/editorwindow.h" line="28"/>
<source>Execute &quot;%1&quot; query</source>
- <translation type="unfinished"></translation>
+ <translation>Vykonať &quot;%1&quot; dotaz</translation>
</message>
<message>
<location filename="../windows/editorwindow.h" line="29"/>
<source>Switch current working database to previous on the list</source>
- <translation type="unfinished"></translation>
+ <translation>Prepnúť sa na predchádzajúcu databázu v zozname</translation>
</message>
<message>
<location filename="../windows/editorwindow.h" line="30"/>
<source>Switch current working database to next on the list</source>
- <translation type="unfinished"></translation>
+ <translation>Prepnúť sa na nasledujúcu databázu v zozname</translation>
</message>
<message>
<location filename="../windows/editorwindow.h" line="31"/>
<source>Go to next editor tab</source>
- <translation type="unfinished"></translation>
+ <translation>Prechod na nasledujúcu záložku editora</translation>
</message>
<message>
<location filename="../windows/editorwindow.h" line="32"/>
<source>Go to previous editor tab</source>
- <translation type="unfinished"></translation>
+ <translation>Prechod na predchádzajúcu záložku editora</translation>
</message>
<message>
<location filename="../windows/editorwindow.h" line="33"/>
<source>Move keyboard input focus to the results view below</source>
- <translation type="unfinished"></translation>
+ <translation>Prepnúť kurzor na výsledky</translation>
</message>
<message>
<location filename="../windows/editorwindow.h" line="34"/>
<source>Move keyboard input focus to the SQL editor above</source>
- <translation type="unfinished"></translation>
+ <translation>Prepnúť kurzor do editora</translation>
</message>
<message>
<location filename="../windows/tablewindow.h" line="30"/>
<source>Table window</source>
- <translation type="unfinished"></translation>
+ <translation>Okno tabuľky</translation>
</message>
<message>
<location filename="../windows/tablewindow.h" line="31"/>
@@ -4491,7 +4491,7 @@ Please enter new, unique name, or press &apos;%1&apos; to abort the operation:</
<message>
<location filename="../windows/tablewindow.h" line="39"/>
<source>Delete selected table constraint</source>
- <translation>Vymazaťˇvybrané obmedzenie</translation>
+ <translation>Vymazať vybrané obmedzenie</translation>
</message>
<message>
<location filename="../windows/tablewindow.h" line="40"/>
diff --git a/SQLiteStudio3/guiSQLiteStudio/uiconfig.h b/SQLiteStudio3/guiSQLiteStudio/uiconfig.h
index 3372979..b434dc8 100644
--- a/SQLiteStudio3/guiSQLiteStudio/uiconfig.h
+++ b/SQLiteStudio3/guiSQLiteStudio/uiconfig.h
@@ -85,9 +85,9 @@ CFG_CATEGORIES(Ui,
)
)
-QString getFileDialogInitPath();
-void setFileDialogInitPath(const QString& path);
-void setFileDialogInitPathByFile(const QString& filePath);
+GUI_API_EXPORT QString getFileDialogInitPath();
+GUI_API_EXPORT void setFileDialogInitPath(const QString& path);
+GUI_API_EXPORT void setFileDialogInitPathByFile(const QString& filePath);
#define CFG_UI CFG_INSTANCE(Ui)
diff --git a/SQLiteStudio3/guiSQLiteStudio/uidebug.cpp b/SQLiteStudio3/guiSQLiteStudio/uidebug.cpp
index 00aaac4..a2ce9f8 100644
--- a/SQLiteStudio3/guiSQLiteStudio/uidebug.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/uidebug.cpp
@@ -12,6 +12,8 @@ bool UI_DEBUG_ENABLED = false;
bool UI_DEBUG_CONSOLE = true;
QString UI_DEBUG_FILE;
+QStringList MsgHandlerThreadProxy::ignoredWarnings;
+
void uiMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if (!UI_DEBUG_ENABLED)
@@ -100,6 +102,9 @@ MsgHandlerThreadProxy::~MsgHandlerThreadProxy()
void MsgHandlerThreadProxy::init()
{
+ ignoredWarnings << QStringLiteral("libpng warning: Unknown iTXt compression type or method");
+ ignoredWarnings << QStringLiteral("QPainter::font: Painter not active");
+
if (sqliteStudioUiDebugConsole)
{
connect(this, SIGNAL(debugRequested(QString)), sqliteStudioUiDebugConsole, SLOT(debug(QString)));
@@ -139,6 +144,9 @@ void MsgHandlerThreadProxy::debug(const QString &msg)
void MsgHandlerThreadProxy::warn(const QString &msg)
{
+ if (ignoredWarnings.contains(msg.mid(25)))
+ return;
+
emit warnRequested(msg);
}
diff --git a/SQLiteStudio3/guiSQLiteStudio/uidebug.h b/SQLiteStudio3/guiSQLiteStudio/uidebug.h
index d2a2a51..449eb29 100644
--- a/SQLiteStudio3/guiSQLiteStudio/uidebug.h
+++ b/SQLiteStudio3/guiSQLiteStudio/uidebug.h
@@ -20,6 +20,8 @@ class GUI_API_EXPORT MsgHandlerThreadProxy : public QObject
void init();
void initFile(const QString& fileName);
+ static QStringList ignoredWarnings;
+
QFile* outFile = nullptr;
QTextStream outFileStream;
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
index 77a4adc..3a315db 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
@@ -621,20 +621,20 @@ bool TableWindow::restoreSession(const QVariant& sessionValue)
QHash<QString, QVariant> value = sessionValue.toHash();
if (value.size() == 0)
{
- notifyWarn("Could not restore window, because no database or table was stored in session for this window.");
+ notifyWarn(tr("Could not restore window %1, because no database or table was stored in session for this window.").arg(value["title"].toString()));
return false;
}
if (!value.contains("db") || !value.contains("table"))
{
- notifyWarn("Could not restore window, because no database or table was stored in session for this window.");
+ notifyWarn(tr("Could not restore window '%1', because no database or table was stored in session for this window.").arg(value["title"].toString()));
return false;
}
db = DBLIST->getByName(value["db"].toString());
if (!db || !db->isValid() || (!db->isOpen() && !db->open()))
{
- notifyWarn(tr("Could not restore window, because database %1 could not be resolved.").arg(value["db"].toString()));
+ notifyWarn(tr("Could not restore window '%1', because database %2 could not be resolved.").arg(value["title"].toString(), value["db"].toString()));
return false;
}
@@ -643,7 +643,7 @@ bool TableWindow::restoreSession(const QVariant& sessionValue)
SchemaResolver resolver(db);
if (!resolver.getTables(database).contains(table, Qt::CaseInsensitive))
{
- notifyWarn(tr("Could not restore window, because the table %1 doesn't exist in the database %2.").arg(table).arg(db->getName()));
+ notifyWarn(tr("Could not restore window '%1'', because the table %2 doesn't exist in the database %3.").arg(value["title"].toString(), table, db->getName()));
return false;
}
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
index a699801..9a30e1c 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
@@ -99,26 +99,26 @@ bool ViewWindow::restoreSession(const QVariant& sessionValue)
QHash<QString, QVariant> value = sessionValue.toHash();
if (value.size() == 0)
{
- notifyWarn("Could not restore window, because no database or view was stored in session for this window.");
+ notifyWarn(tr("Could not restore window '%1', because no database or view was stored in session for this window.").arg(value["title"].toString()));
return false;
}
if (!value.contains("db") || !value.contains("view"))
{
- notifyWarn("Could not restore window, because no database or view was stored in session for this window.");
+ notifyWarn(tr("Could not restore window '%1', because no database or view was stored in session for this window.").arg(value["title"].toString()));
return false;
}
db = DBLIST->getByName(value["db"].toString());
if (!db)
{
- notifyWarn(tr("Could not restore window, because database %1 could not be resolved.").arg(value["db"].toString()));
+ notifyWarn(tr("Could not restore window '%1', because database %2 could not be resolved.").arg(value["title"].toString(), value["db"].toString()));
return false;
}
if (!db->isOpen() && !db->open())
{
- notifyWarn(tr("Could not restore window, because database %1 could not be open.").arg(value["db"].toString()));
+ notifyWarn(tr("Could not restore window '%1', because database %2 could not be open.").arg(value["title"].toString(), value["db"].toString()));
return false;
}
@@ -127,7 +127,7 @@ bool ViewWindow::restoreSession(const QVariant& sessionValue)
SchemaResolver resolver(db);
if (!resolver.getViews(database).contains(view, Qt::CaseInsensitive))
{
- notifyWarn(tr("Could not restore window, because the view %1 doesn't exist in the database %2.").arg(view).arg(db->getName()));
+ notifyWarn(tr("Could not restore window '%1', because the view %2 doesn't exist in the database %3.").arg(value["title"].toString(), view, db->getName()));
return false;
}
diff --git a/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro b/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro
index 8ee61f9..4e3bae0 100644
--- a/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro
+++ b/SQLiteStudio3/sqlitestudiocli/sqlitestudiocli.pro
@@ -21,8 +21,10 @@ TEMPLATE = app
CONFIG += c++11
QMAKE_CXXFLAGS += -pedantic
-linux|portable {
- QMAKE_LFLAGS += -Wl,-rpath,./lib
+linux {
+ portable {
+ QMAKE_LFLAGS += -Wl,-rpath,./lib
+ }
}
TRANSLATIONS += translations/sqlitestudiocli_zh_CN.ts \
@@ -71,7 +73,7 @@ win32: {
}
unix: {
- LIBS += -lreadline -ltermcap
+ LIBS += -lreadline -lcurses
}
HEADERS += \