diff --git a/src/DbStructureModel.cpp b/src/DbStructureModel.cpp index a4cd404d..15d6603c 100644 --- a/src/DbStructureModel.cpp +++ b/src/DbStructureModel.cpp @@ -178,24 +178,32 @@ void DbStructureModel::reloadData() for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it) { // Object node - QTreeWidgetItem* item = addNode(typeToParentItem.value((*it).gettype()), *it); + QString type; + switch((*it).gettype()) + { + case sqlb::Object::ObjectTypes::Table: type = "table"; break; + case sqlb::Object::ObjectTypes::Index: type = "index"; break; + case sqlb::Object::ObjectTypes::Trigger: type = "trigger"; break; + case sqlb::Object::ObjectTypes::View: type = "view"; break; + } + QTreeWidgetItem* item = addNode(typeToParentItem.value(type), *it); // If it is a table or view add the field nodes - if((*it).gettype() == "table" || (*it).gettype() == "view") + if((*it).gettype() == sqlb::Object::ObjectTypes::Table || (*it).gettype() == sqlb::Object::ObjectTypes::View) { // Add extra node for browsable section addNode(typeToParentItem.value("browsable"), *it); // Add field nodes - sqlb::FieldVector pk = (*it).table.primaryKey(); - for(int i=0; i < (*it).table.fields().size(); ++i) + sqlb::FieldVector pk = (*it).object.dynamicCast()->primaryKey(); + for(int i=0; i < (*it).object.dynamicCast()->fields().size(); ++i) { QTreeWidgetItem *fldItem = new QTreeWidgetItem(item); - fldItem->setText(0, (*it).table.fields().at(i)->name()); + fldItem->setText(0, (*it).object.dynamicCast()->fields().at(i)->name()); fldItem->setText(1, "field"); - fldItem->setText(2, (*it).table.fields().at(i)->type()); - fldItem->setText(3, (*it).table.fields().at(i)->toString(" ", " ")); - if(pk.contains((*it).table.fields().at(i))) + fldItem->setText(2, (*it).object.dynamicCast()->fields().at(i)->type()); + fldItem->setText(3, (*it).object.dynamicCast()->fields().at(i)->toString(" ", " ")); + if(pk.contains((*it).object.dynamicCast()->fields().at(i))) fldItem->setIcon(0, QIcon(":/icons/field_key")); else fldItem->setIcon(0, QIcon(":/icons/field")); @@ -279,10 +287,19 @@ bool DbStructureModel::dropMimeData(const QMimeData* data, Qt::DropAction action QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const DBBrowserObject& object) { + QString type; + switch(object.gettype()) + { + case sqlb::Object::ObjectTypes::Table: type = "table"; break; + case sqlb::Object::ObjectTypes::Index: type = "index"; break; + case sqlb::Object::ObjectTypes::Trigger: type = "trigger"; break; + case sqlb::Object::ObjectTypes::View: type = "view"; break; + } + QTreeWidgetItem *item = new QTreeWidgetItem(parent); - item->setIcon(0, QIcon(QString(":/icons/%1").arg(object.gettype()))); + item->setIcon(0, QIcon(QString(":/icons/%1").arg(type))); item->setText(0, object.getname()); - item->setText(1, object.gettype()); + item->setText(1, type); item->setText(3, object.getsql()); return item; diff --git a/src/EditIndexDialog.cpp b/src/EditIndexDialog.cpp index 09b2f939..96d1e9d4 100644 --- a/src/EditIndexDialog.cpp +++ b/src/EditIndexDialog.cpp @@ -33,7 +33,7 @@ EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const QString& indexName, bool if(!newIndex) { // Load the current layour and fill in the dialog fields - index = pdb.getObjectByName(curIndex).index; + index = *(pdb.getObjectByName(curIndex).dynamicCast()); ui->editIndexName->blockSignals(true); ui->editIndexName->setText(index.name()); @@ -69,7 +69,7 @@ void EditIndexDialog::tableChanged(const QString& new_table, bool initialLoad) } // And fill the table again - QStringList fields = pdb.getObjectByName(new_table).table.fieldNames(); + QStringList fields = pdb.getObjectByName(new_table).dynamicCast()->fieldNames(); ui->tableIndexColumns->setRowCount(fields.size()); for(int i=0; i < fields.size(); ++i) { diff --git a/src/EditTableDialog.cpp b/src/EditTableDialog.cpp index d5203ccc..ce65f57d 100644 --- a/src/EditTableDialog.cpp +++ b/src/EditTableDialog.cpp @@ -33,11 +33,11 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const QString& tableName, bool if(m_bNewTable == false) { // Existing table, so load and set the current layout - DBBrowserObject obj = pdb.getObjectByName(curTable); - QString sTablesql = obj.getsql(); - QPair parse_result = sqlb::Table::parseSQL(sTablesql); - m_table = parse_result.first; - m_table.setTemporary(obj.isTemporary()); + sqlb::TablePtr obj = pdb.getObjectByName(curTable).dynamicCast(); + QString sTablesql = obj->originalSql(); + QPair parse_result = sqlb::Table::parseSQL(sTablesql); + m_table = *(parse_result.first.dynamicCast()); + m_table.setTemporary(obj->isTemporary()); ui->labelEditWarning->setVisible(!parse_result.second); // Set without rowid and temporary checkboxex. No need to trigger any events here as we're only loading a table exactly as it is stored by SQLite, so no need @@ -278,7 +278,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) sqlb::FieldVector pk = m_table.primaryKey(); foreach(const DBBrowserObject& fkobj, pdb.objMap.values("table")) { - QList fks = fkobj.table.constraints(sqlb::FieldVector(), sqlb::Constraint::ForeignKeyConstraintType); + QList fks = fkobj.object.dynamicCast()->constraints(sqlb::FieldVector(), sqlb::Constraint::ForeignKeyConstraintType); foreach(sqlb::ConstraintPtr fkptr, fks) { QSharedPointer fk = fkptr.dynamicCast(); @@ -350,7 +350,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) // 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(curTable).table.rowidColumn())) + .arg(sqlb::escapeIdentifier(pdb.getObjectByName(curTable).dynamicCast()->rowidColumn())) .arg(sqlb::escapeIdentifier(curTable)) .arg(sqlb::escapeIdentifier(field->name()))); if(m.data(m.index(0, 0)).toInt() > 0) @@ -578,8 +578,7 @@ void EditTableDialog::removeField() QMessageBox::warning(0, QApplication::applicationName(), pdb.lastError()); } else { //relayout - QString sTablesql = pdb.getObjectByName(curTable).getsql(); - m_table = sqlb::Table::parseSQL(sTablesql).first; + m_table = *(pdb.getObjectByName(curTable).dynamicCast()); populateFields(); } } @@ -662,8 +661,7 @@ void EditTableDialog::moveCurrentField(bool down) QMessageBox::warning(0, QApplication::applicationName(), pdb.lastError()); } else { // Reload table SQL - QString sTablesql = pdb.getObjectByName(curTable).getsql(); - m_table = sqlb::Table::parseSQL(sTablesql).first; + m_table = *(pdb.getObjectByName(curTable).dynamicCast()); populateFields(); // Select old item at new position diff --git a/src/ForeignKeyEditorDelegate.cpp b/src/ForeignKeyEditorDelegate.cpp index 93192325..1a72c583 100644 --- a/src/ForeignKeyEditorDelegate.cpp +++ b/src/ForeignKeyEditorDelegate.cpp @@ -81,9 +81,9 @@ ForeignKeyEditorDelegate::ForeignKeyEditorDelegate(const DBBrowserDB& db, sqlb:: { const auto objects = m_db.getBrowsableObjects(); for (auto& obj : objects) { - if ("table" == obj.gettype()) { - QString tableName = obj.table.name(); - m_tablesIds.insert(tableName, obj.table.fieldNames()); + if (obj.gettype() == sqlb::Object::ObjectTypes::Table) { + QString tableName = obj.object->name(); + m_tablesIds.insert(tableName, obj.object.dynamicCast()->fieldNames()); } } } diff --git a/src/ImportCsvDialog.cpp b/src/ImportCsvDialog.cpp index fca06268..5cd2e877 100644 --- a/src/ImportCsvDialog.cpp +++ b/src/ImportCsvDialog.cpp @@ -186,9 +186,9 @@ void ImportCsvDialog::accept() objectMap objects = pdb->getBrowsableObjects(); for(auto it=objects.constBegin();it!=objects.constEnd();++it) { - if(it.value().gettype() == "table" && it.value().getname() == ui->editName->text()) + if(it.value().gettype() == sqlb::Object::ObjectTypes::Table && it.value().getname() == ui->editName->text()) { - if((size_t)it.value().table.fields().size() != csv.columns()) + if((size_t)it.value().object.dynamicCast()->fields().size() != csv.columns()) { QMessageBox::warning(this, QApplication::applicationName(), tr("There is already a table of that name and an import into an existing table is only possible if the number of columns match.")); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 57662ff2..05785dfb 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -366,9 +366,9 @@ void MainWindow::populateStructure() { QString objectname = it.value().getname(); - for(int i=0; i < (*it).table.fields().size(); ++i) + for(int i=0; i < (*it).object.dynamicCast()->fields().size(); ++i) { - QString fieldname = (*it).table.fields().at(i)->name(); + QString fieldname = (*it).object.dynamicCast()->fields().at(i)->name(); tablesToColumnsMap[objectname].append(fieldname); } } @@ -429,7 +429,7 @@ void MainWindow::populateTable() } else { QVector v; bool only_defaults = true; - const sqlb::FieldVector& tablefields = db.getObjectByName(tablename).table.fields(); + const sqlb::FieldVector& tablefields = db.getObjectByName(tablename).dynamicCast()->fields(); for(int i=0; itype() == sqlb::Object::ObjectTypes::Table && !db.readOnly(); ui->buttonNewRecord->setEnabled(editable); ui->buttonDeleteRecord->setEnabled(editable); ui->dataTable->setEditTriggers(editable ? QAbstractItemView::SelectedClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); @@ -816,7 +816,7 @@ void MainWindow::doubleClickTable(const QModelIndex& index) // * Don't allow editing of other objects than tables (on the browse table) * bool isEditingAllowed = (m_currentTabTableModel == m_browseTableModel) && - (db.getObjectByName(ui->comboBrowseTable->currentText()).gettype() == "table"); + (db.getObjectByName(ui->comboBrowseTable->currentText())->type() == sqlb::Object::ObjectTypes::Table); // Enable or disable the Apply, Null, & Import buttons in the Edit Cell // dock depending on the value of the "isEditingAllowed" bool above @@ -840,7 +840,7 @@ void MainWindow::dataTableSelectionChanged(const QModelIndex& index) } bool editingAllowed = (m_currentTabTableModel == m_browseTableModel) && - (db.getObjectByName(ui->comboBrowseTable->currentText()).gettype() == "table"); + (db.getObjectByName(ui->comboBrowseTable->currentText())->type() == sqlb::Object::ObjectTypes::Table); // Don't allow editing of other objects than tables editDock->setReadOnly(!editingAllowed); @@ -2195,16 +2195,16 @@ void MainWindow::copyCurrentCreateStatement() void MainWindow::jumpToRow(const QString& table, QString column, const QByteArray& value) { // First check if table exists - DBBrowserObject obj = db.getObjectByName(table); - if(obj.getname().size() == 0) + sqlb::TablePtr obj = db.getObjectByName(table).dynamicCast(); + if(!obj) return; // If no column name is set, assume the primary key is meant if(!column.size()) - column = obj.table.fields().at(obj.table.findPk())->name(); + column = obj->fields().at(obj->findPk())->name(); // If column doesn't exist don't do anything - int column_index = obj.table.findField(column); + int column_index = obj->findField(column); if(column_index == -1) return; @@ -2232,7 +2232,7 @@ void MainWindow::showDataColumnPopupMenu(const QPoint& pos) void MainWindow::showRecordPopupMenu(const QPoint& pos) { const QString sCurrentTable = ui->comboBrowseTable->currentText(); - if (!(db.getObjectByName(sCurrentTable).gettype() == "table" && !db.readOnly())) + if(!(db.getObjectByName(sCurrentTable)->type() == sqlb::Object::ObjectTypes::Table && !db.readOnly())) return; int row = ui->dataTable->verticalHeader()->logicalIndexAt(pos); @@ -2257,7 +2257,7 @@ void MainWindow::editDataColumnDisplayFormat() // column is always the rowid column. Ultimately, get the column name from the column object QString current_table = ui->comboBrowseTable->currentText(); int field_number = sender()->property("clicked_column").toInt(); - QString field_name = db.getObjectByName(current_table).table.fields().at(field_number-1)->name(); + QString field_name = db.getObjectByName(current_table).dynamicCast()->fields().at(field_number-1)->name(); // Get the current display format of the field QString current_displayformat = browseTableSettings[current_table].displayFormats[field_number]; diff --git a/src/SqlExecutionArea.cpp b/src/SqlExecutionArea.cpp index bacb8413..e9df7140 100644 --- a/src/SqlExecutionArea.cpp +++ b/src/SqlExecutionArea.cpp @@ -96,14 +96,12 @@ void SqlExecutionArea::saveAsView() { name = QInputDialog::getText(this, qApp->applicationName(), tr("Please specify the view name")).trimmed(); if(name.isEmpty()) - break; - if(!db.getObjectByName(name).getname().isEmpty()) + return; + if(db.getObjectByName(name) != nullptr) QMessageBox::warning(this, qApp->applicationName(), tr("There is already an object with that name. Please choose a different name.")); else break; } - if(name.isEmpty()) - return; // Create the view if(db.executeSQL(QString("CREATE VIEW %1 AS %2;").arg(sqlb::escapeIdentifier(name)).arg(model->query()))) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 0aee15b2..39aa9deb 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -503,7 +503,7 @@ bool DBBrowserDB::dump(const QString& filename, continue; // get columns - QStringList cols(it->table.fieldNames()); + QStringList cols(it->object.dynamicCast()->fieldNames()); QString sQuery = QString("SELECT * FROM %1;").arg(sqlb::escapeIdentifier(it->getTableName())); QByteArray utf8Query = sQuery.toUtf8(); @@ -522,7 +522,7 @@ bool DBBrowserDB::dump(const QString& filename, if (!insertNewSyntx || !counter) { - stream << "INSERT INTO " << sqlb::escapeIdentifier(it->getTableName()); + stream << "INSERT INTO " << sqlb::escapeIdentifier(it->getname()); if (insertColNames) stream << " (" << cols.join(",") << ")"; stream << " VALUES ("; @@ -594,7 +594,7 @@ bool DBBrowserDB::dump(const QString& filename, for(auto it=objMap.constBegin();it!=objMap.constEnd();++it) { // Make sure it's not a table again - if(it.value().gettype() == "table") + if(it.value().gettype() == sqlb::Object::ObjectTypes::Table) continue; // Write the SQL string used to create this object to the output file @@ -732,7 +732,7 @@ bool DBBrowserDB::getRow(const QString& sTableName, const QString& rowid, QList< { QString sQuery = QString("SELECT * FROM %1 WHERE %2='%3';") .arg(sqlb::escapeIdentifier(sTableName)) - .arg(sqlb::escapeIdentifier(getObjectByName(sTableName).table.rowidColumn())) + .arg(sqlb::escapeIdentifier(getObjectByName(sTableName).dynamicCast()->rowidColumn())) .arg(rowid); QByteArray utf8Query = sQuery.toUtf8(); @@ -853,18 +853,18 @@ QString DBBrowserDB::addRecord(const QString& sTableName) { if (!isOpen()) return QString(); - sqlb::Table table = getObjectByName(sTableName).table; + sqlb::TablePtr table = getObjectByName(sTableName).dynamicCast(); // 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; QString pk_value; - if(table.isWithoutRowidTable()) + if(table->isWithoutRowidTable()) { - pk_value = QString::number(max(table, table.fields().at(table.findField(table.rowidColumn()))).toLongLong() + 1); - sInsertstmt = emptyInsertStmt(table, pk_value); + pk_value = QString::number(max(*table, table->fields().at(table->findField(table->rowidColumn()))).toLongLong() + 1); + sInsertstmt = emptyInsertStmt(*table, pk_value); } else { - sInsertstmt = emptyInsertStmt(table); + sInsertstmt = emptyInsertStmt(*table); } if(!executeSQL(sInsertstmt)) @@ -872,7 +872,7 @@ QString DBBrowserDB::addRecord(const QString& sTableName) qWarning() << "addRecord: " << lastErrorMessage; return QString(); } else { - if(table.isWithoutRowidTable()) + if(table->isWithoutRowidTable()) return pk_value; else return QString::number(sqlite3_last_insert_rowid(_db)); @@ -891,7 +891,7 @@ bool DBBrowserDB::deleteRecords(const QString& table, const QStringList& rowids) } QString statement = QString("DELETE FROM %1 WHERE %2 IN (%3);") .arg(sqlb::escapeIdentifier(table)) - .arg(sqlb::escapeIdentifier(getObjectByName(table).table.rowidColumn())) + .arg(sqlb::escapeIdentifier(getObjectByName(table).dynamicCast()->rowidColumn())) .arg(quoted_rowids.join(", ")); if(executeSQL(statement)) ok = true; @@ -908,7 +908,7 @@ bool DBBrowserDB::updateRecord(const QString& table, const QString& column, cons QString sql = QString("UPDATE %1 SET %2=? WHERE %3='%4';") .arg(sqlb::escapeIdentifier(table)) .arg(sqlb::escapeIdentifier(column)) - .arg(sqlb::escapeIdentifier(getObjectByName(table).table.rowidColumn())) + .arg(sqlb::escapeIdentifier(getObjectByName(table).dynamicCast()->rowidColumn())) .arg(rowid); logSQL(sql, kLogMsg_App); @@ -994,19 +994,11 @@ bool DBBrowserDB::renameColumn(const QString& tablename, const sqlb::Table& tabl // 2) Include the addColumn() use case in here, so the calling side doesn't need to know anything about how this class handles table modifications. // 3) Maybe rename this function to alterTable() or something - // Collect information on the current DB layout - QString tableSql = getObjectByName(tablename).getsql(); - if(tableSql.isEmpty()) - { - lastErrorMessage = tr("renameColumn: cannot find table %1.").arg(tablename); - return false; - } - // Create table schema - sqlb::Table oldSchema = sqlb::Table::parseSQL(tableSql).first; + const sqlb::TablePtr oldSchema = getObjectByName(tablename).dynamicCast(); // Check if field actually exists - if(!name.isNull() && oldSchema.findField(name) == -1) + if(!name.isNull() && oldSchema->findField(name) == -1) { lastErrorMessage = tr("renameColumn: cannot find column %1.").arg(name); return false; @@ -1022,7 +1014,7 @@ bool DBBrowserDB::renameColumn(const QString& tablename, const sqlb::Table& tabl // Create a new table with a name that hopefully doesn't exist yet. // Its layout is exactly the same as the one of the table to change - except for the column to change // of course, and the table constraints which are copied from the table parameter. - sqlb::Table newSchema = oldSchema; + sqlb::Table newSchema = *oldSchema; newSchema.setName("sqlitebrowser_rename_column_new_table"); newSchema.setConstraints(table.allConstraints()); newSchema.setRowidColumn(table.rowidColumn()); @@ -1084,19 +1076,19 @@ bool DBBrowserDB::renameColumn(const QString& tablename, const sqlb::Table& tabl for(auto it=objMap.constBegin();it!=objMap.constEnd();++it) { // If this object references the table and it's not the table itself save it's SQL string - if((*it).getTableName() == tablename && (*it).gettype() != "table") + if((*it).getTableName() == tablename && (*it).gettype() != sqlb::Object::ObjectTypes::Table) { // If this is an index, update the fields first. This highly increases the chance that the SQL statement won't throw an // error later on when we try to recreate it. - if((*it).gettype() == "index") + if((*it).gettype() == sqlb::Object::ObjectTypes::Index) { - sqlb::Index idx = (*it).index; - for(int i=0;i(); + for(int i=0;icolumns().size();i++) { - if(idx.column(i)->name() == name) - idx.column(i)->setName(to->name()); + if(idx->column(i)->name() == name) + idx->column(i)->setName(to->name()); } - otherObjectsSql << idx.sql(); + otherObjectsSql << idx->sql(); } else { // If it's a view or a trigger we don't have any chance to corrections yet. Just store the statement as is and // hope for the best. @@ -1187,14 +1179,14 @@ objectMap DBBrowserDB::getBrowsableObjects() const return res; } -DBBrowserObject DBBrowserDB::getObjectByName(const QString& name) const +sqlb::ObjectPtr DBBrowserDB::getObjectByName(const QString& name) const { for(auto it=objMap.constBegin();it!=objMap.constEnd();++it) { if((*it).getname() == name) - return *it; + return it->object; } - return DBBrowserObject(); + return sqlb::ObjectPtr(nullptr); } void DBBrowserDB::logSQL(QString statement, int msgtype) @@ -1237,6 +1229,7 @@ void DBBrowserDB::updateSchema( ) QByteArray utf8Statement = statement.toUtf8(); err=sqlite3_prepare_v2(_db, utf8Statement, utf8Statement.length(), &vm, &tail); + QVector temporary_objects; if (err == SQLITE_OK){ logSQL(statement, kLogMsg_App); while ( sqlite3_step(vm) == SQLITE_ROW ){ @@ -1247,32 +1240,45 @@ void DBBrowserDB::updateSchema( ) QString val_temp = QString::fromUtf8((const char*)sqlite3_column_text(vm, 4)); val_sql.replace("\r", ""); - if(val_type == "table" || val_type == "index" || val_type == "view" || val_type == "trigger") - objMap.insert(val_type, DBBrowserObject(val_name, val_sql, val_type, val_tblname, (val_temp == "1"))); + sqlb::Object::ObjectTypes type; + if(val_type == "table") + type = sqlb::Object::ObjectTypes::Table; + else if(val_type == "index") + type = sqlb::Object::ObjectTypes::Index; + else if(val_type == "trigger") + type = sqlb::Object::ObjectTypes::Trigger; + else if(val_type == "view") + type = sqlb::Object::ObjectTypes::View; else - qWarning() << tr("unknown object type %1").arg(val_type); + continue; + + DBBrowserObject obj(val_name, val_sql, type, val_tblname); + if((type == sqlb::Object::ObjectTypes::Table || type == sqlb::Object::ObjectTypes::Index) && !val_sql.isEmpty()) + { + obj.object = sqlb::Object::parseSQL(type, val_sql).first; + if(val_temp == "1") + obj.object->setTemporary(true); + } + objMap.insert(val_type, obj); } sqlite3_finalize(vm); }else{ qWarning() << tr("could not get list of db objects: %1, %2").arg(err).arg(sqlite3_errmsg(_db)); } - //now get the field list for each table + //now get the field list for views objectMap::Iterator it; for ( it = objMap.begin(); it != objMap.end(); ++it ) { // Use our SQL parser to generate the field list for tables. For views we currently have to fall back to the // pragma SQLite offers. - if((*it).gettype() == "table") + if((*it).gettype() == sqlb::Object::ObjectTypes::View) { - (*it).table = sqlb::Table::parseSQL((*it).getsql()).first; - } else if((*it).gettype() == "index" && !(*it).getsql().isEmpty()) { - (*it).index = sqlb::Index::parseSQL((*it).getsql()).first; - } else if((*it).gettype() == "view") { statement = QString("PRAGMA TABLE_INFO(%1);").arg(sqlb::escapeIdentifier((*it).getname())); logSQL(statement, kLogMsg_App); err=sqlite3_prepare_v2(_db,statement.toUtf8(),statement.length(), &vm, &tail); + sqlb::Table* view_dummy = new sqlb::Table(""); if (err == SQLITE_OK){ while ( sqlite3_step(vm) == SQLITE_ROW ){ if (sqlite3_column_count(vm)==6) @@ -1281,13 +1287,14 @@ void DBBrowserDB::updateSchema( ) QString val_type = QString::fromUtf8((const char *)sqlite3_column_text(vm, 2)); sqlb::FieldPtr f(new sqlb::Field(val_name, val_type)); - (*it).table.addField(f); + view_dummy->addField(f); } } sqlite3_finalize(vm); } else{ lastErrorMessage = tr("could not get types"); } + (*it).object = sqlb::TablePtr(view_dummy); } } diff --git a/src/sqlitedb.h b/src/sqlitedb.h index c4024bb6..5071a57e 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -21,25 +21,22 @@ typedef QMultiMap objectMap; class DBBrowserObject { public: - DBBrowserObject() : table(""), index(""), name( "" ) { } - DBBrowserObject(const QString& wname, const QString& wsql, const QString& wtype, const QString& tbl_name, bool temp) - : table(wname), index(wname), name( wname), sql( wsql ), type(wtype), table_name(tbl_name), temporary(temp) + DBBrowserObject() : name( "" ) { } + DBBrowserObject(const QString& wname, const QString& wsql, sqlb::Object::ObjectTypes wtype, const QString& tbl_name) + : name( wname), sql( wsql ), type(wtype), table_name(tbl_name) { } QString getname() const { return name; } QString getsql() const { return sql; } - QString gettype() const { return type; } + sqlb::Object::ObjectTypes gettype() const { return type; } QString getTableName() const { return table_name; } - bool isTemporary() const { return temporary; } - sqlb::Table table; - sqlb::Index index; + sqlb::ObjectPtr object; private: QString name; QString sql; - QString type; + sqlb::Object::ObjectTypes type; QString table_name; // The name of the table this object references, interesting for views, triggers and indices - bool temporary; }; class DBBrowserDB : public QObject @@ -109,7 +106,7 @@ public: bool renameColumn(const QString& tablename, const sqlb::Table& table, const QString& name, sqlb::FieldPtr to, int move = 0); objectMap getBrowsableObjects() const; - DBBrowserObject getObjectByName(const QString& name) const; + sqlb::ObjectPtr getObjectByName(const QString& name) const; bool isOpen() const; bool encrypted() const { return isEncrypted; } bool readOnly() const { return isReadOnly; } diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 1fd6e59e..c18065e3 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -47,13 +47,13 @@ void SqliteTableModel::setTable(const QString& table, const QVector& di m_vDataTypes.push_back(SQLITE_INTEGER); bool allOk = false; - if(m_db.getObjectByName(table).gettype() == "table") + if(m_db.getObjectByName(table)->type() == sqlb::Object::ObjectTypes::Table) { - sqlb::Table t = sqlb::Table::parseSQL(m_db.getObjectByName(table).getsql()).first; - if(t.fields().size()) // parsing was OK + sqlb::TablePtr t = sqlb::Table::parseSQL(m_db.getObjectByName(table)->originalSql()).first.dynamicCast(); + if(t->fields().size()) // parsing was OK { - m_headers.push_back(t.rowidColumn()); - m_headers.append(t.fieldNames()); + m_headers.push_back(t->rowidColumn()); + m_headers.append(t->fieldNames()); // parse columns types static QStringList dataTypes = QStringList() @@ -61,7 +61,7 @@ void SqliteTableModel::setTable(const QString& table, const QVector& di << "REAL" << "TEXT" << "BLOB"; - foreach(const sqlb::FieldPtr fld, t.fields()) + foreach(const sqlb::FieldPtr fld, t->fields()) { QString name(fld->type().toUpper()); int colType = dataTypes.indexOf(name); @@ -267,13 +267,13 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const sqlb::ForeignKeyClause SqliteTableModel::getForeignKeyClause(int column) const { - DBBrowserObject obj = m_db.getObjectByName(m_sTable); - if(obj.getname().size() && (column >= 0 && column < obj.table.fields().count())) + sqlb::TablePtr obj = m_db.getObjectByName(m_sTable).dynamicCast(); + if(obj->name().size() && (column >= 0 && column < obj->fields().count())) { // Note that the rowid column has number -1 here, it can safely be excluded since there will never be a // foreign key on that column. - sqlb::ConstraintPtr ptr = obj.table.constraint({obj.table.fields().at(column)}, sqlb::Constraint::ForeignKeyConstraintType); + sqlb::ConstraintPtr ptr = obj->constraint({obj->fields().at(column)}, sqlb::Constraint::ForeignKeyConstraintType); if(ptr) return *(ptr.dynamicCast()); } @@ -432,11 +432,11 @@ QModelIndex SqliteTableModel::dittoRecord(int old_row) int firstEditedColumn = 0; int new_row = rowCount() - 1; - sqlb::Table t = sqlb::Table::parseSQL(m_db.getObjectByName(m_sTable).getsql()).first; + sqlb::TablePtr t = sqlb::Table::parseSQL(m_db.getObjectByName(m_sTable)->originalSql()).first.dynamicCast(); - sqlb::FieldVector pk = t.primaryKey(); - for (int col = 0; col < t.fields().size(); ++col) { - if(!pk.contains(t.fields().at(col))) { + sqlb::FieldVector pk = t->primaryKey(); + for (int col = 0; col < t->fields().size(); ++col) { + if(!pk.contains(t->fields().at(col))) { if (!firstEditedColumn) firstEditedColumn = col + 1; diff --git a/src/sqlitetypes.cpp b/src/sqlitetypes.cpp index b86d7052..3d202403 100644 --- a/src/sqlitetypes.cpp +++ b/src/sqlitetypes.cpp @@ -22,6 +22,27 @@ QStringList fieldVectorToFieldNames(const FieldVector& vector) return result; } +QPair Object::parseSQL(Object::ObjectTypes type, const QString& sSQL) +{ + // Parse SQL statement according to type + QPair result; + switch(type) + { + case Object::ObjectTypes::Table: + result = Table::parseSQL(sSQL); + break; + case Object::ObjectTypes::Index: + result = Index::parseSQL(sSQL); + break; + default: + return QPair(ObjectPtr(nullptr), false); + } + + // Strore the original SQL statement and return the result + result.first->setOriginalSql(sSQL); + return result; +} + bool ForeignKeyClause::isSet() const { return m_override.size() || m_table.size(); @@ -256,7 +277,7 @@ bool Table::hasAutoIncrement() const return false; } -QPair Table::parseSQL(const QString &sSQL) +QPair Table::parseSQL(const QString &sSQL) { std::stringstream s; s << sSQL.toStdString(); @@ -276,7 +297,7 @@ QPair Table::parseSQL(const QString &sSQL) // Note: this needs to be done in two separate lines because otherwise the optimiser might decide to // fetch the value for the second part of the pair (the modify supported flag) first. If it does so it will // always be set to true because the table() method hasn't run yet and it's only set to false in there. - sqlb::Table tab = ctw.table(); + sqlb::TablePtr tab = ctw.table(); return qMakePair(tab, ctw.modifysupported()); } catch(antlr::ANTLRException& ex) @@ -288,7 +309,7 @@ QPair Table::parseSQL(const QString &sSQL) qCritical() << "Sqlite parse error: " << sSQL; //TODO } - return qMakePair(Table(""), false); + return qMakePair(TablePtr(new Table("")), false); } QString Table::sql() const @@ -463,9 +484,9 @@ QString columnname(const antlr::RefAST& n) } } -Table CreateTableWalker::table() +TablePtr CreateTableWalker::table() { - Table tab(""); + Table* tab = new Table(""); if( m_root ) //CREATE TABLE { @@ -491,7 +512,7 @@ Table CreateTableWalker::table() } // Extract and set table name - tab.setName(tablename(s)); + tab->setName(tablename(s)); // Special handling for virtual tables. If this is a virtual table, extract the USING part and skip all the // rest of this function because virtual tables don't have column definitons @@ -499,12 +520,12 @@ Table CreateTableWalker::table() { s = s->getNextSibling(); // USING s = s->getNextSibling(); // module name - tab.setVirtualUsing(concatTextAST(s, true)); + tab->setVirtualUsing(concatTextAST(s, true)); m_bModifySupported = false; // TODO Maybe get the column list using the 'pragma table_info()' approach we're using for views - return tab; + return TablePtr(tab); } // This is a normal table, not a virtual one @@ -555,7 +576,7 @@ Table CreateTableWalker::table() do { QString col = columnname(tc); - FieldPtr field = tab.field(tab.findField(col)); + FieldPtr field = tab->field(tab->findField(col)); fields.push_back(field); tc = tc->getNextSibling(); @@ -579,7 +600,7 @@ Table CreateTableWalker::table() } } while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN); - tab.addConstraint(fields, ConstraintPtr(pk)); + tab->addConstraint(fields, ConstraintPtr(pk)); } break; case sqlite3TokenTypes::UNIQUE: @@ -593,7 +614,7 @@ Table CreateTableWalker::table() do { QString col = columnname(tc); - FieldPtr field = tab.field(tab.findField(col)); + FieldPtr field = tab->field(tab->findField(col)); fields.push_back(field); tc = tc->getNextSibling(); @@ -617,7 +638,7 @@ Table CreateTableWalker::table() fields[0]->setUnique(true); delete unique; } else { - tab.addConstraint(fields, ConstraintPtr(unique)); + tab->addConstraint(fields, ConstraintPtr(unique)); } } break; @@ -634,7 +655,7 @@ Table CreateTableWalker::table() do { QString col = columnname(tc); - FieldPtr field = tab.field(tab.findField(col)); + FieldPtr field = tab->field(tab->findField(col)); fields.push_back(field); tc = tc->getNextSibling(); @@ -666,7 +687,7 @@ Table CreateTableWalker::table() } fk->setConstraint(concatTextAST(tc, true)); - tab.addConstraint(fields, ConstraintPtr(fk)); + tab->addConstraint(fields, ConstraintPtr(fk)); } break; default: @@ -685,15 +706,15 @@ Table CreateTableWalker::table() s = s->getNextSibling(); // WITHOUT s = s->getNextSibling(); // ROWID - tab.setRowidColumn(tab.fields().at(tab.findPk())->name()); + tab->setRowidColumn(tab->fields().at(tab->findPk())->name()); } } } - return tab; + return TablePtr(tab); } -void CreateTableWalker::parsecolumn(Table& table, antlr::RefAST c) +void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c) { QString colname; QString type = "TEXT"; @@ -832,17 +853,17 @@ void CreateTableWalker::parsecolumn(Table& table, antlr::RefAST c) FieldPtr f = FieldPtr(new Field(colname, type, notnull, defaultvalue, check, unique)); f->setAutoIncrement(autoincrement); - table.addField(f); + table->addField(f); if(foreignKey) - table.addConstraint({f}, ConstraintPtr(foreignKey)); + table->addConstraint({f}, ConstraintPtr(foreignKey)); if(primaryKey) { FieldVector v; - if(table.constraint(v, Constraint::PrimaryKeyConstraintType)) - table.primaryKeyRef().push_back(f); + if(table->constraint(v, Constraint::PrimaryKeyConstraintType)) + table->primaryKeyRef().push_back(f); else - table.addConstraint({f}, ConstraintPtr(primaryKey)); + table->addConstraint({f}, ConstraintPtr(primaryKey)); } } @@ -923,7 +944,7 @@ QString Index::sql() const return sql + ";"; } -QPair Index::parseSQL(const QString& sSQL) +QPair Index::parseSQL(const QString& sSQL) { std::stringstream s; s << sSQL.toStdString(); @@ -943,7 +964,7 @@ QPair Index::parseSQL(const QString& sSQL) // Note: this needs to be done in two separate lines because otherwise the optimiser might decide to // fetch the value for the second part of the pair (the modify supported flag) first. If it does so it will // always be set to true because the table() method hasn't run yet and it's only set to false in there. - sqlb::Index index = ctw.index(); + sqlb::IndexPtr index = ctw.index(); return qMakePair(index, ctw.modifysupported()); } catch(antlr::ANTLRException& ex) @@ -955,12 +976,12 @@ QPair Index::parseSQL(const QString& sSQL) qCritical() << "Sqlite parse error: " << sSQL; //TODO } - return qMakePair(Index(""), false); + return qMakePair(IndexPtr(new Index("")), false); } -Index CreateIndexWalker::index() +IndexPtr CreateIndexWalker::index() { - Index index(""); + Index* index = new Index(""); if(m_root) // CREATE INDEX { @@ -975,18 +996,18 @@ Index CreateIndexWalker::index() { // Is this a unique index? if(s->getType() == Sqlite3Lexer::UNIQUE) - index.setUnique(true); + index->setUnique(true); s = s->getNextSibling(); } // Extract and set index name - index.setName(tablename(s)); + index->setName(tablename(s)); // Get table name s = s->getNextSibling(); // ON s = s->getNextSibling(); // table name - index.setTable(tablename(s)); + index->setTable(tablename(s)); s = s->getNextSibling(); // LPAREN s = s->getNextSibling(); // first column name @@ -1012,15 +1033,15 @@ Index CreateIndexWalker::index() m_bModifySupported = false; } else { s = s->getNextSibling(); // expr - index.setWhereExpr(concatTextAST(s, true)); + index->setWhereExpr(concatTextAST(s, true)); } } } - return index; + return IndexPtr(index); } -void CreateIndexWalker::parsecolumn(Index& index, antlr::RefAST c) +void CreateIndexWalker::parsecolumn(Index* index, antlr::RefAST c) { QString name; bool isExpression; @@ -1074,7 +1095,7 @@ void CreateIndexWalker::parsecolumn(Index& index, antlr::RefAST c) c = c->getNextSibling(); } - index.addColumn(IndexedColumnPtr(new IndexedColumn(name, isExpression, order))); + index->addColumn(IndexedColumnPtr(new IndexedColumn(name, isExpression, order))); } } //namespace sqlb diff --git a/src/sqlitetypes.h b/src/sqlitetypes.h index d41e898d..bbbd76de 100644 --- a/src/sqlitetypes.h +++ b/src/sqlitetypes.h @@ -15,15 +15,71 @@ namespace sqlb { QString escapeIdentifier(QString id); +class Object; +class Table; +class Index; class Field; class Constraint; class IndexedColumn; +typedef QSharedPointer ObjectPtr; +typedef QSharedPointer TablePtr; +typedef QSharedPointer IndexPtr; typedef QSharedPointer FieldPtr; typedef QSharedPointer ConstraintPtr; typedef QVector FieldVector; typedef QSharedPointer IndexedColumnPtr; typedef QVector IndexedColumnVector; +class Object +{ +public: + enum ObjectTypes + { + Table, + Index, + View, + Trigger + }; + + explicit Object(const QString& name): m_name(name), m_temporary(false) {} + virtual ~Object() {} + + virtual ObjectTypes type() const = 0; + + void setName(const QString& name) { m_name = name; } + const QString& name() const { return m_name; } + + void setOriginalSql(const QString& original_sql) { m_originalSql = original_sql; } + QString originalSql() const { return m_originalSql; } + + void setTemporary(bool temp) { m_temporary = temp; } + bool isTemporary() const { return m_temporary; } + + virtual QString baseTable() const { return QString(); } + + virtual void clear() {} + + /** + * @brief Returns the CREATE statement for this object + * @return A QString with the CREATE statement. + */ + virtual QString sql() const = 0; + + /** + * @brief parseSQL Parses the CREATE statement in sSQL. + * @param sSQL The type of the object. + * @param sSQL The create statement. + * @return A pair. The first part is the parsed database object, the second a bool indicating if our + * parser fully understood the statement. The object may be empty if parsing failed. + */ + static QPair parseSQL(Object::ObjectTypes type, const QString& sSQL); + +protected: + QString m_name; + QString m_originalSql; + bool m_temporary; +}; + class Constraint { public: @@ -160,15 +216,14 @@ private: typedef QMultiHash ConstraintMap; -class Table +class Table : public Object { public: - explicit Table(const QString& name): m_name(name), m_rowidColumn("_rowid_"), m_temporary(false) {} + explicit Table(const QString& name): Object(name), m_rowidColumn("_rowid_") {} virtual ~Table(); - void setName(const QString& name) { m_name = name; } + virtual ObjectTypes type() const { return Object::Table; } - const QString& name() const { return m_name; } const FieldVector& fields() const { return m_fields; } /** @@ -192,9 +247,6 @@ public: QString virtualUsing() const { return m_virtual; } bool isVirtual() const { return !m_virtual.isEmpty(); } - void setTemporary(bool temp) { m_temporary = temp; } - bool isTemporary() const { return m_temporary; } - void clear(); void addConstraint(FieldVector fields, ConstraintPtr constraint); @@ -224,18 +276,16 @@ public: * if our modify dialog supports the table. * The table object may be a empty table if parsing failed. */ - static QPair parseSQL(const QString& sSQL); + static QPair parseSQL(const QString& sSQL); private: QStringList fieldList() const; bool hasAutoIncrement() const; private: - QString m_name; FieldVector m_fields; QString m_rowidColumn; ConstraintMap m_constraints; QString m_virtual; - bool m_temporary; }; /** @@ -251,11 +301,11 @@ public: , m_bModifySupported(true) {} - Table table(); + TablePtr table(); bool modifysupported() const { return m_bModifySupported; } private: - void parsecolumn(Table& table, antlr::RefAST c); + void parsecolumn(Table* table, antlr::RefAST c); private: antlr::RefAST m_root; @@ -291,14 +341,15 @@ private: QString m_order; }; -class Index +class Index : public Object { public: - explicit Index(const QString& name): m_name(name), m_unique(false) {} + explicit Index(const QString& name): Object(name), m_unique(false) {} virtual ~Index(); - void setName(const QString& name) { m_name = name; } - const QString& name() const { return m_name; } + virtual ObjectTypes type() const { return Object::Index; } + + virtual QString baseTable() const { return m_table; } void setUnique(bool unique) { m_unique = unique; } bool unique() const { return m_unique; } @@ -334,10 +385,9 @@ public: * if our parser fully understood the statement. * The index object may be if parsing failed. */ - static QPair parseSQL(const QString& sSQL); + static QPair parseSQL(const QString& sSQL); private: - QString m_name; bool m_unique; QString m_table; QString m_whereExpr; @@ -357,11 +407,11 @@ public: , m_bModifySupported(true) {} - Index index(); + IndexPtr index(); bool modifysupported() const { return m_bModifySupported; } private: - void parsecolumn(Index& index, antlr::RefAST c); + void parsecolumn(Index* index, antlr::RefAST c); private: antlr::RefAST m_root;