mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-02-07 03:58:28 -06:00
Support non-integer primary keys
Add support for non-integer primary keys, especially on table without rowid column. The previous code often assumed that the rowid column or its equivalent was a 64bit integer but SQLite allows any data, including text, to be stored in there. See issue #240.
This commit is contained in:
@@ -629,9 +629,9 @@ bool DBBrowserDB::executeMultiSQL(const QString& statement, bool dirty, bool log
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBBrowserDB::getRow(const QString& sTableName, qint64 rowid, QList<QByteArray>& rowdata)
|
||||
bool DBBrowserDB::getRow(const QString& sTableName, const QString& rowid, QList<QByteArray>& rowdata)
|
||||
{
|
||||
QString sQuery = QString("SELECT * FROM `%1` WHERE `%2`=%3;").arg(sTableName).arg(getObjectByName(sTableName).table.rowidColumn()).arg(rowid);
|
||||
QString sQuery = QString("SELECT * FROM `%1` WHERE `%2`='%3';").arg(sTableName).arg(getObjectByName(sTableName).table.rowidColumn()).arg(rowid);
|
||||
QByteArray utf8Query = sQuery.toUtf8();
|
||||
sqlite3_stmt *stmt;
|
||||
bool ret = false;
|
||||
@@ -663,12 +663,12 @@ bool DBBrowserDB::getRow(const QString& sTableName, qint64 rowid, QList<QByteArr
|
||||
return ret;
|
||||
}
|
||||
|
||||
qint64 DBBrowserDB::max(const sqlb::Table& t, sqlb::FieldPtr field) const
|
||||
QString DBBrowserDB::max(const sqlb::Table& t, sqlb::FieldPtr field) const
|
||||
{
|
||||
QString sQuery = QString("SELECT MAX(`%2`) from `%1`;").arg(t.name()).arg(field->name());
|
||||
QString sQuery = QString("SELECT MAX(CAST(`%2` AS INTEGER)) FROM `%1`;").arg(t.name()).arg(field->name());
|
||||
QByteArray utf8Query = sQuery.toUtf8();
|
||||
sqlite3_stmt *stmt;
|
||||
qint64 ret = 0;
|
||||
QString ret = "0";
|
||||
|
||||
int status = sqlite3_prepare_v2(_db, utf8Query, utf8Query.size(), &stmt, NULL);
|
||||
if(SQLITE_OK == status)
|
||||
@@ -678,7 +678,7 @@ qint64 DBBrowserDB::max(const sqlb::Table& t, sqlb::FieldPtr field) const
|
||||
{
|
||||
if(sqlite3_column_count(stmt) == 1)
|
||||
{
|
||||
ret = sqlite3_column_int64(stmt, 0);
|
||||
ret = QString::fromUtf8(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,7 +687,7 @@ qint64 DBBrowserDB::max(const sqlb::Table& t, sqlb::FieldPtr field) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString DBBrowserDB::emptyInsertStmt(const sqlb::Table& t, qint64 pk_value) const
|
||||
QString DBBrowserDB::emptyInsertStmt(const sqlb::Table& t, const QString& pk_value) const
|
||||
{
|
||||
QString stmt = QString("INSERT INTO `%1`").arg(t.name());
|
||||
|
||||
@@ -695,31 +695,22 @@ QString DBBrowserDB::emptyInsertStmt(const sqlb::Table& t, qint64 pk_value) cons
|
||||
QStringList fields;
|
||||
foreach(sqlb::FieldPtr f, t.fields()) {
|
||||
if(f->primaryKey()) {
|
||||
if(f->isInteger())
|
||||
{
|
||||
fields << f->name();
|
||||
fields << f->name();
|
||||
|
||||
if(pk_value != -1)
|
||||
vals << QString::number(pk_value);
|
||||
if(!pk_value.isNull())
|
||||
{
|
||||
vals << pk_value;
|
||||
} else {
|
||||
if(f->notnull())
|
||||
{
|
||||
QString maxval = this->max(t, f);
|
||||
vals << QString::number(maxval.toLongLong() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(f->notnull())
|
||||
{
|
||||
qint64 maxval = this->max(t, f);
|
||||
vals << QString::number(maxval + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
vals << "NULL";
|
||||
}
|
||||
vals << "NULL";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fields << f->name();
|
||||
|
||||
vals << "''";
|
||||
}
|
||||
} else if(f->notnull() && f->defaultValue().length() == 0) {
|
||||
fields << f->name();
|
||||
|
||||
@@ -751,21 +742,19 @@ QString DBBrowserDB::emptyInsertStmt(const sqlb::Table& t, qint64 pk_value) cons
|
||||
return stmt;
|
||||
}
|
||||
|
||||
qint64 DBBrowserDB::addRecord(const QString& sTableName)
|
||||
QString DBBrowserDB::addRecord(const QString& sTableName)
|
||||
{
|
||||
if (!isOpen()) return false;
|
||||
if (!isOpen()) return QString();
|
||||
|
||||
sqlb::Table table = getObjectByName(sTableName).table;
|
||||
|
||||
// For tables without rowid we have to set the primary key by ourselves. We do so by querying for the largest value in the PK column
|
||||
// and adding one to it.
|
||||
QString sInsertstmt;
|
||||
qint64 pk_value;
|
||||
QString pk_value;
|
||||
if(table.isWithoutRowidTable())
|
||||
{
|
||||
SqliteTableModel m(this, this);
|
||||
m.setQuery(QString("SELECT MAX(`%1`) FROM `%2`;").arg(table.rowidColumn()).arg(sTableName));
|
||||
pk_value = m.data(m.index(0, 0)).toLongLong() + 1;
|
||||
pk_value = QString::number(max(table, table.fields().at(table.findField(table.rowidColumn()))).toLongLong() + 1);
|
||||
sInsertstmt = emptyInsertStmt(table, pk_value);
|
||||
} else {
|
||||
sInsertstmt = emptyInsertStmt(table);
|
||||
@@ -774,22 +763,22 @@ qint64 DBBrowserDB::addRecord(const QString& sTableName)
|
||||
if(!executeSQL(sInsertstmt))
|
||||
{
|
||||
qWarning() << "addRecord: " << lastErrorMessage;
|
||||
return -1;
|
||||
return QString();
|
||||
} else {
|
||||
if(table.isWithoutRowidTable())
|
||||
return pk_value;
|
||||
else
|
||||
return sqlite3_last_insert_rowid(_db);
|
||||
return QString::number(sqlite3_last_insert_rowid(_db));
|
||||
}
|
||||
}
|
||||
|
||||
bool DBBrowserDB::deleteRecord(const QString& table, qint64 rowid)
|
||||
bool DBBrowserDB::deleteRecord(const QString& table, const QString& rowid)
|
||||
{
|
||||
if (!isOpen()) return false;
|
||||
bool ok = false;
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
QString statement = QString("DELETE FROM `%1` WHERE `%2`=%3;").arg(table).arg(getObjectByName(table).table.rowidColumn()).arg(rowid);
|
||||
QString statement = QString("DELETE FROM `%1` WHERE `%2`='%3';").arg(table).arg(getObjectByName(table).table.rowidColumn()).arg(rowid);
|
||||
if(executeSQL(statement))
|
||||
ok = true;
|
||||
else
|
||||
@@ -798,13 +787,13 @@ bool DBBrowserDB::deleteRecord(const QString& table, qint64 rowid)
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DBBrowserDB::updateRecord(const QString& table, const QString& column, qint64 row, const QByteArray& value, bool itsBlob)
|
||||
bool DBBrowserDB::updateRecord(const QString& table, const QString& column, const QString& rowid, const QByteArray& value, bool itsBlob)
|
||||
{
|
||||
if (!isOpen()) return false;
|
||||
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
QString sql = QString("UPDATE `%1` SET `%2`=? WHERE `%3`=%4;").arg(table).arg(column).arg(getObjectByName(table).table.rowidColumn()).arg(row);
|
||||
QString sql = QString("UPDATE `%1` SET `%2`=? WHERE `%3`='%4';").arg(table).arg(column).arg(getObjectByName(table).table.rowidColumn()).arg(rowid);
|
||||
|
||||
logSQL(sql, kLogMsg_App);
|
||||
setRestorePoint();
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
* @param rowdata A list of QByteArray containing the row data.
|
||||
* @return true if statement execution was ok, else false.
|
||||
*/
|
||||
bool getRow(const QString& sTableName, qint64 rowid, QList<QByteArray>& rowdata);
|
||||
bool getRow(const QString& sTableName, const QString& rowid, QList<QByteArray>& rowdata);
|
||||
|
||||
/**
|
||||
* @brief max Queries the table t for the max value of field.
|
||||
@@ -74,19 +74,19 @@ public:
|
||||
* @param field Field to get the max value
|
||||
* @return the max value of the field or 0 on error
|
||||
*/
|
||||
qint64 max(const sqlb::Table& t, sqlb::FieldPtr field) const;
|
||||
QString max(const sqlb::Table& t, sqlb::FieldPtr field) const;
|
||||
|
||||
void updateSchema();
|
||||
qint64 addRecord(const QString& sTableName);
|
||||
QString addRecord(const QString& sTableName);
|
||||
|
||||
/**
|
||||
* @brief Creates an empty insert statement.
|
||||
* @param pk_value This optional parameter can be used to manually set a specific value for the primary key column
|
||||
* @return An sqlite conform INSERT INTO statement with empty values. (NULL,'',0)
|
||||
*/
|
||||
QString emptyInsertStmt(const sqlb::Table& t, qint64 pk_value = -1) const;
|
||||
bool deleteRecord(const QString& table, qint64 rowid);
|
||||
bool updateRecord(const QString& table, const QString& column, qint64 row, const QByteArray& value, bool itsBlob);
|
||||
QString emptyInsertStmt(const sqlb::Table& t, const QString& pk_value = QString()) const;
|
||||
bool deleteRecord(const QString& table, const QString& rowid);
|
||||
bool updateRecord(const QString& table, const QString& column, const QString& rowid, const QByteArray& value, bool itsBlob);
|
||||
|
||||
bool createTable(const QString& name, const sqlb::FieldVector& structure);
|
||||
bool renameTable(const QString& from_table, const QString& to_table);
|
||||
|
||||
@@ -282,7 +282,7 @@ bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value,
|
||||
if(oldValue == newValue && oldValue.isNull() == newValue.isNull())
|
||||
return true;
|
||||
|
||||
if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), m_data[index.row()].at(0).toLongLong(), newValue, isBinary(index)))
|
||||
if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), m_data[index.row()].at(0), newValue, isBinary(index)))
|
||||
{
|
||||
// Only update the cache if this row has already been read, if not there's no need to do any changes to the cache
|
||||
if(index.row() < m_data.size())
|
||||
@@ -345,14 +345,14 @@ bool SqliteTableModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
DataType tempList;
|
||||
for(int i=row; i < row + count; ++i)
|
||||
{
|
||||
qint64 rowid = m_db->addRecord(m_sTable);
|
||||
if(rowid < 0)
|
||||
QString rowid = m_db->addRecord(m_sTable);
|
||||
if(rowid.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_rowCount++;
|
||||
tempList.append(blank_data);
|
||||
tempList[i - row].replace(0, QByteArray::number(rowid));
|
||||
tempList[i - row].replace(0, rowid.toUtf8());
|
||||
|
||||
// update column with default values
|
||||
QByteArrayList rowdata;
|
||||
@@ -380,7 +380,7 @@ bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
|
||||
for(int i=count-1;i>=0;i--)
|
||||
{
|
||||
m_db->deleteRecord(m_sTable, m_data.at(row + i).at(0).toLongLong());
|
||||
m_db->deleteRecord(m_sTable, m_data.at(row + i).at(0));
|
||||
m_data.removeAt(row + i);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user