1 #include "sqlquerymodelcolumn.h"
2 #include "iconmanager.h"
3 #include <QDebug>
4
SqlQueryModelColumn(const QueryExecutor::ResultColumnPtr & resultColumn)5 SqlQueryModelColumn::SqlQueryModelColumn(const QueryExecutor::ResultColumnPtr& resultColumn)
6 {
7 displayName = resultColumn->displayName;
8 column = resultColumn->column;
9 alias = resultColumn->alias;
10 table = resultColumn->table;
11 tableAlias = resultColumn->tableAlias;
12 database = resultColumn->database.isEmpty() ? "main": resultColumn->database;
13 for (QueryExecutor::ColumnEditionForbiddenReason reason : resultColumn->editionForbiddenReasons)
14 editionForbiddenReason << SqlQueryModelColumn::convert(reason);
15 }
16
~SqlQueryModelColumn()17 SqlQueryModelColumn::~SqlQueryModelColumn()
18 {
19 for (Constraint* constr : constraints)
20 delete constr;
21
22 constraints.clear();
23 }
24
initMeta()25 void SqlQueryModelColumn::initMeta()
26 {
27 qRegisterMetaType<SqlQueryModelColumn*>("SqlQueryModelColumn*");
28 qRegisterMetaTypeStreamOperators<SqlQueryModelColumn*>("SqlQueryModelColumn*");
29 }
30
convert(QueryExecutor::EditionForbiddenReason reason)31 SqlQueryModelColumn::EditionForbiddenReason SqlQueryModelColumn::convert(QueryExecutor::EditionForbiddenReason reason)
32 {
33 switch (reason)
34 {
35 case QueryExecutor::EditionForbiddenReason::NOT_A_SELECT:
36 return EditionForbiddenReason::NOT_A_SELECT;
37 case QueryExecutor::EditionForbiddenReason::SMART_EXECUTION_FAILED:
38 return EditionForbiddenReason::SMART_EXECUTION_FAILED;
39 }
40 return static_cast<EditionForbiddenReason>(-1);
41 }
42
convert(QueryExecutor::ColumnEditionForbiddenReason reason)43 SqlQueryModelColumn::EditionForbiddenReason SqlQueryModelColumn::convert(QueryExecutor::ColumnEditionForbiddenReason reason)
44 {
45 switch (reason)
46 {
47 case QueryExecutor::ColumnEditionForbiddenReason::EXPRESSION:
48 return EditionForbiddenReason::EXPRESSION;
49 case QueryExecutor::ColumnEditionForbiddenReason::SYSTEM_TABLE:
50 return EditionForbiddenReason::SYSTEM_TABLE;
51 case QueryExecutor::ColumnEditionForbiddenReason::COMPOUND_SELECT:
52 return EditionForbiddenReason::COMPOUND_SELECT;
53 case QueryExecutor::ColumnEditionForbiddenReason::GROUPED_RESULTS:
54 return EditionForbiddenReason::GROUPED_RESULTS;
55 case QueryExecutor::ColumnEditionForbiddenReason::DISTINCT_RESULTS:
56 return EditionForbiddenReason::DISTINCT_RESULTS;
57 case QueryExecutor::ColumnEditionForbiddenReason::COMM_TAB_EXPR:
58 return EditionForbiddenReason::COMMON_TABLE_EXPRESSION;
59 }
60 return static_cast<EditionForbiddenReason>(-1);
61 }
62
resolveMessage(SqlQueryModelColumn::EditionForbiddenReason reason)63 QString SqlQueryModelColumn::resolveMessage(SqlQueryModelColumn::EditionForbiddenReason reason)
64 {
65 switch (reason)
66 {
67 case EditionForbiddenReason::COMPOUND_SELECT:
68 return QObject::tr("Cannot edit columns that are result of compound %1 statements (one that includes %2, %3 or %4 keywords).")
69 .arg("SELECT", "UNION", "INTERSECT", "EXCEPT");
70 case EditionForbiddenReason::SMART_EXECUTION_FAILED:
71 return QObject::tr("The query execution mechanism had problems with extracting ROWID's properly. This might be a bug in the application. You may want to report this.");
72 case EditionForbiddenReason::EXPRESSION:
73 return QObject::tr("Requested column is a result of SQL expression, instead of a simple column selection. Such columns cannot be edited.");
74 case EditionForbiddenReason::SYSTEM_TABLE:
75 return QObject::tr("Requested column belongs to restricted SQLite table. Those tables cannot be edited directly.");
76 case EditionForbiddenReason::NOT_A_SELECT:
77 return QObject::tr("Cannot edit results of query other than %1.").arg("SELECT");
78 case EditionForbiddenReason::GROUPED_RESULTS:
79 return QObject::tr("Cannot edit columns that are result of aggregated %1 statements.").arg("SELECT");
80 case EditionForbiddenReason::DISTINCT_RESULTS:
81 return QObject::tr("Cannot edit columns that are result of %1 statement.").arg("SELECT DISTINCT");
82 case EditionForbiddenReason::COMMON_TABLE_EXPRESSION:
83 return QObject::tr("Cannot edit columns that are result of common table expression statement (%1).").arg("WITH ... SELECT ...");
84 case EditionForbiddenReason::GENERATED_COLUMN:
85 return QObject::tr("Cannot edit table generated columns.");
86 }
87 qCritical() << "Reached null text message for SqlQueryModel::EditionForbiddenReason. This should not happen!";
88 return QString();
89 }
90
postProcessConstraints()91 void SqlQueryModelColumn::postProcessConstraints()
92 {
93 if (isGenerated())
94 editionForbiddenReason << EditionForbiddenReason::GENERATED_COLUMN;
95 }
96
isNumeric()97 bool SqlQueryModelColumn::isNumeric()
98 {
99 return dataType.isNumeric();
100 }
101
isNull()102 bool SqlQueryModelColumn::isNull()
103 {
104 return dataType.isNull();
105 }
106
canEdit()107 bool SqlQueryModelColumn::canEdit()
108 {
109 return editionForbiddenReason.size() == 0;
110 }
111
getEditionForbiddenReason()112 QString SqlQueryModelColumn::getEditionForbiddenReason()
113 {
114 if (canEdit())
115 return QString();
116
117 // We sort reasons to get most significant reason at first position.
118 QList<EditionForbiddenReason> list = editionForbiddenReason.values();
119 std::sort(list.begin(), list.end());
120 return resolveMessage(list[0]);
121 }
122
isPk() const123 bool SqlQueryModelColumn::isPk() const
124 {
125 return getConstraints<ConstraintPk*>().size() > 0;
126 }
127
isRowIdPk() const128 bool SqlQueryModelColumn::isRowIdPk() const
129 {
130 if (dataType.getType() != DataType::INTEGER)
131 return false;
132
133 for (ConstraintPk* pk : getConstraints<ConstraintPk*>())
134 if (pk->scope == Constraint::Scope::COLUMN)
135 return true;
136
137 return false;
138 }
139
isAutoIncr() const140 bool SqlQueryModelColumn::isAutoIncr() const
141 {
142 for (ConstraintPk* pk : getConstraints<ConstraintPk*>())
143 if (pk->autoIncrement)
144 return true;
145
146 return false;
147 }
148
isNotNull() const149 bool SqlQueryModelColumn::isNotNull() const
150 {
151 return getConstraints<ConstraintNotNull*>().size() > 0;
152 }
153
isUnique() const154 bool SqlQueryModelColumn::isUnique() const
155 {
156 return getConstraints<ConstraintUnique*>().size() > 0;
157 }
158
isFk() const159 bool SqlQueryModelColumn::isFk() const
160 {
161 return getConstraints<ConstraintFk*>().size() > 0;
162 }
163
isDefault() const164 bool SqlQueryModelColumn::isDefault() const
165 {
166 return getConstraints<ConstraintDefault*>().size() > 0;
167 }
168
isCollate() const169 bool SqlQueryModelColumn::isCollate() const
170 {
171 return getConstraints<ConstraintCollate*>().size() > 0;
172 }
173
isGenerated() const174 bool SqlQueryModelColumn::isGenerated() const
175 {
176 return getConstraints<ConstraintGenerated*>().size() > 0;
177 }
178
getFkConstraints() const179 QList<SqlQueryModelColumn::ConstraintFk*> SqlQueryModelColumn::getFkConstraints() const
180 {
181 return getConstraints<ConstraintFk*>();
182 }
183
getDefaultConstraint() const184 SqlQueryModelColumn::ConstraintDefault* SqlQueryModelColumn::getDefaultConstraint() const
185 {
186 QList<ConstraintDefault*> list = getConstraints<ConstraintDefault*>();
187 if (list.size() == 0)
188 return nullptr;
189
190 return list[0];
191 }
192
qHash(SqlQueryModelColumn::EditionForbiddenReason reason)193 int qHash(SqlQueryModelColumn::EditionForbiddenReason reason)
194 {
195 return static_cast<int>(reason);
196 }
197
operator <<(QDataStream & out,const SqlQueryModelColumn * col)198 QDataStream&operator <<(QDataStream& out, const SqlQueryModelColumn* col)
199 {
200 out << reinterpret_cast<quint64>(col);
201 return out;
202 }
203
operator >>(QDataStream & in,SqlQueryModelColumn * & col)204 QDataStream&operator >>(QDataStream& in, SqlQueryModelColumn*& col)
205 {
206 quint64 ptr;
207 in >> ptr;
208 col = reinterpret_cast<SqlQueryModelColumn*>(ptr);
209 return in;
210 }
211
212
create(const QString & column,SqliteCreateTable::ConstraintPtr tableConstraint)213 SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(const QString& column, SqliteCreateTable::ConstraintPtr tableConstraint)
214 {
215 Constraint* constr = nullptr;
216 switch (tableConstraint->type)
217 {
218 case SqliteCreateTable::Constraint::PRIMARY_KEY:
219 {
220 if (!tableConstraint->doesAffectColumn(column))
221 return nullptr;
222
223 constr = new ConstraintPk();
224 constr->type = Type::PRIMARY_KEY;
225 break;
226 }
227 case SqliteCreateTable::Constraint::UNIQUE:
228 {
229 constr = new ConstraintUnique();
230 constr->type = Type::UNIQUE;
231 break;
232 }
233 case SqliteCreateTable::Constraint::CHECK:
234 {
235 ConstraintCheck* check = new ConstraintCheck();
236 check->condition = tableConstraint->expr->detokenize();
237 constr = check;
238 constr->type = Type::CHECK;
239 break;
240 }
241 case SqliteCreateTable::Constraint::FOREIGN_KEY:
242 {
243 int idx = tableConstraint->getAffectedColumnIdx(column);
244 if (idx < 0 || tableConstraint->foreignKey->indexedColumns.size() <= idx)
245 {
246 // This case is perfectly fine if there are for example 2 foreign keys on the table,
247 // for 2 different columns. For each of those columns there will be 1 FK
248 // that enters here.
249 //qWarning() << "Could not find FK column for definition:" << tableConstraint->detokenize();
250 return nullptr;
251 }
252
253 ConstraintFk* fk = new ConstraintFk();
254 fk->foreignTable = tableConstraint->foreignKey->foreignTable;
255 fk->foreignColumn = tableConstraint->foreignKey->indexedColumns[idx]->name;
256
257 constr = fk;
258 constr->type = Type::FOREIGN_KEY;
259 break;
260 }
261 default:
262 return nullptr;
263 }
264
265 constr->scope = Scope::TABLE;
266 constr->definition = tableConstraint->detokenize();
267 return constr;
268 }
269
create(SqliteCreateTable::Column::ConstraintPtr columnConstraint)270 SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(SqliteCreateTable::Column::ConstraintPtr columnConstraint)
271 {
272 Constraint* constr = nullptr;
273 switch (columnConstraint->type)
274 {
275 case SqliteCreateTable::Column::Constraint::PRIMARY_KEY:
276 {
277 ConstraintPk* pk = new ConstraintPk();
278 pk->autoIncrement = columnConstraint->autoincrKw;
279 constr = pk;
280 constr->type = Type::PRIMARY_KEY;
281 break;
282 }
283 case SqliteCreateTable::Column::Constraint::NOT_NULL:
284 {
285 constr = new ConstraintNotNull();
286 constr->type = Type::NOT_NULL;
287 break;
288 }
289 case SqliteCreateTable::Column::Constraint::UNIQUE:
290 {
291 constr = new ConstraintUnique();
292 constr->type = Type::UNIQUE;
293 break;
294 }
295 case SqliteCreateTable::Column::Constraint::CHECK:
296 {
297 ConstraintCheck* check = new ConstraintCheck();
298 check->condition = columnConstraint->expr->detokenize();
299 constr = check;
300 constr->type = Type::CHECK;
301 break;
302 }
303 case SqliteCreateTable::Column::Constraint::DEFAULT:
304 {
305 ConstraintDefault* def = new ConstraintDefault();
306 if (!columnConstraint->id.isNull())
307 def->defaultValue = columnConstraint->id;
308 else if (!columnConstraint->ctime.isNull())
309 def->defaultValue = columnConstraint->ctime;
310 else if (columnConstraint->expr)
311 def->defaultValue = columnConstraint->expr->detokenize();
312 else
313 def->defaultValue = columnConstraint->literalValue.toString();
314
315 constr = def;
316 constr->type = Type::DEFAULT;
317 break;
318 }
319 case SqliteCreateTable::Column::Constraint::COLLATE:
320 {
321 ConstraintCollate* collate = new ConstraintCollate();
322 collate->collationName = columnConstraint->collationName;
323 constr = collate;
324 constr->type = Type::COLLATE;
325 break;
326 }
327 case SqliteCreateTable::Column::Constraint::GENERATED:
328 {
329 ConstraintGenerated* generate = new ConstraintGenerated();
330 generate->generatedType = columnConstraint->generatedType;
331 constr = generate;
332 constr->type = Type::GENERATED;
333 break;
334 }
335 case SqliteCreateTable::Column::Constraint::FOREIGN_KEY:
336 {
337 if (columnConstraint->foreignKey->indexedColumns.size() == 0)
338 {
339 qWarning() << "No foreign column defined for FK column constraint while creating SqlQueryModelColumn::Constraint.";
340 return nullptr;
341 }
342
343 ConstraintFk* fk = new ConstraintFk();
344 fk->foreignTable = columnConstraint->foreignKey->foreignTable;
345 fk->foreignColumn = columnConstraint->foreignKey->indexedColumns.first()->name;
346
347 constr = fk;
348 constr->type = Type::FOREIGN_KEY;
349 break;
350 }
351 default:
352 return nullptr;
353 }
354
355 constr->scope = Scope::COLUMN;
356 constr->definition = columnConstraint->detokenize();
357 return constr;
358 }
359
360 template <class T>
getConstraints() const361 QList<T> SqlQueryModelColumn::getConstraints() const
362 {
363 QList<T> results;
364 for (Constraint* constr : constraints)
365 if (dynamic_cast<T>(constr))
366 results << dynamic_cast<T>(constr);
367
368 return results;
369 }
370
371
getTypeString() const372 QString SqlQueryModelColumn::ConstraintPk::getTypeString() const
373 {
374 return "PRIMARY KEY";
375 }
376
getDetails() const377 QString SqlQueryModelColumn::ConstraintPk::getDetails() const
378 {
379 QStringList detailList;
380 if (autoIncrement)
381 detailList << "AUTOINCREMENT";
382
383 if (onConflict != SqliteConflictAlgo::null)
384 detailList << QObject::tr("on conflict: %1", "data view tooltip").arg(sqliteConflictAlgo(onConflict));
385
386 if (detailList.size() > 0)
387 return "("+detailList.join(", ")+")";
388
389 return "";
390 }
391
getIcon() const392 Icon* SqlQueryModelColumn::ConstraintPk::getIcon() const
393 {
394 return ICONS.CONSTRAINT_PRIMARY_KEY;
395 }
396
getTypeString() const397 QString SqlQueryModelColumn::ConstraintFk::getTypeString() const
398 {
399 return "FOREIGN KEY";
400 }
401
getDetails() const402 QString SqlQueryModelColumn::ConstraintFk::getDetails() const
403 {
404 return "("+QObject::tr("references table %1, column %2", "data view tooltip").arg(foreignTable).arg(foreignColumn)+")";
405 }
406
getIcon() const407 Icon* SqlQueryModelColumn::ConstraintFk::getIcon() const
408 {
409 return ICONS.CONSTRAINT_FOREIGN_KEY;
410 }
411
getTypeString() const412 QString SqlQueryModelColumn::ConstraintUnique::getTypeString() const
413 {
414 return "UNIQUE";
415 }
416
getDetails() const417 QString SqlQueryModelColumn::ConstraintUnique::getDetails() const
418 {
419 if (onConflict != SqliteConflictAlgo::null)
420 return "("+QObject::tr("on conflict: %1", "data view tooltip").arg(sqliteConflictAlgo(onConflict))+")";
421
422 return QString();
423 }
424
getIcon() const425 Icon* SqlQueryModelColumn::ConstraintUnique::getIcon() const
426 {
427 return ICONS.CONSTRAINT_UNIQUE;
428 }
429
getTypeString() const430 QString SqlQueryModelColumn::ConstraintNotNull::getTypeString() const
431 {
432 return "NOT NULL";
433 }
434
getDetails() const435 QString SqlQueryModelColumn::ConstraintNotNull::getDetails() const
436 {
437 if (onConflict != SqliteConflictAlgo::null)
438 return "("+QObject::tr("on conflict: %1", "data view tooltip").arg(sqliteConflictAlgo(onConflict))+")";
439
440 return QString();
441 }
442
getIcon() const443 Icon* SqlQueryModelColumn::ConstraintNotNull::getIcon() const
444 {
445 return ICONS.CONSTRAINT_NOT_NULL;
446 }
447
getTypeString() const448 QString SqlQueryModelColumn::ConstraintDefault::getTypeString() const
449 {
450 return "DEFAULT";
451 }
452
getDetails() const453 QString SqlQueryModelColumn::ConstraintDefault::getDetails() const
454 {
455 return "("+defaultValue+")";
456 }
457
getIcon() const458 Icon* SqlQueryModelColumn::ConstraintDefault::getIcon() const
459 {
460 return ICONS.CONSTRAINT_DEFAULT;
461 }
462
getTypeString() const463 QString SqlQueryModelColumn::ConstraintCheck::getTypeString() const
464 {
465 return "CHECK";
466 }
467
getDetails() const468 QString SqlQueryModelColumn::ConstraintCheck::getDetails() const
469 {
470 QStringList detailList;
471 detailList << QObject::tr("condition: %1", "data view tooltip").arg(condition);
472
473 if (onConflict != SqliteConflictAlgo::null)
474 detailList << QObject::tr("on conflict: %1", "data view tooltip").arg(sqliteConflictAlgo(onConflict));
475
476 return "("+detailList.join(", ")+")";
477 }
478
getIcon() const479 Icon* SqlQueryModelColumn::ConstraintCheck::getIcon() const
480 {
481 return ICONS.CONSTRAINT_CHECK;
482 }
483
getTypeString() const484 QString SqlQueryModelColumn::ConstraintCollate::getTypeString() const
485 {
486 return "COLLATE";
487 }
488
getDetails() const489 QString SqlQueryModelColumn::ConstraintCollate::getDetails() const
490 {
491 return "("+QObject::tr("collation name: %1", "data view tooltip").arg(collationName)+")";
492 }
493
getIcon() const494 Icon* SqlQueryModelColumn::ConstraintCollate::getIcon() const
495 {
496 return ICONS.CONSTRAINT_COLLATION;
497 }
498
getTypeString() const499 QString SqlQueryModelColumn::ConstraintGenerated::getTypeString() const
500 {
501 return "GENERATED";
502 }
503
getDetails() const504 QString SqlQueryModelColumn::ConstraintGenerated::getDetails() const
505 {
506 return "("+QObject::tr("generated column type: %1", "data view tooltip")
507 .arg(SqliteCreateTable::Column::Constraint::toString(generatedType))+")";
508 }
509
getIcon() const510 Icon* SqlQueryModelColumn::ConstraintGenerated::getIcon() const
511 {
512 return generatedType == SqliteCreateTable::Column::Constraint::GeneratedType::STORED ?
513 ICONS.CONSTRAINT_GENERATED_STORED : ICONS.CONSTRAINT_GENERATED_VIRTUAL;
514 }
515