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:
mgrojo
2021-06-19 00:07:35 +02:00
parent 0f8b3cc092
commit 6510bb104e
3 changed files with 16 additions and 12 deletions
+12 -9
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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))) {