KDb

KDbQuerySchema.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2003-2018 JarosÅ‚aw Staniek <[email protected]>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "KDbQuerySchema.h"
21 #include "KDbQuerySchema_p.h"
22 #include "KDbQueryAsterisk.h"
23 #include "KDbConnection.h"
24 #include "KDbConnection_p.h"
25 #include "kdb_debug.h"
26 #include "KDbLookupFieldSchema.h"
27 #include "KDbOrderByColumn.h"
28 #include "KDbParser_p.h"
29 #include "KDbQuerySchemaParameter.h"
30 #include "KDbRelationship.h"
31 
33  KDb::IdentifierEscapingType escapingType)
34 {
35  switch (escapingType) {
37  if (conn)
38  return conn->escapeIdentifier(name);
39  break;
40  case KDb::KDbEscaping:
41  return KDb::escapeIdentifier(name);
42  }
43  return QLatin1Char('"') + name + QLatin1Char('"');
44 }
45 
47  : KDbFieldList(false)//fields are not owned by KDbQuerySchema object
48  , KDbObject(KDb::QueryObjectType)
49  , d(new KDbQuerySchemaPrivate(this))
50 {
51 }
52 
54  : KDbFieldList(false)//fields are not owned by KDbQuerySchema object
55  , KDbObject(KDb::QueryObjectType)
56  , d(new KDbQuerySchemaPrivate(this))
57 {
58  if (tableSchema) {
59  d->masterTable = tableSchema;
60  /*if (!d->masterTable) {
61  kdbWarning() << "!d->masterTable";
62  m_name.clear();
63  return;
64  }*/
65  addTable(d->masterTable);
66  //defaults:
67  //inherit name from a table
68  setName(d->masterTable->name());
69  //inherit caption from a table
70  setCaption(d->masterTable->caption());
71 
72  // add explicit field list to avoid problems (e.g. with fields added outside of the app):
73  foreach(KDbField* f, *d->masterTable->fields()) {
74  addField(f);
75  }
76  }
77 }
78 
80  : KDbFieldList(querySchema, false /* !deepCopyFields */)
81  , KDbObject(querySchema)
82  , d(new KDbQuerySchemaPrivate(this, querySchema.d))
83 {
84  //only deep copy query asterisks
85  foreach(KDbField* f, *querySchema.fields()) {
86  KDbField *copiedField;
87  if (dynamic_cast<KDbQueryAsterisk*>(f)) {
88  copiedField = f->copy();
89  if (static_cast<const KDbFieldList *>(f->parent()) == &querySchema) {
90  copiedField->setParent(this);
91  }
92  }
93  else {
94  copiedField = f;
95  }
96  addField(copiedField);
97  }
98  // this deep copy must be after the 'd' initialization because fieldsExpanded() is used there
99  d->orderByColumnList = new KDbOrderByColumnList(*querySchema.d->orderByColumnList, conn,
100  const_cast<KDbQuerySchema*>(&querySchema), this);
101 }
102 
103 KDbQuerySchema::~KDbQuerySchema()
104 {
105  delete d;
106 }
107 
109 {
112  d->clear();
113 }
114 
115 /*virtual*/
116 bool KDbQuerySchema::insertField(int position, KDbField *field)
117 {
118  return insertFieldInternal(position, field, -1/*don't bind*/, true);
119 }
120 
122 {
123  return insertFieldInternal(position, field, -1/*don't bind*/, false);
124 }
125 
126 bool KDbQuerySchema::insertField(int position, KDbField *field, int bindToTable)
127 {
128  return insertFieldInternal(position, field, bindToTable, true);
129 }
130 
131 bool KDbQuerySchema::insertInvisibleField(int position, KDbField *field, int bindToTable)
132 {
133  return insertFieldInternal(position, field, bindToTable, false);
134 }
135 
137  int bindToTable, bool visible)
138 {
139  if (!field) {
140  kdbWarning() << "!field";
141  return false;
142  }
143 
144  if (position > fieldCount()) {
145  kdbWarning() << "position" << position << "out of range";
146  return false;
147  }
148  if (!field->isQueryAsterisk() && !field->isExpression() && !field->table()) {
149  kdbWarning() << "field" << field->name() << "must contain table information!";
150  return false;
151  }
152  if (fieldCount() >= d->visibility.size()) {
153  d->visibility.resize(d->visibility.size()*2);
154  d->tablesBoundToColumns.resize(d->tablesBoundToColumns.size()*2);
155  }
156  d->clearCachedData();
157  if (!KDbFieldList::insertField(position, field)) {
158  return false;
159  }
160  if (field->isQueryAsterisk()) {
161  d->asterisks.append(field);
162  //if this is single-table asterisk,
163  //add a table to list if doesn't exist there:
164  if (field->table() && !d->tables.contains(field->table()))
165  d->tables.append(field->table());
166  } else if (field->table()) {
167  //add a table to list if doesn't exist there:
168  if (!d->tables.contains(field->table()))
169  d->tables.append(field->table());
170  }
171  //update visibility
172  //--move bits to make a place for a new one
173  for (int i = fieldCount() - 1; i > position; i--)
174  d->visibility.setBit(i, d->visibility.testBit(i - 1));
175  d->visibility.setBit(position, visible);
176 
177  //bind to table
178  if (bindToTable < -1 || bindToTable > d->tables.count()) {
179  kdbWarning() << "bindToTable" << bindToTable << "out of range";
180  bindToTable = -1;
181  }
182  //--move items to make a place for a new one
183  for (int i = fieldCount() - 1; i > position; i--)
184  d->tablesBoundToColumns[i] = d->tablesBoundToColumns[i-1];
185  d->tablesBoundToColumns[position] = bindToTable;
186 
187 #ifdef KDB_QUERYSCHEMA_DEBUG
188  querySchemaDebug() << "bound to table" << bindToTable;
189  if (bindToTable == -1)
190  querySchemaDebug() << " <NOT SPECIFIED>";
191  else
192  querySchemaDebug() << " name=" << d->tables.at(bindToTable)->name()
193  << " alias=" << tableAlias(bindToTable);
194  QString s;
195  for (int i = 0; i < fieldCount();i++)
196  s += (QString::number(d->tablesBoundToColumns[i]) + QLatin1Char(' '));
197  querySchemaDebug() << "tablesBoundToColumns == [" << s << "]";
198 #endif
199 
200  if (field->isExpression())
201  d->regenerateExprAliases = true;
202 
203  return true;
204 }
205 
206 int KDbQuerySchema::tableBoundToColumn(int columnPosition) const
207 {
208  int res = d->tablesBoundToColumns.value(columnPosition, -99);
209  if (res == -99) {
210  kdbWarning() << "columnPosition" << columnPosition << "out of range";
211  return -1;
212  }
213  return res;
214 }
215 
217 {
218  return insertField(fieldCount(), field);
219 }
220 
221 bool KDbQuerySchema::addField(KDbField* field, int bindToTable)
222 {
223  return insertField(fieldCount(), field, bindToTable);
224 }
225 
226 bool KDbQuerySchema::addInvisibleField(KDbField* field)
227 {
229 }
230 
231 bool KDbQuerySchema::addInvisibleField(KDbField* field, int bindToTable)
232 {
233  return insertInvisibleField(fieldCount(), field, bindToTable);
234 }
235 
237 {
238  int indexOfAsterisk = -1;
239  if (field->isQueryAsterisk()) {
240  indexOfAsterisk = d->asterisks.indexOf(field);
241  }
243  return false;
244  }
245  d->clearCachedData();
246  if (indexOfAsterisk >= 0) {
247  //querySchemaDebug() << "d->asterisks.removeAt:" << field;
248  //field->debug();
249  d->asterisks.removeAt(indexOfAsterisk); //this will destroy this asterisk
250  }
251 //! @todo should we also remove table for this field or asterisk?
252  return true;
253 }
254 
256 {
257  KDbField *field = new KDbField(this, expr);
258  bool ok;
259  if (visible) {
260  ok = addField(field);
261  } else {
262  ok = addInvisibleField(field);
263  }
264  if (!ok) {
265  delete field;
266  }
267  d->ownedExpressionFields.append(field);
268  return ok;
269 }
270 
272 {
273  return addExpressionInternal(expr, true);
274 }
275 
276 bool KDbQuerySchema::addInvisibleExpression(const KDbExpression& expr)
277 {
278  return addExpressionInternal(expr, false);
279 }
280 
281 bool KDbQuerySchema::isColumnVisible(int position) const
282 {
283  return (position < fieldCount()) ? d->visibility.testBit(position) : false;
284 }
285 
286 void KDbQuerySchema::setColumnVisible(int position, bool visible)
287 {
288  if (position < fieldCount())
289  d->visibility.setBit(position, visible);
290 }
291 
293 {
294  if (!asterisk) {
295  return false;
296  }
297  //make unique name
298  asterisk->setName((asterisk->table() ? (asterisk->table()->name() + QLatin1String(".*"))
299  : QString(QLatin1Char('*')))
300  + QString::number(asterisks()->count()));
301  return visible ? addField(asterisk) : addInvisibleField(asterisk);
302 }
303 
305 {
306  return addAsteriskInternal(asterisk, true);
307 }
308 
309 bool KDbQuerySchema::addInvisibleAsterisk(KDbQueryAsterisk *asterisk)
310 {
311  return addAsteriskInternal(asterisk, false);
312 }
313 
314 QDebug operator<<(QDebug dbg, const KDbConnectionAndQuerySchema &connectionAndSchema)
315 {
316  KDbConnection* conn = std::get<0>(connectionAndSchema);
317  const KDbQuerySchema& query = std::get<1>(connectionAndSchema);
318  //fields
319  KDbTableSchema *mt = query.masterTable();
320  dbg.nospace() << "QUERY";
321  dbg.space() << static_cast<const KDbObject&>(query) << '\n';
322  dbg.nospace() << " - MASTERTABLE=" << (mt ? mt->name() : QLatin1String("<NULL>"))
323  << "\n - COLUMNS:\n";
324  if (query.fieldCount() > 0)
325  dbg.nospace() << static_cast<const KDbFieldList&>(query) << '\n';
326  else
327  dbg.nospace() << "<NONE>\n";
328 
329  if (query.fieldCount() == 0)
330  dbg.nospace() << " - NO FIELDS\n";
331  else
332  dbg.nospace() << " - FIELDS EXPANDED (";
333 
334  int fieldsExpandedCount = 0;
335  bool first;
336  if (query.fieldCount() > 0) {
337  const KDbQueryColumnInfo::Vector fe(query.fieldsExpanded(conn));
338  fieldsExpandedCount = fe.size();
339  dbg.nospace() << fieldsExpandedCount << "):\n";
340  first = true;
341  for (int i = 0; i < fieldsExpandedCount; i++) {
342  KDbQueryColumnInfo *ci = fe[i];
343  if (first)
344  first = false;
345  else
346  dbg.nospace() << ",\n";
347  dbg.nospace() << *ci;
348  }
349  dbg.nospace() << '\n';
350  }
351 
352  //it's safer to delete fieldsExpanded for now
353  // (debugString() could be called before all fields are added)
354 
355  //bindings
356  dbg.nospace() << " - BINDINGS:\n";
357  bool bindingsExist = false;
358  for (int i = 0; i < query.fieldCount(); i++) {
359  const int tablePos = query.tableBoundToColumn(i);
360  if (tablePos >= 0) {
361  const QString tAlias(query.tableAlias(tablePos));
362  if (!tAlias.isEmpty()) {
363  bindingsExist = true;
364  dbg.space() << "FIELD";
365  dbg.space() << static_cast<const KDbFieldList&>(query).field(i)->name();
366  dbg.space() << "USES ALIAS";
367  dbg.space() << tAlias;
368  dbg.space() << "OF TABLE";
369  dbg.space() << query.tables()->at(tablePos)->name() << '\n';
370  }
371  }
372  }
373  if (!bindingsExist) {
374  dbg.nospace() << "<NONE>\n";
375  }
376 
377  //tables
378  dbg.nospace() << " - TABLES:\n";
379  first = true;
380  foreach(KDbTableSchema *table, *query.tables()) {
381  if (first)
382  first = false;
383  else
384  dbg.nospace() << ",";
385  dbg.space() << table->name();
386  }
387  if (query.tables()->isEmpty())
388  dbg.nospace() << "<NONE>";
389 
390  //aliases
391  dbg.nospace() << "\n - COLUMN ALIASES:\n";
392  if (query.columnAliasesCount() == 0) {
393  dbg.nospace() << "<NONE>\n";
394  }
395  else {
396  int i = -1;
397  foreach(KDbField *f, *query.fields()) {
398  i++;
399  const QString alias(query.columnAlias(i));
400  if (!alias.isEmpty()) {
401  dbg.nospace() << QString::fromLatin1("FIELD #%1:").arg(i);
402  dbg.space() << (f->name().isEmpty()
403  ? QLatin1String("<NONAME>") : f->name()) << " -> " << alias << '\n';
404  }
405  }
406  }
407 
408  dbg.nospace() << "- TABLE ALIASES:\n";
409  if (query.tableAliasesCount() == 0) {
410  dbg.nospace() << "<NONE>\n";
411  }
412  else {
413  int i = -1;
414  foreach(KDbTableSchema* table, *query.tables()) {
415  i++;
416  const QString alias(query.tableAlias(i));
417  if (!alias.isEmpty()) {
418  dbg.nospace() << QString::fromLatin1("table #%1:").arg(i);
419  dbg.space() << (table->name().isEmpty()
420  ? QLatin1String("<NONAME>") : table->name()) << " -> " << alias << '\n';
421  }
422  }
423  }
424  if (!query.whereExpression().isNull()) {
425  dbg.nospace() << " - WHERE EXPRESSION:\n" << query.whereExpression() << '\n';
426  }
427  dbg.nospace() << qPrintable(QString::fromLatin1(" - ORDER BY (%1):\n").arg(query.orderByColumnList()->count()));
428  if (query.orderByColumnList()->isEmpty()) {
429  dbg.nospace() << "<NONE>\n";
430  } else {
431  dbg.nospace() << *query.orderByColumnList();
432  }
433  return dbg.nospace();
434 }
435 
437 {
438  if (d->masterTable)
439  return d->masterTable;
440  if (d->tables.isEmpty())
441  return nullptr;
442 
443  //try to find master table if there's only one table (with possible aliasses)
444  QString tableNameLower;
445  int num = -1;
446  foreach(KDbTableSchema *table, d->tables) {
447  num++;
448  if (!tableNameLower.isEmpty() && table->name().toLower() != tableNameLower) {
449  //two or more different tables
450  return nullptr;
451  }
452  tableNameLower = tableAlias(num);
453  }
454  return d->tables.first();
455 }
456 
458 {
459  if (table)
460  d->masterTable = table;
461 }
462 
464 {
465  return &d->tables;
466 }
467 
469 {
470  querySchemaDebug() << (void *)table << "alias=" << alias;
471  if (!table)
472  return;
473 
474  // only append table if: it has alias or it has no alias but there is no such table on the list
475  if (alias.isEmpty() && d->tables.contains(table)) {
476  int num = -1;
477  foreach(KDbTableSchema *t, d->tables) {
478  num++;
479  if (0 == t->name().compare(table->name(), Qt::CaseInsensitive)) {
480  if (tableAlias(num).isEmpty()) {
481  querySchemaDebug() << "table" << table->name() << "without alias already added";
482  return;
483  }
484  }
485  }
486  }
487  d->tables.append(table);
488  if (!alias.isEmpty())
489  setTableAlias(d->tables.count() - 1, alias);
490 }
491 
493 {
494  if (!table)
495  return;
496  if (d->masterTable == table)
497  d->masterTable = nullptr;
498  d->tables.removeAt(d->tables.indexOf(table));
499 //! @todo remove fields!
500 }
501 
503 {
504 //! @todo maybe use tables_byname?
505  foreach(KDbTableSchema *table, d->tables) {
506  if (0 == table->name().compare(tableName, Qt::CaseInsensitive)) {
507  return table;
508  }
509  }
510  return nullptr;
511 }
512 
514 {
515  return d->tables.contains(table);
516 }
517 
518 KDbField* KDbQuerySchema::findTableField(const QString &fieldOrTableAndFieldName) const
519 {
520  QString tableName, fieldName;
521  if (!KDb::splitToTableAndFieldParts(fieldOrTableAndFieldName,
522  &tableName, &fieldName,
524  return nullptr;
525  }
526  if (tableName.isEmpty()) {
527  foreach(KDbTableSchema *table, d->tables) {
528  if (table->field(fieldName))
529  return table->field(fieldName);
530  }
531  return nullptr;
532  }
533  KDbTableSchema *tableSchema = table(tableName);
534  if (!tableSchema)
535  return nullptr;
536  return tableSchema->field(fieldName);
537 }
538 
540 {
541  return d->columnAliasesCount();
542 }
543 
545 {
546  return d->columnAlias(position);
547 }
548 
549 bool KDbQuerySchema::hasColumnAlias(int position) const
550 {
551  return d->hasColumnAlias(position);
552 }
553 
554 bool KDbQuerySchema::setColumnAlias(int position, const QString& alias)
555 {
556  if (position >= fieldCount()) {
557  kdbWarning() << "position" << position << "out of range!";
558  return false;
559  }
560  const QString fixedAlias(alias.trimmed());
561  KDbField *f = KDbFieldList::field(position);
562  if (f->captionOrName().isEmpty() && fixedAlias.isEmpty()) {
563  kdbWarning() << "position" << position << "could not remove alias when no name is specified for expression column!";
564  return false;
565  }
566  return d->setColumnAlias(position, fixedAlias);
567 }
568 
570 {
571  return d->tableAliases.count();
572 }
573 
575 {
576  return d->tableAliases.value(position);
577 }
578 
580 {
581  const int pos = tablePosition(tableName);
582  if (pos == -1) {
583  return QString();
584  }
585  return d->tableAliases.value(pos);
586 }
587 
589 {
590  const int pos = tablePosition(tableName);
591  if (pos == -1) {
592  return QString();
593  }
594  return KDb::iifNotEmpty(d->tableAliases.value(pos), tableName);
595 }
596 
598 {
599  return d->tablePositionForAlias(name);
600 }
601 
602 int KDbQuerySchema::tablePosition(const QString& tableName) const
603 {
604  int num = -1;
605  foreach(KDbTableSchema* table, d->tables) {
606  num++;
607  if (0 == table->name().compare(tableName, Qt::CaseInsensitive)) {
608  return num;
609  }
610  }
611  return -1;
612 }
613 
615 {
616  QList<int> result;
617  int num = -1;
618  foreach(KDbTableSchema* table, d->tables) {
619  num++;
620  if (0 == table->name().compare(tableName, Qt::CaseInsensitive)) {
621  result += num;
622  }
623  }
624  return result;
625 }
626 
627 bool KDbQuerySchema::hasTableAlias(int position) const
628 {
629  return d->tableAliases.contains(position);
630 }
631 
632 bool KDbQuerySchema::hasTableAlias(const QString &name) const
633 {
634  return d->tablePositionForAlias(name) != -1;
635 }
636 
638 {
639  return d->columnPositionForAlias(name);
640 }
641 
643 {
644  return d->columnPositionForAlias(name) != -1;
645 }
646 
647 bool KDbQuerySchema::setTableAlias(int position, const QString& alias)
648 {
649  if (position >= d->tables.count()) {
650  kdbWarning() << "position" << position << "out of range!";
651  return false;
652  }
653  const QString fixedAlias(alias.trimmed());
654  if (fixedAlias.isEmpty()) {
655  const QString oldAlias(d->tableAliases.take(position));
656  if (!oldAlias.isEmpty()) {
657  d->removeTablePositionForAlias(oldAlias);
658  }
659  return true;
660  }
661  return d->setTableAlias(position, fixedAlias);
662 }
663 
665 {
666  return &d->relations;
667 }
668 
670 {
671  return &d->asterisks;
672 }
673 
675 {
676  return d->sql;
677 }
678 
680 {
681  d->sql = sql;
682 }
683 
684 const KDbField* KDbQuerySchema::field(KDbConnection *conn, const QString& identifier,
685  ExpandMode mode) const
686 {
687  KDbQueryColumnInfo *ci = columnInfo(conn, identifier, mode);
688  return ci ? ci->field() : nullptr;
689 }
690 
692 {
693  return const_cast<KDbField *>(
694  static_cast<const KDbQuerySchema *>(this)->field(conn, identifier, mode));
695 }
696 
698 {
699  return KDbFieldList::field(id);
700 }
701 
702 const KDbField* KDbQuerySchema::field(int id) const
703 {
704  return KDbFieldList::field(id);
705 }
706 
708  ExpandMode mode) const
709 {
710  const KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn);
711  return mode == ExpandMode::Expanded ? cache->columnInfosByNameExpanded.value(identifier)
712  : cache->columnInfosByName.value(identifier);
713 }
714 
716  KDbConnection *conn, FieldsExpandedMode mode, bool onlyVisible) const
717 {
718  if (!conn) {
719  kdbWarning() << "Connection required";
721  }
722  KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn);
723  const KDbQueryColumnInfo::Vector *realFieldsExpanded
724  = onlyVisible ? &cache->visibleFieldsExpanded : &cache->fieldsExpanded;
727  {
728  //a ref to a proper pointer (as we cache the vector for two cases)
729  KDbQueryColumnInfo::Vector& tmpFieldsExpandedWithInternal =
731  (onlyVisible ? cache->visibleFieldsExpandedWithInternal : cache->fieldsExpandedWithInternal)
732  : (onlyVisible ? cache->visibleFieldsExpandedWithInternalAndRecordId : cache->fieldsExpandedWithInternalAndRecordId);
733  //special case
734  if (tmpFieldsExpandedWithInternal.isEmpty()) {
735  //glue expanded and internal fields and cache it
736  const int internalFieldCount = cache->internalFields.size();
737  const int fieldsExpandedVectorSize = realFieldsExpanded->size();
738  const int size = fieldsExpandedVectorSize + internalFieldCount
739  + ((mode == FieldsExpandedMode::WithInternalFieldsAndRecordId) ? 1 : 0) /*ROWID*/;
740  tmpFieldsExpandedWithInternal.resize(size);
741  for (int i = 0; i < fieldsExpandedVectorSize; ++i) {
742  tmpFieldsExpandedWithInternal[i] = realFieldsExpanded->at(i);
743  }
744  if (internalFieldCount > 0) {
745  for (int i = 0; i < internalFieldCount; ++i) {
746  KDbQueryColumnInfo *info = cache->internalFields[i];
747  tmpFieldsExpandedWithInternal[fieldsExpandedVectorSize + i] = info;
748  }
749  }
751  if (!d->fakeRecordIdField) {
752  d->fakeRecordIdField = new KDbField(QLatin1String("rowID"), KDbField::BigInteger);
753  d->fakeRecordIdCol = new KDbQueryColumnInfo(d->fakeRecordIdField, QString(), true);
754  d->fakeRecordIdCol->d->querySchema = this;
755  d->fakeRecordIdCol->d->connection = conn;
756  }
757  tmpFieldsExpandedWithInternal[fieldsExpandedVectorSize + internalFieldCount] = d->fakeRecordIdCol;
758  }
759  }
760  return tmpFieldsExpandedWithInternal;
761  }
762 
763  if (mode == FieldsExpandedMode::Default) {
764  return *realFieldsExpanded;
765  }
766 
767  //mode == Unique:
768  QSet<QString> columnsAlreadyFound;
769  const int fieldsExpandedCount(realFieldsExpanded->count());
770  KDbQueryColumnInfo::Vector result(fieldsExpandedCount); //initial size is set
771  //compute unique list
772  int uniqueListCount = 0;
773  for (int i = 0; i < fieldsExpandedCount; i++) {
774  KDbQueryColumnInfo *ci = realFieldsExpanded->at(i);
775  if (!columnsAlreadyFound.contains(ci->aliasOrName())) {
776  columnsAlreadyFound.insert(ci->aliasOrName());
777  result[uniqueListCount++] = ci;
778  }
779  }
780  result.resize(uniqueListCount); //update result size
781  return result;
782 }
783 
785 {
786  KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn);
787  return cache->internalFields;
788 }
789 
791 {
793 }
794 
795 inline static QString lookupColumnKey(KDbField *foreignField, KDbField* field)
796 {
797  QString res;
798  if (field->table()) // can be 0 for anonymous fields built as joined multiple visible columns
799  res = field->table()->name() + QLatin1Char('.');
800  return res + field->name() + QLatin1Char('_') + foreignField->table()->name()
801  + QLatin1Char('.') + foreignField->name();
802 }
803 
804 KDbQuerySchemaFieldsExpanded *KDbQuerySchema::computeFieldsExpanded(KDbConnection *conn) const
805 {
806  KDbQuerySchemaFieldsExpanded *cache = conn->d->fieldsExpanded(this);
807  if (cache) {
808  return cache;
809  }
810  cache = new KDbQuerySchemaFieldsExpanded;
812 
813  //collect all fields in a list (not a vector yet, because we do not know its size)
814  KDbQueryColumnInfo::List list; //temporary
815  KDbQueryColumnInfo::List lookup_list; //temporary, for collecting additional fields related to lookup fields
816  QHash<KDbQueryColumnInfo*, bool> columnInfosOutsideAsterisks; //helper for filling d->columnInfosByName
817  int i = 0;
818  int numberOfColumnsWithMultipleVisibleFields = 0; //used to find an unique name for anonymous field
819  int fieldPosition = -1;
820  for (KDbField *f : *fields()) {
821  fieldPosition++;
822  if (f->isQueryAsterisk()) {
823  if (static_cast<KDbQueryAsterisk*>(f)->isSingleTableAsterisk()) {
824  const KDbField::List *ast_fields = static_cast<KDbQueryAsterisk*>(f)->table()->fields();
825  foreach(KDbField *ast_f, *ast_fields) {
826  KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(ast_f, QString()/*no field for asterisk!*/,
827  isColumnVisible(fieldPosition));
828  ci->d->querySchema = this;
829  ci->d->connection = conn;
830  list.append(ci);
831  querySchemaDebug() << "caching (unexpanded) columns order:" << *ci
832  << "at position" << fieldPosition;
833  cache->columnsOrder.insert(ci, fieldPosition);
834  }
835  } else {//all-tables asterisk: iterate through table list
836  foreach(KDbTableSchema *table, d->tables) {
837  //add all fields from this table
838  const KDbField::List *tab_fields = table->fields();
839  foreach(KDbField *tab_f, *tab_fields) {
840 //! @todo (js): perhaps not all fields should be appended here
841 // d->detailedVisibility += isFieldVisible(fieldPosition);
842 // list.append(tab_f);
843  KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(tab_f, QString()/*no field for asterisk!*/,
844  isColumnVisible(fieldPosition));
845  ci->d->querySchema = this;
846  ci->d->connection = conn;
847  list.append(ci);
848  querySchemaDebug() << "caching (unexpanded) columns order:" << *ci
849  << "at position" << fieldPosition;
850  cache->columnsOrder.insert(ci, fieldPosition);
851  }
852  }
853  }
854  } else {
855  //a single field
856  KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(f, columnAlias(fieldPosition), isColumnVisible(fieldPosition));
857  ci->d->querySchema = this;
858  ci->d->connection = conn;
859  list.append(ci);
860  columnInfosOutsideAsterisks.insert(ci, true);
861  querySchemaDebug() << "caching (unexpanded) column's order:" << *ci << "at position"
862  << fieldPosition;
863  cache->columnsOrder.insert(ci, fieldPosition);
864  cache->columnsOrderWithoutAsterisks.insert(ci, fieldPosition);
865 
866  //handle lookup field schema
867  KDbLookupFieldSchema *lookupFieldSchema = f->table() ? f->table()->lookupFieldSchema(*f) : nullptr;
868  if (!lookupFieldSchema || lookupFieldSchema->boundColumn() < 0)
869  continue;
870  // Lookup field schema found:
871  // Now we also need to fetch "visible" value from the lookup table, not only the value of binding.
872  // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken)
873  // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField"
874  KDbLookupFieldSchemaRecordSource recordSource = lookupFieldSchema->recordSource();
875  if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Table) {
876  KDbTableSchema *lookupTable = conn->tableSchema(recordSource.name());
877  KDbFieldList* visibleColumns = nullptr;
878  KDbField *boundField = nullptr;
879  if (lookupTable
880  && lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
881  && (visibleColumns = lookupTable->subList(lookupFieldSchema->visibleColumns()))
882  && (boundField = lookupTable->field(lookupFieldSchema->boundColumn()))) {
883  KDbField *visibleColumn = nullptr;
884  // for single visible column, just add it as-is
885  if (visibleColumns->fieldCount() == 1) {
886  visibleColumn = visibleColumns->fields()->first();
887  } else {
888  // for multiple visible columns, build an expression column
889  // (the expression object will be owned by column info)
890  visibleColumn = new KDbField();
891  visibleColumn->setName(
892  QString::fromLatin1("[multiple_visible_fields_%1]")
893  .arg(++numberOfColumnsWithMultipleVisibleFields));
894  visibleColumn->setExpression(
895  KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QVariant()/*not important*/));
896  cache->ownedVisibleFields.append(visibleColumn); // remember to delete later
897  }
898 
899  KDbQueryColumnInfo *lookupCi = new KDbQueryColumnInfo(
900  visibleColumn, QString(), true /*visible*/, ci /*foreign*/);
901  lookupCi->d->querySchema = this;
902  lookupCi->d->connection = conn;
903  lookup_list.append(lookupCi);
904  /*
905  //add visibleField to the list of SELECTed fields if it is not yes present there
906  if (!findTableField( visibleField->table()->name()+"."+visibleField->name() )) {
907  if (!table( visibleField->table()->name() )) {
908  }
909  if (!sql.isEmpty())
910  sql += QString::fromLatin1(", ");
911  sql += (escapeIdentifier(visibleField->table()->name(), drvEscaping) + "."
912  + escapeIdentifier(visibleField->name(), drvEscaping));
913  }*/
914  }
915  delete visibleColumns;
916  } else if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Query) {
917  KDbQuerySchema *lookupQuery = conn->querySchema(recordSource.name());
918  if (!lookupQuery)
919  continue;
920  const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded(
921  lookupQuery->fieldsExpanded(conn));
922  if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count())
923  continue;
924  KDbQueryColumnInfo *boundColumnInfo = nullptr;
925  if (!(boundColumnInfo = lookupQueryFieldsExpanded.value(lookupFieldSchema->boundColumn())))
926  continue;
927  KDbField *boundField = boundColumnInfo->field();
928  if (!boundField)
929  continue;
930  const QList<int> visibleColumns(lookupFieldSchema->visibleColumns());
931  bool ok = true;
932  // all indices in visibleColumns should be in [0..lookupQueryFieldsExpanded.size()-1]
933  foreach(int visibleColumn, visibleColumns) {
934  if (visibleColumn >= lookupQueryFieldsExpanded.count()) {
935  ok = false;
936  break;
937  }
938  }
939  if (!ok)
940  continue;
941  KDbField *visibleColumn = nullptr;
942  // for single visible column, just add it as-is
943  if (visibleColumns.count() == 1) {
944  visibleColumn = lookupQueryFieldsExpanded.value(visibleColumns.first())->field();
945  } else {
946  // for multiple visible columns, build an expression column
947  // (the expression object will be owned by column info)
948  visibleColumn = new KDbField();
949  visibleColumn->setName(
950  QString::fromLatin1("[multiple_visible_fields_%1]")
951  .arg(++numberOfColumnsWithMultipleVisibleFields));
952  visibleColumn->setExpression(
953  KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QVariant()/*not important*/));
954  cache->ownedVisibleFields.append(visibleColumn); // remember to delete later
955  }
956 
957  KDbQueryColumnInfo *lookupCi = new KDbQueryColumnInfo(
958  visibleColumn, QString(), true /*visible*/, ci /*foreign*/);
959  lookupCi->d->querySchema = this;
960  lookupCi->d->connection = conn;
961  lookup_list.append(lookupCi);
962  /*
963  //add visibleField to the list of SELECTed fields if it is not yes present there
964  if (!findTableField( visibleField->table()->name()+"."+visibleField->name() )) {
965  if (!table( visibleField->table()->name() )) {
966  }
967  if (!sql.isEmpty())
968  sql += QString::fromLatin1(", ");
969  sql += (escapeIdentifier(visibleField->table()->name(), drvEscaping) + "."
970  + escapeIdentifier(visibleField->name(), drvEscaping));
971  }*/
972  }
973  }
974  }
975  //prepare clean vector for expanded list, and a map for order information
976  cache->fieldsExpanded.resize(list.count());
977  cache->visibleFieldsExpanded.resize(list.count());
978 
979  /*fill (based on prepared 'list' and 'lookup_list'):
980  -the vector
981  -the map
982  -"fields by name" dictionary
983  */
984  i = -1;
985  int visibleIndex = -1;
986  foreach(KDbQueryColumnInfo* ci, list) {
987  i++;
988  cache->fieldsExpanded[i] = ci;
989  if (ci->isVisible()) {
990  ++visibleIndex;
991  cache->visibleFieldsExpanded[visibleIndex] = ci;
992  }
993  cache->columnsOrderExpanded.insert(ci, i);
994  //remember field by name/alias/table.name if there's no such string yet in d->columnInfosByNameExpanded
995  if (!ci->alias().isEmpty()) {
996  //store alias and table.alias
997  if (!cache->columnInfosByNameExpanded.contains(ci->alias())) {
998  cache->columnInfosByNameExpanded.insert(ci->alias(), ci);
999  }
1000  QString tableAndAlias(ci->alias());
1001  if (ci->field()->table())
1002  tableAndAlias.prepend(ci->field()->table()->name() + QLatin1Char('.'));
1003  if (!cache->columnInfosByNameExpanded.contains(tableAndAlias)) {
1004  cache->columnInfosByNameExpanded.insert(tableAndAlias, ci);
1005  }
1006  //the same for "unexpanded" list
1007  if (columnInfosOutsideAsterisks.contains(ci)) {
1008  if (!cache->columnInfosByName.contains(ci->alias())) {
1009  cache->columnInfosByName.insert(ci->alias(), ci);
1010  }
1011  if (!cache->columnInfosByName.contains(tableAndAlias)) {
1012  cache->columnInfosByName.insert(tableAndAlias, ci);
1013  }
1014  }
1015  } else {
1016  //no alias: store name and table.name
1017  if (!cache->columnInfosByNameExpanded.contains(ci->field()->name())) {
1018  cache->columnInfosByNameExpanded.insert(ci->field()->name(), ci);
1019  }
1020  QString tableAndName(ci->field()->name());
1021  if (ci->field()->table())
1022  tableAndName.prepend(ci->field()->table()->name() + QLatin1Char('.'));
1023  if (!cache->columnInfosByNameExpanded.contains(tableAndName)) {
1024  cache->columnInfosByNameExpanded.insert(tableAndName, ci);
1025  }
1026  //the same for "unexpanded" list
1027  if (columnInfosOutsideAsterisks.contains(ci)) {
1028  if (!cache->columnInfosByName.contains(ci->field()->name())) {
1029  cache->columnInfosByName.insert(ci->field()->name(), ci);
1030  }
1031  if (!cache->columnInfosByName.contains(tableAndName)) {
1032  cache->columnInfosByName.insert(tableAndName, ci);
1033  }
1034  }
1035  }
1036  }
1037  cache->visibleFieldsExpanded.resize(visibleIndex + 1);
1038 
1039  //remove duplicates for lookup fields
1040  QHash<QString, int> lookup_dict; //used to fight duplicates and to update KDbQueryColumnInfo::indexForVisibleLookupValue()
1041  // (a mapping from table.name string to int* lookupFieldIndex
1042  i = 0;
1043  for (QMutableListIterator<KDbQueryColumnInfo*> it(lookup_list); it.hasNext();) {
1044  KDbQueryColumnInfo* ci = it.next();
1045  const QString key(lookupColumnKey(ci->foreignColumn()->field(), ci->field()));
1046  if (lookup_dict.contains(key)) {
1047  // this table.field is already fetched by this query
1048  it.remove();
1049  delete ci;
1050  } else {
1051  lookup_dict.insert(key, i);
1052  i++;
1053  }
1054  }
1055 
1056  //create internal expanded list with lookup fields
1057  cache->internalFields.resize(lookup_list.count());
1058  i = -1;
1059  foreach(KDbQueryColumnInfo *ci, lookup_list) {
1060  i++;
1061  //add it to the internal list
1062  cache->internalFields[i] = ci;
1063  cache->columnsOrderExpanded.insert(ci, list.count() + i);
1064  }
1065 
1066  //update KDbQueryColumnInfo::indexForVisibleLookupValue() cache for columns
1067  numberOfColumnsWithMultipleVisibleFields = 0;
1068  for (i = 0; i < cache->fieldsExpanded.size(); i++) {
1069  KDbQueryColumnInfo* ci = cache->fieldsExpanded[i];
1070 //! @todo KDbQuerySchema itself will also support lookup fields...
1071  KDbLookupFieldSchema *lookupFieldSchema
1072  = ci->field()->table() ? ci->field()->table()->lookupFieldSchema(*ci->field()) : nullptr;
1073  if (!lookupFieldSchema || lookupFieldSchema->boundColumn() < 0)
1074  continue;
1075  const KDbLookupFieldSchemaRecordSource recordSource = lookupFieldSchema->recordSource();
1076  if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Table) {
1077  KDbTableSchema *lookupTable = conn->tableSchema(recordSource.name());
1078  KDbFieldList* visibleColumns = nullptr;
1079  if (lookupTable
1080  && lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
1081  && (visibleColumns = lookupTable->subList(lookupFieldSchema->visibleColumns()))) {
1082  // for single visible column, just add it as-is
1083  if (visibleColumns->fieldCount() == 1) {
1084  KDbField *visibleColumn = visibleColumns->fields()->first();
1085  const QString key(lookupColumnKey(ci->field(), visibleColumn));
1086  int index = lookup_dict.value(key, -99);
1087  if (index != -99)
1088  ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index);
1089  } else {
1090  const QString key(QString::fromLatin1("[multiple_visible_fields_%1]_%2.%3")
1091  .arg(++numberOfColumnsWithMultipleVisibleFields)
1092  .arg(ci->field()->table()->name(), ci->field()->name()));
1093  int index = lookup_dict.value(key, -99);
1094  if (index != -99)
1095  ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index);
1096  }
1097  }
1098  delete visibleColumns;
1099  } else if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Query) {
1100  KDbQuerySchema *lookupQuery = conn->querySchema(recordSource.name());
1101  if (!lookupQuery)
1102  continue;
1103  const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded(
1104  lookupQuery->fieldsExpanded(conn));
1105  if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count())
1106  continue;
1107  KDbQueryColumnInfo *boundColumnInfo = nullptr;
1108  if (!(boundColumnInfo = lookupQueryFieldsExpanded.value(lookupFieldSchema->boundColumn())))
1109  continue;
1110  KDbField *boundField = boundColumnInfo->field();
1111  if (!boundField)
1112  continue;
1113  const QList<int> visibleColumns(lookupFieldSchema->visibleColumns());
1114  // for single visible column, just add it as-is
1115  if (visibleColumns.count() == 1) {
1116  if (lookupQueryFieldsExpanded.count() > visibleColumns.first()) { // sanity check
1117  KDbField *visibleColumn = lookupQueryFieldsExpanded.at(visibleColumns.first())->field();
1118  const QString key(lookupColumnKey(ci->field(), visibleColumn));
1119  int index = lookup_dict.value(key, -99);
1120  if (index != -99)
1121  ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index);
1122  }
1123  } else {
1124  const QString key(QString::fromLatin1("[multiple_visible_fields_%1]_%2.%3")
1125  .arg(++numberOfColumnsWithMultipleVisibleFields)
1126  .arg(ci->field()->table()->name(), ci->field()->name()));
1127  int index = lookup_dict.value(key, -99);
1128  if (index != -99)
1129  ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index);
1130  }
1131  } else {
1132  kdbWarning() << "unsupported record source type" << recordSource.typeName();
1133  }
1134  }
1135  if (d->recentConnection != conn) {
1136  if (d->recentConnection) {
1137  // connection changed: remove old cache
1138  d->recentConnection->d->removeFieldsExpanded(this);
1139  }
1140  d->recentConnection = conn;
1141  }
1142  conn->d->insertFieldsExpanded(this, guard.take());
1143  return cache;
1144 }
1145 
1147  ColumnsOrderMode mode) const
1148 {
1149  KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn);
1150  if (mode == ColumnsOrderMode::UnexpandedList) {
1151  return cache->columnsOrder;
1153  return cache->columnsOrderWithoutAsterisks;
1154  }
1155  return cache->columnsOrderExpanded;
1156 }
1157 
1159 {
1160  if (d->pkeyFieldsOrder)
1161  return *d->pkeyFieldsOrder;
1162 
1163  KDbTableSchema *tbl = masterTable();
1164  if (!tbl || !tbl->primaryKey())
1165  return QVector<int>();
1166 
1167  //get order of PKEY fields (e.g. for records updating or inserting )
1168  KDbIndexSchema *pkey = tbl->primaryKey();
1169  querySchemaDebug() << *pkey;
1170  d->pkeyFieldsOrder = new QVector<int>(pkey->fieldCount(), -1);
1171 
1172  d->pkeyFieldCount = 0;
1174  const int fCount = fieldsExpanded.count();
1175  for (int i = 0; i < fCount; i++) {
1176  const KDbQueryColumnInfo *fi = fieldsExpanded[i];
1177  const int fieldIndex = fi->field()->table() == tbl ? pkey->indexOf(*fi->field()) : -1;
1178  if (fieldIndex != -1 /* field found in PK */
1179  && d->pkeyFieldsOrder->at(fieldIndex) == -1 /* first time */)
1180  {
1181  querySchemaDebug() << "FIELD" << fi->field()->name() << "IS IN PKEY AT POSITION #"
1182  << fieldIndex;
1183  (*d->pkeyFieldsOrder)[fieldIndex] = i;
1184  d->pkeyFieldCount++;
1185  }
1186  }
1187  querySchemaDebug() << d->pkeyFieldCount << " OUT OF " << pkey->fieldCount()
1188  << " PKEY'S FIELDS FOUND IN QUERY " << name();
1189  return *d->pkeyFieldsOrder;
1190 }
1191 
1193 {
1194  (void)pkeyFieldsOrder(conn); /* rebuild information */
1195  return d->pkeyFieldCount;
1196 }
1197 
1198 KDbRelationship* KDbQuerySchema::addRelationship(KDbField *field1, KDbField *field2)
1199 {
1200 //@todo: find existing global db relationships
1201  KDbRelationship *r = new KDbRelationship(this, field1, field2);
1202  if (r->isEmpty()) {
1203  delete r;
1204  return nullptr;
1205  }
1206 
1207  d->relations.append(r);
1208  return r;
1209 }
1210 
1212 {
1213  if (!d->autoincFields) {
1214  d->autoincFields = new KDbQueryColumnInfo::List();
1215  }
1216  KDbTableSchema *mt = masterTable();
1217  if (!mt) {
1218  kdbWarning() << "no master table!";
1219  return d->autoincFields;
1220  }
1221  if (d->autoincFields->isEmpty()) {//no cache
1223  for (int i = 0; i < fieldsExpanded.count(); i++) {
1225  if (ci->field()->table() == mt && ci->field()->isAutoIncrement()) {
1226  d->autoincFields->append(ci);
1227  }
1228  }
1229  }
1230  return d->autoincFields;
1231 }
1232 
1233 // static
1235  KDbConnection *conn,
1236  KDb::IdentifierEscapingType escapingType)
1237 {
1238  KDbEscapedString result;
1239  result.reserve(256);
1240  bool start = true;
1241  foreach(KDbQueryColumnInfo* ci, infolist) {
1242  if (!start)
1243  result += ",";
1244  else
1245  start = false;
1246  result += escapeIdentifier(ci->field()->name(), conn, escapingType);
1247  }
1248  return result;
1249 }
1250 
1252 {
1253 // QWeakPointer<const KDbDriver> driverWeakPointer
1254 // = DriverManagerInternal::self()->driverWeakPointer(*conn->driver());
1255  if ( /*d->lastUsedDriverForAutoIncrementSQLFieldsList != driverWeakPointer
1256  ||*/ d->autoIncrementSqlFieldsList.isEmpty())
1257  {
1258  d->autoIncrementSqlFieldsList = KDbQuerySchema::sqlColumnsList(*autoIncrementFields(conn), conn);
1259  //d->lastUsedDriverForAutoIncrementSQLFieldsList = driverWeakPointer;
1260  }
1261  return d->autoIncrementSqlFieldsList;
1262 }
1263 
1264 static void setResult(const KDbParseInfoInternal &parseInfo,
1265  QString *errorMessage, QString *errorDescription)
1266 {
1267  if (errorMessage) {
1268  *errorMessage = parseInfo.errorMessage();
1269  }
1270  if (errorDescription) {
1271  *errorDescription = parseInfo.errorDescription();
1272  }
1273 }
1274 
1276  QString *errorDescription)
1277 {
1278  KDbExpression newWhereExpr = expr.clone();
1279  KDbParseInfoInternal parseInfo(this);
1280  QString tempErrorMessage;
1281  QString tempErrorDescription;
1282  QString *errorMessagePointer = errorMessage ? errorMessage : &tempErrorMessage;
1283  QString *errorDescriptionPointer
1284  = errorDescription ? errorDescription : &tempErrorDescription;
1285  if (!newWhereExpr.validate(&parseInfo)) {
1286  setResult(parseInfo, errorMessagePointer, errorDescription);
1287  kdbWarning() << "message=" << *errorMessagePointer
1288  << "description=" << *errorDescriptionPointer;
1289  kdbWarning() << newWhereExpr;
1290  d->whereExpr = KDbExpression();
1291  return false;
1292  }
1293  errorMessagePointer->clear();
1294  errorDescriptionPointer->clear();
1295  KDbQuerySchemaPrivate::setWhereExpressionInternal(this, newWhereExpr);
1296  return true;
1297 }
1298 
1300  KDbToken relation, QString *errorMessage,
1301  QString *errorDescription)
1302 {
1303  KDbToken token;
1304  if (value.isNull()) {
1305  token = KDbToken::SQL_NULL;
1306  } else {
1307  const KDbField::Type type = field->type(); // cache: evaluating type of expressions can be expensive
1309  token = KDbToken::INTEGER_CONST;
1310  } else if (KDbField::isFPNumericType(type)) {
1311  token = KDbToken::REAL_CONST;
1312  } else {
1313  token = KDbToken::CHARACTER_STRING_LITERAL;
1314  }
1315 //! @todo date, time
1316  }
1317 
1318  KDbBinaryExpression newExpr(
1319  KDbConstExpression(token, value),
1320  relation,
1321  KDbVariableExpression((field->table() ? (field->table()->name() + QLatin1Char('.')) : QString()) + field->name())
1322  );
1323  const KDbExpression origWhereExpr = d->whereExpr;
1324  if (!d->whereExpr.isNull()) {
1325  newExpr = KDbBinaryExpression(
1326  d->whereExpr,
1327  KDbToken::AND,
1328  newExpr
1329  );
1330  }
1331  const bool result = setWhereExpression(newExpr, errorMessage, errorDescription);
1332  if (!result) { // revert, setWhereExpression() cleared it
1333  d->whereExpr = origWhereExpr;
1334  }
1335  return result;
1336 }
1337 
1338 /*
1339 void KDbQuerySchema::addToWhereExpression(KDbField *field, const QVariant& value)
1340  switch (value.type()) {
1341  case Int: case UInt: case Bool: case LongLong: case ULongLong:
1342  token = INTEGER_CONST;
1343  break;
1344  case Double:
1345  token = REAL_CONST;
1346  break;
1347  default:
1348  token = CHARACTER_STRING_LITERAL;
1349  }
1350 //! @todo date, time
1351 
1352 */
1353 
1355 {
1356  return d->whereExpr;
1357 }
1358 
1360 {
1361  delete d->orderByColumnList;
1362  d->orderByColumnList = new KDbOrderByColumnList(list, nullptr, nullptr, nullptr);
1363 // all field names should be found, exit otherwise ..........?
1364 }
1365 
1367 {
1368  return d->orderByColumnList;
1369 }
1370 
1372 {
1373  return d->orderByColumnList;
1374 }
1375 
1377 {
1380  for (int i = 0; i < fieldsExpanded.count(); ++i) {
1382  if (!ci->field()->expression().isNull()) {
1383  ci->field()->expression().getQueryParameters(&params);
1384  }
1385  }
1386  KDbExpression where = whereExpression();
1387  if (!where.isNull()) {
1388  where.getQueryParameters(&params);
1389  }
1390  return params;
1391 }
1392 
1393 bool KDbQuerySchema::validate(QString *errorMessage, QString *errorDescription)
1394 {
1395  KDbParseInfoInternal parseInfo(this);
1396  foreach(KDbField* f, *fields()) {
1397  if (f->isExpression()) {
1398  if (!f->expression().validate(&parseInfo)) {
1399  setResult(parseInfo, errorMessage, errorDescription);
1400  return false;
1401  }
1402  }
1403  }
1404  if (!whereExpression().validate(&parseInfo)) {
1405  setResult(parseInfo, errorMessage, errorDescription);
1406  return false;
1407  }
1408  return true;
1409 }
void append(const T &value)
bool isNull() const const
T & first()
KDbField::List * autoIncrementFields() const
KDB_EXPORT QString escapeIdentifier(const QString &string)
Definition: KDb.cpp:1334
KDbQueryColumnInfo::Vector fieldsExpanded(KDbConnection *conn, FieldsExpandedMode mode=FieldsExpandedMode::Default) const
const T value(const Key &key) const const
bool validate(KDbParseInfo *parseInfo)
bool isEmpty() const const
std::optional< QSqlQuery > query(const QString &queryStatement)
KDbQueryColumnInfo::Vector internalFields(KDbConnection *conn) const
void setMasterTable(KDbTableSchema *table)
QString columnAlias(int position) const
Provides information about lookup field's setup.
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
int tablePosition(const QString &tableName) const
bool setTableAlias(int position, const QString &alias)
int columnAliasesCount() const
KDbIndexSchema * primaryKey()
KDbTableSchema * table(const QString &tableName) const
QString number(int n, int base)
QString tableAliasOrName(const QString &tableName) const
KDB_EXPORT bool splitToTableAndFieldParts(const QString &string, QString *tableName, QString *fieldName, SplitToTableAndFieldPartsOptions option=FailIfNoTableOrFieldName)
Definition: KDb.cpp:603
CaseInsensitive
int tableBoundToColumn(int columnPosition) const
A type-safe KDbSQL token It can be used in KDb expressions.
Definition: KDbToken.h:36
KDbTableSchema * masterTable() const
The KDbBinaryExpression class represents binary operation.
@ Expanded
Expanded list of the query fields is computed so queries with asterisks are processed well.
bool isQueryAsterisk() const
Definition: KDbField.h:640
QList< KDbQuerySchemaParameter > parameters(KDbConnection *conn) const
bool insertInvisibleField(int position, KDbField *field)
bool setWhereExpression(const KDbExpression &expr, QString *errorMessage=nullptr, QString *errorDescription=nullptr)
Sets a WHERE expression exp.
KDbTableSchema * table()
Definition: KDbField.cpp:585
int count(const T &value) const const
The KDbVariableExpression class represents variables such as fieldname or tablename....
virtual KDbField * field(int id)
QDebug & nospace()
QString trimmed() const const
void clear()
A database connectivity and creation framework.
Q_SCRIPTABLE Q_NOREPLY void start()
bool setColumnAlias(int position, const QString &alias)
Specialized string for escaping.
QString & prepend(QChar ch)
KDbExpression whereExpression() const
@ Query
named query as lookup record source
QString name
KDbOrderByColumnList * orderByColumnList()
bool addAsterisk(KDbQueryAsterisk *asterisk)
virtual QString escapeIdentifier(const QString &id) const
Identifier escaping function in the associated KDbDriver.
QDebug & space()
KDbEscapedString autoIncrementSqlFieldsList(KDbConnection *conn) const
void setStatement(const KDbEscapedString &sql)
@ DriverEscaping
Identifiers are escaped by driver.
Definition: KDbGlobal.h:145
QVector< int > pkeyFieldsOrder(KDbConnection *conn) const
KDbExpression expression()
Definition: KDbField.cpp:1011
void setExpression(const KDbExpression &expr)
Definition: KDbField.cpp:1021
bool isFPNumericType() const
Definition: KDbField.h:335
int pkeyFieldCount(KDbConnection *conn)
virtual KDbField * copy()
Definition: KDbField.cpp:374
virtual bool insertField(int index, KDbField *field)
virtual void clear()
Clears all properties except 'type'.
Definition: KDbObject.cpp:34
QList< KDbTableSchema * > * tables() const
void resize(int size)
QHash::iterator insert(const Key &key, const T &value)
int indexOf(const KDbField &field) const
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
T value(int i) const const
bool addToWhereExpression(KDbField *field, const QVariant &value, KDbToken relation='=', QString *errorMessage=nullptr, QString *errorDescription=nullptr)
Appends a part to WHERE expression.
QList< int > tablePositions(const QString &tableName) const
QString aliasOrName() const
void setIndexForVisibleLookupValue(int index)
Record source information that can be specified for the lookup field schema.
bool isIntegerType() const
Definition: KDbField.h:326
bool hasNext() const const
void addTable(KDbTableSchema *table, const QString &alias=QString())
KDbQuerySchema * querySchema(int queryId)
QList< KDbRelationship * > * relationships() const
@ Table
table as lookup record source
KDbField * findTableField(const QString &fieldOrTableAndFieldName) const
KDbField::List * asterisks() const
KDbLookupFieldSchemaRecordSource recordSource() const
const T & at(int i) const const
bool isEmpty() const const
KDbRelationship * addRelationship(KDbField *field1, KDbField *field2)
bool validate(QString *errorMessage=nullptr, QString *errorDescription=nullptr)
T iifNotEmpty(const T &string, const T &stringIfEmpty)
Definition: KDb.h:820
const KDbTableSchema * table() const
KDbFieldList * subList(const QString &n1, const QString &n2=QString(), const QString &n3=QString(), const QString &n4=QString(), const QString &n5=QString(), const QString &n6=QString(), const QString &n7=QString(), const QString &n8=QString(), const QString &n9=QString(), const QString &n10=QString(), const QString &n11=QString(), const QString &n12=QString(), const QString &n13=QString(), const QString &n14=QString(), const QString &n15=QString(), const QString &n16=QString(), const QString &n17=QString(), const QString &n18=QString())
ColumnsOrderMode
Mode for columnsOrder()
@ SetFieldNameIfNoTableName
see splitToTableAndFieldParts()
Definition: KDb.h:248
virtual bool removeField(KDbField *field)
void resize(int size)
ExpandMode
Mode for field() and columnInfo()
@ BigInteger
Definition: KDbField.h:91
int columnPositionForAlias(const QString &name) const
Type type() const
Definition: KDbField.cpp:379
@ UnexpandedListWithoutAsterisks
A map for unexpanded list is created, with asterisks skipped.
KDbQueryColumnInfo * columnInfo(KDbConnection *conn, const QString &identifier, ExpandMode mode=ExpandMode::Expanded) const
FieldsExpandedMode
Mode for fieldsExpanded() and visibleFieldsExpanded()
void setColumnVisible(int position, bool visible)
Sets visibility flag for column at position to visible.
KDbTableSchema * tableSchema(int tableId)
KDbField::List * fields()
bool contains(const T &value) const const
@ WithInternalFieldsAndRecordId
Like WithInternalFields but record ID (big int type) field is appended after internal fields.
void setName(const QString &name)
Definition: KDbField.cpp:615
KDbQueryColumnInfo::Vector fieldsExpandedInternal(KDbConnection *conn, FieldsExpandedMode mode, bool onlyVisible) const
Used by fieldsExpanded(KDbConnection*, FieldsExpandedMode) and visibleFieldsExpanded(KDbConnection*,...
int tableAliasesCount() const
bool addField(KDbField *field)
Appends field to the columns list.
Provides information about database index that can be created for a database table.
QString toLower() const const
bool isAutoIncrement() const
Definition: KDbField.h:282
QHash< KDbQueryColumnInfo *, int > columnsOrder(KDbConnection *conn, ColumnsOrderMode mode=ColumnsOrderMode::ExpandedList) const
@ UnexpandedList
A map for unexpanded list is created.
KDbQuerySchema provides information about database query.
bool removeField(KDbField *field) override
QList< int > visibleColumns() const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
KDbExpression clone() const
Creates a deep (not shallow) copy of the KDbExpression.
QString & insert(int position, QChar ch)
void setOrderByColumnList(const KDbOrderByColumnList &list)
QString fromLatin1(const char *str, int size)
static KDbEscapedString sqlColumnsList(const KDbQueryColumnInfo::List &infolist, KDbConnection *conn=nullptr, KDb::IdentifierEscapingType escapingType=KDb::DriverEscaping)
bool isNull() const
bool addExpression(const KDbExpression &expr)
Appends a column built on top of expr expression.
bool addExpressionInternal(const KDbExpression &expr, bool visible)
Internal method used by all add*Expression methods.
void setParent(KDbFieldList *parent)
Sets parent for this field.
Definition: KDbField.cpp:251
const char * name(StandardAction id)
KDbQuerySchemaFieldsExpanded * computeFieldsExpanded(KDbConnection *conn) const
bool isColumnVisible(int position) const
QSet::iterator insert(const T &value)
Meta-data for a field.
Definition: KDbField.h:71
bool isExpression() const
Definition: KDbField.cpp:1006
int count(const T &value) const const
int fieldCount() const
KDbEscapedString statement() const
int size() const const
bool hasTableAlias(int position) const
KDbQueryAsterisk class encapsulates information about single asterisk in query definition.
bool addAsteriskInternal(KDbQueryAsterisk *asterisk, bool visible)
Internal method used by add*Asterisk() methods.
int compare(const QString &other, Qt::CaseSensitivity cs) const const
int tablePositionForAlias(const QString &name) const
KDbOrderByColumnList provides list of sorted columns for a query schema.
void clear() override
Provides database connection, allowing queries and data modification.
Definition: KDbConnection.h:51
QString name() const
Definition: KDbField.cpp:256
The KDbConstExpression class represents const expression.
bool contains(const Key &key) const const
KDbFieldList * parent()
Definition: KDbField.cpp:241
@ WithInternalFields
Like Default but internal fields (for lookup) are appended.
bool contains(KDbTableSchema *table) const
The KDbExpression class represents a base class for all expressions.
Definition: KDbExpression.h:51
bool hasColumnAlias(int position) const
Helper class that assigns additional information for the column in a query.
IdentifierEscapingType
Escaping type for identifiers.
Definition: KDbGlobal.h:144
@ Default
All fields are returned even if duplicated.
virtual void clear()
KDbQueryColumnInfo * expandedOrInternalField(KDbConnection *conn, int index) const
QString captionOrName() const
Definition: KDbField.cpp:326
QString tableAlias(int position) const
void removeTable(KDbTableSchema *table)
void getQueryParameters(QList< KDbQuerySchemaParameter > *params)
bool insertFieldInternal(int position, KDbField *field, int bindToTable, bool visible)
Internal method used by all insert*Field methods.
bool insertField(int position, KDbField *field) override
KDbQueryColumnInfo * foreignColumn()
const KDbField * field(KDbConnection *conn, const QString &identifier, ExpandMode mode=ExpandMode::Expanded) const
@ KDbEscaping
Identifiers are escaped using KDb's generic convention.
Definition: KDbGlobal.h:146
KDbLookupFieldSchema * lookupFieldSchema(const KDbField &field)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Tue Aug 9 2022 04:12:04 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.