KDb

KDbQuerySchema.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
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;
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 for (KDbField* f : qAsConst(*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
103KDbQuerySchema::~KDbQuerySchema()
104{
105 delete d;
106}
107
109{
112 d->clear();
113}
114
115/*virtual*/
116bool 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
126bool KDbQuerySchema::insertField(int position, KDbField *field, int bindToTable)
127{
128 return insertFieldInternal(position, field, bindToTable, true);
129}
130
131bool 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
206int 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
221bool KDbQuerySchema::addField(KDbField* field, int bindToTable)
222{
223 return insertField(fieldCount(), field, bindToTable);
224}
225
226bool KDbQuerySchema::addInvisibleField(KDbField* field)
227{
229}
230
231bool 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
276bool KDbQuerySchema::addInvisibleExpression(const KDbExpression& expr)
277{
278 return addExpressionInternal(expr, false);
279}
280
281bool KDbQuerySchema::isColumnVisible(int position) const
282{
283 return (position < fieldCount()) ? d->visibility.testBit(position) : false;
284}
285
286void 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
309bool KDbQuerySchema::addInvisibleAsterisk(KDbQueryAsterisk *asterisk)
310{
311 return addAsteriskInternal(asterisk, false);
312}
313
314QDebug 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
518KDbField* 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
549bool KDbQuerySchema::hasColumnAlias(int position) const
550{
551 return d->hasColumnAlias(position);
552}
553
554bool 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
602int 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
627bool KDbQuerySchema::hasTableAlias(int position) const
628{
629 return d->tableAliases.contains(position);
630}
631
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
647bool 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
684const 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
702const 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
794
795inline 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
804KDbQuerySchemaFieldsExpanded *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();
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
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
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();
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);
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
1198KDbRelationship* 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 }
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
1264static 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,
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/*
1339void 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 }
1387 if (!where.isNull()) {
1388 where.getQueryParameters(&params);
1389 }
1390 return params;
1391}
1392
1393bool 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}
The KDbBinaryExpression class represents binary operation.
Provides database connection, allowing queries and data modification.
virtual QString escapeIdentifier(const QString &id) const
Identifier escaping function in the associated KDbDriver.
KDbTableSchema * tableSchema(int tableId)
KDbQuerySchema * querySchema(int queryId)
The KDbConstExpression class represents const expression.
Specialized string for escaping.
The KDbExpression class represents a base class for all expressions.
bool isNull() const
bool validate(KDbParseInfo *parseInfo)
void getQueryParameters(QList< KDbQuerySchemaParameter > *params)
KDbExpression clone() const
Creates a deep (not shallow) copy of the KDbExpression.
KDbField::List * autoIncrementFields() const
virtual bool removeField(KDbField *field)
virtual KDbField * field(int id)
virtual void clear()
int indexOf(const KDbField &field) 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())
int fieldCount() const
virtual bool insertField(int index, KDbField *field)
bool isEmpty() const
KDbField::List * fields()
Meta-data for a field.
Definition KDbField.h:72
KDbTableSchema * table()
Definition KDbField.cpp:585
QString name() const
Definition KDbField.cpp:256
bool isAutoIncrement() const
Definition KDbField.h:282
bool isQueryAsterisk() const
Definition KDbField.h:640
bool isExpression() const
QString captionOrName() const
Definition KDbField.cpp:326
@ BigInteger
Definition KDbField.h:91
bool isFPNumericType() const
Definition KDbField.h:335
Type type() const
Definition KDbField.cpp:379
void setName(const QString &name)
Definition KDbField.cpp:615
bool isIntegerType() const
Definition KDbField.h:326
KDbExpression expression()
void setExpression(const KDbExpression &expr)
Provides information about database index that can be created for a database table.
Record source information that can be specified for the lookup field schema.
@ Table
table as lookup record source
@ Query
named query as lookup record source
Provides information about lookup field's setup.
QList< int > visibleColumns() const
KDbLookupFieldSchemaRecordSource recordSource() const
virtual void clear()
Clears all properties except 'type'.
Definition KDbObject.cpp:34
KDbOrderByColumnList provides list of sorted columns for a query schema.
KDbQueryAsterisk class encapsulates information about single asterisk in query definition.
const KDbTableSchema * table() const
Helper class that assigns additional information for the column in a query.
KDbQueryColumnInfo * foreignColumn()
void setIndexForVisibleLookupValue(int index)
QString aliasOrName() const
KDbQuerySchema provides information about database query.
KDbQueryColumnInfo::Vector fieldsExpanded(KDbConnection *conn, FieldsExpandedMode mode=FieldsExpandedMode::Default) const
ColumnsOrderMode
Mode for columnsOrder()
@ UnexpandedList
A map for unexpanded list is created.
@ UnexpandedListWithoutAsterisks
A map for unexpanded list is created, with asterisks skipped.
bool insertField(int position, KDbField *field) override
bool addAsteriskInternal(KDbQueryAsterisk *asterisk, bool visible)
Internal method used by add*Asterisk() methods.
KDbRelationship * addRelationship(KDbField *field1, KDbField *field2)
KDbExpression whereExpression() const
QList< KDbQuerySchemaParameter > parameters(KDbConnection *conn) const
int pkeyFieldCount(KDbConnection *conn)
bool setTableAlias(int position, const QString &alias)
void setStatement(const KDbEscapedString &sql)
bool addExpression(const KDbExpression &expr)
Appends a column built on top of expr expression.
KDbEscapedString statement() const
int tablePositionForAlias(const QString &name) const
QString columnAlias(int position) const
int tableBoundToColumn(int columnPosition) const
bool isColumnVisible(int position) const
bool insertFieldInternal(int position, KDbField *field, int bindToTable, bool visible)
Internal method used by all insert*Field methods.
KDbEscapedString autoIncrementSqlFieldsList(KDbConnection *conn) const
KDbOrderByColumnList * orderByColumnList()
FieldsExpandedMode
Mode for fieldsExpanded() and visibleFieldsExpanded()
@ WithInternalFields
Like Default but internal fields (for lookup) are appended.
@ Default
All fields are returned even if duplicated.
@ WithInternalFieldsAndRecordId
Like WithInternalFields but record ID (big int type) field is appended after internal fields.
int tableAliasesCount() const
void setMasterTable(KDbTableSchema *table)
void setColumnVisible(int position, bool visible)
Sets visibility flag for column at position to visible.
bool hasColumnAlias(int position) const
QString tableAliasOrName(const QString &tableName) const
KDbQueryColumnInfo * columnInfo(KDbConnection *conn, const QString &identifier, ExpandMode mode=ExpandMode::Expanded) const
bool addToWhereExpression(KDbField *field, const QVariant &value, KDbToken relation='=', QString *errorMessage=nullptr, QString *errorDescription=nullptr)
Appends a part to WHERE expression.
void removeTable(KDbTableSchema *table)
int columnPositionForAlias(const QString &name) const
QVector< int > pkeyFieldsOrder(KDbConnection *conn) const
bool contains(KDbTableSchema *table) const
KDbQueryColumnInfo::Vector fieldsExpandedInternal(KDbConnection *conn, FieldsExpandedMode mode, bool onlyVisible) const
Used by fieldsExpanded(KDbConnection*, FieldsExpandedMode) and visibleFieldsExpanded(KDbConnection*,...
bool removeField(KDbField *field) override
QString tableAlias(int position) const
const KDbField * field(KDbConnection *conn, const QString &identifier, ExpandMode mode=ExpandMode::Expanded) const
bool addField(KDbField *field)
Appends field to the columns list.
KDbQueryColumnInfo * expandedOrInternalField(KDbConnection *conn, int index) const
bool setColumnAlias(int position, const QString &alias)
bool validate(QString *errorMessage=nullptr, QString *errorDescription=nullptr)
QHash< KDbQueryColumnInfo *, int > columnsOrder(KDbConnection *conn, ColumnsOrderMode mode=ColumnsOrderMode::ExpandedList) const
ExpandMode
Mode for field() and columnInfo()
@ Expanded
Expanded list of the query fields is computed so queries with asterisks are processed well.
bool hasTableAlias(int position) const
QList< int > tablePositions(const QString &tableName) const
int columnAliasesCount() const
KDbField * findTableField(const QString &fieldOrTableAndFieldName) const
bool setWhereExpression(const KDbExpression &expr, QString *errorMessage=nullptr, QString *errorDescription=nullptr)
Sets a WHERE expression exp.
KDbTableSchema * table(const QString &tableName) const
static KDbEscapedString sqlColumnsList(const KDbQueryColumnInfo::List &infolist, KDbConnection *conn=nullptr, KDb::IdentifierEscapingType escapingType=KDb::DriverEscaping)
void clear() override
int tablePosition(const QString &tableName) const
KDbTableSchema * masterTable() const
KDbQueryColumnInfo::Vector internalFields(KDbConnection *conn) const
KDbField::List * asterisks() const
bool addExpressionInternal(const KDbExpression &expr, bool visible)
Internal method used by all add*Expression methods.
QList< KDbRelationship * > * relationships() const
bool addAsterisk(KDbQueryAsterisk *asterisk)
void addTable(KDbTableSchema *table, const QString &alias=QString())
KDbQuerySchemaFieldsExpanded * computeFieldsExpanded(KDbConnection *conn) const
QList< KDbTableSchema * > * tables() const
bool insertInvisibleField(int position, KDbField *field)
void setOrderByColumnList(const KDbOrderByColumnList &list)
KDbLookupFieldSchema * lookupFieldSchema(const KDbField &field)
KDbIndexSchema * primaryKey()
A type-safe KDbSQL token It can be used in KDb expressions.
Definition KDbToken.h:37
The KDbVariableExpression class represents variables such as fieldname or tablename....
Q_SCRIPTABLE Q_NOREPLY void start()
std::optional< QSqlQuery > query(const QString &queryStatement)
A database connectivity and creation framework.
@ SetFieldNameIfNoTableName
see splitToTableAndFieldParts()
Definition KDb.h:248
KDB_EXPORT QString escapeIdentifier(const QString &string)
Definition KDb.cpp:1334
IdentifierEscapingType
Escaping type for identifiers.
Definition KDbGlobal.h:144
@ DriverEscaping
Identifiers are escaped by driver.
Definition KDbGlobal.h:145
@ KDbEscaping
Identifiers are escaped using KDb's generic convention.
Definition KDbGlobal.h:146
T iifNotEmpty(const T &string, const T &stringIfEmpty)
Definition KDb.h:820
KDB_EXPORT bool splitToTableAndFieldParts(const QString &string, QString *tableName, QString *fieldName, SplitToTableAndFieldPartsOptions option=FailIfNoTableOrFieldName)
Definition KDb.cpp:603
QDebug operator<<(QDebug dbg, const DcrawInfoContainer &c)
QString name(StandardAction id)
QDebug & nospace()
QDebug & space()
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
T value(const Key &key) const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype count() const const
T & first()
bool isEmpty() const const
void resize(qsizetype size)
qsizetype size() const const
T value(qsizetype i) const const
bool hasNext() const const
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
QString arg(Args &&... args) const const
void clear()
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & prepend(QChar ch)
QString toLower() const const
QString trimmed() const const
CaseInsensitive
bool isNull() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:00:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.