Skip to content

Commit 2117deb

Browse files
author
Paweł Salawa
committed
#3035 The INSTEAD OF triggers on Views are now honored when present. Add/Delete row in the View Window now appears if respective INSTEAD OF trigger is present for the View.
1 parent 4efad9f commit 2117deb

11 files changed

Lines changed: 409 additions & 239 deletions

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
- ADDED: #5290 Execute From File Dialog now supports 3 execution modes, allowing SQLite CLI dot commands in the executed file.
4848
- ADDED: #4873 Execute From File Dialog remembers its settings for another use.
4949
- ADDED: #4225 Foreign Key constraint configuration dialogs now warns about using foreign column that is neither PRIMARY KEY nor UNIQUE.
50+
- ADDED: #3035 The INSTEAD OF triggers on Views are now honored when present. Add/Delete row in the View Window now appears if respective INSTEAD OF trigger is present for the View.
5051
- ADDED: #5085 Added 'Cut' entry to context menu of data Grid View.
5152
- ADDED: #4709 Added option in configuration to sort data by single-click on a header. Ehnanced data header tooltips to include this information there.
5253
- ADDED: #3566 Added option in configuration to make the Enter key move to the next row, if it was used to finish data editing in Grid View. The option is also available from the Grid's context menu.

Letos/gui/datagrid/sqldatasourcequerymodel.cpp

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "sqldatasourcequerymodel.h"
22
#include "common/utils_sql.h"
3+
#include "services/notifymanager.h"
34

45
SqlDataSourceQueryModel::SqlDataSourceQueryModel(QObject *parent) :
56
SqlQueryModel(parent)
@@ -88,6 +89,132 @@ QString SqlDataSourceQueryModel::regExpFilterValueProcessor(const QString& value
8889
return pattern.arg(escapeString(value));
8990
}
9091

92+
bool SqlDataSourceQueryModel::commitAddedRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers)
93+
{
94+
if (columns.size() != itemsInRow.size())
95+
{
96+
qCritical() << "Tried to SqlDataSourceQueryModel::commitAddedRow() with number of columns in argument different than model resolved for the table.";
97+
return false;
98+
}
99+
100+
// Check that just in case:
101+
if (columns.size() == 0)
102+
{
103+
qCritical() << "Tried to SqlDataSourceQueryModel::commitAddedRow() with number of resolved columns in the table equal to 0!";
104+
return false;
105+
}
106+
107+
// Prepare column placeholders and their values
108+
QStringList colNameList;
109+
QStringList bindParams;
110+
QList<QVariant> args;
111+
prepareColumnsAndBindParams(itemsInRow, colNameList, bindParams, args);
112+
113+
// Prepare SQL query
114+
QString sql = getInsertSql(colNameList, bindParams);
115+
116+
// Execute query
117+
SqlQueryPtr result = db->exec(sql, args);
118+
119+
// Handle error
120+
if (result->isError())
121+
{
122+
QString errMsg = tr("Error while committing new row: %1").arg(result->getErrorText());
123+
for (SqlQueryItem* item : itemsInRow)
124+
item->setCommittingError(true, errMsg);
125+
126+
notifyError(errMsg);
127+
return false;
128+
}
129+
130+
// Take values from RETURNING clause to get actual values (because of DEFAULT, AUTOINCR).
131+
QList<SqlResultsRowPtr> rows = result->getAll();
132+
if (rows.size() != 1)
133+
{
134+
qCritical() << "After inserting new row to the table, number of rows returned from the query != 1. This should not happen. Number:" << rows.size();
135+
return false;
136+
}
137+
138+
QList<QVariant> rowValues = rows.first()->valueList();
139+
if (columns.size() != rowValues.size())
140+
{
141+
qCritical() << "Tried to SqlDataSourceQueryModel::commitAddedRow() with number of columns in argument different than RETURNING values from the INSERT.";
142+
return false;
143+
}
144+
145+
return commitAddedRowPostprocess(itemsInRow, rowValues, result->getInsertRowId(), successfulCommitHandlers);
146+
}
147+
148+
bool SqlDataSourceQueryModel::commitDeletedRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers)
149+
{
150+
if (itemsInRow.size() == 0)
151+
{
152+
qCritical() << "Tried to SqlDataSourceQueryModel::commitDeletedRow() with number of items equal to 0!";
153+
return false;
154+
}
155+
156+
RowId rowId = getRowIdForRow(itemsInRow);
157+
if (rowId.isEmpty())
158+
return false;
159+
160+
CommitDeleteQueryBuilder queryBuilder;
161+
queryBuilder.setTableOrView(getTableOrView());
162+
queryBuilder.setRowId(rowId);
163+
164+
QString sql = queryBuilder.build();
165+
QHash<QString, QVariant> args = queryBuilder.getQueryArgs();
166+
167+
SqlQueryPtr result = db->exec(sql, args);
168+
if (result->isError())
169+
{
170+
QString errMsg = tr("Error while deleting row from %1: %2").arg(getTableOrView(), result->getErrorText());
171+
for (SqlQueryItem* item : itemsInRow)
172+
item->setCommittingError(true, errMsg);
173+
174+
notifyError(errMsg);
175+
return false;
176+
}
177+
178+
if (!SqlQueryModel::commitDeletedRow(itemsInRow, successfulCommitHandlers))
179+
qCritical() << "Could not delete row from SqlQueryView while committing row deletion.";
180+
181+
return commitDeletedRowPostprocess(itemsInRow, result->getAll().size(), successfulCommitHandlers);
182+
}
183+
184+
QString SqlDataSourceQueryModel::getInsertSql(QStringList& colNameList, QStringList& sqlValues)
185+
{
186+
static_qstring(insertTpl, "INSERT INTO %1 %2 %3 RETURNING *");
187+
static_qstring(valuesTpl, "VALUES (%1)");
188+
static_qstring(columnsTpl, "(%1)");
189+
190+
QString wrappedTableName = getDataSource();
191+
QString colNames = colNameList.join(", ");
192+
if (colNameList.isEmpty())
193+
return insertTpl.arg(wrappedTableName, "", "DEFAULT VALUES");
194+
else
195+
return insertTpl.arg(wrappedTableName, columnsTpl.arg(colNames), valuesTpl.arg(sqlValues.join(", ")));
196+
}
197+
198+
void SqlDataSourceQueryModel::prepareColumnsAndBindParams(const QList<SqlQueryItem*>& itemsInRow, QStringList& colNameList,
199+
QStringList& bindParams, QList<QVariant>& args)
200+
{
201+
SqlQueryItem* item = nullptr;
202+
int i = 0;
203+
for (SqlQueryModelColumnPtr& modelColumn : columns)
204+
{
205+
item = itemsInRow[i++];
206+
if (!modelColumn->canEdit())
207+
continue;
208+
209+
if (item->isUntouched() && modelColumn->hasDefaultValueForInsert())
210+
continue;
211+
212+
colNameList << wrapObjIfNeeded(modelColumn->column);
213+
bindParams << ":arg" + QString::number(i);
214+
args << item->getValue();
215+
}
216+
}
217+
91218
void SqlDataSourceQueryModel::applySqlFilter(const QString& value)
92219
{
93220
if (value.isEmpty())
@@ -146,7 +273,34 @@ QString SqlDataSourceQueryModel::getDatabasePrefix()
146273
return wrapObjIfNeeded(database) + ".";
147274
}
148275

149-
QString SqlDataSourceQueryModel::getDataSource()
276+
QString SqlDataSourceQueryModel::CommitDeleteQueryBuilder::build()
277+
{
278+
QString dbAndTable;
279+
if (!database.isNull())
280+
dbAndTable += database+".";
281+
282+
dbAndTable += tableOrView;
283+
QString conditions = RowIdConditionBuilder::build();
284+
285+
static_qstring(sql, "DELETE FROM %1 WHERE %2 RETURNING 1;");
286+
return sql.arg(dbAndTable, conditions);
287+
}
288+
289+
290+
QString SqlDataSourceQueryModel::SelectColumnsQueryBuilder::build()
291+
{
292+
QString dbAndTable;
293+
if (!database.isNull())
294+
dbAndTable += database+".";
295+
296+
dbAndTable += tableOrView;
297+
QString conditions = RowIdConditionBuilder::build();
298+
299+
static_qstring(sql, "SELECT %1 FROM %2 WHERE %3 LIMIT 1;");
300+
return sql.arg(columns.join(", "), dbAndTable, conditions);
301+
}
302+
303+
void SqlDataSourceQueryModel::SelectColumnsQueryBuilder::addColumn(const QString& col)
150304
{
151-
return QString();
305+
columns << col;
152306
}

Letos/gui/datagrid/sqldatasourcequerymodel.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include "gui_global.h"
55
#include "sqlquerymodel.h"
66

7+
/**
8+
* @brief The SqlDataSourceQueryModel class is an abstract class for models that are based on a single data source, like a table or a view.
9+
*/
710
class GUI_API_EXPORT SqlDataSourceQueryModel : public SqlQueryModel
811
{
912
public:
@@ -23,23 +26,53 @@ class GUI_API_EXPORT SqlDataSourceQueryModel : public SqlQueryModel
2326
void resetFilter();
2427

2528
protected:
29+
class CommitDeleteQueryBuilder : public CommitUpdateQueryBuilder
30+
{
31+
public:
32+
QString build();
33+
};
34+
35+
class SelectColumnsQueryBuilder : public CommitUpdateQueryBuilder
36+
{
37+
public:
38+
QString build();
39+
void addColumn(const QString& col);
40+
};
41+
2642
typedef std::function<QString(const QString&)> FilterValueProcessor;
2743

2844
static QString stringFilterValueProcessor(const QString& value);
2945
static QString strictFilterValueProcessor(const QString& value);
3046
static QString regExpFilterValueProcessor(const QString& value);
3147

48+
bool commitAddedRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers);
49+
bool commitDeletedRow(const QList<SqlQueryItem*>& itemsInRow, QList<CommitSuccessfulHandler>& successfulCommitHandlers);
50+
void prepareColumnsAndBindParams(const QList<SqlQueryItem*>& itemsInRow,
51+
QStringList& colNameList, QStringList& sqlValues, QList<QVariant>& args);
52+
QString getInsertSql(QStringList& colNameList, QStringList& sqlValues);
53+
QString getDatabasePrefix();
3254
void applyFilter(const QString& value, FilterValueProcessor valueProc);
3355
void applyFilter(const QStringList& values, FilterValueProcessor valueProc);
3456

35-
QString getDatabasePrefix();
57+
virtual bool commitAddedRowPostprocess(const QList<SqlQueryItem*>& itemsInRow, const QList<QVariant>& rowValues, const RowId& rowId,
58+
QList<SqlQueryModel::CommitSuccessfulHandler>& successfulCommitHandlers) = 0;
59+
virtual bool commitDeletedRowPostprocess(const QList<SqlQueryItem*>& itemsInRow, int deletedRowCount,
60+
QList<SqlQueryModel::CommitSuccessfulHandler>& successfulCommitHandlers) = 0;
61+
virtual RowId getRowIdForRow(const QList<SqlQueryItem*>& itemsInRow) = 0;
3662

3763
/**
3864
* @brief Get the data source for this object.
3965
* Default implementation returns an empty string. Working implementation
40-
* (i.e. for a table) should return the data source string.
66+
* (i.e. for a table) should return the data source string (i.e. dbname.table),
67+
* both parts of name already wrapped if needed.
68+
*/
69+
virtual QString getDataSource() = 0;
70+
71+
/**
72+
* @brief Just table or view part of the datasource
73+
* @return
4174
*/
42-
virtual QString getDataSource();
75+
virtual QString getTableOrView() = 0;
4376

4477
QString database;
4578
};

Letos/gui/datagrid/sqlquerymodel.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ void SqlQueryModel::refreshGeneratedColumns(const QList<SqlQueryItem*>& items, Q
509509
continue;
510510

511511
builder.setDatabase(wrapObjIfNeeded(table.getDatabase()));
512-
builder.setTable(wrapObjIfNeeded(table.getTable()));
512+
builder.setTableOrView(wrapObjIfNeeded(table.getTable()));
513513

514514
QHash<RowId, QSet<SqlQueryItem*>> itemsPerRowId;
515515
for (SqlQueryItem* item : itemsIt.value())
@@ -538,7 +538,7 @@ QHash<SqlQueryItem*, QVariant> SqlQueryModel::readCellValues(SelectCellsQueryBui
538538
// Handling error
539539
if (results->isError())
540540
{
541-
qCritical() << "Could not load cell values for table" << queryBuilder.getTable() << ", so defaulting them with NULL. Error from database was:"
541+
qCritical() << "Could not load cell values for table" << queryBuilder.getTableOrView() << ", so defaulting them with NULL. Error from database was:"
542542
<< results->getErrorText();
543543

544544
for (SqlQueryItem* item : concatSet(itemsPerRowId.values()))
@@ -549,7 +549,7 @@ QHash<SqlQueryItem*, QVariant> SqlQueryModel::readCellValues(SelectCellsQueryBui
549549

550550
if (!results->hasNext())
551551
{
552-
qCritical() << "Could not load cell values for table" << queryBuilder.getTable() << ", so defaulting them with NULL. There were no result rows.";
552+
qCritical() << "Could not load cell values for table" << queryBuilder.getTableOrView() << ", so defaulting them with NULL. There were no result rows.";
553553

554554
for (SqlQueryItem* item : concatSet(itemsPerRowId.values()))
555555
values[item] = QVariant();
@@ -563,7 +563,7 @@ QHash<SqlQueryItem*, QVariant> SqlQueryModel::readCellValues(SelectCellsQueryBui
563563
SqlResultsRowPtr row = results->next();
564564
if (row->valueList().size() != queryBuilder.getColumnCount())
565565
{
566-
qCritical() << "Could not load cell values for table" << queryBuilder.getTable() << ", so defaulting them with NULL. Number of columns from results was invalid:"
566+
qCritical() << "Could not load cell values for table" << queryBuilder.getTableOrView() << ", so defaulting them with NULL. Number of columns from results was invalid:"
567567
<< row->valueList().size() << ", while expected:" << queryBuilder.getColumnCount();
568568

569569
for (SqlQueryItem* item : concatSet(itemsPerRowId.values()))
@@ -880,7 +880,7 @@ bool SqlQueryModel::commitEditedRow(const QList<SqlQueryItem*>& itemsInRow, QLis
880880
queryBuilder.setRowId(rowId);
881881

882882
// Database and table
883-
queryBuilder.setTable(wrapObjIfNeeded(table.getTable()));
883+
queryBuilder.setTableOrView(wrapObjIfNeeded(table.getTable()));
884884
if (!table.getDatabase().isNull())
885885
{
886886
QString tableDb = getDatabaseForCommit(table.getDatabase());
@@ -2228,7 +2228,7 @@ bool SqlQueryModel::isExecutionInProgress() const
22282228
void SqlQueryModel::CommitUpdateQueryBuilder::clear()
22292229
{
22302230
database.clear();
2231-
table.clear();
2231+
tableOrView.clear();
22322232
columns.clear();
22332233
queryArgs.clear();
22342234
conditions.clear();
@@ -2240,9 +2240,9 @@ void SqlQueryModel::CommitUpdateQueryBuilder::setDatabase(const QString& databas
22402240
this->database = database;
22412241
}
22422242

2243-
void SqlQueryModel::CommitUpdateQueryBuilder::setTable(const QString& table)
2243+
void SqlQueryModel::CommitUpdateQueryBuilder::setTableOrView(const QString& tableOrView)
22442244
{
2245-
this->table = table;
2245+
this->tableOrView = tableOrView;
22462246
}
22472247

22482248
void SqlQueryModel::CommitUpdateQueryBuilder::setColumn(const QString& column)
@@ -2279,7 +2279,7 @@ QString SqlQueryModel::CommitUpdateQueryBuilder::build()
22792279
if (!database.isNull())
22802280
dbAndTable += database + ".";
22812281

2282-
dbAndTable += table;
2282+
dbAndTable += tableOrView;
22832283

22842284
int argIndex = 0;
22852285
QString arg;
@@ -2337,7 +2337,7 @@ QString SqlQueryModel::SelectCellsQueryBuilder::build()
23372337
if (!database.isNull())
23382338
dbAndTable += database + ".";
23392339

2340-
dbAndTable += table;
2340+
dbAndTable += tableOrView;
23412341

23422342
QStringList selectColumns;
23432343
for (const QString& col : columns)
@@ -2364,7 +2364,7 @@ QString SqlQueryModel::SelectCellsQueryBuilder::build()
23642364
void SqlQueryModel::SelectCellsQueryBuilder::clear()
23652365
{
23662366
database = QString();
2367-
table = QString();
2367+
tableOrView = QString();
23682368
rowIdColumns.clear();
23692369
columns.clear();
23702370
conditions.clear();
@@ -2378,9 +2378,9 @@ void SqlQueryModel::SelectCellsQueryBuilder::setDatabase(const QString& database
23782378
this->database = database;
23792379
}
23802380

2381-
void SqlQueryModel::SelectCellsQueryBuilder::setTable(const QString& table)
2381+
void SqlQueryModel::SelectCellsQueryBuilder::setTableOrView(const QString& tableOrView)
23822382
{
2383-
this->table = table;
2383+
this->tableOrView = tableOrView;
23842384
}
23852385

23862386
void SqlQueryModel::SelectCellsQueryBuilder::addColumn(const QString& column)
@@ -2402,9 +2402,9 @@ int SqlQueryModel::SelectCellsQueryBuilder::getColumnCount() const
24022402
return columns.size();
24032403
}
24042404

2405-
QString SqlQueryModel::SelectCellsQueryBuilder::getTable() const
2405+
QString SqlQueryModel::SelectCellsQueryBuilder::getTableOrView() const
24062406
{
2407-
return table;
2407+
return tableOrView;
24082408
}
24092409

24102410
QString SqlQueryModel::SelectCellsQueryBuilder::getDatabase() const

0 commit comments

Comments
 (0)