When updating a field try to respect the affinity data type if possible

When modifying the value of a field in the Browse Data tab, we used to
always hand over the new value to SQLite as text. This works most of the
time but can cause problems e.g. when a CHECK constraint checks the data
type of the value and expects something other than text. It is also a
pretty lazy approach in general.

This commit checks if the new value matches the affinity data type of
the column, and if it does hands over the new value as the correct data
type.

See issue #1952.
This commit is contained in:
Martin Kleusberg
2019-08-01 13:30:01 +02:00
parent 05181df8b8
commit 74e0df376a
3 changed files with 30 additions and 7 deletions

View File

@@ -1398,7 +1398,7 @@ bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStri
}
bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const std::string& column,
const QString& rowid, const QByteArray& value, bool itsBlob, const sqlb::StringVector& pseudo_pk)
const QString& rowid, const QByteArray& value, int force_type, const sqlb::StringVector& pseudo_pk)
{
waitForDbRelease();
if (!isOpen()) return false;
@@ -1439,13 +1439,17 @@ bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const std::s
if(sqlite3_prepare_v2(_db, sql.toUtf8(), -1, &stmt, nullptr) != SQLITE_OK)
success = 0;
if(success == 1) {
if(itsBlob)
if(force_type == SQLITE_BLOB)
{
if(sqlite3_bind_blob(stmt, 1, rawValue, value.length(), SQLITE_STATIC))
success = -1;
}
else
{
} else if(force_type == SQLITE_INTEGER) {
if(sqlite3_bind_int(stmt, 1, value.toInt()))
success = -1;
} else if(force_type == SQLITE_FLOAT) {
if(sqlite3_bind_double(stmt, 1, value.toDouble()))
success = -1;
} else {
if(sqlite3_bind_text(stmt, 1, rawValue, value.length(), SQLITE_STATIC))
success = -1;
}

View File

@@ -173,7 +173,7 @@ private:
public:
QString addRecord(const sqlb::ObjectIdentifier& tablename);
bool deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids, const sqlb::StringVector& pseudo_pk = {});
bool updateRecord(const sqlb::ObjectIdentifier& table, const std::string& column, const QString& rowid, const QByteArray& value, bool itsBlob, 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 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);

View File

@@ -466,7 +466,26 @@ bool SqliteTableModel::setTypedData(const QModelIndex& index, bool isBlob, const
if(oldValue == newValue && oldValue.isNull() == newValue.isNull())
return true;
if(m_db.updateRecord(m_query.table(), m_headers.at(column), cached_row.at(0), newValue, isBlob, m_query.rowIdColumns()))
// Determine type. If the BLOB flag is set, it's always BLOB. If the affinity data type of the modified column is something numeric,
// we check if the new value is also numeric. In that case we can safely set the data type to INTEGER or FLOAT. In all other cases we
// default to TEXT.
int type = SQLITE_TEXT;
if(isBlob)
{
type = SQLITE_BLOB;
} else if(m_vDataTypes.at(column) == SQLITE_INTEGER) {
bool ok;
newValue.toInt(&ok);
if(ok)
type = SQLITE_INTEGER;
} else if(m_vDataTypes.at(column) == SQLITE_FLOAT) {
bool ok;
newValue.toFloat(&ok);
if(ok)
type = SQLITE_FLOAT;
}
if(m_db.updateRecord(m_query.table(), m_headers.at(column), cached_row.at(0), newValue, type, m_query.rowIdColumns()))
{
cached_row[column] = newValue;