File indexing completed on 2024-04-28 05:48:41
0001 /* 0002 SPDX-FileCopyrightText: 2010 Marco Mentasti <marcomentasti@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "schemawidget.h" 0008 #include "sqlmanager.h" 0009 0010 #include <KLocalizedString> 0011 #include <ktexteditor/application.h> 0012 #include <ktexteditor/editor.h> 0013 #include <ktexteditor/mainwindow.h> 0014 #include <ktexteditor/view.h> 0015 0016 #include <QApplication> 0017 #include <QDrag> 0018 #include <QMenu> 0019 #include <QMimeData> 0020 #include <QMouseEvent> 0021 #include <QSqlDatabase> 0022 #include <QSqlField> 0023 #include <QSqlIndex> 0024 #include <QSqlRecord> 0025 #include <QStringList> 0026 0027 SchemaWidget::SchemaWidget(QWidget *parent, SQLManager *manager) 0028 : QTreeWidget(parent) 0029 , m_manager(manager) 0030 { 0031 m_tablesLoaded = false; 0032 m_viewsLoaded = false; 0033 0034 setHeaderLabels(QStringList() << i18nc("@title:column", "Database schema")); 0035 0036 setContextMenuPolicy(Qt::CustomContextMenu); 0037 setDragDropMode(QAbstractItemView::DragOnly); 0038 setDragEnabled(true); 0039 setAcceptDrops(false); 0040 0041 connect(this, &SchemaWidget::customContextMenuRequested, this, &SchemaWidget::slotCustomContextMenuRequested); 0042 connect(this, &SchemaWidget::itemExpanded, this, &SchemaWidget::slotItemExpanded); 0043 } 0044 0045 SchemaWidget::~SchemaWidget() 0046 { 0047 } 0048 0049 bool SchemaWidget::isConnectionValidAndOpen() 0050 { 0051 return m_manager->isValidAndOpen(m_connectionName); 0052 } 0053 0054 void SchemaWidget::deleteChildren(QTreeWidgetItem *item) 0055 { 0056 const QList<QTreeWidgetItem *> items = item->takeChildren(); 0057 0058 for (QTreeWidgetItem *i : items) { 0059 delete i; 0060 } 0061 } 0062 0063 void SchemaWidget::buildTree(const QString &connection) 0064 { 0065 m_connectionName = connection; 0066 0067 clear(); 0068 0069 m_tablesLoaded = false; 0070 m_viewsLoaded = false; 0071 0072 if (!m_connectionName.isEmpty()) { 0073 buildDatabase(new QTreeWidgetItem(this)); 0074 } 0075 } 0076 0077 void SchemaWidget::refresh() 0078 { 0079 buildTree(m_connectionName); 0080 } 0081 0082 void SchemaWidget::buildDatabase(QTreeWidgetItem *databaseItem) 0083 { 0084 QSqlDatabase db = QSqlDatabase::database(m_connectionName); 0085 QString dbname = (db.isValid() ? db.databaseName() : m_connectionName); 0086 0087 databaseItem->setText(0, dbname); 0088 databaseItem->setIcon(0, QIcon::fromTheme(QStringLiteral("server-database"))); 0089 0090 QTreeWidgetItem *tablesItem = new QTreeWidgetItem(databaseItem, TablesFolderType); 0091 tablesItem->setText(0, i18nc("@title Folder name", "Tables")); 0092 tablesItem->setIcon(0, QIcon::fromTheme(QStringLiteral("folder"))); 0093 tablesItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 0094 0095 QTreeWidgetItem *viewsItem = new QTreeWidgetItem(databaseItem, ViewsFolderType); 0096 viewsItem->setText(0, i18nc("@title Folder name", "Views")); 0097 viewsItem->setIcon(0, QIcon::fromTheme(QStringLiteral("folder"))); 0098 viewsItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 0099 0100 databaseItem->setExpanded(true); 0101 } 0102 0103 void SchemaWidget::buildTables(QTreeWidgetItem *tablesItem) 0104 { 0105 if (!isConnectionValidAndOpen()) { 0106 return; 0107 } 0108 0109 QTreeWidgetItem *systemTablesItem = new QTreeWidgetItem(tablesItem, SystemTablesFolderType); 0110 systemTablesItem->setText(0, i18nc("@title Folder name", "System Tables")); 0111 systemTablesItem->setIcon(0, QIcon::fromTheme(QStringLiteral("folder"))); 0112 systemTablesItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 0113 0114 QSqlDatabase db = QSqlDatabase::database(m_connectionName); 0115 QStringList tables = db.tables(QSql::SystemTables); 0116 0117 for (const QString &table : qAsConst(tables)) { 0118 QTreeWidgetItem *item = new QTreeWidgetItem(systemTablesItem, SystemTableType); 0119 item->setText(0, table); 0120 item->setIcon(0, QIcon(QLatin1String(":/katesql/pics/16-actions-sql-table.png"))); 0121 item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 0122 } 0123 0124 tables = db.tables(QSql::Tables); 0125 0126 for (const QString &table : qAsConst(tables)) { 0127 QTreeWidgetItem *item = new QTreeWidgetItem(tablesItem, TableType); 0128 item->setText(0, table); 0129 item->setIcon(0, QIcon(QLatin1String(":/katesql/pics/16-actions-sql-table.png"))); 0130 item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 0131 } 0132 0133 m_tablesLoaded = true; 0134 } 0135 0136 void SchemaWidget::buildViews(QTreeWidgetItem *viewsItem) 0137 { 0138 if (!isConnectionValidAndOpen()) { 0139 return; 0140 } 0141 0142 QSqlDatabase db = QSqlDatabase::database(m_connectionName); 0143 0144 const QStringList views = db.tables(QSql::Views); 0145 0146 for (const QString &view : views) { 0147 QTreeWidgetItem *item = new QTreeWidgetItem(viewsItem, ViewType); 0148 item->setText(0, view); 0149 item->setIcon(0, QIcon(QLatin1String(":/katesql/pics/16-actions-sql-view.png"))); 0150 item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 0151 } 0152 0153 m_viewsLoaded = true; 0154 } 0155 0156 void SchemaWidget::buildFields(QTreeWidgetItem *tableItem) 0157 { 0158 if (!isConnectionValidAndOpen()) { 0159 return; 0160 } 0161 0162 QSqlDatabase db = QSqlDatabase::database(m_connectionName); 0163 0164 QString tableName = tableItem->text(0); 0165 0166 QSqlIndex pk = db.primaryIndex(tableName); 0167 QSqlRecord rec = db.record(tableName); 0168 0169 for (int i = 0; i < rec.count(); ++i) { 0170 QSqlField f = rec.field(i); 0171 0172 QString fieldName = f.name(); 0173 0174 QTreeWidgetItem *item = new QTreeWidgetItem(tableItem, FieldType); 0175 item->setText(0, fieldName); 0176 0177 if (pk.contains(fieldName)) { 0178 item->setIcon(0, QIcon(QLatin1String(":/katesql/pics/16-actions-sql-field-pk.png"))); 0179 } else { 0180 item->setIcon(0, QIcon(QLatin1String(":/katesql/pics/16-actions-sql-field.png"))); 0181 } 0182 } 0183 } 0184 0185 void SchemaWidget::mousePressEvent(QMouseEvent *event) 0186 { 0187 if (event->button() == Qt::LeftButton) { 0188 m_dragStartPosition = event->pos(); 0189 } 0190 QTreeWidget::mousePressEvent(event); 0191 } 0192 0193 void SchemaWidget::mouseMoveEvent(QMouseEvent *event) 0194 { 0195 if (!(event->buttons() & Qt::LeftButton)) { 0196 return; 0197 } 0198 if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance()) { 0199 return; 0200 } 0201 0202 // QTreeWidgetItem *item = currentItem(); 0203 QTreeWidgetItem *item = itemAt(event->pos()); 0204 0205 if (!item) { 0206 return; 0207 } 0208 0209 if (item->type() != SchemaWidget::SystemTableType && item->type() != SchemaWidget::TableType && item->type() != SchemaWidget::ViewType 0210 && item->type() != SchemaWidget::FieldType) { 0211 return; 0212 } 0213 0214 QDrag *drag = new QDrag(this); 0215 QMimeData *mimeData = new QMimeData; 0216 0217 if (item->type() == SchemaWidget::FieldType) { 0218 mimeData->setText(QStringLiteral("%1.%2").arg(item->parent()->text(0), item->text(0))); 0219 } else { 0220 mimeData->setText(item->text(0)); 0221 } 0222 0223 drag->setMimeData(mimeData); 0224 drag->exec(Qt::CopyAction); 0225 0226 QTreeWidget::mouseMoveEvent(event); 0227 } 0228 0229 void SchemaWidget::slotItemExpanded(QTreeWidgetItem *item) 0230 { 0231 if (!item) { 0232 return; 0233 } 0234 0235 switch (item->type()) { 0236 case SchemaWidget::TablesFolderType: { 0237 if (!m_tablesLoaded) { 0238 buildTables(item); 0239 } 0240 } break; 0241 0242 case SchemaWidget::ViewsFolderType: { 0243 if (!m_viewsLoaded) { 0244 buildViews(item); 0245 } 0246 } break; 0247 0248 case SchemaWidget::TableType: 0249 case SchemaWidget::SystemTableType: 0250 case SchemaWidget::ViewType: { 0251 if (item->childCount() == 0) { 0252 buildFields(item); 0253 } 0254 } break; 0255 0256 default: 0257 break; 0258 } 0259 } 0260 0261 void SchemaWidget::slotCustomContextMenuRequested(const QPoint &pos) 0262 { 0263 QMenu menu(this); 0264 QTreeWidgetItem *item = itemAt(pos); 0265 0266 if (item) { 0267 if (item->type() == SchemaWidget::SystemTableType || item->type() == SchemaWidget::TableType || item->type() == SchemaWidget::ViewType 0268 || item->type() == SchemaWidget::FieldType) { 0269 menu.addAction(QIcon::fromTheme(QStringLiteral("view-sort-descending")), 0270 i18nc("@action:inmenu Context menu", "Select Data"), 0271 this, 0272 &SchemaWidget::executeSelect); 0273 QMenu *submenu = menu.addMenu(QIcon::fromTheme(QStringLiteral("tools-wizard")), i18nc("@action:inmenu Submenu title", "Generate")); 0274 0275 submenu->addAction(i18n("SELECT"), this, &SchemaWidget::generateSelectIntoView); 0276 submenu->addAction(i18n("UPDATE"), this, &SchemaWidget::generateUpdateIntoView); 0277 submenu->addAction(i18n("INSERT"), this, &SchemaWidget::generateInsertIntoView); 0278 submenu->addAction(i18n("DELETE"), this, &SchemaWidget::generateDeleteIntoView); 0279 menu.addSeparator(); 0280 } 0281 } 0282 menu.addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("@action:inmenu Context menu", "Refresh"), this, &SchemaWidget::refresh); 0283 0284 menu.exec(mapToGlobal(pos)); 0285 } 0286 0287 QString SchemaWidget::generateStatement(QSqlDriver::StatementType statementType) 0288 { 0289 if (!isConnectionValidAndOpen()) { 0290 return {}; 0291 } 0292 0293 QSqlDatabase db = QSqlDatabase::database(m_connectionName); 0294 0295 QSqlDriver *drv = db.driver(); 0296 0297 if (!drv) { 0298 return {}; 0299 } 0300 0301 QTreeWidgetItem *item = currentItem(); 0302 0303 if (!item) { 0304 return {}; 0305 } 0306 0307 QString statement; 0308 0309 switch (item->type()) { 0310 case TableType: 0311 case SystemTableType: 0312 case ViewType: { 0313 QString tableName = item->text(0); 0314 0315 QSqlRecord rec = db.record(tableName); 0316 0317 // set all fields to a value (NULL) 0318 // values are needed to generate update and insert statements 0319 if (statementType == QSqlDriver::UpdateStatement || statementType == QSqlDriver::InsertStatement) { 0320 for (int i = 0, n = rec.count(); i < n; ++i) { 0321 rec.setNull(i); 0322 } 0323 } 0324 0325 statement = drv->sqlStatement(statementType, tableName, rec, false); 0326 } break; 0327 0328 case FieldType: { 0329 QString tableName = item->parent()->text(0); 0330 QSqlRecord rec = db.record(tableName); 0331 0332 // get the selected column... 0333 QSqlField field = rec.field(item->text(0)); 0334 0335 // ...and set its value to NULL 0336 field.clear(); 0337 0338 // clear all columns and re-append the selected one 0339 rec.clear(); 0340 rec.append(field); 0341 0342 statement = drv->sqlStatement(statementType, tableName, rec, false); 0343 0344 if (statementType == QSqlDriver::DeleteStatement) { 0345 statement += 0346 QLatin1Char(' ') + drv->sqlStatement(QSqlDriver::WhereStatement, tableName, rec, false).replace(QLatin1String(" IS NULL"), QLatin1String("=?")); 0347 } 0348 } break; 0349 } 0350 0351 // replace NULL with a more generic '?' 0352 statement.replace(QLatin1String("NULL"), QLatin1String("?")); 0353 return statement; 0354 } 0355 0356 void SchemaWidget::pasteStatementIntoActiveView(const QString &statement) 0357 { 0358 KTextEditor::MainWindow *mw = KTextEditor::Editor::instance()->application()->activeMainWindow(); 0359 KTextEditor::View *kv = mw->activeView(); 0360 qDebug() << "Generated statement:" << statement; 0361 0362 if (!kv) { 0363 return; 0364 } 0365 // paste statement in the active view 0366 kv->insertText(statement); 0367 kv->setFocus(); 0368 } 0369 void SchemaWidget::executeStatement(QSqlDriver::StatementType statementType) 0370 { 0371 const QString statement = generateStatement(statementType); 0372 if (statement.length()) { 0373 m_manager->runQuery(statement, m_connectionName); 0374 } 0375 } 0376 void SchemaWidget::executeSelect() 0377 { 0378 executeStatement(QSqlDriver::SelectStatement); 0379 } 0380 0381 void SchemaWidget::generateAndPasteStatement(QSqlDriver::StatementType statementType) 0382 { 0383 QString statement = generateStatement(statementType); 0384 pasteStatementIntoActiveView(statement); 0385 } 0386 void SchemaWidget::generateSelectIntoView() 0387 { 0388 generateAndPasteStatement(QSqlDriver::SelectStatement); 0389 } 0390 0391 void SchemaWidget::generateUpdateIntoView() 0392 { 0393 generateAndPasteStatement(QSqlDriver::UpdateStatement); 0394 } 0395 0396 void SchemaWidget::generateInsertIntoView() 0397 { 0398 generateAndPasteStatement(QSqlDriver::InsertStatement); 0399 } 0400 0401 void SchemaWidget::generateDeleteIntoView() 0402 { 0403 generateAndPasteStatement(QSqlDriver::DeleteStatement); 0404 } 0405 0406 #include "moc_schemawidget.cpp"