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