mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-05-18 03:29:25 -05:00
Improve support for BLOB columns as primary key
Working cases tables without rowid and BLOB as primary key: - Inserted value is actually TEXT, for example, from the text editor in the GUI. - Inserted value is really BLOB, for example, from the binary editor or from SQL and the data is fully binary. Not yet working case: - Inserted value is of type BLOB, but the actual data is recognized by DB Browser as text. In this case, no type affinity is applied by SQLite and the row is not properly selected. See issue #2738
This commit is contained in:
+12
-9
@@ -52,7 +52,13 @@ QString escapeString(const QString& literal)
|
||||
{
|
||||
return QString::fromStdString(escapeString(literal.toStdString()));
|
||||
}
|
||||
}
|
||||
QString escapeByteArray(const QByteArray& literal)
|
||||
{
|
||||
if(isTextOnly(literal))
|
||||
return sqlb::escapeString(literal);
|
||||
else
|
||||
return QString("X'%1'").arg(QString(literal.toHex()));
|
||||
}}
|
||||
|
||||
// collation callbacks
|
||||
int collCompare(void* /*pArg*/, int sizeA, const void* sA, int sizeB, const void* sB)
|
||||
@@ -969,10 +975,7 @@ bool DBBrowserDB::dump(const QString& filePath,
|
||||
stream << QString("X'%1'").arg(QString(bcontent.toHex()));
|
||||
break;
|
||||
case SQLITE_TEXT:
|
||||
if(isTextOnly(bcontent))
|
||||
stream << sqlb::escapeString(bcontent);
|
||||
else
|
||||
stream << QString("X'%1'").arg(QString(bcontent.toHex()));
|
||||
stream << sqlb::escapeByteArray(bcontent);
|
||||
break;
|
||||
case SQLITE_NULL:
|
||||
stream << "NULL";
|
||||
@@ -1438,7 +1441,7 @@ QString DBBrowserDB::addRecord(const sqlb::ObjectIdentifier& tablename)
|
||||
}
|
||||
}
|
||||
|
||||
bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const std::vector<QString>& rowids, const sqlb::StringVector& pseudo_pk)
|
||||
bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const std::vector<QByteArray>& rowids, const sqlb::StringVector& pseudo_pk)
|
||||
{
|
||||
if (!isOpen()) return false;
|
||||
|
||||
@@ -1452,7 +1455,7 @@ bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const std::
|
||||
|
||||
// Quote all values in advance
|
||||
std::vector<std::string> quoted_rowids;
|
||||
std::transform(rowids.begin(), rowids.end(), std::back_inserter(quoted_rowids), [](const auto& rowid) { return sqlb::escapeString((rowid.toStdString())); });
|
||||
std::transform(rowids.begin(), rowids.end(), std::back_inserter(quoted_rowids), [](const auto& rowid) { return sqlb::escapeByteArray(rowid).toStdString(); });
|
||||
|
||||
// For a single rowid column we can use a SELECT ... IN(...) statement which is faster.
|
||||
// For multiple rowid columns we have to use sqlb_make_single_value to decode the composed rowid values.
|
||||
@@ -1480,7 +1483,7 @@ bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const std::
|
||||
}
|
||||
|
||||
bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const std::string& column,
|
||||
const QString& rowid, const QByteArray& value, int force_type, const sqlb::StringVector& pseudo_pk)
|
||||
const QByteArray& rowid, const QByteArray& value, int force_type, const sqlb::StringVector& pseudo_pk)
|
||||
{
|
||||
waitForDbRelease();
|
||||
if (!isOpen()) return false;
|
||||
@@ -1497,7 +1500,7 @@ bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const std::s
|
||||
|
||||
// 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 += sqlb::escapeIdentifier(pks.front()) + "=" + sqlb::escapeString(rowid.toStdString());
|
||||
sql += sqlb::escapeIdentifier(pks.front()) + "=" + sqlb::escapeByteArray(rowid).toStdString();
|
||||
else
|
||||
sql += "sqlb_make_single_value(" + sqlb::joinStringVector(sqlb::escapeIdentifier(pks), ",") + ")=" + sqlb::escapeString(rowid.toStdString());
|
||||
|
||||
|
||||
+3
-2
@@ -46,6 +46,7 @@ namespace sqlb
|
||||
{
|
||||
QString escapeIdentifier(const QString& id);
|
||||
QString escapeString(const QString& literal);
|
||||
QString escapeByteArray(const QByteArray& literal);
|
||||
}
|
||||
|
||||
/// represents a single SQLite database. except when noted otherwise,
|
||||
@@ -193,8 +194,8 @@ private:
|
||||
|
||||
public:
|
||||
QString addRecord(const sqlb::ObjectIdentifier& tablename);
|
||||
bool deleteRecords(const sqlb::ObjectIdentifier& table, const std::vector<QString>& rowids, const sqlb::StringVector& pseudo_pk = {});
|
||||
bool updateRecord(const sqlb::ObjectIdentifier& table, const std::string& column, const QString& rowid, const QByteArray& value, int force_type = 0, const sqlb::StringVector& pseudo_pk = {});
|
||||
bool deleteRecords(const sqlb::ObjectIdentifier& table, const std::vector<QByteArray>& rowids, const sqlb::StringVector& pseudo_pk = {});
|
||||
bool updateRecord(const sqlb::ObjectIdentifier& table, const std::string& column, const QByteArray& rowid, const QByteArray& value, int force_type = 0, const sqlb::StringVector& pseudo_pk = {});
|
||||
|
||||
bool createTable(const sqlb::ObjectIdentifier& name, const sqlb::FieldVector& structure);
|
||||
bool renameTable(const std::string& schema, const std::string& from_table, const std::string& to_table);
|
||||
|
||||
@@ -653,7 +653,7 @@ bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<QString> rowids;
|
||||
std::vector<QByteArray> rowids;
|
||||
for(int i=count-1;i>=0;i--)
|
||||
{
|
||||
if(m_cache.count(static_cast<size_t>(row+i))) {
|
||||
|
||||
Reference in New Issue
Block a user