#include "QueryThread.h" #include "DataModel.h" #include "AddModel.h" #include #include #include #include #include #include // uncomment next line to unarm the delete method - delete method does actually not delete content //#define UNARMED const char browserName[] = "SQLBrowser"; const char driverName[] = "QSQLITE"; QueryWorker::QueryWorker() : m_dy(false) , m_idle(true) { //qWarning() << "QueryWorker"; //test.dummy(); } QueryWorker::~QueryWorker() { //qWarning() << "~QueryWorker"; m_idle = true; m_sqlDB.close(); //QSqlDatabase::removeDatabase(BROWSER_NAME); } void QueryWorker::slotGetTableList() { m_idle = false; if (m_sqlDB.isOpen()) { QStringList tables = m_sqlDB.tables(); tables.append(m_sqlDB.tables(QSql::Views)); tables.sort(); emit signalNewTableList(tables); } else { //qWarning() << "mep..."; } m_idle = true; } void QueryWorker::slotConnectIt(const LoginData &loginData) { m_idle = false; // SQLite if (!loginData.filename.isEmpty()) { m_sqlDB = QSqlDatabase::addDatabase(QLatin1String(driverName), QLatin1String(browserName)); m_sqlDB.setDatabaseName(loginData.filename); } else // DBMS { // CONNECT & TEST m_sqlDB = QSqlDatabase::addDatabase(loginData.driver, QLatin1String(browserName)); m_sqlDB.setDatabaseName(loginData.database); m_sqlDB.setHostName(loginData.hostname); m_sqlDB.setPort(loginData.port); m_sqlDB.setUserName(loginData.username); m_sqlDB.setPassword(loginData.password); } if (!m_sqlDB.open()) { emit signalConnectResult(m_sqlDB.lastError().text()); } else { emit signalConnectResult(QString()); // connected! } m_idle = true; } void QueryWorker::slotDeleteLines(Query* caller, const QList &selectedRows, DataModel* dataModel) { m_idle = false; if (dataModel->getTableName().isEmpty()) { emit signalDeleteLinesResult(caller, 0, QStringLiteral("This is a custom query"), QList()); m_idle = true; return; } QString deleteStatement(QStringLiteral("DELETE FROM %1 WHERE %2")); if (!selectedRows.count()) { emit signalDeleteLinesResult(caller, 0, QStringLiteral("No line(s) selected to delete"), QList()); m_idle = true; return; } // DELETE FROM table_name WHERE (x1=1 AND x2=2) OR (x1=2 AND x=3) QStringList rows; // (x1=1 AND x2=2) (x1=2 AND x2=3) - join with "AND" ! rows.reserve(selectedRows.size()); for (int rowIterator = 0; rowIterator < selectedRows.count(); rowIterator++) { QStringList whereStatements; int columnCount = dataModel->columnCount(); whereStatements.reserve(columnCount); int row = selectedRows.at(rowIterator).row(); //qWarning() << "row" << row; for (int column = 0; column < columnCount; column++) { //qWarning() << "col" << column; QVariant var = dataModel->data(dataModel->index(row, column), Qt::DisplayRole); QString value = valueToSqlString(var.toString(), dataModel->getHeaderList(), column); QStringList whereStatement; whereStatement.reserve(2); whereStatement.append(dataModel->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString()); // column name whereStatement.append(value); if (value == QStringLiteral("NULL")) whereStatements.append(whereStatement.join(QStringLiteral(" IS "))); // >>> column_name = %value% <<< else whereStatements.append(whereStatement.join(QStringLiteral("="))); // >>> column_name IS NULL <<< } QString rowText(QStringLiteral("(%1)").arg(whereStatements.join(QStringLiteral(" AND ")))); rows.append(rowText); // >>> column_name1 = 'value1' AND column_name2 = 'value2' <<< } deleteStatement = deleteStatement.arg(dataModel->getTableName(), rows.join(QStringLiteral(" OR "))); qWarning() << "OKAY! my statement = >>>" << deleteStatement; int rowsAffected = -1; emit signalDeleteLinesResult(caller, rowsAffected, #ifdef UNARMED QString(), #else executeQuery(&rowsAffected, deleteStatement), #endif selectedRows); m_idle = true; } QString QueryWorker::selectQuery(const QString &queryString, DataModel* dataModel) { m_idle = false; QString result; QSqlQuery q(m_sqlDB); if (q.exec(queryString) && !m_dy) // execute query first.. { // header //HeaderList headerList; const auto record = q.record(); int querySize = q.size(); // aka rows int recordCount = record.count(); // aka columns int statusStep = querySize / 100; int statusCurrent = 0; // NOT in percent! (maximum = querySize) int statusPercent = 0; // 0 - 100 // TODO: let's ignore the pk autoincrement first... //qWarning() << "primaryIndex" << m_sqlDB.primaryIndex(dataModel->getTableName()); //q.record().field(i).setAutoValue(true); for (int i = 0; i < recordCount; i++) { dataModel->addColumn(record.fieldName(i), record.field(i).type()); if (m_dy) break; } // data //DataList dataList; dataModel->dataListReserve(querySize); // speedup Data data; data.reserve(recordCount); while (q.next() && !m_dy) { if (statusCurrent >= statusStep) { statusPercent++; statusCurrent = 0; emit signalSelectQueryStatus(statusPercent); } statusCurrent++; data.clear(); for (int i = 0; i < recordCount; i++) data.append(q.value(i)); dataModel->addLine(data); } // done reading, create model //DataModel* dataModel = 0; if (!m_dy) { // resultHeaderList = headerList; // resultDataList = dataList; connect(this, &QueryWorker::signalSwapModelData, dataModel, &DataModel::slotSwapLists); emit signalSwapModelData(); // single shot.... for threading disconnect(this, &QueryWorker::signalSwapModelData, dataModel, &DataModel::slotSwapLists); //qWarning() << "QueryThread: headerList.count()" << headerList->count(); //qWarning() << "QueryThread: dataList.count()" << dataList->count(); } } else // query failed { if (!m_dy) // only an error when not dying { result = /* emit signalSelectQueryResult(*/q.lastError().text()/*, 0)*/; // TODO: respect qt4 return invalid pointer } } m_idle = true; return result; } void QueryWorker::slotSetData(Query* caller, DataModel* model, const QModelIndex &index, const QVariant &value) { // TODO: use executeQuery here! m_idle = false; QString queryString(QStringLiteral("UPDATE %1 SET %2=%3 WHERE %4")); int row = index.row(); /* %1 */ QString tableName = model->getTableName(); if (tableName.isEmpty()) { emit signalSetDataResult(caller, QStringLiteral("This is a custom query")); m_idle = true; return; } /* %2 */ QString columnToChange = model->headerData(index.column(), Qt::Horizontal).toString(); const HeaderList &headerList = model->getHeaderList(); /* %3 */ const QString &valueForColumn = valueToSqlString(value, headerList, index.column()); // cannot get from model, as setData retured FALSE, which resets the change /* %4 */ QString allColumnValuesInRow; QStringList allColumnValuesInRowList; allColumnValuesInRowList.reserve(model->columnCount()); QString condition; for (int i = 0; i < model->columnCount(); i++) { QString columnName = model->headerData(i, Qt::Horizontal).toString(); QString columnValue = valueToSqlString(model->data(model->index(row, i)).toString(), headerList, i); // nur für Spalte mit Namen 'id' if (columnName.compare(QStringLiteral("id"), Qt::CaseInsensitive) == 0) condition = QStringLiteral("%1=%2").arg(columnName, columnValue); QString statement(QStringLiteral("%1=%2").arg(columnName, columnValue)); if (model->data(model->index(row, i)).isNull()) statement = QStringLiteral("%1 IS NULL").arg(columnName); allColumnValuesInRowList.append(statement); } allColumnValuesInRow = allColumnValuesInRowList.join(QStringLiteral(" AND ")); if (condition.isEmpty()) condition = allColumnValuesInRow; queryString = queryString.arg(tableName, columnToChange, valueForColumn, condition); // qCritical() << "queryString =" << queryString; QSqlQuery q(m_sqlDB); if (q.exec(queryString) && !m_dy) // execute set data query { // worked? then change value in model // signalSetDataResult(caller, QStringLiteral("Num affected rows: %1").arg(q.numRowsAffected())); auto con = connect(this, &QueryWorker::signalSetDataInModel, model, &DataModel::slotSetData); if (value.toString().isEmpty()) emit signalSetDataInModel(index, QVariant()); // single shot.... for threading else emit signalSetDataInModel(index, value); // single shot.... for threading disconnect(con); emit signalSetDataResult(caller, QString()); // empty string = success } else { emit signalSetDataResult(caller, q.lastError().text()); // error } m_idle = true; } void QueryWorker::slotTableQuery(Query* caller, DataModel *model) { // no need to unidle here, as selectQuery does it! //qWarning() << "QueryWorker: model" << model; if (model->getTableName().isEmpty()) { emit signalTableQueryResult(caller, QStringLiteral("This is a custom query")); return; } QString queryString(QStringLiteral("SELECT * FROM %1").arg(model->getTableName())); emit signalTableQueryResult(caller, selectQuery(queryString, model)); } void QueryWorker::slotCustomQuery(Query* caller, const QString &queryString, DataModel *model) { // no need to unidle here, as selectQuery does it! model->setTableName(QString()); // set empty table name, as this is not a table query anymore! emit signalCustomQueryResult(caller, selectQuery(queryString, model)); } void QueryWorker::slotAddLines(Query* caller, AddModel* addModel) { if (!addModel) // no lines to add { emit signalAddLinesResult(caller, 0, QString()); return; } // no need to unidle here, as executeQuery does it! QString addQuery(QStringLiteral("INSERT INTO %1 VALUES %2")); QStringList rows; rows.reserve(addModel->rowCount()); for (int row = 0; row < addModel->rowCount() - 1 /* ignore empty add-line */; row++) { QStringList columns; int columnCount = addModel->columnCount(); columns.reserve(columnCount); for (int col = 0; col < columnCount; col++) { //qWarning() << "col" << col << "type" << addModel->getColumnType(col); columns << valueToSqlString(addModel->data(addModel->index(row,col)), addModel->getHeaderList(), col); ////#warning implement IS NULL ! // if (addModel->getColumnType(col) == QVariant::Int) // get sql type // { // columns << addModel->data(addModel->index(row, col)).toString(); // } // else // string // { // QString str = "'%1'"; // str = str.arg(addModel->data(addModel->index(row, col)).toString()); // columns << str; // } } const QString rowString(QLatin1Char('(') + columns.join(QLatin1Char(',')) + QLatin1Char(')')); rows << rowString; } addQuery = addQuery.arg(addModel->getTableName(), rows.join(QLatin1Char(','))); int affectedRows = -1; emit signalAddLinesResult(caller, affectedRows, executeQuery(&affectedRows, addQuery)); qWarning() << "OKAY! add query = " << addQuery; } //QString QueryWorker::valueFromSqlString(const QString& val, const HeaderList &headerList, const int col) //{ // // value comes from sql database and must be made 'visual' // QVariant::Type type = headerList.at(col).second; // get column type from model // //qWarning() << "QueryWorker::variantToSqlString: type =" << type; // if (val.isEmpty()) // if val is empty, return empty sql type "NULL" // { // return "NULL"; // } // if (type == QVariant::String) // col is string type // { // return "'" + val + "'"; // } // else // col is ANY int type // { // return val; // } //} QString QueryWorker::valueToSqlString(const QVariant& val, const HeaderList &headerList, const int col) { // value must be converted to sql value QVariant::Type type = headerList.at(col).second; // get column type from model //qWarning() << "QueryWorker::valueToSqlString: val" << val << "type" << type << "("<