Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+510
View File
@@ -0,0 +1,510 @@
#include "QueryThread.h"
#include "DataModel.h"
#include "AddModel.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlField>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QDebug>
// 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<QModelIndex> &selectedRows, DataModel* dataModel)
{
m_idle = false;
if (dataModel->getTableName().isEmpty())
{
emit signalDeleteLinesResult(caller, 0, QStringLiteral("This is a custom query"), QList<QModelIndex>());
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<QModelIndex>());
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 << "("<<type<<")";
if (val.isNull() || val.toString().isEmpty()) // if val is empty, return empty sql type "NULL"
return QStringLiteral("NULL");
//if (type == QVariant::String) // col is string type
switch (type)
{
case QVariant::Int:
return QString::number(val.toInt());
case QVariant::UInt:
return QString::number(val.toUInt());
case QVariant::LongLong:
return QString::number(val.toLongLong());
case QVariant::ULongLong:
return QString::number(val.toULongLong());
case QVariant::Double:
return QString::number(val.toDouble());
default: // any other than numeric type
break;
}
return QStringLiteral("'%1'").arg(val.toString());
}
QString QueryWorker::executeQuery(int* affectedRows, const QString &queryString)
{
m_idle = false;
QString result;
QSqlQuery q(m_sqlDB);
if (q.exec(queryString))
{
qWarning() << "executeQuery: success: numRowsAffected" << q.numRowsAffected();
*affectedRows= q.numRowsAffected();
}
else // error
{
*affectedRows = 0;
result = q.lastError().text(); // error (no rows affected)
// if (result.isEmpty())
// {
// result = "UNKNOWN ERROR";
// }
}
m_idle = true;
return result;
}