Skip to content

Commit

Permalink
Fix browsing tables without rowid and multiple primary key columns
Browse files Browse the repository at this point in the history
This fixes a possible crash when browsing a table without a rowid column
and with multiple columns in its primary key. To work with these tables
we need to pretend it still has only one primary key column (as in the
rowid case) and to do so we combine all the values of the primary key
columns into a single value. For simplicity we were using the JSON
library (which we are using anyway in the project) to achieve this,
however this turned out to be problematic for some values. This commit
introduced a new, more robust approach to combine multiple values into a
single value.

See issue sqlitebrowser#2832.
  • Loading branch information
MKleusberg committed Sep 11, 2021
1 parent a302128 commit 0b1ff64
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 16 deletions.
15 changes: 6 additions & 9 deletions src/sqlitedb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@
#include <QDebug>
#include <QThread>
#include <QRegularExpression>
#include <json.hpp>

#include <algorithm>
#include <array>
#include <atomic>
#include <cctype>
#include <chrono>
#include <cstring>
#include <functional>

using json = nlohmann::json;

QStringList DBBrowserDB::Datatypes = {"INTEGER", "TEXT", "BLOB", "REAL", "NUMERIC"};

// Helper template to allow turning member functions into a C-style function pointer
Expand Down Expand Up @@ -86,15 +84,14 @@ static int sqlite_compare_utf16ci( void* /*arg*/,int size1, const void *str1, in

static void sqlite_make_single_value(sqlite3_context* ctx, int num_arguments, sqlite3_value* arguments[])
{
json array;
QByteArray output;
for(int i=0;i<num_arguments;i++)
array.push_back(reinterpret_cast<const char*>(sqlite3_value_text(arguments[i])));
output += QByteArray::number(sqlite3_value_bytes(arguments[i])) + ":" + 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());
char* output_str = new char[static_cast<size_t>(output.size()) + 1];
std::strcpy(output_str, output);

sqlite3_result_text(ctx, output_str, static_cast<int>(output.length()), [](void* ptr) {
sqlite3_result_text(ctx, output_str, output.size(), [](void* ptr) {
char* cptr = static_cast<char*>(ptr);
delete cptr;
});
Expand Down
11 changes: 4 additions & 7 deletions src/sqlitetablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,8 @@
#include <QRegularExpression>
#include <QPushButton>

#include <json.hpp>

#include "RowLoader.h"

using json = nlohmann::json;

SqliteTableModel::SqliteTableModel(DBBrowserDB& db, QObject* parent, const QString& encoding, bool force_wait)
: QAbstractTableModel(parent)
, m_db(db)
Expand Down Expand Up @@ -514,14 +510,15 @@ bool SqliteTableModel::setTypedData(const QModelIndex& index, bool isBlob, const
{
cached_row[0] = newValue;
} else {
json array;
assert(m_headers.size() == cached_row.size());
QByteArray output;
for(size_t i=0;i<m_query.rowIdColumns().size();i++)
{
auto it = std::find(m_headers.begin()+1, m_headers.end(), m_query.rowIdColumns().at(i)); // +1 in order to omit the rowid column itself
array.push_back(cached_row[static_cast<size_t>(std::distance(m_headers.begin(), it))]);
auto v = cached_row[static_cast<size_t>(std::distance(m_headers.begin(), it))];
output += QByteArray::number(v.size()) + ":" + v;
}
cached_row[0] = QByteArray::fromStdString(array.dump());
cached_row[0] = output;
}
const QModelIndex& rowidIndex = index.sibling(index.row(), 0);
lock.unlock();
Expand Down

0 comments on commit 0b1ff64

Please sign in to comment.