Use a custom extension function instead of the json_array function

For multi-column PKs in WITHOUT ROWID tables the first implementation
added a dependency on the JSON extension by using the json_array
function. To make sure DB4S works even without this extension, this
commit adds and makes use of a custom extension function which
essentially does the same thing as json_array.
This commit is contained in:
Martin Kleusberg
2019-04-02 17:56:22 +02:00
parent 726db220d1
commit 20724ee139
2 changed files with 39 additions and 7 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ std::string Query::buildQuery(bool withRowid) const
{
selector = sqlb::escapeIdentifier(m_rowid_columns.at(0)) + ",";
} else {
selector += "json_array(";
selector += "sqlb_make_single_value(";
for(size_t i=0;i<m_rowid_columns.size();i++)
selector += sqlb::escapeIdentifier(m_rowid_columns.at(i)) + ",";
selector.pop_back(); // Remove the last comma
+38 -6
View File
@@ -20,6 +20,9 @@
#include <atomic>
#include <algorithm>
#include <cctype>
#include <json.hpp>
using json = nlohmann::json;
QStringList DBBrowserDB::Datatypes = QStringList() << "INTEGER" << "TEXT" << "BLOB" << "REAL" << "NUMERIC";
@@ -60,6 +63,22 @@ static int sqlite_compare_utf16ci( void* /*arg*/,int size1, const void *str1, in
return QString::compare(string1, string2, Qt::CaseInsensitive);
}
static void sqlite_make_single_value(sqlite3_context* ctx, int num_arguments, sqlite3_value* arguments[])
{
json array;
for(int i=0;i<num_arguments;i++)
array.push_back(reinterpret_cast<const char*>(sqlite3_value_text(arguments[i])));
std::string output = array.dump();
char* output_str = new char[output.size()+1];
std::strcpy(output_str, output.c_str());
sqlite3_result_text(ctx, output_str, output.length(), [](void* ptr) {
char* cptr = static_cast<char*>(ptr);
delete cptr;
});
}
void DBBrowserDB::collationNeeded(void* /*pData*/, sqlite3* /*db*/, int eTextRep, const char* sCollationName)
{
QString name(sCollationName);
@@ -168,6 +187,19 @@ bool DBBrowserDB::open(const QString& db, bool readOnly)
if(Settings::getValue("extensions", "disableregex").toBool() == false)
sqlite3_create_function(_db, "REGEXP", 2, SQLITE_UTF8, nullptr, regexp, nullptr, nullptr);
// Register our internal helper function for putting multiple values into a single column
sqlite3_create_function_v2(
_db,
"sqlb_make_single_value",
-1,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
nullptr,
sqlite_make_single_value,
nullptr,
nullptr,
nullptr
);
// Check if file is read only. In-memory databases are never read only
if(db == ":memory:")
{
@@ -1144,13 +1176,13 @@ bool DBBrowserDB::getRow(const sqlb::ObjectIdentifier& table, const QString& row
QString sQuery = QString("SELECT * FROM %1 WHERE ")
.arg(table.toString());
// For a single rowid column we can use a simple WHERE condition, for multiple rowid columns we have to use json_array to decode the composed rowid values.
// For a single rowid column we can use a simple WHERE condition, for multiple rowid columns we have to use sqlb_make_single_value to decode the composed rowid values.
QStringList pks = getObjectByName<sqlb::Table>(table)->rowidColumns();
if(pks.size() == 1)
{
sQuery += QString("%1='%2;").arg(sqlb::escapeIdentifier(pks.front())).arg(rowid);
} else {
sQuery += QString("json_array(%1)='%2';")
sQuery += QString("sqlb_make_single_value(%1)='%2';")
.arg(sqlb::escapeIdentifier(pks).join(","))
.arg(QString(rowid).replace("'", "''"));
}
@@ -1317,7 +1349,7 @@ bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStri
quoted_rowids.append("'" + rowid.replace("'", "''") + "'");
// For a single rowid column we can use a SELECT ... IN(...) statement which is faster.
// For multiple rowid columns we have to use json_array to decode the composed rowid values.
// For multiple rowid columns we have to use sqlb_make_single_value to decode the composed rowid values.
QString statement;
if(pks.size() == 1)
{
@@ -1328,7 +1360,7 @@ bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStri
} else {
statement = QString("DELETE FROM %1 WHERE ").arg(table.toString());
statement += "json_array(";
statement += "sqlb_make_single_value(";
for(const auto& pk : pks)
statement += sqlb::escapeIdentifier(pk) + ",";
statement.chop(1);
@@ -1362,14 +1394,14 @@ bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const QStrin
.arg(table.toString())
.arg(sqlb::escapeIdentifier(column));
// For a single rowid column we can use a simple WHERE condition, for multiple rowid columns we have to use json_array to decode the composed rowid values.
// For a single rowid column we can use a simple WHERE condition, for multiple rowid columns we have to use sqlb_make_single_value to decode the composed rowid values.
if(pks.size() == 1)
{
sql += QString("%1='%2';")
.arg(sqlb::escapeIdentifier(pks.first()))
.arg(QString(rowid).replace("'", "''"));
} else {
sql += QString("json_array(%1)='%2';")
sql += QString("sqlb_make_single_value(%1)='%2';")
.arg(sqlb::escapeIdentifier(pks).join(","))
.arg(QString(rowid).replace("'", "''"));
}