File indexing completed on 2024-05-12 15:27:51

0001 /***************************************************************************
0002     File                 : ImportSQLDatabaseWidget.cpp
0003     Project              : LabPlot
0004     Description          : Datapicker
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2016 by Ankit Wagadre (wagadre.ankit@gmail.com)
0007     Copyright            : (C) 2016-2017 Alexander Semke (alexander.semke@web.de)
0008 
0009  ***************************************************************************/
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "ImportSQLDatabaseWidget.h"
0030 #include "DatabaseManagerDialog.h"
0031 #include "DatabaseManagerWidget.h"
0032 #include "backend/datasources/AbstractDataSource.h"
0033 #include "backend/datasources/filters/AbstractFileFilter.h"
0034 #include "backend/lib/macros.h"
0035 
0036 #include <QTimer>
0037 #include <QSqlError>
0038 #include <QSqlQuery>
0039 #include <QSqlRecord>
0040 #include <QStandardItem>
0041 
0042 #include <KConfig>
0043 #include <KConfigGroup>
0044 #include <KLocalizedString>
0045 #include <KMessageBox>
0046 #include <KSharedConfig>
0047 #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING
0048 #include <KF5/KSyntaxHighlighting/SyntaxHighlighter>
0049 #include <KF5/KSyntaxHighlighting/Definition>
0050 #include <KF5/KSyntaxHighlighting/Theme>
0051 #endif
0052 
0053 ImportSQLDatabaseWidget::ImportSQLDatabaseWidget(QWidget* parent) : QWidget(parent) {
0054     ui.setupUi(this);
0055 
0056     ui.cbImportFrom->addItem(i18n("Table"));
0057     ui.cbImportFrom->addItem(i18n("Custom query"));
0058 
0059     ui.bDatabaseManager->setIcon(QIcon::fromTheme("network-server-database"));
0060     ui.bDatabaseManager->setToolTip(i18n("Manage connections"));
0061     ui.twPreview->setEditTriggers(QAbstractItemView::NoEditTriggers);
0062 
0063     ui.cbDecimalSeparator->addItem(i18n("Point '.'"));
0064     ui.cbDecimalSeparator->addItem(i18n("Comma ','"));
0065     ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats());
0066 
0067     const QString textNumberFormatShort = i18n("This option determines how the imported strings have to be converted to numbers.");
0068     const QString textNumberFormat = textNumberFormatShort + "<br><br>" + i18n(
0069                                          "When point character is used for the decimal separator, the valid number representations are:"
0070                                          "<ul>"
0071                                          "<li>1234.56</li>"
0072                                          "<li>1,234.56</li>"
0073                                          "<li>etc.</li>"
0074                                          "</ul>"
0075                                          "For comma as the decimal separator, the valid number representations are:"
0076                                          "<ul>"
0077                                          "<li>1234,56</li>"
0078                                          "<li>1.234,56</li>"
0079                                          "<li>etc.</li>"
0080                                          "</ul>"
0081                                      );
0082 
0083     ui.lDecimalSeparator->setToolTip(textNumberFormatShort);
0084     ui.lDecimalSeparator->setWhatsThis(textNumberFormat);
0085     ui.cbDecimalSeparator->setToolTip(textNumberFormatShort);
0086     ui.cbDecimalSeparator->setWhatsThis(textNumberFormat);
0087 
0088     const QString textDateTimeFormatShort = i18n("This option determines how the imported strings have to be converted to calendar date, i.e. year, month, and day numbers in the Gregorian calendar and to time.");
0089     const QString textDateTimeFormat = textDateTimeFormatShort + "<br><br>" + i18n(
0090                                            "Expressions that may be used for the date part of format string:"
0091                                            "<table>"
0092                                            "<tr><td>d</td><td>the day as number without a leading zero (1 to 31).</td></tr>"
0093                                            "<tr><td>dd</td><td>the day as number with a leading zero (01 to 31).</td></tr>"
0094                                            "<tr><td>ddd</td><td>the abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name.</td></tr>"
0095                                            "<tr><td>dddd</td><td>the long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name.</td></tr>"
0096                                            "<tr><td>M</td><td>the month as number without a leading zero (1 to 12).</td></tr>"
0097                                            "<tr><td>MM</td><td>the month as number with a leading zero (01 to 12).</td></tr>"
0098                                            "<tr><td>MMM</td><td>the abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name.</td></tr>"
0099                                            "<tr><td>MMMM</td><td>the long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name.</td></tr>"
0100                                            "<tr><td>yy</td><td>the year as two digit number (00 to 99).</td></tr>"
0101                                            "<tr><td>yyyy</td><td>the year as four digit number. If the year is negative, a minus sign is prepended in addition.</td></tr>"
0102                                            "</table><br><br>"
0103                                            "Expressions that may be used for the time part of the format string:"
0104                                            "<table>"
0105                                            "<tr><td>h</td><td>the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)</td></tr>"
0106                                            "<tr><td>hh</td><td>the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)</td></tr>"
0107                                            "<tr><td>H</td><td>the hour without a leading zero (0 to 23, even with AM/PM display)</td></tr>"
0108                                            "<tr><td>HH</td><td>the hour with a leading zero (00 to 23, even with AM/PM display)</td></tr>"
0109                                            "<tr><td>m</td><td>the minute without a leading zero (0 to 59)</td></tr>"
0110                                            "<tr><td>mm</td><td>the minute with a leading zero (00 to 59)</td></tr>"
0111                                            "<tr><td>s</td><td>the second without a leading zero (0 to 59)</td></tr>"
0112                                            "<tr><td>ss</td><td>the second with a leading zero (00 to 59)</td></tr>"
0113                                            "<tr><td>z</td><td>the milliseconds without leading zeroes (0 to 999)</td></tr>"
0114                                            "<tr><td>zzz</td><td>the milliseconds with leading zeroes (000 to 999)</td></tr>"
0115                                            "<tr><td>AP or A</td><td>interpret as an AM/PM time. AP must be either 'AM' or 'PM'.</td></tr>"
0116                                            "<tr><td>ap or a</td><td>Interpret as an AM/PM time. ap must be either 'am' or 'pm'.</td></tr>"
0117                                            "</table><br><br>"
0118                                            "Examples are:"
0119                                            "<table>"
0120                                            "<tr><td>dd.MM.yyyy</td><td>20.07.1969</td></tr>"
0121                                            "<tr><td>ddd MMMM d yy</td><td>Sun July 20 69</td></tr>"
0122                                            "<tr><td>'The day is' dddd</td><td>The day is Sunday</td></tr>"
0123                                            "</table>");
0124 
0125     ui.lDateTimeFormat->setToolTip(textDateTimeFormatShort);
0126     ui.lDateTimeFormat->setWhatsThis(textDateTimeFormat);
0127     ui.cbDateTimeFormat->setToolTip(textDateTimeFormatShort);
0128     ui.cbDateTimeFormat->setWhatsThis(textDateTimeFormat);
0129 
0130 #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING
0131     m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(ui.teQuery->document());
0132     m_highlighter->setDefinition(m_repository.definitionForName("SQL"));
0133     m_highlighter->setTheme(  (palette().color(QPalette::Base).lightness() < 128)
0134                                 ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme)
0135                                 : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme) );
0136 #endif
0137 
0138     m_configPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).constFirst() +  "sql_connections";
0139 
0140     connect( ui.cbConnection, SIGNAL(currentIndexChanged(int)), SLOT(connectionChanged()) );
0141     connect( ui.cbImportFrom, SIGNAL(currentIndexChanged(int)), SLOT(importFromChanged(int)) );
0142     connect( ui.bDatabaseManager, SIGNAL(clicked()), this, SLOT(showDatabaseManager()) );
0143     connect( ui.lwTables, SIGNAL(currentRowChanged(int)), this, SLOT(refreshPreview()) );
0144     connect( ui.bRefreshPreview, SIGNAL(clicked()), this, SLOT(refreshPreview()) );
0145 
0146     //defer the loading of settings a bit in order to show the dialog prior to blocking the GUI in refreshPreview()
0147     QTimer::singleShot( 100, this, SLOT(loadSettings()) );
0148 }
0149 
0150 void ImportSQLDatabaseWidget::loadSettings() {
0151     m_initializing = true;
0152 
0153     //read available connections
0154     readConnections();
0155 
0156     //load last used connection and other settings
0157     KConfigGroup config(KSharedConfig::openConfig(), "ImportSQLDatabaseWidget");
0158     ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(config.readEntry("Connection", "")));
0159     ui.cbImportFrom->setCurrentIndex(config.readEntry("ImportFrom", 0));
0160     importFromChanged(ui.cbImportFrom->currentIndex());
0161 
0162     //TODO: use general setting for decimal separator?
0163     const QChar decimalSeparator = QLocale().decimalPoint();
0164     int index = (decimalSeparator == '.') ? 0 : 1;
0165     ui.cbDecimalSeparator->setCurrentIndex(config.readEntry("DecimalSeparator", index));
0166 
0167     ui.cbDateTimeFormat->setCurrentText(config.readEntry("DateTimeFormat", "yyyy-dd-MM hh:mm:ss:zzz"));
0168     QList<int> defaultSizes{100, 100};
0169     ui.splitterMain->setSizes(config.readEntry("SplitterMainSizes", defaultSizes));
0170     ui.splitterPreview->setSizes(config.readEntry("SplitterPreviewSizes", defaultSizes));
0171     //TODO
0172 
0173 
0174     m_initializing = false;
0175 
0176     //all settings loaded -> trigger the selection of the last used connection in order to get the data preview
0177     connectionChanged();
0178 }
0179 
0180 ImportSQLDatabaseWidget::~ImportSQLDatabaseWidget() {
0181     // save current settings
0182     KConfigGroup config(KSharedConfig::openConfig(), "ImportSQLDatabaseWidget");
0183     config.writeEntry("Connection", ui.cbConnection->currentText());
0184     config.writeEntry("ImportFrom", ui.cbImportFrom->currentIndex());
0185     config.writeEntry("DecimalSeparator", ui.cbDecimalSeparator->currentIndex());
0186     config.writeEntry("DateTimeFormat", ui.cbDateTimeFormat->currentText());
0187     config.writeEntry("SplitterMainSizes", ui.splitterMain->sizes());
0188     config.writeEntry("SplitterPreviewSizes", ui.splitterPreview->sizes());
0189     //TODO
0190 }
0191 
0192 /*!
0193  * in case the import from a table is selected, returns the currently selected database table.
0194  * returns empty string otherwise.
0195  */
0196 QString ImportSQLDatabaseWidget::selectedTable() const {
0197     if (ui.cbImportFrom->currentIndex() == 0) {
0198         if (ui.lwTables->currentItem())
0199             return ui.lwTables->currentItem()->text();
0200     }
0201 
0202     return QString();
0203 }
0204 
0205 /*!
0206     returns \c true if a working connections was selected and a table (or custom query) is provided and ready to be imported.
0207     returns \c false otherwise.
0208  */
0209 bool ImportSQLDatabaseWidget::isValid() const {
0210     return m_valid;
0211 }
0212 
0213 /*!
0214     returns \c true if the selected table or the result of a custom query contains numeric data only.
0215     returns \c false otherwise.
0216  */
0217 bool ImportSQLDatabaseWidget::isNumericData() const {
0218     return m_numeric;
0219 }
0220 
0221 /*!
0222     loads all available saved connections
0223 */
0224 void ImportSQLDatabaseWidget::readConnections() {
0225     DEBUG("ImportSQLDatabaseWidget: reading available connections");
0226     KConfig config(m_configPath, KConfig::SimpleConfig);
0227     for (const auto& name : config.groupList())
0228         ui.cbConnection->addItem(name);
0229 }
0230 
0231 void ImportSQLDatabaseWidget::connectionChanged() {
0232     if (m_initializing)
0233         return;
0234 
0235     QDEBUG("ImportSQLDatabaseWidget: connecting to " + ui.cbConnection->currentText());
0236 
0237     //clear the previously shown content
0238     ui.teQuery->clear();
0239     ui.lwTables->clear();
0240     ui.twPreview->clear();
0241     ui.twPreview->setColumnCount(0);
0242     ui.twPreview->setRowCount(0);
0243 
0244     if (ui.cbConnection->currentIndex() == -1)
0245         return;
0246 
0247     //connection name was changed, determine the current connections settings
0248     KConfig config(m_configPath, KConfig::SimpleConfig);
0249     KConfigGroup group = config.group(ui.cbConnection->currentText());
0250 
0251     //close and remove the previous connection, if available
0252     if (m_db.isOpen()) {
0253         m_db.close();
0254         QSqlDatabase::removeDatabase(m_db.driverName());
0255     }
0256 
0257     //open the selected connection
0258     const QString& driver = group.readEntry("Driver");
0259     m_db = QSqlDatabase::addDatabase(driver);
0260 
0261     const QString& dbName = group.readEntry("DatabaseName");
0262     if (DatabaseManagerWidget::isFileDB(driver)) {
0263         if (!QFile::exists(dbName)) {
0264             KMessageBox::error(this, i18n("Couldn't find the database file '%1'. Please check the connection settings.", dbName),
0265                                     i18n("Connection Failed"));
0266             setInvalid();
0267             return;
0268         } else
0269             m_db.setDatabaseName(dbName);
0270     } else if (DatabaseManagerWidget::isODBC(driver)) {
0271         if (group.readEntry("CustomConnectionEnabled", false))
0272             m_db.setDatabaseName(group.readEntry("CustomConnectionString"));
0273         else
0274             m_db.setDatabaseName(dbName);
0275     } else {
0276         m_db.setDatabaseName(dbName);
0277         m_db.setHostName( group.readEntry("HostName") );
0278         m_db.setPort( group.readEntry("Port", 0) );
0279         m_db.setUserName( group.readEntry("UserName") );
0280         m_db.setPassword( group.readEntry("Password") );
0281     }
0282 
0283     WAIT_CURSOR;
0284     if (!m_db.open()) {
0285         RESET_CURSOR;
0286         KMessageBox::error(this, i18n("Failed to connect to the database '%1'. Please check the connection settings.", ui.cbConnection->currentText()) +
0287                                     QLatin1String("\n\n") + m_db.lastError().databaseText(),
0288                                  i18n("Connection Failed"));
0289         setInvalid();
0290         return;
0291     }
0292 
0293     //show all available database tables
0294     if (m_db.tables().size()) {
0295         ui.lwTables->addItems(m_db.tables());
0296         ui.lwTables->setCurrentRow(0);
0297         for (int i = 0; i < ui.lwTables->count(); ++i)
0298             ui.lwTables->item(i)->setIcon(QIcon::fromTheme("view-form-table"));
0299     } else
0300         setInvalid();
0301 
0302     //show the last used query
0303     ui.teQuery->setText(group.readEntry("Query"));
0304 
0305     RESET_CURSOR;
0306 }
0307 
0308 void ImportSQLDatabaseWidget::refreshPreview() {
0309     if (!ui.lwTables->currentItem()) {
0310         setInvalid();
0311         return;
0312     }
0313 
0314     WAIT_CURSOR;
0315     ui.twPreview->clear();
0316 
0317     bool customQuery = (ui.cbImportFrom->currentIndex() != 0);
0318 
0319     //save the last used custom query
0320     if (customQuery) {
0321         KConfig config(m_configPath, KConfig::SimpleConfig);
0322         KConfigGroup group = config.group(ui.cbConnection->currentText());
0323         group.writeEntry("Query", ui.teQuery->toPlainText().simplified());
0324     }
0325 
0326     //execute the current query (select on a table or a custom query)
0327     const QString& query = currentQuery(true);
0328     if (query.isEmpty()) {
0329         RESET_CURSOR;
0330         setInvalid();
0331         return;
0332     }
0333 
0334     QSqlQuery q;
0335     q.prepare(currentQuery(true));
0336     q.setForwardOnly(true);
0337     q.exec();
0338     if (!q.isActive() || !q.next()) { // check if query was successful and got to first record
0339         RESET_CURSOR;
0340         if (!q.lastError().databaseText().isEmpty())
0341             KMessageBox::error(this, q.lastError().databaseText(), i18n("Unable to Execute Query"));
0342 
0343         setInvalid();
0344         return;
0345     }
0346 
0347     //resize the table to the number of columns (=number of fields in the result set)
0348     m_cols = q.record().count();
0349     ui.twPreview->setColumnCount(m_cols);
0350 
0351     //determine the names and the data type (column modes) of the table columns.
0352     //check whether we have numerical data only by checking the data types of the first record.
0353     m_columnNames.clear();
0354     m_columnModes.clear();
0355     bool numeric = true;
0356     //TODO: use general setting for decimal separator?
0357     QLocale::Language lang;
0358     if (ui.cbDecimalSeparator->currentIndex() == 0)
0359         lang = QLocale::Language::C;
0360     else
0361         lang = QLocale::Language::German;
0362     const QString& dateTimeFormat = ui.cbDateTimeFormat->currentText();
0363 //  ui.twPreview->setRowCount(1); //add the first row for the check boxes
0364     for (int i = 0; i < m_cols; ++i) {
0365         //name
0366         m_columnNames << q.record().fieldName(i);
0367 
0368         //value and type
0369         const QString valueString = q.record().value(i).toString();
0370         AbstractColumn::ColumnMode mode = AbstractFileFilter::columnMode(valueString, dateTimeFormat, lang);
0371         m_columnModes << mode;
0372         if (mode != AbstractColumn::ColumnMode::Numeric)
0373             numeric = false;
0374 
0375         //header item
0376         auto* item = new QTableWidgetItem(m_columnNames[i] + QLatin1String(" {") + ENUM_TO_STRING(AbstractColumn, ColumnMode, mode) + QLatin1String("}"));
0377         item->setTextAlignment(Qt::AlignLeft);
0378         item->setIcon(AbstractColumn::iconForMode(mode));
0379         ui.twPreview->setHorizontalHeaderItem(i, item);
0380 
0381         //create checked items
0382 //      QTableWidgetItem* itemChecked = new QTableWidgetItem();
0383 //      itemChecked->setCheckState(Qt::Checked);
0384 //      ui.twPreview->setItem(0, i, itemChecked);
0385     }
0386 
0387     //preview the data
0388     int row = 0;
0389     do {
0390         for (int col = 0; col < m_cols; ++col) {
0391             ui.twPreview->setRowCount(row+1);
0392             ui.twPreview->setItem(row, col, new QTableWidgetItem(q.value(col).toString()) );
0393         }
0394         row++;
0395 
0396         //in case a custom query is executed, check whether the row number limit is reached
0397         if (customQuery && row >= ui.sbPreviewLines->value())
0398             break;
0399     } while (q.next());
0400 
0401     ui.twPreview->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
0402 
0403     setValid();
0404 
0405     if (numeric != m_numeric) {
0406         m_numeric = numeric;
0407         emit stateChanged();
0408     }
0409 
0410     RESET_CURSOR;
0411 }
0412 
0413 void ImportSQLDatabaseWidget::importFromChanged(int index) {
0414     if (index == 0) { //import from a table
0415         ui.gbQuery->hide();
0416         ui.lwTables->show();
0417     } else { //import the result set of a custom query
0418         ui.gbQuery->show();
0419         ui.lwTables->hide();
0420         ui.twPreview->clear();
0421     }
0422 
0423     refreshPreview();
0424 }
0425 
0426 void ImportSQLDatabaseWidget::read(AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) {
0427     if (!dataSource)
0428         return;
0429 
0430     WAIT_CURSOR;
0431     //execute the current query (select on a table or a custom query)
0432     QSqlQuery q;
0433 //  q.setForwardOnly(true); //TODO: crashes most probably because of q.last() and q.first() below
0434     q.prepare(currentQuery());
0435     if (!q.exec() || !q.isActive()) {
0436         RESET_CURSOR;
0437         if (!q.lastError().databaseText().isEmpty())
0438             KMessageBox::error(this, q.lastError().databaseText(), i18n("Unable to Execute Query"));
0439 
0440         setInvalid();
0441         return;
0442     }
0443 
0444     //determine the number of rows/records to read
0445     q.last();
0446     const int rows = q.at()+1;
0447     q.first();
0448 
0449     // pointers to the actual data containers
0450     //columnOffset indexes the "start column" in the datasource. Data will be imported starting from this column.
0451     std::vector<void*> dataContainer;
0452     int columnOffset = dataSource->prepareImport(dataContainer, importMode, rows, m_cols, m_columnNames, m_columnModes);
0453 
0454     //number and DateTime formatting
0455     const QString& dateTimeFormat = ui.cbDateTimeFormat->currentText();
0456 
0457     //TODO: use general setting for decimal separator?
0458     QLocale::Language lang;
0459     if (ui.cbDecimalSeparator->currentIndex() == 0)
0460         lang = QLocale::Language::C;
0461     else
0462         lang = QLocale::Language::German;
0463     const QLocale numberFormat = QLocale(lang);
0464 
0465     //read the data
0466     int row = 0;
0467     do {
0468         for (int col = 0; col < m_cols; ++col) {
0469             const QString valueString = q.record().value(col).toString();
0470 
0471             // set value depending on data type
0472             switch (m_columnModes[col]) {
0473             case AbstractColumn::ColumnMode::Numeric: {
0474                 bool isNumber;
0475                 const double value = numberFormat.toDouble(valueString, &isNumber);
0476                 static_cast<QVector<double>*>(dataContainer[col])->operator[](row) = (isNumber ? value : NAN);
0477                 break;
0478             }
0479             case AbstractColumn::ColumnMode::Integer: {
0480                 bool isNumber;
0481                 const int value = numberFormat.toInt(valueString, &isNumber);
0482                 static_cast<QVector<int>*>(dataContainer[col])->operator[](row) = (isNumber ? value : NAN);
0483                 break;
0484             }
0485             case AbstractColumn::ColumnMode::BigInt: {
0486                 bool isNumber;
0487                 const qint64 value = numberFormat.toLongLong(valueString, &isNumber);
0488                 static_cast<QVector<qint64>*>(dataContainer[col])->operator[](row) = (isNumber ? value : NAN);
0489                 break;
0490             }
0491             case AbstractColumn::ColumnMode::DateTime: {
0492                 const QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat);
0493                 static_cast<QVector<QDateTime>*>(dataContainer[col])->operator[](row) = valueDateTime.isValid() ? valueDateTime : QDateTime();
0494                 break;
0495             }
0496             case AbstractColumn::ColumnMode::Text:
0497                 static_cast<QVector<QString>*>(dataContainer[col])->operator[](row) = valueString;
0498                 break;
0499             case AbstractColumn::ColumnMode::Month: // never happens
0500             case AbstractColumn::ColumnMode::Day:
0501                 break;
0502             }
0503         }
0504 
0505         row++;
0506         emit completed(100 * row/rows);
0507     } while (q.next());
0508     DEBUG(" Read " << row << " rows");
0509 
0510     dataSource->finalizeImport(columnOffset, 1, m_cols, dateTimeFormat, importMode);
0511     RESET_CURSOR;
0512 }
0513 
0514 QString ImportSQLDatabaseWidget::currentQuery(bool preview) {
0515     QString query;
0516     const bool customQuery = (ui.cbImportFrom->currentIndex() != 0);
0517     if ( !customQuery ) {
0518         const QString& tableName = ui.lwTables->currentItem()->text();
0519         if (!preview) {
0520             query = QLatin1String("SELECT * FROM ") + tableName;
0521         } else {
0522             //preview the content of the currently selected table
0523             const QString& driver = m_db.driverName();
0524             const QString& limit = QString::number(ui.sbPreviewLines->value());
0525             if ( (driver == QLatin1String("QSQLITE3")) || (driver == QLatin1String("QSQLITE"))
0526                 || (driver == QLatin1String("QMYSQL3")) || (driver == QLatin1String("QMYSQL"))
0527                 || (driver == QLatin1String("QPSQL"))  )
0528                 query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" LIMIT ") +  limit;
0529             else if (driver == QLatin1String("QOCI"))
0530                 query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" ROWNUM<=") + limit;
0531             else if (driver == QLatin1String("QDB2"))
0532                 query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" FETCH FIRST ") + limit + QLatin1String(" ROWS ONLY");
0533             else if (driver == QLatin1String("QIBASE"))
0534                 query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" ROWS ") + limit;
0535             else
0536                 //for ODBC the DBMS is not known and it's not clear what syntax to use -> select all rows
0537                 query = QLatin1String("SELECT * FROM ") + tableName;
0538         }
0539     } else {
0540         //preview the result of a custom query
0541         query = ui.teQuery->toPlainText().simplified();
0542     }
0543 
0544     return query;
0545 }
0546 
0547 /*!
0548     shows the database manager where the connections are created and edited.
0549     The selected connection is selected in the connection combo box in this widget.
0550 **/
0551 void ImportSQLDatabaseWidget::showDatabaseManager() {
0552     auto* dlg = new DatabaseManagerDialog(this, ui.cbConnection->currentText());
0553 
0554     if (dlg->exec() == QDialog::Accepted) {
0555         //re-read the available connections to be in sync with the changes in DatabaseManager
0556         m_initializing = true;
0557         ui.cbConnection->clear();
0558         readConnections();
0559 
0560         //select the connection the user has selected in DatabaseManager
0561         const QString& conn = dlg->connection();
0562         ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(conn));
0563         m_initializing = false;
0564 
0565         connectionChanged();
0566     }
0567 
0568     delete dlg;
0569 }
0570 
0571 void ImportSQLDatabaseWidget::setInvalid() {
0572     if (m_valid) {
0573         ui.twPreview->setColumnCount(0);
0574         ui.twPreview->setRowCount(0);
0575 
0576         m_valid = false;
0577         emit stateChanged();
0578     }
0579 }
0580 
0581 void ImportSQLDatabaseWidget::setValid() {
0582     if (!m_valid) {
0583         m_valid = true;
0584         emit stateChanged();
0585     }
0586 }