mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 11:00:44 -06:00
Initial support for multiple primary key columns in WITHOUT ROWID tables
This add initial and mostly untested support for WITHOUT ROWID tables with multiple primary key columns. It should now be possible to update and to delete records in these tables. This commit also improves the overall handling of multiple primary key columns in preparation for better support of them in general. Note that this makes us depend on an SQLite version with a built-in JSON extension. See issues #516, #1075, and #1834.
This commit is contained in:
@@ -372,8 +372,8 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column)
|
||||
// we need to check for this case and cancel here. Maybe we can think of some way to modify the INSERT INTO ... SELECT statement
|
||||
// to at least replace all troublesome NULL values by the default value
|
||||
SqliteTableModel m(pdb, this);
|
||||
m.setQuery(QString("SELECT COUNT(%1) FROM %2 WHERE %3 IS NULL;")
|
||||
.arg(sqlb::escapeIdentifier(pdb.getObjectByName<sqlb::Table>(curTable)->rowidColumn()))
|
||||
m.setQuery(QString("SELECT COUNT(%1) FROM %2 WHERE coalesce(NULL,%3) IS NULL;")
|
||||
.arg(sqlb::escapeIdentifier(pdb.getObjectByName<sqlb::Table>(curTable)->rowidColumns()).join(","))
|
||||
.arg(curTable.toString())
|
||||
.arg(sqlb::escapeIdentifier(field.name())));
|
||||
if(!m.completeCache())
|
||||
@@ -666,27 +666,31 @@ void EditTableDialog::setWithoutRowid(bool without_rowid)
|
||||
if(without_rowid)
|
||||
{
|
||||
// Before setting the without rowid flag, first perform a check to see if the table meets all the required criteria for without rowid tables
|
||||
auto pk = m_table.findPk();
|
||||
if(pk == m_table.fields.end() || pk->autoIncrement())
|
||||
auto pks = m_table.primaryKey();
|
||||
for(const auto& pk_name : pks)
|
||||
{
|
||||
QMessageBox::information(this, QApplication::applicationName(),
|
||||
tr("Please add a field which meets the following criteria before setting the without rowid flag:\n"
|
||||
" - Primary key flag set\n"
|
||||
" - Auto increment disabled"));
|
||||
auto pk = sqlb::findField(m_table, pk_name);
|
||||
if(pk == m_table.fields.end() || pk->autoIncrement())
|
||||
{
|
||||
QMessageBox::information(this, QApplication::applicationName(),
|
||||
tr("Please add a field which meets the following criteria before setting the without rowid flag:\n"
|
||||
" - Primary key flag set\n"
|
||||
" - Auto increment disabled"));
|
||||
|
||||
// Reset checkbox state to unchecked. Block any signals while doing this in order to avoid an extra call to
|
||||
// this function being triggered.
|
||||
ui->checkWithoutRowid->blockSignals(true);
|
||||
ui->checkWithoutRowid->setChecked(false);
|
||||
ui->checkWithoutRowid->blockSignals(false);
|
||||
return;
|
||||
// Reset checkbox state to unchecked. Block any signals while doing this in order to avoid an extra call to
|
||||
// this function being triggered.
|
||||
ui->checkWithoutRowid->blockSignals(true);
|
||||
ui->checkWithoutRowid->setChecked(false);
|
||||
ui->checkWithoutRowid->blockSignals(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If it does, override the the rowid column name of the table object with the name of the primary key.
|
||||
m_table.setRowidColumn(pk->name());
|
||||
// If it does, override the the rowid column names of the table object with the names of the primary keys.
|
||||
m_table.setRowidColumns(pks);
|
||||
} else {
|
||||
// If the without rowid flag is unset no further checks are required. Just set the rowid column name back to "_rowid_"
|
||||
m_table.setRowidColumn("_rowid_");
|
||||
m_table.setRowidColumns({"_rowid_"});
|
||||
}
|
||||
|
||||
// Update the SQL preview
|
||||
|
||||
@@ -140,7 +140,7 @@ QWidget* ExtendedTableWidgetEditorDelegate::createEditor(QWidget* parent, const
|
||||
// If no column name is set, assume the primary key is meant
|
||||
if(fk.columns().isEmpty()) {
|
||||
sqlb::TablePtr obj = m->db().getObjectByName<sqlb::Table>(foreignTable);
|
||||
column = obj->findPk()->name();
|
||||
column = obj->primaryKey().first();
|
||||
} else
|
||||
column = fk.columns().at(0);
|
||||
|
||||
|
||||
@@ -967,7 +967,7 @@ void MainWindow::addRecord()
|
||||
|
||||
void MainWindow::insertValues()
|
||||
{
|
||||
QString pseudo_pk = m_browseTableModel->hasPseudoPk() ? m_browseTableModel->pseudoPk() : QString();
|
||||
QString pseudo_pk = m_browseTableModel->hasPseudoPk() ? QString::fromStdString(m_browseTableModel->pseudoPk().front()) : QString();
|
||||
AddRecordDialog dialog(db, currentlyBrowsedTableName(), this, pseudo_pk);
|
||||
if (dialog.exec())
|
||||
populateTable();
|
||||
@@ -3314,7 +3314,7 @@ void MainWindow::jumpToRow(const sqlb::ObjectIdentifier& table, QString column,
|
||||
|
||||
// If no column name is set, assume the primary key is meant
|
||||
if(!column.size())
|
||||
column = obj->findPk()->name();
|
||||
column = obj->primaryKey().first();
|
||||
|
||||
// If column doesn't exist don't do anything
|
||||
auto column_index = sqlb::findField(obj, column);
|
||||
@@ -3564,7 +3564,7 @@ void MainWindow::unlockViewEditing(bool unlock, QString pk)
|
||||
|
||||
// (De)activate editing
|
||||
enableEditing(unlock);
|
||||
m_browseTableModel->setPseudoPk(pk);
|
||||
m_browseTableModel->setPseudoPk({pk.toStdString()});
|
||||
|
||||
// Update checked status of the popup menu action
|
||||
ui->actionUnlockViewEditing->blockSignals(true);
|
||||
|
||||
@@ -12,7 +12,7 @@ Query::Query()
|
||||
void Query::clear()
|
||||
{
|
||||
m_table.clear();
|
||||
m_rowid_column = "_rowid_";
|
||||
m_rowid_columns = {"_rowid_"};
|
||||
m_selected_columns.clear();
|
||||
m_where.clear();
|
||||
m_sort.clear();
|
||||
@@ -45,7 +45,20 @@ std::string Query::buildQuery(bool withRowid) const
|
||||
// Selector and display formats
|
||||
std::string selector;
|
||||
if (withRowid)
|
||||
selector = sqlb::escapeIdentifier(m_rowid_column) + ",";
|
||||
{
|
||||
// We select the rowid data into a JSON array in case there are multiple rowid columns in order to have all values at hand.
|
||||
// If there is only one rowid column, we leave it as is.
|
||||
if(m_rowid_columns.size() == 1)
|
||||
{
|
||||
selector = sqlb::escapeIdentifier(m_rowid_columns.at(0)) + ",";
|
||||
} else {
|
||||
selector += "json_array(";
|
||||
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
|
||||
selector += "),";
|
||||
}
|
||||
}
|
||||
|
||||
if(m_selected_columns.empty())
|
||||
{
|
||||
|
||||
@@ -61,9 +61,10 @@ public:
|
||||
void setTable(const sqlb::ObjectIdentifier& table) { m_table = table; }
|
||||
sqlb::ObjectIdentifier table() const { return m_table; }
|
||||
|
||||
void setRowIdColumn(const std::string& rowid) { m_rowid_column = rowid; }
|
||||
std::string rowIdColumn() const { return m_rowid_column; }
|
||||
bool hasCustomRowIdColumn() const { return m_rowid_column != "rowid" && m_rowid_column != "_rowid_"; }
|
||||
void setRowIdColumns(const std::vector<std::string>& rowids) { m_rowid_columns = rowids; }
|
||||
std::vector<std::string> rowIdColumns() const { return m_rowid_columns; }
|
||||
void setRowIdColumn(const std::string& rowid) { m_rowid_columns = {rowid}; }
|
||||
bool hasCustomRowIdColumn() const { return m_rowid_columns.size() != 1 || (m_rowid_columns.at(0) != "rowid" && m_rowid_columns.at(0) != "_rowid_"); }
|
||||
|
||||
const std::vector<SelectedColumn>& selectedColumns() const { return m_selected_columns; }
|
||||
std::vector<SelectedColumn>& selectedColumns() { return m_selected_columns; }
|
||||
@@ -78,7 +79,7 @@ public:
|
||||
private:
|
||||
std::vector<std::string> m_column_names;
|
||||
sqlb::ObjectIdentifier m_table;
|
||||
std::string m_rowid_column;
|
||||
std::vector<std::string> m_rowid_columns;
|
||||
std::vector<SelectedColumn> m_selected_columns;
|
||||
std::unordered_map<int, std::string> m_where;
|
||||
std::vector<SortedColumn> m_sort;
|
||||
|
||||
@@ -333,7 +333,7 @@ Table& Table::operator=(const Table& rhs)
|
||||
Object::operator=(rhs);
|
||||
|
||||
// Just assign the strings
|
||||
m_rowidColumn = rhs.m_rowidColumn;
|
||||
m_rowidColumns = rhs.m_rowidColumns;
|
||||
m_virtual = rhs.m_virtual;
|
||||
|
||||
// Clear the fields and the constraints first in order to avoid duplicates and/or old data in the next step
|
||||
@@ -354,7 +354,7 @@ bool Table::operator==(const Table& rhs) const
|
||||
if(!Object::operator==(rhs))
|
||||
return false;
|
||||
|
||||
if(m_rowidColumn != rhs.m_rowidColumn)
|
||||
if(m_rowidColumns != rhs.m_rowidColumns)
|
||||
return false;
|
||||
if(m_virtual != rhs.m_virtual)
|
||||
return false;
|
||||
@@ -389,17 +389,6 @@ bool Table::operator==(const Table& rhs) const
|
||||
return true;
|
||||
}
|
||||
|
||||
Table::field_iterator Table::findPk()
|
||||
{
|
||||
// TODO This is a stupid function (and always was) which should be fixed/improved
|
||||
|
||||
QStringList pk = primaryKey();
|
||||
if(pk.empty())
|
||||
return fields.end();
|
||||
else
|
||||
return findField(this, pk.at(0));
|
||||
}
|
||||
|
||||
QStringList Table::fieldList() const
|
||||
{
|
||||
QStringList sl;
|
||||
@@ -1009,7 +998,7 @@ TablePtr CreateTableWalker::table()
|
||||
s = s->getNextSibling(); // WITHOUT
|
||||
s = s->getNextSibling(); // ROWID
|
||||
|
||||
tab->setRowidColumn(tab->findPk()->name());
|
||||
tab->setRowidColumns(tab->primaryKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ private:
|
||||
class Table : public Object
|
||||
{
|
||||
public:
|
||||
explicit Table(const QString& name): Object(name), m_rowidColumn("_rowid_") {}
|
||||
explicit Table(const QString& name): Object(name), m_rowidColumns({"_rowid_"}) {}
|
||||
Table& operator=(const Table& rhs);
|
||||
|
||||
bool operator==(const Table& rhs) const;
|
||||
@@ -420,9 +420,9 @@ public:
|
||||
|
||||
QStringList fieldNames() const;
|
||||
|
||||
void setRowidColumn(const QString& rowid) { m_rowidColumn = rowid; }
|
||||
const QString& rowidColumn() const { return m_rowidColumn; }
|
||||
bool isWithoutRowidTable() const { return m_rowidColumn != "_rowid_"; }
|
||||
void setRowidColumns(const QStringList& rowid) { m_rowidColumns = rowid; }
|
||||
const QStringList& rowidColumns() const { return m_rowidColumns; }
|
||||
bool isWithoutRowidTable() const { return m_rowidColumns != (QStringList() << "_rowid_"); }
|
||||
|
||||
void setVirtualUsing(const QString& virt_using) { m_virtual = virt_using; }
|
||||
QString virtualUsing() const { return m_virtual; }
|
||||
@@ -442,8 +442,6 @@ public:
|
||||
void removeKeyFromAllConstraints(const QString& key);
|
||||
void renameKeyInAllConstraints(const QString& key, const QString& to);
|
||||
|
||||
field_iterator findPk();
|
||||
|
||||
/**
|
||||
* @brief parseSQL Parses the create Table statement in sSQL.
|
||||
* @param sSQL The create table statement.
|
||||
@@ -455,7 +453,7 @@ private:
|
||||
bool hasAutoIncrement() const;
|
||||
|
||||
private:
|
||||
QString m_rowidColumn;
|
||||
QStringList m_rowidColumns;
|
||||
ConstraintMap m_constraints;
|
||||
QString m_virtual;
|
||||
};
|
||||
|
||||
@@ -1141,10 +1141,19 @@ bool DBBrowserDB::getRow(const sqlb::ObjectIdentifier& table, const QString& row
|
||||
if(!_db)
|
||||
return false;
|
||||
|
||||
QString sQuery = QString("SELECT * FROM %1 WHERE %2='%3';")
|
||||
.arg(table.toString())
|
||||
.arg(sqlb::escapeIdentifier(getObjectByName<sqlb::Table>(table)->rowidColumn()))
|
||||
.arg(rowid);
|
||||
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.
|
||||
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';")
|
||||
.arg(sqlb::escapeIdentifier(pks).join(","))
|
||||
.arg(QString(rowid).replace("'", "''"));
|
||||
}
|
||||
|
||||
QByteArray utf8Query = sQuery.toUtf8();
|
||||
sqlite3_stmt *stmt;
|
||||
@@ -1270,7 +1279,9 @@ QString DBBrowserDB::addRecord(const sqlb::ObjectIdentifier& tablename)
|
||||
QString pk_value;
|
||||
if(table->isWithoutRowidTable())
|
||||
{
|
||||
pk_value = QString::number(max(tablename, *sqlb::findField(table, table->rowidColumn())).toLongLong() + 1);
|
||||
// For multiple rowid columns we just use the value of the last one and increase that one by one. If this doesn't yield a valid combination
|
||||
// the insert record dialog should pop up automatically.
|
||||
pk_value = QString::number(max(tablename, *sqlb::findField(table, table->rowidColumns().last())).toLongLong() + 1);
|
||||
sInsertstmt = emptyInsertStmt(tablename.schema(), *table, pk_value);
|
||||
} else {
|
||||
sInsertstmt = emptyInsertStmt(tablename.schema(), *table);
|
||||
@@ -1288,26 +1299,42 @@ QString DBBrowserDB::addRecord(const sqlb::ObjectIdentifier& tablename)
|
||||
}
|
||||
}
|
||||
|
||||
bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids, const QString& pseudo_pk)
|
||||
bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids, const std::vector<std::string>& pseudo_pk)
|
||||
{
|
||||
if (!isOpen()) return false;
|
||||
|
||||
// Get primary key of the object to edit.
|
||||
QString pk = primaryKeyForEditing(table, pseudo_pk);
|
||||
if(pk.isNull())
|
||||
QStringList pks = primaryKeyForEditing(table, pseudo_pk);
|
||||
if(pks.isEmpty())
|
||||
{
|
||||
lastErrorMessage = tr("Cannot delete this object");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Quote all values in advance
|
||||
QStringList quoted_rowids;
|
||||
for(QString rowid : rowids)
|
||||
quoted_rowids.append("'" + rowid.replace("'", "''") + "'");
|
||||
|
||||
QString statement = QString("DELETE FROM %1 WHERE %2 IN (%3);")
|
||||
.arg(table.toString())
|
||||
.arg(pk)
|
||||
.arg(quoted_rowids.join(", "));
|
||||
// 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.
|
||||
QString statement;
|
||||
if(pks.size() == 1)
|
||||
{
|
||||
statement = QString("DELETE FROM %1 WHERE %2 IN (%3);")
|
||||
.arg(table.toString())
|
||||
.arg(pks.at(0))
|
||||
.arg(quoted_rowids.join(", "));
|
||||
} else {
|
||||
statement = QString("DELETE FROM %1 WHERE ").arg(table.toString());
|
||||
|
||||
statement += "json_array(";
|
||||
for(const auto& pk : pks)
|
||||
statement += sqlb::escapeIdentifier(pk) + ",";
|
||||
statement.chop(1);
|
||||
statement += QString(") IN (%1)").arg(quoted_rowids.join(", "));
|
||||
}
|
||||
|
||||
if(executeSQL(statement))
|
||||
{
|
||||
return true;
|
||||
@@ -1318,24 +1345,34 @@ bool DBBrowserDB::deleteRecords(const sqlb::ObjectIdentifier& table, const QStri
|
||||
}
|
||||
|
||||
bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const QString& column,
|
||||
const QString& rowid, const QByteArray& value, bool itsBlob, const QString& pseudo_pk)
|
||||
const QString& rowid, const QByteArray& value, bool itsBlob, const std::vector<std::string>& pseudo_pk)
|
||||
{
|
||||
waitForDbRelease();
|
||||
if (!isOpen()) return false;
|
||||
|
||||
// Get primary key of the object to edit.
|
||||
QString pk = primaryKeyForEditing(table, pseudo_pk);
|
||||
if(pk.isNull())
|
||||
QStringList pks = primaryKeyForEditing(table, pseudo_pk);
|
||||
if(pks.isEmpty())
|
||||
{
|
||||
lastErrorMessage = tr("Cannot set data on this object");
|
||||
return false;
|
||||
}
|
||||
|
||||
QString sql = QString("UPDATE %1 SET %2=? WHERE %3='%4';")
|
||||
QString sql = QString("UPDATE %1 SET %2=? WHERE ")
|
||||
.arg(table.toString())
|
||||
.arg(sqlb::escapeIdentifier(column))
|
||||
.arg(sqlb::escapeIdentifier(pk))
|
||||
.arg(QString(rowid).replace("'", "''"));
|
||||
.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.
|
||||
if(pks.size() == 1)
|
||||
{
|
||||
sql += QString("%1='%2';")
|
||||
.arg(sqlb::escapeIdentifier(pks.first()))
|
||||
.arg(QString(rowid).replace("'", "''"));
|
||||
} else {
|
||||
sql += QString("json_array(%1)='%2';")
|
||||
.arg(sqlb::escapeIdentifier(pks).join(","))
|
||||
.arg(QString(rowid).replace("'", "''"));
|
||||
}
|
||||
|
||||
logSQL(sql, kLogMsg_App);
|
||||
setSavepoint();
|
||||
@@ -1375,22 +1412,25 @@ bool DBBrowserDB::updateRecord(const sqlb::ObjectIdentifier& table, const QStrin
|
||||
}
|
||||
}
|
||||
|
||||
QString DBBrowserDB::primaryKeyForEditing(const sqlb::ObjectIdentifier& table, const QString& pseudo_pk) const
|
||||
QStringList DBBrowserDB::primaryKeyForEditing(const sqlb::ObjectIdentifier& table, const std::vector<std::string>& pseudo_pk) const
|
||||
{
|
||||
// This function returns the primary key of the object to edit. For views we support 'pseudo' primary keys which must be specified manually.
|
||||
// If no pseudo pk is specified we'll take the rowid column of the table instead. If this neither a table nor was a pseudo-PK specified,
|
||||
// it is most likely a view that hasn't been configured for editing yet. In this case we return a null string to abort.
|
||||
|
||||
if(pseudo_pk.isEmpty())
|
||||
if(pseudo_pk.empty())
|
||||
{
|
||||
sqlb::TablePtr tbl = getObjectByName<sqlb::Table>(table);
|
||||
if(tbl)
|
||||
return tbl->rowidColumn();
|
||||
return tbl->rowidColumns();
|
||||
} else {
|
||||
return pseudo_pk;
|
||||
QStringList ret;
|
||||
for(const auto& col : pseudo_pk)
|
||||
ret << QString::fromStdString(col);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return QString();
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
bool DBBrowserDB::createTable(const sqlb::ObjectIdentifier& name, const sqlb::FieldVector& structure)
|
||||
|
||||
@@ -164,8 +164,8 @@ private:
|
||||
|
||||
public:
|
||||
QString addRecord(const sqlb::ObjectIdentifier& tablename);
|
||||
bool deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids, const QString& pseudo_pk = QString());
|
||||
bool updateRecord(const sqlb::ObjectIdentifier& table, const QString& column, const QString& rowid, const QByteArray& value, bool itsBlob, const QString& pseudo_pk = QString());
|
||||
bool deleteRecords(const sqlb::ObjectIdentifier& table, const QStringList& rowids, const std::vector<std::string>& pseudo_pk = {});
|
||||
bool updateRecord(const sqlb::ObjectIdentifier& table, const QString& column, const QString& rowid, const QByteArray& value, bool itsBlob, const std::vector<std::string>& pseudo_pk = {});
|
||||
|
||||
bool createTable(const sqlb::ObjectIdentifier& name, const sqlb::FieldVector& structure);
|
||||
bool renameTable(const QString& schema, const QString& from_table, const QString& to_table);
|
||||
@@ -261,7 +261,7 @@ private:
|
||||
bool isEncrypted;
|
||||
bool isReadOnly;
|
||||
|
||||
QString primaryKeyForEditing(const sqlb::ObjectIdentifier& table, const QString& pseudo_pk) const;
|
||||
QStringList primaryKeyForEditing(const sqlb::ObjectIdentifier& table, const std::vector<std::string>& pseudo_pk) const;
|
||||
|
||||
// SQLite Callbacks
|
||||
void collationNeeded(void* pData, sqlite3* db, int eTextRep, const char* sCollationName);
|
||||
|
||||
@@ -132,9 +132,12 @@ void SqliteTableModel::setQuery(const sqlb::Query& query)
|
||||
sqlb::TablePtr t = m_db.getObjectByName<sqlb::Table>(query.table());
|
||||
if(t && t->fields.size()) // parsing was OK
|
||||
{
|
||||
QString rowid = t->rowidColumn();
|
||||
m_query.setRowIdColumn(rowid.toStdString());
|
||||
m_headers.push_back(rowid);
|
||||
QStringList rowids = t->rowidColumns();
|
||||
std::vector<std::string> rowids_std;
|
||||
for(const auto& rowid : rowids)
|
||||
rowids_std.push_back(rowid.toStdString());
|
||||
m_query.setRowIdColumns(rowids_std);
|
||||
m_headers.push_back(rowids.join(","));
|
||||
m_headers.append(t->fieldNames());
|
||||
|
||||
// parse columns types
|
||||
@@ -160,7 +163,7 @@ void SqliteTableModel::setQuery(const sqlb::Query& query)
|
||||
if(!allOk)
|
||||
{
|
||||
QString sColumnQuery = QString::fromUtf8("SELECT * FROM %1;").arg(query.table().toString());
|
||||
if(m_query.rowIdColumn().empty())
|
||||
if(m_query.rowIdColumns().empty())
|
||||
m_query.setRowIdColumn("_rowid_");
|
||||
m_headers.push_back("_rowid_");
|
||||
m_headers.append(getColumns(nullptr, sColumnQuery, m_vDataTypes));
|
||||
@@ -453,10 +456,13 @@ 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(index.column()), cached_row.at(0), newValue, isBlob, QString::fromStdString(m_query.rowIdColumn())))
|
||||
if(m_db.updateRecord(m_query.table(), m_headers.at(index.column()), cached_row.at(0), newValue, isBlob, m_query.rowIdColumns()))
|
||||
{
|
||||
cached_row.replace(index.column(), newValue);
|
||||
if(m_headers.at(index.column()).toStdString() == m_query.rowIdColumn()) {
|
||||
QStringList header;
|
||||
for(const auto& col : m_query.rowIdColumns())
|
||||
header += QString::fromStdString(col);
|
||||
if(m_headers.at(index.column()) == header.join(",")) {
|
||||
cached_row.replace(0, newValue);
|
||||
const QModelIndex& rowidIndex = index.sibling(index.row(), 0);
|
||||
lock.unlock();
|
||||
@@ -596,7 +602,7 @@ bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = m_db.deleteRecords(m_query.table(), rowids, QString::fromStdString(m_query.rowIdColumn()));
|
||||
bool ok = m_db.deleteRecords(m_query.table(), rowids, m_query.rowIdColumns());
|
||||
|
||||
if (ok) {
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
@@ -830,18 +836,23 @@ bool SqliteTableModel::dropMimeData(const QMimeData* data, Qt::DropAction, int r
|
||||
return false;
|
||||
}
|
||||
|
||||
void SqliteTableModel::setPseudoPk(QString pseudoPk)
|
||||
void SqliteTableModel::setPseudoPk(std::vector<std::string> pseudoPk)
|
||||
{
|
||||
if(pseudoPk.isNull())
|
||||
pseudoPk = QString("_rowid_");
|
||||
if(pseudoPk.empty())
|
||||
pseudoPk.emplace_back("_rowid_");
|
||||
|
||||
// Do nothing if the value didn't change
|
||||
if(m_query.rowIdColumn() == pseudoPk.toStdString())
|
||||
if(m_query.rowIdColumns() == pseudoPk)
|
||||
return;
|
||||
|
||||
m_query.setRowIdColumn(pseudoPk.toStdString());
|
||||
m_query.setRowIdColumns(pseudoPk);
|
||||
if(m_headers.size())
|
||||
m_headers[0] = pseudoPk;
|
||||
{
|
||||
QStringList headers;
|
||||
for(const auto& col : pseudoPk)
|
||||
headers << QString::fromStdString(col);
|
||||
m_headers[0] = headers.join(",");
|
||||
}
|
||||
|
||||
buildQuery();
|
||||
}
|
||||
|
||||
@@ -99,9 +99,9 @@ public:
|
||||
QString encoding() const { return m_encoding; }
|
||||
|
||||
// The pseudo-primary key is exclusively for editing views
|
||||
void setPseudoPk(QString pseudoPk);
|
||||
void setPseudoPk(std::vector<std::string> pseudoPk);
|
||||
bool hasPseudoPk() const;
|
||||
QString pseudoPk() const { return QString::fromStdString(m_query.rowIdColumn()); }
|
||||
std::vector<std::string> pseudoPk() const { return m_query.rowIdColumns(); }
|
||||
|
||||
sqlb::ForeignKeyClause getForeignKeyClause(int column) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user