From c7de94cb7739bf6e87aa3152d0686f6c4908f1e9 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Tue, 26 Aug 2014 18:38:04 +0200 Subject: [PATCH] Make it possible to add rows to a WITHOUT rowid table This commit allows you to add new rows to a table without rowid column. The main problem here is that SQLite won't create the next value for the primary key column itself, so we have to do that instead. See issue #51. --- src/sqlitedb.cpp | 26 ++++++++++++++++++++------ src/sqlitetypes.cpp | 29 +++++++++++++++-------------- src/sqlitetypes.h | 4 +++- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index d8e2b90c..279f3fb7 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -462,11 +462,22 @@ int DBBrowserDB::addRecord(const QString& sTableName) char *errmsg; if (!isOpen()) return false; - // add record is seldom called, for now this is ok - // but if we ever going to add a lot of records - // we should cache the parsed tables somewhere - sqlb::Table table = sqlb::Table::parseSQL(getObjectByName(sTableName).getsql()).first; - QString sInsertstmt = table.emptyInsertStmt(); + 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; + int pk_value; + if(table.isWithoutRowidTable()) + { + SqliteTableModel m(this, this); + m.setQuery(QString("SELECT MAX(`%1`) FROM `%2`;").arg(getObjectByName(sTableName).table.rowidColumn()).arg(sTableName)); + pk_value = m.data(m.index(0, 0)).toInt() + 1; + sInsertstmt = table.emptyInsertStmt(pk_value); + } else { + sInsertstmt = table.emptyInsertStmt(); + } + lastErrorMessage = ""; logSQL(sInsertstmt, kLogMsg_App); setRestorePoint(); @@ -477,7 +488,10 @@ int DBBrowserDB::addRecord(const QString& sTableName) qWarning() << "addRecord: " << lastErrorMessage; return -1; } else { - return sqlite3_last_insert_rowid(_db); + if(table.isWithoutRowidTable()) + return pk_value; + else + return sqlite3_last_insert_rowid(_db); } } diff --git a/src/sqlitetypes.cpp b/src/sqlitetypes.cpp index cda5ecf8..e3ae856f 100644 --- a/src/sqlitetypes.cpp +++ b/src/sqlitetypes.cpp @@ -156,28 +156,29 @@ QPair Table::parseSQL(const QString &sSQL) return qMakePair(Table(""), false); } -QString Table::emptyInsertStmt() const +QString Table::emptyInsertStmt(int pk_value) const { QString stmt = QString("INSERT INTO `%1`").arg(m_name); QStringList vals; QStringList fields; foreach(FieldPtr f, m_fields) { - if(f->notnull()) + if( f->primaryKey() && f->isInteger() ) { fields << f->name(); - if( f->primaryKey() && f->isInteger() ) - { + + if(pk_value != -1) + vals << QString::number(pk_value); + else vals << "NULL"; - } else { - if(f->isInteger()) - vals << "0"; - else - vals << "''"; - } - } - else - { + } else if(f->notnull()) { + fields << f->name(); + + if(f->isInteger()) + vals << "0"; + else + vals << "''"; + } else { // don't insert into fields with a default value // or we will never see it. if(f->defaultValue().length() == 0) @@ -228,7 +229,7 @@ QString Table::sql() const sql += "\n)"; // without rowid - if(m_rowidColumn != "_rowid_") + if(isWithoutRowidTable()) sql += " WITHOUT ROWID"; return sql + ";"; diff --git a/src/sqlitetypes.h b/src/sqlitetypes.h index 6065de47..59c3bdbe 100644 --- a/src/sqlitetypes.h +++ b/src/sqlitetypes.h @@ -82,9 +82,10 @@ public: /** * @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; + QString emptyInsertStmt(int pk_value = -1) const; /** * @brief Returns the CREATE TABLE statement for this table object @@ -98,6 +99,7 @@ public: void setField(int index, FieldPtr f) { m_fields[index] = f; } void setRowidColumn(const QString& rowid) { m_rowidColumn = rowid; } const QString& rowidColumn() const { return m_rowidColumn; } + bool isWithoutRowidTable() const { return m_rowidColumn != "_rowid_"; } void clear(); /**