diff options
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/plugins')
16 files changed, 244 insertions, 127 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp index 4a19489..854e7b4 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp @@ -10,6 +10,9 @@ Db* DbPluginSqlite3::getInstance(const QString& name, const QString& path, const if (!db->openForProbing()) { + if (errorMessage) + *errorMessage = db->getErrorText(); + delete db; return nullptr; } @@ -17,9 +20,13 @@ Db* DbPluginSqlite3::getInstance(const QString& name, const QString& path, const SqlQueryPtr results = db->exec("SELECT * FROM sqlite_master"); if (results->isError()) { + if (errorMessage) + *errorMessage = db->getErrorText(); + delete db; return nullptr; } + results.clear(); db->closeQuiet(); return db; diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h index 79b762a..417c93e 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h @@ -10,7 +10,7 @@ class DbPluginSqlite3 : public BuiltInPlugin, public DbPlugin SQLITESTUDIO_PLUGIN_TITLE("SQLite 3") SQLITESTUDIO_PLUGIN_DESC("SQLite 3 databases support.") - SQLITESTUDIO_PLUGIN_VERSION(10000) + SQLITESTUDIO_PLUGIN_VERSION(10001) SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl") public: diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp index b890c26..5887eac 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginstdfilebase.cpp @@ -11,6 +11,9 @@ Db *DbPluginStdFileBase::getInstance(const QString &name, const QString &path, c if (!db->openForProbing())
{
+ if (errorMessage)
+ *errorMessage = db->getErrorText();
+
delete db;
return nullptr;
}
@@ -18,6 +21,9 @@ Db *DbPluginStdFileBase::getInstance(const QString &name, const QString &path, c SqlQueryPtr results = db->exec("SELECT * FROM sqlite_master");
if (results->isError())
{
+ if (errorMessage)
+ *errorMessage = db->getErrorText();
+
delete db;
return nullptr;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp index 149e92b..e1d9f98 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp @@ -22,7 +22,7 @@ bool PopulateRandomEngine::beforePopulating(Db* db, const QString& table) { UNUSED(db); UNUSED(table); - QRandomGenerator::system()->seed(QDateTime::currentDateTime().toTime_t()); + randomGenerator = QRandomGenerator::securelySeeded(); range = cfg.PopulateRandom.MaxValue.get() - cfg.PopulateRandom.MinValue.get() + 1; return (range > 0); } @@ -30,7 +30,7 @@ bool PopulateRandomEngine::beforePopulating(Db* db, const QString& table) QVariant PopulateRandomEngine::nextValue(bool& nextValueError) { UNUSED(nextValueError); - QString randValue = QString::number((QRandomGenerator::system()->generate() % range) + cfg.PopulateRandom.MinValue.get()); + QString randValue = QString::number((randomGenerator.generate() % range) + cfg.PopulateRandom.MinValue.get()); return (cfg.PopulateRandom.Prefix.get() + randValue + cfg.PopulateRandom.Suffix.get()); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h index f4e9feb..29bb02b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h @@ -4,6 +4,7 @@ #include "builtinplugin.h" #include "populateplugin.h" #include "config_builder.h" +#include <QRandomGenerator> CFG_CATEGORIES(PopulateRandomConfig, CFG_CATEGORY(PopulateRandom, @@ -43,5 +44,6 @@ class PopulateRandomEngine : public PopulateEngine private: CFG_LOCAL(PopulateRandomConfig, cfg) int range; + QRandomGenerator randomGenerator; }; #endif // POPULATERANDOM_H diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp index e2f8733..33ac1d6 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp @@ -23,7 +23,7 @@ bool PopulateRandomTextEngine::beforePopulating(Db* db, const QString& table) { UNUSED(db); UNUSED(table); - QRandomGenerator::system()->seed(QDateTime::currentDateTime().toTime_t()); + randomGenerator = QRandomGenerator::securelySeeded(); range = cfg.PopulateRandomText.MaxLength.get() - cfg.PopulateRandomText.MinLength.get() + 1; chars = ""; @@ -55,7 +55,7 @@ bool PopulateRandomTextEngine::beforePopulating(Db* db, const QString& table) QVariant PopulateRandomTextEngine::nextValue(bool& nextValueError) { UNUSED(nextValueError); - int lgt = (QRandomGenerator::system()->generate() % range) + cfg.PopulateRandomText.MinLength.get(); + int lgt = (randomGenerator.generate() % range) + cfg.PopulateRandomText.MinLength.get(); return randStr(lgt, chars); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h index 892b302..fdc5c43 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h @@ -4,6 +4,7 @@ #include "builtinplugin.h" #include "populateplugin.h" #include "config_builder.h" +#include <QRandomGenerator> CFG_CATEGORIES(PopulateRandomTextConfig, CFG_CATEGORY(PopulateRandomText, @@ -47,6 +48,7 @@ class PopulateRandomTextEngine : public PopulateEngine CFG_LOCAL(PopulateRandomTextConfig, cfg) int range; QString chars; + QRandomGenerator randomGenerator; }; #endif // POPULATERANDOMTEXT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp index 79a8ac1..8c356f9 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp @@ -4,6 +4,27 @@ #include "services/pluginmanager.h" #include "services/notifymanager.h" +class PopulateFunctionInfoImpl : public ScriptingPlugin::FunctionInfo +{ + public: + PopulateFunctionInfoImpl(bool rowCount) + { + args = QStringList({"dbName", "tableName"}); + if (rowCount) + args << "rowCount"; + } + + QString getName() const {return QString();} + QStringList getArguments() const {return args;} + bool getUndefinedArgs() const {return false;} + + private: + QStringList args; +}; + +PopulateFunctionInfoImpl populateInitFunctionInfo(false); +PopulateFunctionInfoImpl populateNextFunctionInfo(true); + PopulateScript::PopulateScript() { } @@ -49,9 +70,9 @@ bool PopulateScriptEngine::beforePopulating(Db* db, const QString& table) if (!initCode.trimmed().isEmpty()) { if (dbAwarePlugin) - dbAwarePlugin->evaluate(context, initCode, evalArgs, db); + dbAwarePlugin->evaluate(context, initCode, populateInitFunctionInfo, evalArgs, db); else - scriptingPlugin->evaluate(context, initCode, evalArgs); + scriptingPlugin->evaluate(context, initCode, populateInitFunctionInfo, evalArgs); if (scriptingPlugin->hasError(context)) { @@ -71,9 +92,9 @@ QVariant PopulateScriptEngine::nextValue(bool& nextValueError) { QVariant result; if (dbAwarePlugin) - result = dbAwarePlugin->evaluate(context, cfg.PopulateScript.Code.get(), evalArgs, db); + result = dbAwarePlugin->evaluate(context, cfg.PopulateScript.Code.get(), populateNextFunctionInfo, evalArgs, db); else - result = scriptingPlugin->evaluate(context, cfg.PopulateScript.Code.get(), evalArgs); + result = scriptingPlugin->evaluate(context, cfg.PopulateScript.Code.get(), populateNextFunctionInfo, evalArgs); if (scriptingPlugin->hasError(context)) { diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h index d081073..80aa758 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h @@ -15,33 +15,45 @@ class ScriptingPlugin : virtual public Plugin virtual ~Context() {} }; + class FunctionInfo + { + public: + virtual QString getName() const = 0; + virtual QStringList getArguments() const = 0; + virtual bool getUndefinedArgs() const = 0; + }; + virtual QString getLanguage() const = 0; virtual Context* createContext() = 0; virtual void releaseContext(Context* context) = 0; virtual void resetContext(Context* context) = 0; virtual void setVariable(Context* context, const QString& name, const QVariant& value) = 0; virtual QVariant getVariable(Context* context, const QString& name) = 0; - virtual QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args = QList<QVariant>()) = 0; + virtual QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, + const QList<QVariant>& args = QList<QVariant>()) = 0; virtual bool hasError(Context* context) const = 0; virtual QString getErrorMessage(Context* context) const = 0; - virtual QVariant evaluate(const QString& code, const QList<QVariant>& args = QList<QVariant>(), QString* errorMessage = nullptr) = 0; + virtual QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args = QList<QVariant>(), + QString* errorMessage = nullptr) = 0; virtual QString getIconPath() const = 0; }; class DbAwareScriptingPlugin : public ScriptingPlugin { public: - virtual QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking = false) = 0; - virtual QVariant evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking = false, QString* errorMessage = nullptr) = 0; + virtual QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, + Db* db, bool locking = false) = 0; + virtual QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, + bool locking = false, QString* errorMessage = nullptr) = 0; - QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args) + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args) { - return evaluate(context, code, args, nullptr, true); + return evaluate(context, code, funcInfo, args, nullptr, true); } - QVariant evaluate(const QString& code, const QList<QVariant>& args, QString* errorMessage = nullptr) + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, QString* errorMessage = nullptr) { - return evaluate(code, args, nullptr, true, errorMessage); + return evaluate(code, funcInfo, args, nullptr, true, errorMessage); } }; diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp index f88fa85..5305f24 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp @@ -1,42 +1,39 @@ #include "scriptingqt.h" -#include "common/unused.h" #include "common/global.h" #include "scriptingqtdbproxy.h" -#include <QScriptEngine> +#include "services/notifymanager.h" +#include <QJSEngine> #include <QMutex> #include <QMutexLocker> #include <QDebug> -static QScriptValue scriptingQtDebug(QScriptContext *context, QScriptEngine *engine) +ScriptingQt::ScriptingQt() { - UNUSED(engine); - QStringList args; - for (int i = 0; i < context->argumentCount(); i++) - args << context->argument(i).toString(); - - qDebug() << "[ScriptingQt]" << args; - return QScriptValue(); + managedMainContextsMutex = new QMutex(); } -ScriptingQt::ScriptingQt() +ScriptingQt::~ScriptingQt() { - mainEngineMutex = new QMutex(); + safe_delete(managedMainContextsMutex); } -ScriptingQt::~ScriptingQt() +QJSValueList ScriptingQt::toValueList(QJSEngine* engine, const QList<QVariant>& values) { - safe_delete(mainEngineMutex); + QJSValueList result; + for (const QVariant& value : values) + result << engine->toScriptValue(value); + + return result; } QString ScriptingQt::getLanguage() const { - return QStringLiteral("QtScript"); + return QStringLiteral("JavaScript"); } ScriptingPlugin::Context* ScriptingQt::createContext() { ContextQt* ctx = new ContextQt; - ctx->engine->pushContext(); contexts << ctx; return ctx; } @@ -56,60 +53,54 @@ void ScriptingQt::resetContext(ScriptingPlugin::Context* context) ContextQt* ctx = getContext(context); if (!ctx) return; - - ctx->engine->popContext(); - ctx->engine->pushContext(); } -QVariant ScriptingQt::evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage) +QVariant ScriptingQt::evaluate(const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage) { - QMutexLocker locker(mainEngineMutex); - - // Enter a new context - QScriptContext* engineContext = mainContext->engine->pushContext(); - // Call the function - QVariant result = evaluate(mainContext, engineContext, code, args, db, locking); + ContextQt* context = getMainContext(); + QVariant result = evaluate(context, code, funcInfo, args, db, locking); // Handle errors - if (!mainContext->error.isEmpty()) - *errorMessage = mainContext->error; - - // Leave the context to reset "this". - mainContext->engine->popContext(); + if (!context->error.isEmpty()) + *errorMessage = context->error; return result; } -QVariant ScriptingQt::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking) +QVariant ScriptingQt::evaluate(ScriptingPlugin::Context* context, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking) { ContextQt* ctx = getContext(context); if (!ctx) return QVariant(); - return evaluate(ctx, ctx->engine->currentContext(), code, args, db, locking); + return evaluate(ctx, code, funcInfo, args, db, locking); } -QVariant ScriptingQt::evaluate(ContextQt* ctx, QScriptContext* engineContext, const QString& code, const QList<QVariant>& args, Db* db, bool locking) +QVariant ScriptingQt::evaluate(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking) { // Define function to call - QScriptValue functionValue = getFunctionValue(ctx, code); + QJSValue functionValue = getFunctionValue(ctx, code, funcInfo); // Db for this evaluation ctx->dbProxy->setDb(db); ctx->dbProxy->setUseDbLocking(locking); // Call the function - QScriptValue result; + QJSValue result; if (args.size() > 0) - result = functionValue.call(engineContext->activationObject(), ctx->engine->toScriptValue(args)); + result = functionValue.call(toValueList(ctx->engine, args)); else - result = functionValue.call(engineContext->activationObject()); + result = functionValue.call(); // Handle errors ctx->error.clear(); - if (ctx->engine->hasUncaughtException()) - ctx->error = ctx->engine->uncaughtException().toString(); + if (result.isError()) + { + ctx->error = QString("Uncaught exception at line %1: %2").arg( + result.property("lineNumber").toString(), + result.toString()); + } ctx->dbProxy->setDb(nullptr); ctx->dbProxy->setUseDbLocking(false); @@ -117,6 +108,20 @@ QVariant ScriptingQt::evaluate(ContextQt* ctx, QScriptContext* engineContext, co return convertVariant(result.toVariant()); } +ScriptingQt::ContextQt* ScriptingQt::getMainContext() +{ + if (mainContext.hasLocalData()) + return mainContext.localData(); + + ContextQt* context = new ContextQt(); + mainContext.setLocalData(context); + + QMutexLocker locker(managedMainContextsMutex); + managedMainContexts << context; + + return context; +} + QVariant ScriptingQt::convertVariant(const QVariant& value, bool wrapStrings) { switch (value.type()) @@ -148,7 +153,7 @@ QVariant ScriptingQt::convertVariant(const QVariant& value, bool wrapStrings) case QVariant::List: { QStringList list; - for (const QVariant& var : value.toList()) + for (QVariant& var : value.toList()) list << convertVariant(var, true).toString(); return "[" + list.join(", ") + "]"; @@ -176,7 +181,7 @@ void ScriptingQt::setVariable(ScriptingPlugin::Context* context, const QString& if (!ctx) return; - ctx->engine->globalObject().setProperty(name, ctx->engine->newVariant(value)); + ctx->engine->globalObject().setProperty(name, ctx->engine->toScriptValue(value)); } QVariant ScriptingQt::getVariable(ScriptingPlugin::Context* context, const QString& name) @@ -185,7 +190,7 @@ QVariant ScriptingQt::getVariable(ScriptingPlugin::Context* context, const QStri if (!ctx) return QVariant(); - QScriptValue value = ctx->engine->globalObject().property(name); + QJSValue value = ctx->engine->globalObject().property(name); return convertVariant(value.toVariant()); } @@ -214,20 +219,28 @@ QString ScriptingQt::getIconPath() const bool ScriptingQt::init() { - QMutexLocker locker(mainEngineMutex); - mainContext = new ContextQt; return true; } void ScriptingQt::deinit() { - for (Context* ctx : contexts) + for (Context*& ctx : contexts) delete ctx; contexts.clear(); - QMutexLocker locker(mainEngineMutex); - safe_delete(mainContext); + QMutexLocker locker(managedMainContextsMutex); + for (ContextQt*& ctx : managedMainContexts) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + ctx->engine->setInterrupted(true); +#else + // FIXME No way to cleanly interrupt QJSEngine before Qt 5.14 +#endif + delete ctx; + } + + managedMainContexts.clear(); } ScriptingQt::ContextQt* ScriptingQt::getContext(ScriptingPlugin::Context* context) const @@ -239,31 +252,30 @@ ScriptingQt::ContextQt* ScriptingQt::getContext(ScriptingPlugin::Context* contex return ctx; } -QScriptValue ScriptingQt::getFunctionValue(ContextQt* ctx, const QString& code) +QJSValue ScriptingQt::getFunctionValue(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo) { - static const QString fnDef = QStringLiteral("(function () {%1\n})"); + static const QString fnDef = QStringLiteral("(function (%1) {%2\n})"); - QScriptProgram* prog = nullptr; - if (!ctx->scriptCache.contains(code)) - { - prog = new QScriptProgram(fnDef.arg(code)); - ctx->scriptCache.insert(code, prog); - } - else - { - prog = ctx->scriptCache[code]; - } - return ctx->engine->evaluate(*prog); + QString fullCode = fnDef.arg(funcInfo.getArguments().join(", "), code); + QJSValue* func = ctx->scriptCache[fullCode]; + if (func) + return *func; + + func = new QJSValue(ctx->engine->evaluate(fullCode)); + ctx->scriptCache.insert(fullCode, func); + return *func; } ScriptingQt::ContextQt::ContextQt() { - engine = new QScriptEngine(); + engine = new QJSEngine(); + engine->installExtensions(QJSEngine::ConsoleExtension); - dbProxy = new ScriptingQtDbProxy(); - dbProxyScriptValue = engine->newQObject(dbProxy, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); + dbProxy = new ScriptingQtDbProxy(engine); + dbProxyScriptValue = engine->newQObject(dbProxy); + console = new ScriptingQtConsole(engine); - engine->globalObject().setProperty("debug", engine->newFunction(scriptingQtDebug)); + engine->globalObject().setProperty("console", engine->newQObject(console)); engine->globalObject().setProperty("db", dbProxyScriptValue); scriptCache.setMaxCost(cacheSize); @@ -271,6 +283,19 @@ ScriptingQt::ContextQt::ContextQt() ScriptingQt::ContextQt::~ContextQt() { - safe_delete(engine); + safe_delete(console); safe_delete(dbProxy); + safe_delete(engine); +} + +ScriptingQtConsole::ScriptingQtConsole(QJSEngine* engine) : + QObject(), engine(engine) +{ +} + +QJSValue ScriptingQtConsole::log(const QJSValue& value) +{ + static_qstring(tpl, "[JS] %1"); + NOTIFY_MANAGER->info(tpl.arg(ScriptingQt::convertVariant(value.toVariant()).toString())); + return QJSValue(); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h index 125789a..a7e156c 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h @@ -6,33 +6,35 @@ #include <QHash> #include <QVariant> #include <QCache> -#include <QScriptValue> -#include <QScriptProgram> +#include <QJSValue> +#include <QThreadStorage> -class QScriptEngine; class QMutex; -class QScriptContext; class ScriptingQtDbProxy; +class ScriptingQtConsole; class ScriptingQt : public BuiltInPlugin, public DbAwareScriptingPlugin { Q_OBJECT - SQLITESTUDIO_PLUGIN_TITLE("Qt scripting") - SQLITESTUDIO_PLUGIN_DESC("Qt scripting support.") - SQLITESTUDIO_PLUGIN_VERSION(10000) + SQLITESTUDIO_PLUGIN_TITLE("JavaScript scripting") + SQLITESTUDIO_PLUGIN_DESC("JavaScript scripting support.") + SQLITESTUDIO_PLUGIN_VERSION(10100) SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl") public: ScriptingQt(); ~ScriptingQt(); + static QJSValueList toValueList(QJSEngine* engine, const QList<QVariant>& values); + static QVariant convertVariant(const QVariant& value, bool wrapStrings = false); + QString getLanguage() const; Context* createContext(); void releaseContext(Context* context); void resetContext(Context* context); - QVariant evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking = false, QString* errorMessage = nullptr); - QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking = false); + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking = false, QString* errorMessage = nullptr); + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking = false); void setVariable(Context* context, const QString& name, const QVariant& value); QVariant getVariable(Context* context, const QString& name); bool hasError(Context* context) const; @@ -50,23 +52,39 @@ class ScriptingQt : public BuiltInPlugin, public DbAwareScriptingPlugin ContextQt(); ~ContextQt(); - QScriptEngine* engine = nullptr; - QCache<QString,QScriptProgram> scriptCache; + QJSEngine* engine = nullptr; + QCache<QString, QJSValue> scriptCache; QString error; ScriptingQtDbProxy* dbProxy = nullptr; - QScriptValue dbProxyScriptValue; + ScriptingQtConsole* console = nullptr; + QJSValue dbProxyScriptValue; }; ContextQt* getContext(ScriptingPlugin::Context* context) const; - QScriptValue getFunctionValue(ContextQt* ctx, const QString& code); - QVariant evaluate(ContextQt* ctx, QScriptContext* engineContext, const QString& code, const QList<QVariant>& args, Db* db, bool locking); - QVariant convertVariant(const QVariant& value, bool wrapStrings = false); + QJSValue getFunctionValue(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo); + QVariant evaluate(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking); + ContextQt* getMainContext(); static const constexpr int cacheSize = 5; - ContextQt* mainContext = nullptr; + QThreadStorage<ContextQt*> mainContext; QList<Context*> contexts; - QMutex* mainEngineMutex = nullptr; + QList<ContextQt*> managedMainContexts; + QMutex* managedMainContextsMutex = nullptr; +}; + +class ScriptingQtConsole : public QObject +{ + Q_OBJECT + + public: + ScriptingQtConsole(QJSEngine* engine); + + private: + QJSEngine* engine = nullptr; + + public slots: + QJSValue log(const QJSValue& value); }; #endif // SCRIPTINGQT_H diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png Binary files differindex 220ea27..ce8e60a 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp index ff3c7ee..cc9fa2d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp @@ -1,11 +1,11 @@ +#include "scriptingqt.h" #include "scriptingqtdbproxy.h" #include "db/db.h" #include "db/sqlquery.h" -#include <QScriptContext> -#include <QScriptEngine> +#include <QJSEngine> -ScriptingQtDbProxy::ScriptingQtDbProxy(QObject *parent) : - QObject(parent) +ScriptingQtDbProxy::ScriptingQtDbProxy(QJSEngine* engine, QObject *parent) : + QObject(parent), engine(engine) { } Db* ScriptingQtDbProxy::getDb() const @@ -40,12 +40,12 @@ QHash<QString, QVariant> ScriptingQtDbProxy::mapToHash(const QMap<QString, QVari } QVariant ScriptingQtDbProxy::evalInternal(const QString& sql, const QList<QVariant>& listArgs, const QMap<QString, QVariant>& mapArgs, - bool singleCell, const QScriptValue* funcPtr) + bool singleCell, const QJSValue* funcPtr) { if (!db) { QString funcName = singleCell ? QStringLiteral("db.onecolumn()") : QStringLiteral("db.eval()"); - context()->throwError(tr("No database available in current context, while called QtScript's %1 command.").arg(funcName)); + engine->throwError(tr("No database available in current context, while called JavaScript's %1 command.").arg(funcName)); return evalInternalErrorResult(singleCell); } @@ -62,7 +62,7 @@ QVariant ScriptingQtDbProxy::evalInternal(const QString& sql, const QList<QVaria if (results->isError()) { QString funcName = singleCell ? QStringLiteral("db.onecolumn()") : QStringLiteral("db.eval()"); - context()->throwError(tr("Error from %1: %2").arg(funcName, results->getErrorText())); + engine->throwError(tr("Error from %1: %2").arg(funcName, results->getErrorText())); return evalInternalErrorResult(singleCell); } @@ -72,15 +72,15 @@ QVariant ScriptingQtDbProxy::evalInternal(const QString& sql, const QList<QVaria } else if (funcPtr) { - QScriptValue func(*funcPtr); + QJSValue func(*funcPtr); SqlResultsRowPtr row; - QScriptValue funcArgs; - QScriptValue funcResult; + QJSValueList funcArgs; + QJSValue funcResult; while (results->hasNext()) { row = results->next(); - funcArgs = context()->engine()->toScriptValue(row->valueList()); - funcResult = func.call(context()->thisObject(), funcArgs); + funcArgs = ScriptingQt::toValueList(engine, row->valueList()); + funcResult = func.call(funcArgs); if (!funcResult.isUndefined()) break; } @@ -123,12 +123,12 @@ QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap<QString, QVaria return evalInternal(sql, QList<QVariant>(), args, false); } -QVariant ScriptingQtDbProxy::eval(const QString& sql, const QList<QVariant>& args, const QScriptValue& func) +QVariant ScriptingQtDbProxy::eval(const QString& sql, const QList<QVariant>& args, const QJSValue& func) { return evalInternal(sql, args, QMap<QString, QVariant>(), false, &func); } -QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap<QString, QVariant>& args, const QScriptValue& func) +QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap<QString, QVariant>& args, const QJSValue& func) { return evalInternal(sql, QList<QVariant>(), args, false, &func); } diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h index add9540..9a86f6b 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h @@ -2,18 +2,18 @@ #define SCRIPTINGQTDBPROXY_H #include <QObject> -#include <QScriptable> #include <QHash> #include <QList> #include <QVariant> +#include <QJSValue> class Db; -class ScriptingQtDbProxy : public QObject, protected QScriptable +class ScriptingQtDbProxy : public QObject { Q_OBJECT public: - explicit ScriptingQtDbProxy(QObject *parent = 0); + explicit ScriptingQtDbProxy(QJSEngine* engine, QObject *parent = 0); Db* getDb() const; void setDb(Db* value); @@ -23,20 +23,21 @@ class ScriptingQtDbProxy : public QObject, protected QScriptable private: QVariant evalInternal(const QString& sql, const QList<QVariant>& listArgs, const QMap<QString, QVariant>& mapArgs, bool singleCell, - const QScriptValue* funcPtr = nullptr); + const QJSValue* funcPtr = nullptr); QVariant evalInternalErrorResult(bool singleCell); static QHash<QString, QVariant> mapToHash(const QMap<QString, QVariant>& map); Db* db = nullptr; bool useDbLocking = false; + QJSEngine* engine = nullptr; public slots: QVariant eval(const QString& sql); QVariant eval(const QString& sql, const QList<QVariant>& args); QVariant eval(const QString& sql, const QMap<QString, QVariant>& args); - QVariant eval(const QString& sql, const QList<QVariant>& args, const QScriptValue& func); - QVariant eval(const QString& sql, const QMap<QString, QVariant>& args, const QScriptValue& func); + QVariant eval(const QString& sql, const QList<QVariant>& args, const QJSValue& func); + QVariant eval(const QString& sql, const QMap<QString, QVariant>& args, const QJSValue& func); QVariant onecolumn(const QString& sql, const QList<QVariant>& args); QVariant onecolumn(const QString& sql, const QMap<QString, QVariant>& args); }; diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp index 93a6d91..7edd7e7 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp @@ -38,7 +38,7 @@ void ScriptingSql::resetContext(ScriptingPlugin::Context* context) dynamic_cast<SqlContext*>(context)->errorText.clear(); } -QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking) +QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking) { SqlContext* ctx = dynamic_cast<SqlContext*>(context); ctx->errorText.clear(); @@ -58,14 +58,15 @@ QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString QString sql = code; if (ctx->variables.size() > 0) { - QString value; for (const QString& key : ctx->variables.keys()) { - value = "'" + ctx->variables[key].toString() + "'"; + QString value = "'" + ctx->variables[key].toString() + "'"; sql.replace(":" + key, value).replace("@" + key, value).replace("$" + key, value); } } + replaceNamedArgs(sql, funcInfo, args); + SqlQueryPtr result = theDb->exec(sql, args, execFlags); if (result->isError()) { @@ -76,7 +77,7 @@ QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString return result->getSingleCell(); } -QVariant ScriptingSql::evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage) +QVariant ScriptingSql::evaluate(const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage) { Db* theDb = nullptr; @@ -91,7 +92,10 @@ QVariant ScriptingSql::evaluate(const QString& code, const QList<QVariant>& args if (!locking) execFlags |= Db::Flag::NO_LOCK; - SqlQueryPtr result = theDb->exec(code, args, execFlags); + QString sql = code; + replaceNamedArgs(sql, funcInfo, args); + + SqlQueryPtr result = theDb->exec(sql, args, execFlags); if (result->isError()) { *errorMessage = result->getErrorText(); @@ -144,3 +148,18 @@ void ScriptingSql::deinit() safe_delete(memDb); } + +void ScriptingSql::replaceNamedArgs(QString& sql, const ScriptingPlugin::FunctionInfo& funcInfo, const QList<QVariant>& args) +{ + int i = 0; + for (const QString& key : funcInfo.getArguments()) + { + if (i >= args.size()) + break; + + QString value = "'" + args[i++].toString() + "'"; + sql.replace(":" + key, value) + .replace("@" + key, value) + .replace("$" + key, value); + } +} diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h index 7b8fd3b..8fa51db 100644 --- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h +++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h @@ -28,8 +28,10 @@ class ScriptingSql : public BuiltInPlugin, public DbAwareScriptingPlugin Context* createContext(); void releaseContext(Context* context); void resetContext(Context* context); - QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking); - QVariant evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage); + QVariant evaluate(Context* context, const QString& code, const FunctionInfo& funcInfo, + const QList<QVariant>& args, Db* db, bool locking); + QVariant evaluate(const QString& code, const FunctionInfo& funcInfo, + const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage); void setVariable(Context* context, const QString& name, const QVariant& value); QVariant getVariable(Context* context, const QString& name); bool hasError(Context* context) const; @@ -39,6 +41,8 @@ class ScriptingSql : public BuiltInPlugin, public DbAwareScriptingPlugin void deinit(); private: + void replaceNamedArgs(QString& sql, const FunctionInfo& funcInfo, const QList<QVariant>& args); + QList<Context*> contexts; Db* memDb = nullptr; }; |
