KDb

KDbConnection.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2017 Jarosław Staniek <staniek@kde.org>
3
4 This program 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 program 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 program; see the file COPYING. 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 "KDbConnection.h"
21#include "KDbConnection_p.h"
22#include "KDbCursor.h"
23#include "KDbDriverBehavior.h"
24#include "KDbDriverMetaData.h"
25#include "KDbDriver_p.h"
26#include "KDbLookupFieldSchema.h"
27#include "KDbNativeStatementBuilder.h"
28#include "KDbQuerySchema.h"
29#include "KDbQuerySchema_p.h"
30#include "KDbRecordData.h"
31#include "KDbRecordEditBuffer.h"
32#include "KDbRelationship.h"
33#include "KDbSqlRecord.h"
34#include "KDbSqlResult.h"
35#include "KDbTableOrQuerySchema.h"
36#include "KDbTableSchemaChangeListener.h"
37#include "KDbTransactionData.h"
38#include "KDbTransactionGuard.h"
39#include "kdb_debug.h"
40
41#include <QDir>
42#include <QFileInfo>
43#include <QDomDocument>
44
45/*! Version number of extended table schema.
46
47 List of changes:
48 * 2: (Kexi 2.5.0) Added maxLengthIsDefault property (type: bool, if true, KDbField::maxLengthStrategy() == KDbField::DefaultMaxLength)
49 * 1: (Kexi 1.x) Initial version
50*/
51#define KDB_EXTENDED_TABLE_SCHEMA_VERSION 2
52
53KDbConnectionInternal::KDbConnectionInternal(KDbConnection *conn)
54 : connection(conn)
55{
56}
57
58class CursorDeleter
59{
60public:
61 explicit CursorDeleter(KDbCursor *cursor) {
62 delete cursor;
63 }
64};
65
66//================================================
67
68class Q_DECL_HIDDEN KDbConnectionOptions::Private
69{
70public:
71 Private() : connection(nullptr) {}
72 Private(const Private &other) {
73 copy(other);
74 }
75#define KDbConnectionOptionsPrivateArgs(o) std::tie(o.connection)
76 void copy(const Private &other) {
77 KDbConnectionOptionsPrivateArgs((*this)) = KDbConnectionOptionsPrivateArgs(other);
78 }
79 bool operator==(const Private &other) const {
80 return KDbConnectionOptionsPrivateArgs((*this)) == KDbConnectionOptionsPrivateArgs(other);
81 }
82 KDbConnection *connection;
83};
84
85KDbConnectionOptions::KDbConnectionOptions()
86 : d(new Private)
87{
88 KDbUtils::PropertySet::insert("readOnly", false, tr("Read only", "Read only connection"));
89}
90
91KDbConnectionOptions::KDbConnectionOptions(const KDbConnectionOptions &other)
92 : KDbUtils::PropertySet(other)
93 , d(new Private(*other.d))
94{
95}
96
97KDbConnectionOptions::~KDbConnectionOptions()
98{
99 delete d;
100}
101
102KDbConnectionOptions& KDbConnectionOptions::operator=(const KDbConnectionOptions &other)
103{
104 if (this != &other) {
106 d->copy(*other.d);
107 }
108 return *this;
109}
110
112{
113 return KDbUtils::PropertySet::operator==(other) && *d == *other.d;
114}
115
117{
118 return property("readOnly").value().toBool();
119}
120
121void KDbConnectionOptions::insert(const QByteArray &name, const QVariant &value,
122 const QString &caption)
123{
124 if (name == "readOnly") {
125 setReadOnly(value.toBool());
126 return;
127 }
128 QString realCaption;
129 if (property(name).caption().isEmpty()) { // don't allow to change the caption
130 realCaption = caption;
131 }
132 KDbUtils::PropertySet::insert(name, value, realCaption);
133}
134
135void KDbConnectionOptions::setCaption(const QByteArray &name, const QString &caption)
136{
137 if (name == "readOnly") {
138 return;
139 }
141}
142
144{
145 if (name == "readOnly") {
146 setReadOnly(value.toBool());
147 return;
148 }
150}
151
153{
154 if (name == "readOnly") {
155 return;
156 }
158}
159
161{
162 if (d->connection && d->connection->isConnected()) {
163 return; //sanity
164 }
165 KDbUtils::PropertySet::setValue("readOnly", set);
166}
167
168void KDbConnectionOptions::setConnection(KDbConnection *connection)
169{
170 d->connection = connection;
171}
172
173//================================================
174
175KDbConnectionPrivate::KDbConnectionPrivate(KDbConnection* const conn, KDbDriver *drv, const KDbConnectionData& _connData,
176 const KDbConnectionOptions &_options)
177 : conn(conn)
178 , connData(_connData)
179 , options(_options)
180 , driver(drv)
181 , dbProperties(conn)
182{
183 options.setConnection(conn);
184}
185
186KDbConnectionPrivate::~KDbConnectionPrivate()
187{
188 options.setConnection(nullptr);
189 deleteAllCursors();
190 delete m_parser;
191 qDeleteAll(tableSchemaChangeListeners);
192 qDeleteAll(obsoleteQueries);
193}
194
195void KDbConnectionPrivate::deleteAllCursors()
196{
197 QSet<KDbCursor*> cursorsToDelete(cursors);
198 cursors.clear();
199 for(KDbCursor* c : cursorsToDelete) {
200 CursorDeleter deleter(c);
201 }
202}
203
204void KDbConnectionPrivate::errorInvalidDBContents(const QString& details)
205{
206 conn->m_result = KDbResult(ERR_INVALID_DATABASE_CONTENTS,
207 KDbConnection::tr("Invalid database contents. %1").arg(details));
208}
209
210QString KDbConnectionPrivate::strItIsASystemObject() const
211{
212 return KDbConnection::tr("It is a system object.");
213}
214
215void KDbConnectionPrivate::setupKDbSystemSchema()
216{
217 if (!m_internalKDbTables.isEmpty()) {
218 return; //already set up
219 }
220 {
221 KDbInternalTableSchema *t_objects = new KDbInternalTableSchema(QLatin1String("kexi__objects"));
222 t_objects->addField(new KDbField(QLatin1String("o_id"),
223 KDbField::Integer, KDbField::PrimaryKey | KDbField::AutoInc, KDbField::Unsigned));
224 t_objects->addField(new KDbField(QLatin1String("o_type"), KDbField::Byte, {}, KDbField::Unsigned));
225 t_objects->addField(new KDbField(QLatin1String("o_name"), KDbField::Text));
226 t_objects->addField(new KDbField(QLatin1String("o_caption"), KDbField::Text));
227 t_objects->addField(new KDbField(QLatin1String("o_desc"), KDbField::LongText));
228 //kdbDebug() << *t_objects;
229 insertTable(t_objects);
230 }
231 {
232 KDbInternalTableSchema *t_objectdata = new KDbInternalTableSchema(QLatin1String("kexi__objectdata"));
233 t_objectdata->addField(new KDbField(QLatin1String("o_id"),
234 KDbField::Integer, KDbField::NotNull, KDbField::Unsigned));
235 t_objectdata->addField(new KDbField(QLatin1String("o_data"), KDbField::LongText));
236 t_objectdata->addField(new KDbField(QLatin1String("o_sub_id"), KDbField::Text));
237 insertTable(t_objectdata);
238 }
239 {
240 KDbInternalTableSchema *t_fields = new KDbInternalTableSchema(QLatin1String("kexi__fields"));
241 t_fields->addField(new KDbField(QLatin1String("t_id"), KDbField::Integer, {}, KDbField::Unsigned));
242 t_fields->addField(new KDbField(QLatin1String("f_type"), KDbField::Byte, {}, KDbField::Unsigned));
243 t_fields->addField(new KDbField(QLatin1String("f_name"), KDbField::Text));
244 t_fields->addField(new KDbField(QLatin1String("f_length"), KDbField::Integer));
245 t_fields->addField(new KDbField(QLatin1String("f_precision"), KDbField::Integer));
246 t_fields->addField(new KDbField(QLatin1String("f_constraints"), KDbField::Integer));
247 t_fields->addField(new KDbField(QLatin1String("f_options"), KDbField::Integer));
248 t_fields->addField(new KDbField(QLatin1String("f_default"), KDbField::Text));
249 //these are additional properties:
250 t_fields->addField(new KDbField(QLatin1String("f_order"), KDbField::Integer));
251 t_fields->addField(new KDbField(QLatin1String("f_caption"), KDbField::Text));
252 t_fields->addField(new KDbField(QLatin1String("f_help"), KDbField::LongText));
253 insertTable(t_fields);
254 }
255 {
257 t_db->addField(new KDbField(QLatin1String("db_property"),
258 KDbField::Text, KDbField::NoConstraints, KDbField::NoOptions, 32));
259 t_db->addField(new KDbField(QLatin1String("db_value"), KDbField::LongText));
260 insertTable(t_db);
261 }
262}
263
264void KDbConnectionPrivate::insertTable(KDbTableSchema* tableSchema)
265{
266 KDbInternalTableSchema* internalTable = dynamic_cast<KDbInternalTableSchema*>(tableSchema);
267 if (internalTable) {
268 m_internalKDbTables.insert(internalTable);
269 } else {
270 m_tables.insert(tableSchema->id(), tableSchema);
271 }
272 m_tablesByName.insert(tableSchema->name(), tableSchema);
273}
274
275void KDbConnectionPrivate::removeTable(int id)
276{
277 QScopedPointer<KDbTableSchema> toDelete(m_tables.take(id));
278 if (!toDelete) {
279 kdbWarning() << "Could not find table to delete with id=" << id;
280 return;
281 }
283 const int count = m_tablesByName.remove(toDelete->name());
284 Q_ASSERT_X(count == 1, "KDbConnectionPrivate::removeTable", "Table to remove not found");
285}
286
287void KDbConnectionPrivate::takeTable(KDbTableSchema* tableSchema)
288{
289 if (m_tables.isEmpty()) {
290 return;
291 }
292 m_tables.take(tableSchema->id());
293 m_tablesByName.take(tableSchema->name());
294}
295
296void KDbConnectionPrivate::renameTable(KDbTableSchema* tableSchema, const QString& newName)
297{
298 m_tablesByName.take(tableSchema->name());
299 tableSchema->setName(newName);
300 m_tablesByName.insert(tableSchema->name(), tableSchema);
301}
302
303void KDbConnectionPrivate::changeTableId(KDbTableSchema* tableSchema, int newId)
304{
305 m_tables.take(tableSchema->id());
306 m_tables.insert(newId, tableSchema);
307}
308
309void KDbConnectionPrivate::clearTables()
310{
311 m_tablesByName.clear();
312 qDeleteAll(m_internalKDbTables);
313 m_internalKDbTables.clear();
314 QHash<int, KDbTableSchema*> tablesToDelete(m_tables);
315 m_tables.clear();
316 qDeleteAll(tablesToDelete);
317}
318
319void KDbConnectionPrivate::insertQuery(KDbQuerySchema* query)
320{
321 m_queries.insert(query->id(), query);
322 m_queriesByName.insert(query->name(), query);
323}
324
325void KDbConnectionPrivate::removeQuery(KDbQuerySchema* querySchema)
326{
327 m_queriesByName.remove(querySchema->name());
328 m_queries.remove(querySchema->id());
329 delete querySchema;
330}
331
332void KDbConnectionPrivate::setQueryObsolete(KDbQuerySchema* query)
333{
334 obsoleteQueries.insert(query);
335 m_queriesByName.take(query->name());
336 m_queries.take(query->id());
337}
338
339void KDbConnectionPrivate::clearQueries()
340{
341 qDeleteAll(m_queries);
342 m_queries.clear();
343}
344
345KDbTableSchema* KDbConnectionPrivate::setupTableSchema(KDbTableSchema *table)
346{
347 Q_ASSERT(table);
348 QScopedPointer<KDbTableSchema> newTable(table);
349 KDbCursor *cursor;
350 if (!(cursor = conn->executeQuery(
351 KDbEscapedString("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
352 "f_options, f_default, f_order, f_caption, f_help "
353 "FROM kexi__fields WHERE t_id=%1 ORDER BY f_order")
354 .arg(driver->valueToSql(KDbField::Integer, table->id())))))
355 {
356 return nullptr;
357 }
358 if (!cursor->moveFirst()) {
359 if (!cursor->result().isError() && cursor->eof()) {
360 conn->m_result = KDbResult(tr("Table has no fields defined."));
361 }
362 conn->deleteCursor(cursor);
363 return nullptr;
364 }
365
366 // For each field: load its schema
367 KDbRecordData fieldData;
368 bool ok = true;
369 while (!cursor->eof()) {
370// kdbDebug()<<"@@@ f_name=="<<cursor->value(2).asCString();
371 if (!cursor->storeCurrentRecord(&fieldData)) {
372 ok = false;
373 break;
374 }
375 KDbField *f = conn->setupField(fieldData);
376 if (!f || !table->addField(f)) {
377 ok = false;
378 break;
379 }
380 cursor->moveNext();
381 }
382
383 if (!ok) {//error:
384 conn->deleteCursor(cursor);
385 return nullptr;
386 }
387
388 if (!conn->deleteCursor(cursor)) {
389 return nullptr;
390 }
391
392 if (!conn->loadExtendedTableSchemaData(table)) {
393 return nullptr;
394 }
395 //store locally:
396 insertTable(table);
397 return newTable.take();
398}
399
400KDbQuerySchema* KDbConnectionPrivate::setupQuerySchema(KDbQuerySchema *query)
401{
402 Q_ASSERT(query);
403 QScopedPointer<KDbQuerySchema> newQuery(query);
404 QString sql;
405 if (!conn->loadDataBlock(query->id(), &sql, QLatin1String("sql"))) {
406 conn->m_result = KDbResult(
407 ERR_OBJECT_NOT_FOUND,
408 tr("Could not find definition for query \"%1\". Deleting this query is recommended.")
409 .arg(query->name()));
410 return nullptr;
411 }
412 const QString queryName(query->name());
413 if (!parser()->parse(KDbEscapedString(sql), query)) {
414 newQuery.take(); // query is destroyed by the parser
415 conn->m_result = KDbResult(
416 ERR_SQL_PARSE_ERROR, tr("<p>Could not load definition for query \"%1\". "
417 "SQL statement for this query is invalid:<br><tt>%2</tt></p>\n"
418 "<p>This query can be edited only in Text View.</p>")
419 .arg(queryName, sql));
420 return nullptr;
421 }
422 insertQuery(query);
423 return newQuery.take();
424}
425
426KDbQuerySchemaFieldsExpanded *KDbConnectionPrivate::fieldsExpanded(const KDbQuerySchema *query)
427{
428 return m_fieldsExpandedCache[query];
429}
430
431void KDbConnectionPrivate::insertFieldsExpanded(const KDbQuerySchema *query, KDbQuerySchemaFieldsExpanded *cache)
432{
433 m_fieldsExpandedCache.insert(query, cache);
434}
435
436void KDbConnectionPrivate::removeFieldsExpanded(const KDbQuerySchema *query)
437{
438 //kdbDebug() << "**CACHE REMOVE**" << query;
439 m_fieldsExpandedCache.remove(query);
440}
441
442//================================================
443
444namespace {
445//! @internal static: list of internal KDb system table names
446class SystemTables : public QStringList
447{
448public:
449 SystemTables()
450 : QStringList({
451 QLatin1String("kexi__objects"),
452 QLatin1String("kexi__objectdata"),
453 QLatin1String("kexi__fields"),
454 QLatin1String("kexi__db")})
455 {}
456};
457}
458
459Q_GLOBAL_STATIC(SystemTables, g_kdbSystemTableNames)
460
462 const KDbConnectionOptions &options)
463 : d(new KDbConnectionPrivate(this, driver, connData, options))
464{
465 if (d->connData.driverId().isEmpty()) {
466 d->connData.setDriverId(d->driver->metaData()->id());
467 }
468}
469
471{
472 disconnect();
473 //do not allow the driver to touch me: I will kill myself.
474 d->driver->d->connections.remove(this);
475}
476
478{
479 KDbConnectionPrivate *thisD = d;
480 d = nullptr; // make sure d is nullptr before destructing
481 delete thisD;
482}
483
485{
486 return d->connData;
487}
488
490{
491 return d->driver;
492}
493
495{
496 clearResult();
497 if (d->isConnected) {
498 m_result = KDbResult(ERR_ALREADY_CONNECTED,
499 tr("Connection already established."));
500 return false;
501 }
502
503 d->serverVersion.clear();
504 if (!(d->isConnected = drv_connect())) {
505 if (m_result.code() == ERR_NONE) {
506 m_result.setCode(ERR_OTHER);
507 }
508 m_result.setMessage(d->driver->metaData()->isFileBased() ?
509 tr("Could not open \"%1\" project file.")
510 .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName()))
511 : tr("Could not connect to \"%1\" database server.")
512 .arg(d->connData.toUserVisibleString()));
513 }
514 if (d->isConnected && !d->driver->behavior()->USING_DATABASE_REQUIRED_TO_CONNECT) {
515 if (!drv_getServerVersion(&d->serverVersion))
516 return false;
517 }
518 return d->isConnected;
519}
520
522{
523 return !d->usedDatabase.isEmpty() && d->isConnected && drv_isDatabaseUsed();
524}
525
527{
528 KDbResultable::clearResult();
529}
530
532{
533 clearResult();
534 if (!d->isConnected)
535 return true;
536
537 if (!closeDatabase())
538 return false;
539
540 bool ok = drv_disconnect();
541 if (ok)
542 d->isConnected = false;
543 return ok;
544}
545
547{
548 return d->isConnected;
549}
550
552{
553 if (d->isConnected) {
554 clearResult();
555 return true;
556 }
557 m_result = KDbResult(ERR_NO_CONNECTION,
558 tr("Not connected to the database server."));
559 return false;
560}
561
563{
564 if (isDatabaseUsed()) {
565 clearResult();
566 return true;
567 }
568 m_result = KDbResult(ERR_NO_DB_USED,
569 tr("Currently no database is used."));
570 return false;
571}
572
574{
575 //kdbDebug() << also_system_db;
576 if (!checkConnected())
577 return QStringList();
578
579 QString tmpdbName;
580 //some engines need to have opened any database before executing "create database"
581 if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
582 return QStringList();
583
584 QStringList list;
585 bool ret = drv_getDatabasesList(&list);
586
587 if (!tmpdbName.isEmpty()) {
588 //whatever result is - now we have to close temporary opened database:
589 if (!closeDatabase())
590 return QStringList();
591 }
592
593 if (!ret)
594 return QStringList();
595
596 if (also_system_db)
597 return list;
598 //filter system databases:
599 for (QMutableListIterator<QString> it(list); it.hasNext();) {
600 if (d->driver->isSystemDatabaseName(it.next())) {
601 it.remove();
602 }
603 }
604 return list;
605}
606
608{
609 list->clear();
610 return true;
611}
612
613bool KDbConnection::drv_databaseExists(const QString &dbName, bool ignoreErrors)
614{
615 QStringList list = databaseNames(true);//also system
616 if (m_result.isError()) {
617 return false;
618 }
619
620 if (list.indexOf(dbName) == -1) {
621 if (!ignoreErrors)
622 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
623 tr("The database \"%1\" does not exist.").arg(dbName));
624 return false;
625 }
626
627 return true;
628}
629
630bool KDbConnection::databaseExists(const QString &dbName, bool ignoreErrors)
631{
632// kdbDebug() << dbName << ignoreErrors;
633 if (d->driver->behavior()->CONNECTION_REQUIRED_TO_CHECK_DB_EXISTENCE && !checkConnected())
634 return false;
635 clearResult();
636
637 if (d->driver->metaData()->isFileBased()) {
638 //for file-based db: file must exists and be accessible
639 QFileInfo file(d->connData.databaseName());
640 if (!file.exists() || (!file.isFile() && !file.isSymLink())) {
641 if (!ignoreErrors)
642 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
643 tr("The database file \"%1\" does not exist.")
644 .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
645 return false;
646 }
647 if (!file.isReadable()) {
648 if (!ignoreErrors)
649 m_result = KDbResult(ERR_ACCESS_RIGHTS,
650 tr("Database file \"%1\" is not readable.")
651 .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
652 return false;
653 }
654 if (!d->options.isReadOnly() && !file.isWritable()) {
655 if (!ignoreErrors)
656 m_result = KDbResult(ERR_ACCESS_RIGHTS,
657 tr("Database file \"%1\" is not writable.")
658 .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
659 return false;
660 }
661 return true;
662 }
663
664 QString tmpdbName;
665 //some engines need to have opened any database before executing "create database"
666 const bool orig_skipDatabaseExistsCheckInUseDatabase = d->skipDatabaseExistsCheckInUseDatabase;
667 d->skipDatabaseExistsCheckInUseDatabase = true;
668 bool ret = useTemporaryDatabaseIfNeeded(&tmpdbName);
669 d->skipDatabaseExistsCheckInUseDatabase = orig_skipDatabaseExistsCheckInUseDatabase;
670 if (!ret)
671 return false;
672
673 ret = drv_databaseExists(dbName, ignoreErrors);
674
675 if (!tmpdbName.isEmpty()) {
676 //whatever result is - now we have to close temporary opened database:
677 if (!closeDatabase())
678 return false;
679 }
680
681 return ret;
682}
683
684#define createDatabase_CLOSE \
685 { if (!closeDatabase()) { \
686 m_result = KDbResult(KDbConnection::tr("Database \"%1\" has been created but " \
687 "could not be closed after creation.").arg(dbName)); \
688 return false; \
689 } }
690
691#define createDatabase_ERROR \
692 { createDatabase_CLOSE; return false; }
693
694
696{
697 if (d->driver->behavior()->CONNECTION_REQUIRED_TO_CREATE_DB && !checkConnected())
698 return false;
699
700 if (databaseExists(dbName)) {
701 m_result = KDbResult(ERR_OBJECT_EXISTS,
702 tr("Database \"%1\" already exists.").arg(dbName));
703 return false;
704 }
705 if (d->driver->isSystemDatabaseName(dbName)) {
706 m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
707 tr("Could not create database \"%1\". This name is reserved for system database.").arg(dbName));
708 return false;
709 }
710 if (d->driver->metaData()->isFileBased()) {
711 //update connection data if filename differs
712 if (QFileInfo(dbName).isAbsolute()) {
713 d->connData.setDatabaseName(dbName);
714 }
715 else {
716 d->connData.setDatabaseName(
717 QFileInfo(d->connData.databaseName()).absolutePath()
718 + QDir::separator() + QFileInfo(dbName).fileName());
719 }
720 }
721
722 QString tmpdbName;
723 //some engines need to have opened any database before executing "create database"
724 if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
725 return false;
726
727 //low-level create
728 if (!drv_createDatabase(dbName)) {
729 m_result.prependMessage(tr("Error creating database \"%1\" on the server.").arg(dbName));
730 (void)closeDatabase();//sanity
731 return false;
732 }
733
734 if (!tmpdbName.isEmpty()) {
735 //whatever result is - now we have to close temporary opened database:
736 if (!closeDatabase())
737 return false;
738 }
739
740 if (!tmpdbName.isEmpty() || !d->driver->behavior()->IS_DB_OPEN_AFTER_CREATE) {
741 //db need to be opened
742 if (!useDatabase(dbName, false/*not yet kexi compatible!*/)) {
743 m_result = KDbResult(tr("Database \"%1\" has been created but could not be opened.").arg(dbName));
744 return false;
745 }
746 } else {
747 //just for the rule
748 d->usedDatabase = dbName;
749 d->isConnected = true;
750 }
751
752 KDbTransaction trans;
753 if (d->driver->transactionsSupported()) {
754 trans = beginTransaction();
755 if (!trans.isActive())
756 return false;
757 }
758
759 //-create system tables schema objects
760 d->setupKDbSystemSchema();
761
762 //-physically create internal KDb tables
763 foreach(KDbInternalTableSchema* t, d->internalKDbTables()) {
764 if (!drv_createTable(*t))
765 createDatabase_ERROR;
766 }
767
768 //-insert KDb version info:
769 // (for compatibility with Kexi expect the legacy kexidb_major_ver/kexidb_minor_ver values)
770 KDbTableSchema *table = d->table(QLatin1String("kexi__db"));
771 if (!table)
772 createDatabase_ERROR;
773 if (!insertRecord(table, QLatin1String("kexidb_major_ver"), KDb::version().major())
774 || !insertRecord(table, QLatin1String("kexidb_minor_ver"), KDb::version().minor()))
775 createDatabase_ERROR;
776
777 if (trans.isActive() && !commitTransaction(trans))
778 createDatabase_ERROR;
779
780 createDatabase_CLOSE;
781 return true;
782}
783
784#undef createDatabase_CLOSE
785#undef createDatabase_ERROR
786
787bool KDbConnection::useDatabase(const QString &dbName, bool kexiCompatible, bool *cancelled, KDbMessageHandler* msgHandler)
788{
789 if (cancelled)
790 *cancelled = false;
791 //kdbDebug() << dbName << kexiCompatible;
792 if (!checkConnected())
793 return false;
794
795 QString my_dbName;
796 if (dbName.isEmpty())
797 my_dbName = d->connData.databaseName();
798 else
799 my_dbName = dbName;
800 if (my_dbName.isEmpty())
801 return false;
802
803 if (d->usedDatabase == my_dbName)
804 return true; //already used
805
806 if (!d->skipDatabaseExistsCheckInUseDatabase) {
807 if (!databaseExists(my_dbName, false /*don't ignore errors*/))
808 return false; //database must exist
809 }
810
811 if (!d->usedDatabase.isEmpty() && !closeDatabase()) //close db if already used
812 return false;
813
814 d->usedDatabase.clear();
815
816 if (!drv_useDatabase(my_dbName, cancelled, msgHandler)) {
817 if (cancelled && *cancelled)
818 return false;
819 QString msg(tr("Opening database \"%1\" failed.").arg(my_dbName));
820 m_result.prependMessage(msg);
821 return false;
822 }
823 if (d->serverVersion.isNull() && d->driver->behavior()->USING_DATABASE_REQUIRED_TO_CONNECT) {
824 // get version just now, it was not possible earlier
825 if (!drv_getServerVersion(&d->serverVersion))
826 return false;
827 }
828
829 //-create system tables schema objects
830 d->setupKDbSystemSchema();
831
832 if (kexiCompatible && my_dbName.compare(anyAvailableDatabaseName(), Qt::CaseInsensitive) != 0) {
833 //-get global database information
834 bool ok;
835 const int major = d->dbProperties.value(QLatin1String("kexidb_major_ver")).toInt(&ok);
836 if (!ok) {
837 m_result = d->dbProperties.result();
838 return false;
839 }
840 const int minor = d->dbProperties.value(QLatin1String("kexidb_minor_ver")).toInt(&ok);
841 if (!ok) {
842 m_result = d->dbProperties.result();
843 return false;
844 }
845 d->databaseVersion.setMajor(major);
846 d->databaseVersion.setMinor(minor);
847 }
848 d->usedDatabase = my_dbName;
849 return true;
850}
851
853{
854 if (d->usedDatabase.isEmpty())
855 return true; //no db used
856 if (!checkConnected())
857 return true;
858
859 bool ret = true;
860
861 /*! @todo (js) add CLEVER algorithm here for nested transactions */
862 if (d->driver->transactionsSupported()) {
863 //rollback all transactions
864 d->dontRemoveTransactions = true; //lock!
865 foreach(const KDbTransaction& tr, d->transactions) {
866 if (!rollbackTransaction(tr)) {//rollback as much as you can, don't stop on prev. errors
867 ret = false;
868 } else {
869 kdbDebug() << "transaction rolled back!";
870 kdbDebug() << "trans.refcount=="
871 << (tr.m_data ? QString::number(tr.m_data->refcount())
872 : QLatin1String("(null)"));
873 }
874 }
875 d->dontRemoveTransactions = false; //unlock!
876 d->transactions.clear(); //free trans. data
877 }
878
879 //delete own cursors:
880 d->deleteAllCursors();
881 //delete own schemas
882 d->clearTables();
883 d->clearQueries();
884
885 if (!drv_closeDatabase())
886 return false;
887
888 d->usedDatabase.clear();
889 return ret;
890}
891
893{
894 return d->usedDatabase;
895}
896
898{
899 if (d->driver->behavior()->USE_TEMPORARY_DATABASE_FOR_CONNECTION_IF_NEEDED && !isDatabaseUsed()) {
900 //we have no db used, but it is required by engine to have used any!
901 *name = anyAvailableDatabaseName();
902 if (name->isEmpty()) {
903 m_result = KDbResult(ERR_NO_DB_USED,
904 tr("Could not find any database for temporary connection."));
905 return false;
906 }
907 const bool orig_skipDatabaseExistsCheckInUseDatabase = d->skipDatabaseExistsCheckInUseDatabase;
908 d->skipDatabaseExistsCheckInUseDatabase = true;
909 bool ret = useDatabase(*name, false);
910 d->skipDatabaseExistsCheckInUseDatabase = orig_skipDatabaseExistsCheckInUseDatabase;
911 if (!ret) {
912 m_result = KDbResult(m_result.code(),
913 tr("Error during starting temporary connection using \"%1\" database name.").arg(*name));
914 return false;
915 }
916 }
917 return true;
918}
919
921{
922 if (d->driver->behavior()->CONNECTION_REQUIRED_TO_DROP_DB && !checkConnected())
923 return false;
924
925 QString dbToDrop;
926 if (dbName.isEmpty() && d->usedDatabase.isEmpty()) {
927 if (!d->driver->metaData()->isFileBased()
928 || (d->driver->metaData()->isFileBased() && d->connData.databaseName().isEmpty()))
929 {
930 m_result = KDbResult(ERR_NO_NAME_SPECIFIED,
931 tr("Could not delete database. Name is not specified."));
932 return false;
933 }
934 //this is a file driver so reuse previously passed filename
935 dbToDrop = d->connData.databaseName();
936 } else {
937 if (dbName.isEmpty()) {
938 dbToDrop = d->usedDatabase;
939 } else {
940 if (d->driver->metaData()->isFileBased()) //lets get full path
941 dbToDrop = QFileInfo(dbName).absoluteFilePath();
942 else
943 dbToDrop = dbName;
944 }
945 }
946
947 if (dbToDrop.isEmpty()) {
948 m_result = KDbResult(ERR_NO_NAME_SPECIFIED,
949 tr("Could not delete database. Name is not specified."));
950 return false;
951 }
952
953 if (d->driver->isSystemDatabaseName(dbToDrop)) {
954 m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
955 tr("Could not delete system database \"%1\".").arg(dbToDrop));
956 return false;
957 }
958
959 if (isDatabaseUsed() && d->usedDatabase == dbToDrop) {
960 //we need to close database because cannot drop used this database
961 if (!closeDatabase())
962 return false;
963 }
964
965 QString tmpdbName;
966 //some engines need to have opened any database before executing "drop database"
967 if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
968 return false;
969
970 //ok, now we have access to dropping
971 bool ret = drv_dropDatabase(dbToDrop);
972
973 if (!tmpdbName.isEmpty()) {
974 //whatever result is - now we have to close temporary opened database:
975 if (!closeDatabase())
976 return false;
977 }
978 return ret;
979}
980
982{
983 if (!checkIsDatabaseUsed()) {
984 if (ok) {
985 *ok = false;
986 }
987 return QStringList();
988 }
990 if (objectType == KDb::AnyObjectType) {
991 sql = "SELECT o_name FROM kexi__objects ORDER BY o_id";
992 } else {
993 sql = KDbEscapedString("SELECT o_name FROM kexi__objects WHERE o_type=%1"
994 " ORDER BY o_id").arg(d->driver->valueToSql(KDbField::Integer, objectType));
995 }
996 QStringList list;
997 const bool success = queryStringListInternal(&sql, &list, nullptr, nullptr, 0, KDb::isIdentifier);
998 if (ok) {
999 *ok = success;
1000 }
1001 if (!success) {
1002 m_result.prependMessage(tr("Could not retrieve list of object names."));
1003 }
1004 return list;
1005}
1006
1007QStringList KDbConnection::tableNames(bool alsoSystemTables, bool* ok)
1008{
1009 QStringList result;
1010 bool success;
1011 if (!ok) {
1012 ok = &success;
1013 }
1014 QStringList list = objectNames(KDb::TableObjectType, ok);
1015 if (!*ok) {
1016 m_result.prependMessage(tr("Could not retrieve list of table names."));
1017 return QStringList();
1018 }
1019 if (alsoSystemTables) {
1020 list += kdbSystemTableNames();
1021 }
1022 const QStringList physicalTableNames = drv_getTableNames(ok);
1023 if (!*ok) {
1024 m_result.prependMessage(tr("Could not retrieve list of physical table names."));
1025 return QStringList();
1026 }
1027 QSet<QString> physicalTableNamesSet;
1028 for (const QString &name : physicalTableNames) {
1029 physicalTableNamesSet.insert(name.toLower());
1030 }
1031 for (const QString &name : qAsConst(list)) {
1032 if (physicalTableNamesSet.contains(name.toLower())) {
1033 result += name;
1034 }
1035 }
1036 return result;
1037}
1038
1040{
1041 return drv_containsTable(tableName);
1042}
1043
1045{
1046 return *g_kdbSystemTableNames;
1047}
1048
1050{
1051 return isConnected() ? d->serverVersion : KDbServerVersionInfo();
1052}
1053
1055{
1056 return isDatabaseUsed() ? d->databaseVersion : KDbVersionInfo();
1057}
1058
1060{
1061 return d->dbProperties;
1062}
1063
1065{
1066 return objectIds(KDb::TableObjectType, ok);
1067}
1068
1070{
1071 return objectIds(KDb::QueryObjectType, ok);
1072}
1073
1074QList<int> KDbConnection::objectIds(int objectType, bool* ok)
1075{
1076 if (!checkIsDatabaseUsed())
1077 return QList<int>();
1078
1079 KDbEscapedString sql;
1080 if (objectType == KDb::AnyObjectType)
1081 sql = "SELECT o_id, o_name FROM kexi__objects ORDER BY o_id";
1082 else
1083 sql = "SELECT o_id, o_name FROM kexi__objects WHERE o_type=" + QByteArray::number(objectType)
1084 + " ORDER BY o_id";
1085
1086 KDbCursor *c = executeQuery(sql);
1087 if (!c) {
1088 if (ok) {
1089 *ok = false;
1090 }
1091 m_result.prependMessage(tr("Could not retrieve list of object identifiers."));
1092 return QList<int>();
1093 }
1094 QList<int> list;
1095 for (c->moveFirst(); !c->eof(); c->moveNext()) {
1096 QString tname = c->value(1).toString(); //kexi__objects.o_name
1097 if (KDb::isIdentifier(tname)) {
1098 list.append(c->value(0).toInt()); //kexi__objects.o_id
1099 }
1100 }
1101 deleteCursor(c);
1102 if (ok) {
1103 *ok = true;
1104 }
1105 return list;
1106}
1107
1108//yeah, it is very efficient:
1109#define C_A(a) , const QVariant& c ## a
1110
1111#define V_A0 d->driver->valueToSql( tableSchema->field(0), c0 )
1112#define V_A(a) + ',' + d->driver->valueToSql( \
1113 tableSchema->field(a) ? tableSchema->field(a)->type() : KDbField::Text, c ## a )
1114
1115// kdbDebug() << "******** " << QString("INSERT INTO ") +
1116// escapeIdentifier(tableSchema->name()) +
1117// " VALUES (" + vals + ")";
1118
1120 KDbFieldList *fields,
1121 const KDbEscapedString &sql)
1122{
1124 if (!drv_beforeInsert(tableSchemaName,fields )) {
1125 return res;
1126 }
1127 res = prepareSql(sql);
1128 if (!res || res->lastResult().isError()) {
1129 res.clear();
1130 return res;
1131 }
1132 if (!drv_afterInsert(tableSchemaName, fields)) {
1133 res.clear();
1134 return res;
1135 }
1136 {
1137 // Fetching is needed to perform real execution at least for some backends.
1138 // Also we are not expecting record but let's delete if there's any.
1139 QSharedPointer<KDbSqlRecord> record = res->fetchRecord();
1140 Q_UNUSED(record)
1141 }
1142 if (res->lastResult().isError()) {
1143 res.clear();
1144 }
1145 return res;
1146}
1147
1148#define C_INS_REC(args, vals) \
1149 QSharedPointer<KDbSqlResult> KDbConnection::insertRecord(KDbTableSchema* tableSchema args) { \
1150 return insertRecordInternal(tableSchema->name(), tableSchema, \
1151 KDbEscapedString("INSERT INTO ") + escapeIdentifier(tableSchema->name()) \
1152 + " (" \
1153 + tableSchema->sqlFieldsList(this) \
1154 + ") VALUES (" + vals + ')'); \
1155 }
1156
1157#define C_INS_REC_ALL \
1158 C_INS_REC( C_A(0), V_A0 ) \
1159 C_INS_REC( C_A(0) C_A(1), V_A0 V_A(1) ) \
1160 C_INS_REC( C_A(0) C_A(1) C_A(2), V_A0 V_A(1) V_A(2) ) \
1161 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3), V_A0 V_A(1) V_A(2) V_A(3) ) \
1162 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) ) \
1163 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) ) \
1164 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5) C_A(6), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) V_A(6) ) \
1165 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5) C_A(6) C_A(7), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) V_A(6) V_A(7) )
1166
1167C_INS_REC_ALL
1168
1169#undef V_A0
1170#undef V_A
1171#undef C_INS_REC
1172
1173#define V_A0 value += d->driver->valueToSql( it.next(), c0 );
1174#define V_A( a ) value += (',' + d->driver->valueToSql( it.next(), c ## a ));
1175
1176#define C_INS_REC(args, vals) \
1177 QSharedPointer<KDbSqlResult> KDbConnection::insertRecord(KDbFieldList* fields args) \
1178 { \
1179 KDbEscapedString value; \
1180 const KDbField::List *flist = fields->fields(); \
1181 QListIterator<KDbField*> it(*flist); \
1182 vals \
1183 it.toFront(); \
1184 QString tableName((it.hasNext() && it.peekNext()->table()) ? it.next()->table()->name() : QLatin1String("??")); \
1185 return insertRecordInternal(tableName, fields, \
1186 KDbEscapedString(QLatin1String("INSERT INTO ") + escapeIdentifier(tableName)) \
1187 + " (" + fields->sqlFieldsList(this) \
1188 + ") VALUES (" + value + ')'); \
1189 }
1190
1191C_INS_REC_ALL
1192
1193#undef C_A
1194#undef V_A
1195#undef V_ALAST
1196#undef C_INS_REC
1197#undef C_INS_REC_ALL
1198
1199QSharedPointer<KDbSqlResult> KDbConnection::insertRecord(KDbTableSchema *tableSchema,
1200 const QList<QVariant> &values)
1201{
1202// Each SQL identifier needs to be escaped in the generated query.
1204 const KDbField::List *flist = tableSchema->fields();
1205 if (flist->isEmpty()) {
1206 return res;
1207 }
1208 KDbField::ListIterator fieldsIt(flist->constBegin());
1210 KDbEscapedString sql;
1211 sql.reserve(4096);
1212 while (fieldsIt != flist->constEnd() && (it != values.end())) {
1213 KDbField *f = *fieldsIt;
1214 if (sql.isEmpty()) {
1215 sql = KDbEscapedString("INSERT INTO ") + escapeIdentifier(tableSchema->name())
1216 + " VALUES (";
1217 }
1218 else {
1219 sql += ',';
1220 }
1221 sql += d->driver->valueToSql(f, *it);
1222// kdbDebug() << "val" << i++ << ": " << d->driver->valueToSql( f, *it );
1223 ++it;
1224 ++fieldsIt;
1225 }
1226 sql += ')';
1227 m_result.setSql(sql);
1229 return res;
1230}
1231
1232QSharedPointer<KDbSqlResult> KDbConnection::insertRecord(KDbFieldList *fields,
1233 const QList<QVariant> &values)
1234{
1235// Each SQL identifier needs to be escaped in the generated query.
1237 const KDbField::List *flist = fields->fields();
1238 if (flist->isEmpty()) {
1239 return res;
1240 }
1241 KDbField::ListIterator fieldsIt(flist->constBegin());
1242 KDbEscapedString sql;
1243 sql.reserve(4096);
1245 const QString tableName(flist->first()->table()->name());
1246 while (fieldsIt != flist->constEnd() && it != values.constEnd()) {
1247 KDbField *f = *fieldsIt;
1248 if (sql.isEmpty()) {
1249 sql = KDbEscapedString("INSERT INTO ") + escapeIdentifier(tableName) + '(' +
1250 fields->sqlFieldsList(this) + ") VALUES (";
1251 }
1252 else {
1253 sql += ',';
1254 }
1255 sql += d->driver->valueToSql(f, *it);
1256// kdbDebug() << "val" << i++ << ": " << d->driver->valueToSql( f, *it );
1257 ++it;
1258 ++fieldsIt;
1259 if (fieldsIt == flist->constEnd())
1260 break;
1261 }
1262 sql += ')';
1263 m_result.setSql(sql);
1264 res = insertRecordInternal(tableName, fields, sql);
1265 return res;
1266}
1267
1268inline static bool checkSql(const KDbEscapedString& sql, KDbResult* result)
1269{
1270 Q_ASSERT(result);
1271 if (!sql.isValid()) {
1272 *result = KDbResult(ERR_SQL_EXECUTION_ERROR,
1273 KDbConnection::tr("SQL statement for execution is invalid or empty."));
1274 result->setErrorSql(sql); //remember for error handling
1275 return false;
1276 }
1277 return true;
1278}
1279
1285
1287{
1288 m_result.setSql(sql);
1289 if (!checkSql(sql, &m_result)) {
1290 return false;
1291 }
1292 if (!drv_executeSql(sql)) {
1293 m_result.setMessage(QString()); //clear as this could be most probably just "Unknown error" string.
1294 m_result.setErrorSql(sql);
1295 m_result.prependMessage(ERR_SQL_EXECUTION_ERROR,
1296 tr("Error while executing SQL statement."));
1297 kdbWarning() << m_result;
1298 return false;
1299 }
1300 return true;
1301}
1302
1304{
1305 for (KDbField::ListIterator it(fieldlist.fieldsIterator()); it != fieldlist.fieldsIteratorConstEnd(); ++it) {
1306 if (d->driver->isSystemFieldName((*it)->name()))
1307 return *it;
1308 }
1309 return nullptr;
1310}
1311
1312//! Creates a KDbField list for kexi__fields, for sanity. Used by createTable()
1313static KDbFieldList* createFieldListForKexi__Fields(KDbTableSchema *kexi__fieldsSchema)
1314{
1315 if (!kexi__fieldsSchema)
1316 return nullptr;
1317 return kexi__fieldsSchema->subList(
1319 << "t_id"
1320 << "f_type"
1321 << "f_name"
1322 << "f_length"
1323 << "f_precision"
1324 << "f_constraints"
1325 << "f_options"
1326 << "f_default"
1327 << "f_order"
1328 << "f_caption"
1329 << "f_help"
1330 );
1331}
1332
1333static QVariant buildLengthValue(const KDbField &f)
1334{
1335 if (f.isFPNumericType()) {
1336 return f.scale();
1337 }
1338 return f.maxLength();
1339}
1340
1341//! builds a list of values for field's @a f properties. Used by createTable().
1342static void buildValuesForKexi__Fields(QList<QVariant>& vals, KDbField* f)
1343{
1344 const KDbField::Type type = f->type(); // cache: evaluating type of expressions can be expensive
1345 vals.clear();
1346 vals
1347 << QVariant(f->table()->id())
1348 << QVariant(type)
1349 << QVariant(f->name())
1350 << buildLengthValue(*f)
1351 << QVariant(KDbField::isFPNumericType(type) ? f->precision() : 0)
1352 << QVariant(f->constraints())
1353 << QVariant(f->options())
1354 // KDb::variantToString() is needed here because the value can be of any QVariant type,
1355 // depending on f->type()
1356 << (f->defaultValue().isNull()
1358 << QVariant(f->order())
1359 << QVariant(f->caption())
1360 << QVariant(f->description());
1361}
1362
1364{
1365 if (!field || !field->table())
1366 return false;
1367 KDbFieldList *fl = createFieldListForKexi__Fields(d->table(QLatin1String("kexi__fields")));
1368 if (!fl)
1369 return false;
1370
1371 QList<QVariant> vals;
1372 buildValuesForKexi__Fields(vals, field);
1374 bool first = true;
1375 KDbEscapedString sql("UPDATE kexi__fields SET ");
1376 foreach(KDbField *f, *fl->fields()) {
1377 sql.append((first ? QString() : QLatin1String(", ")) +
1378 f->name() + QLatin1Char('=') + d->driver->valueToSql(f, *valsIt));
1379 if (first)
1380 first = false;
1381 ++valsIt;
1382 }
1383 delete fl;
1384
1385 sql.append(KDbEscapedString(" WHERE t_id=%1 AND f_name=%2")
1386 .arg(d->driver->valueToSql(KDbField::Integer, field->table()->id()))
1387 .arg(escapeString(field->name())));
1388 return executeSql(sql);
1389}
1390
1391#define createTable_ERR \
1392 { kdbDebug() << "ERROR!"; \
1393 m_result.prependMessage(KDbConnection::tr("Creating table failed.")); \
1394 rollbackAutoCommitTransaction(tg.transaction()); \
1395 return false; }
1396
1398{
1400 return false;
1401
1402 //check if there are any fields
1403 if (tableSchema->fieldCount() < 1) {
1404 clearResult();
1405 m_result = KDbResult(ERR_CANNOT_CREATE_EMPTY_OBJECT,
1406 tr("Could not create table without fields."));
1407 return false;
1408 }
1409 KDbInternalTableSchema* internalTable = dynamic_cast<KDbInternalTableSchema*>(tableSchema);
1410 const QString tableName(tableSchema->name());
1411
1412 if (!internalTable) {
1413 if (d->driver->isSystemObjectName(tableName)) {
1414 clearResult();
1415 m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
1416 tr("System name \"%1\" cannot be used as table name.")
1417 .arg(tableSchema->name()));
1418 return false;
1419 }
1420
1422 if (sys_field) {
1423 clearResult();
1424 m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
1425 tr("System name \"%1\" cannot be used as one of fields in \"%2\" table.")
1426 .arg(sys_field->name(), tableName));
1427 return false;
1428 }
1429 }
1430
1431 bool previousSchemaStillKept = false;
1432
1433 KDbTableSchema *existingTable = nullptr;
1435 //get previous table (do not retrieve, though)
1436 existingTable = this->tableSchema(tableName);
1437 if (existingTable) {
1438 if (existingTable == tableSchema) {
1439 clearResult();
1440 m_result = KDbResult(ERR_OBJECT_EXISTS,
1441 tr("Could not create the same table \"%1\" twice.").arg(tableSchema->name()));
1442 return false;
1443 }
1444//! @todo (js) update any structure (e.g. queries) that depend on this table!
1445 if (existingTable->id() > 0)
1446 tableSchema->setId(existingTable->id()); //copy id from existing table
1447 previousSchemaStillKept = true;
1448 if (!dropTableInternal(existingTable, false /*alsoRemoveSchema*/))
1449 return false;
1450 }
1451 } else {
1452 if (!internalTable && this->tableSchema(tableSchema->name())) {
1453 clearResult();
1454 m_result = KDbResult(ERR_OBJECT_EXISTS,
1455 tr("Table \"%1\" already exists.").arg(tableSchema->name()));
1456 return false;
1457 }
1458 }
1461 return false;
1462
1463 if (internalTable) {
1464 if (!drv_containsTable(internalTable->name())) { // internal table may exist
1466 createTable_ERR;
1467 }
1468 }
1469 } else {
1471 createTable_ERR;
1472 }
1473 }
1474
1475 //add the object data to kexi__* tables
1476 if (!internalTable) {
1477 //update kexi__objects
1479 createTable_ERR;
1480
1481 KDbTableSchema *ts = d->table(QLatin1String("kexi__fields"));
1482 if (!ts)
1483 return false;
1484 //for sanity: remove field info (if any) for this table id
1485 if (!KDb::deleteRecords(this, *ts, QLatin1String("t_id"), tableSchema->id()))
1486 return false;
1487
1488 KDbFieldList *fl = createFieldListForKexi__Fields(ts);
1489 if (!fl)
1490 return false;
1491
1492 foreach(KDbField *f, *tableSchema->fields()) {
1493 QList<QVariant> vals;
1494 buildValuesForKexi__Fields(vals, f);
1495 if (!insertRecord(fl, vals))
1496 createTable_ERR;
1497 }
1498 delete fl;
1499
1501 createTable_ERR;
1502 }
1504 if (res) {
1505 if (!internalTable) {
1506 if (previousSchemaStillKept) {
1507 //remove previous table schema
1508 d->removeTable(tableSchema->id());
1509 }
1510 }
1511 //store locally
1512 d->insertTable(tableSchema);
1513 //ok, this table is not created by the connection
1515 }
1516 return res;
1517}
1518
1520{
1521 clearResult();
1522 if (this->tableSchema(tableSchema.name()) != &tableSchema) {
1523 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1524 tr("Table \"%1\" does not exist.").arg(tableSchema.name()));
1525 return nullptr;
1526 }
1527 KDbTableSchema *copiedTable = new KDbTableSchema(tableSchema, false /* !copyId*/);
1528 // copy name, caption, description
1529 copiedTable->setName(newData.name());
1530 copiedTable->setCaption(newData.caption());
1531 copiedTable->setDescription(newData.description());
1532 // copy the structure and data
1533 if (!createTable(copiedTable,
1535 {
1536 delete copiedTable;
1537 return nullptr;
1538 }
1539 if (!drv_copyTableData(tableSchema, *copiedTable)) {
1540 dropTable(copiedTable);
1541 delete copiedTable;
1542 return nullptr;
1543 }
1544 return copiedTable;
1545}
1546
1548{
1549 clearResult();
1550 KDbTableSchema* ts = tableSchema(tableName);
1551 if (!ts) {
1552 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1553 tr("Table \"%1\" does not exist.").arg(tableName));
1554 return nullptr;
1555 }
1556 return copyTable(*ts, newData);
1557}
1558
1560 const KDbTableSchema &destinationTableSchema)
1561{
1562 KDbEscapedString sql = KDbEscapedString("INSERT INTO %1 SELECT * FROM %2")
1563 .arg(escapeIdentifier(destinationTableSchema.name()))
1565 return executeSql(sql);
1566}
1567
1569{
1570 clearResult();
1571 //remove table schema from kexi__* tables
1572 KDbTableSchema *kexi__objects = d->table(QLatin1String("kexi__objects"));
1573 KDbTableSchema *kexi__objectdata = d->table(QLatin1String("kexi__objectdata"));
1574 if (!kexi__objects || !kexi__objectdata
1575 || !KDb::deleteRecords(this, *kexi__objects, QLatin1String("o_id"), objId) //schema entry
1576 || !KDb::deleteRecords(this, *kexi__objectdata, QLatin1String("o_id"), objId)) //data blocks
1577 {
1578 m_result = KDbResult(ERR_DELETE_SERVER_ERROR,
1579 tr("Could not delete object's data."));
1580 return false;
1581 }
1582 return true;
1583}
1584
1586{
1587 return executeSql(KDbEscapedString("DROP TABLE %1").arg(escapeIdentifier(tableName)));
1588}
1589
1594
1596{
1597 // Each SQL identifier needs to be escaped in the generated query.
1598 clearResult();
1599 if (!tableSchema)
1600 return false;
1601
1602 //be sure that we handle the correct KDbTableSchema object:
1603 if (tableSchema->id() < 0
1604 || this->tableSchema(tableSchema->name()) != tableSchema
1605 || this->tableSchema(tableSchema->id()) != tableSchema) {
1606 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1607 tr("Could not delete table \"%1\". %2")
1608 .arg(tr("Unexpected name or identifier."),
1609 tableSchema->name()));
1610 return false;
1611 }
1612
1614 if (true != res)
1615 return res;
1616
1617 //sanity checks:
1618 if (d->driver->isSystemObjectName(tableSchema->name())) {
1619 m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
1620 tr("Could not delete table \"%1\". %2")
1621 .arg(tableSchema->name(),
1622 d->strItIsASystemObject()));
1623 return false;
1624 }
1625
1628 return false;
1629
1630 //for sanity we're checking if this table exists physically
1631 const tristate result = drv_containsTable(tableSchema->name());
1632 if (~result) {
1633 return cancelled;
1634 }
1635 if (result == true) {
1637 return false;
1638 }
1639
1640 KDbTableSchema *ts = d->table(QLatin1String("kexi__fields"));
1641 if (!ts || !KDb::deleteRecords(this, *ts, QLatin1String("t_id"), tableSchema->id())) //field entries
1642 return false;
1643
1644 //remove table schema from kexi__objects table
1645 if (!removeObject(tableSchema->id())) {
1646 return false;
1647 }
1648
1649 if (alsoRemoveSchema) {
1650//! @todo js: update any structure (e.g. queries) that depend on this table!
1651 tristate res = removeDataBlock(tableSchema->id(), QLatin1String("extended_schema"));
1652 if (!res)
1653 return false;
1654 d->removeTable(tableSchema->id());
1655 }
1657}
1658
1660{
1661 clearResult();
1662 KDbTableSchema* ts = tableSchema(tableName);
1663 if (!ts) {
1664 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1665 tr("Table \"%1\" does not exist.").arg(tableName));
1666 return false;
1667 }
1668 return dropTable(ts);
1669}
1670
1672{
1673 clearResult();
1675 if (true != res)
1676 return res;
1677
1678 if (tableSchema == newTableSchema) {
1679 m_result = KDbResult(ERR_OBJECT_THE_SAME,
1680 tr("Could not alter table \"%1\" using the same table as destination.")
1681 .arg(tableSchema->name()));
1682 return false;
1683 }
1684//! @todo (js) implement real altering
1685//! @todo (js) update any structure (e.g. query) that depend on this table!
1686 bool ok = true;
1687 bool empty;
1688#if 0 //! @todo uncomment:
1689 empty = isEmpty(tableSchema, ok) && ok;
1690#else
1691 empty = true;
1692#endif
1693 if (empty) {
1694 ok = createTable(newTableSchema, KDbConnection::CreateTableOption::Default
1696 }
1697 return ok;
1698}
1699
1700bool KDbConnection::alterTableName(KDbTableSchema* tableSchema, const QString& newName,
1701 AlterTableNameOptions options)
1702{
1703 clearResult();
1704 if (tableSchema != this->tableSchema(tableSchema->id())) {
1705 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1706 tr("Unknown table \"%1\".").arg(tableSchema->name()));
1707 return false;
1708 }
1709 if (newName.isEmpty() || !KDb::isIdentifier(newName)) {
1710 m_result = KDbResult(ERR_INVALID_IDENTIFIER,
1711 tr("Invalid table name \"%1\".").arg(newName));
1712 return false;
1713 }
1714 const QString oldTableName = tableSchema->name();
1715 const QString newTableName = newName.trimmed();
1716 if (oldTableName.trimmed() == newTableName) {
1717 m_result = KDbResult(ERR_OBJECT_THE_SAME,
1718 tr("Could not rename table \"%1\" using the same name.")
1719 .arg(newTableName));
1720 return false;
1721 }
1722//! @todo alter table name for server DB backends!
1723//! @todo what about objects (queries/forms) that use old name?
1724 KDbTableSchema *tableToReplace = this->tableSchema(newName);
1725 const bool destTableExists = tableToReplace != nullptr;
1726 const int origID = destTableExists ? tableToReplace->id() : -1; //will be reused in the new table
1727 if (!(options & AlterTableNameOption::DropDestination) && destTableExists) {
1728 m_result = KDbResult(ERR_OBJECT_EXISTS,
1729 tr("Could not rename table \"%1\" to \"%2\". Table \"%3\" already exists.")
1730 .arg(tableSchema->name(), newName, newName));
1731 return false;
1732 }
1733
1734//helper:
1735#define alterTableName_ERR \
1736 tableSchema->setName(oldTableName) //restore old name
1737
1740 return false;
1741
1742 // drop the table replaced (with schema)
1743 if (destTableExists) {
1744 if (!dropTable(newName)) {
1745 return false;
1746 }
1747
1748 // the new table owns the previous table's id:
1749 if (!executeSql(
1750 KDbEscapedString("UPDATE kexi__objects SET o_id=%1 WHERE o_id=%2 AND o_type=%3")
1751 .arg(d->driver->valueToSql(KDbField::Integer, origID))
1752 .arg(d->driver->valueToSql(KDbField::Integer, tableSchema->id()))
1753 .arg(d->driver->valueToSql(KDbField::Integer, int(KDb::TableObjectType)))))
1754 {
1755 return false;
1756 }
1757 if (!executeSql(KDbEscapedString("UPDATE kexi__fields SET t_id=%1 WHERE t_id=%2")
1758 .arg(d->driver->valueToSql(KDbField::Integer, origID))
1759 .arg(d->driver->valueToSql(KDbField::Integer, tableSchema->id()))))
1760 {
1761 return false;
1762 }
1763
1764 //maintain table ID
1765 d->changeTableId(tableSchema, origID);
1766 tableSchema->setId(origID);
1767 }
1768
1769 if (!drv_alterTableName(tableSchema, newTableName)) {
1770 alterTableName_ERR;
1771 return false;
1772 }
1773
1774 // Update kexi__objects
1775 //! @todo
1776 if (!executeSql(KDbEscapedString("UPDATE kexi__objects SET o_name=%1 WHERE o_id=%2")
1777 .arg(escapeString(tableSchema->name()))
1778 .arg(d->driver->valueToSql(KDbField::Integer, tableSchema->id()))))
1779 {
1780 alterTableName_ERR;
1781 return false;
1782 }
1783//! @todo what about caption?
1784
1785 //restore old name: it will be changed soon!
1786 tableSchema->setName(oldTableName);
1787
1789 alterTableName_ERR;
1790 return false;
1791 }
1792
1793 //update tableSchema:
1794 d->renameTable(tableSchema, newTableName);
1795 return true;
1796}
1797
1799{
1800 const QString oldTableName = tableSchema->name();
1801 tableSchema->setName(newName);
1802
1803 if (!executeSql(KDbEscapedString("ALTER TABLE %1 RENAME TO %2")
1804 .arg(KDbEscapedString(escapeIdentifier(oldTableName)),
1806 {
1807 tableSchema->setName(oldTableName); //restore old name
1808 return false;
1809 }
1810 return true;
1811}
1812
1814{
1815 clearResult();
1816 if (!querySchema)
1817 return false;
1818
1821 return false;
1822
1823 //remove query schema from kexi__objects table
1824 if (!removeObject(querySchema->id())) {
1825 return false;
1826 }
1827
1828//! @todo update any structure that depend on this table!
1829 d->removeQuery(querySchema);
1831}
1832
1833bool KDbConnection::dropQuery(const QString& queryName)
1834{
1835 clearResult();
1836 KDbQuerySchema* qs = querySchema(queryName);
1837 if (!qs) {
1838 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1839 tr("Query \"%1\" does not exist.").arg(queryName));
1840 return false;
1841 }
1842 return dropQuery(qs);
1843}
1844
1846{
1847 Q_ASSERT(ok);
1849 const KDbEscapedString sql(d->driver->behavior()->GET_TABLE_NAMES_SQL);
1850 if (sql.isEmpty()) {
1851 *ok = false;
1852 return QStringList();
1853 }
1855 if (!result) {
1856 *ok = false;
1857 return QStringList();
1858 }
1859 Q_FOREVER {
1860 QSharedPointer<KDbSqlRecord> record = result->fetchRecord();
1861 if (!record) {
1862 if (result->lastResult().isError()) {
1863 *ok = false;
1864 return QStringList();
1865 }
1866 break;
1867 }
1868 tableNames.append(record->stringValue(0));
1869 }
1870 *ok = true;
1871 return tableNames;
1872}
1873
1875{
1877 KDbEscapedString sql;
1878 if (!builder.generateCreateTableStatement(&sql,tableSchema)) {
1879 return false;
1880 }
1881 //kdbDebug() << "******** " << sql;
1882 return executeSql(sql);
1883}
1884
1886{
1887 KDbTableSchema *ts = tableSchema(tableName);
1888 if (!ts)
1889 return false;
1890 return drv_createTable(*ts);
1891}
1892
1894{
1895 if ((d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
1896 || !d->autoCommit) {
1898 return true;
1899 }
1900
1901 // commit current transaction (if present) for drivers
1902 // that allow single transaction per connection
1903 if (d->driver->behavior()->features & KDbDriver::SingleTransactions) {
1904 if (d->defaultTransactionStartedInside) //only commit internally started transaction
1907 return false; //we have a real error
1908 }
1909
1910 d->defaultTransactionStartedInside = d->default_trans.isNull();
1911 if (!d->defaultTransactionStartedInside) {
1912 tg->setTransaction(d->default_trans);
1913 tg->doNothing();
1914 return true; //reuse externally started transaction
1915 }
1916 } else if (!(d->driver->behavior()->features & KDbDriver::MultipleTransactions)) {
1918 return true; //no trans. supported at all - just return
1919 }
1921 return !m_result.isError();
1922}
1923
1925{
1926 if (d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
1927 return true;
1928 if (trans.isNull() || !d->driver->transactionsSupported())
1929 return true;
1930 if (d->driver->behavior()->features & KDbDriver::SingleTransactions) {
1931 if (!d->defaultTransactionStartedInside) //only commit internally started transaction
1932 return true; //give up
1933 }
1935}
1936
1938{
1939 if (trans.isNull() || !d->driver->transactionsSupported())
1940 return true;
1941 return rollbackTransaction(trans);
1942}
1943
1944#define SET_ERR_TRANS_NOT_SUPP \
1945 { m_result = KDbResult(ERR_UNSUPPORTED_DRV_FEATURE, \
1946 KDbConnection::tr("Transactions are not supported for \"%1\" driver.").arg( d->driver->metaData()->name() )); }
1947
1948#define SET_BEGIN_TR_ERROR \
1949 { if (!m_result.isError()) \
1950 m_result = KDbResult(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, \
1951 KDbConnection::tr("Begin transaction failed.")); }
1952
1954{
1955 if (!checkIsDatabaseUsed())
1956 return KDbTransaction();
1957 KDbTransaction trans;
1958 if (d->driver->behavior()->features & KDbDriver::IgnoreTransactions) {
1959 //we're creating dummy transaction data here,
1960 //so it will look like active
1961 trans.m_data = new KDbTransactionData(this);
1962 d->transactions.append(trans);
1963 return trans;
1964 }
1965 if (d->driver->behavior()->features & KDbDriver::SingleTransactions) {
1966 if (d->default_trans.isActive()) {
1967 m_result = KDbResult(ERR_TRANSACTION_ACTIVE,
1968 tr("Transaction already started."));
1969 return KDbTransaction();
1970 }
1971 if (!(trans.m_data = drv_beginTransaction())) {
1972 SET_BEGIN_TR_ERROR;
1973 return KDbTransaction();
1974 }
1975 d->default_trans = trans;
1976 d->transactions.append(trans);
1977 return d->default_trans;
1978 }
1979 if (d->driver->behavior()->features & KDbDriver::MultipleTransactions) {
1980 if (!(trans.m_data = drv_beginTransaction())) {
1981 SET_BEGIN_TR_ERROR;
1982 return KDbTransaction();
1983 }
1984 d->transactions.append(trans);
1985 return trans;
1986 }
1987
1988 SET_ERR_TRANS_NOT_SUPP;
1989 return KDbTransaction();
1990}
1991
1994{
1995 if (!isDatabaseUsed())
1996 return false;
1997 if (!d->driver->transactionsSupported()
1998 && !(d->driver->behavior()->features & KDbDriver::IgnoreTransactions)) {
1999 SET_ERR_TRANS_NOT_SUPP;
2000 return false;
2001 }
2002 KDbTransaction t = trans;
2003 if (!t.isActive()) { //try default tr.
2004 if (!d->default_trans.isActive()) {
2006 return true;
2007 }
2008 clearResult();
2009 m_result = KDbResult(ERR_NO_TRANSACTION_ACTIVE,
2010 tr("Transaction not started."));
2011 return false;
2012 }
2013 t = d->default_trans;
2014 d->default_trans = KDbTransaction(); //now: no default tr.
2015 }
2016 bool ret = true;
2017 if (!(d->driver->behavior()->features & KDbDriver::IgnoreTransactions))
2018 ret = drv_commitTransaction(t.m_data);
2019 if (t.m_data)
2020 t.m_data->setActive(false); //now this transaction if inactive
2021 if (!d->dontRemoveTransactions) //true=transaction obj will be later removed from list
2022 d->transactions.removeAt(d->transactions.indexOf(t));
2023 if (!ret && !m_result.isError())
2024 m_result = KDbResult(ERR_ROLLBACK_OR_COMMIT_TRANSACTION,
2025 tr("Error on commit transaction."));
2026 return ret;
2027}
2028
2031{
2032 if (!isDatabaseUsed())
2033 return false;
2034 if (!d->driver->transactionsSupported()
2035 && !(d->driver->behavior()->features & KDbDriver::IgnoreTransactions)) {
2036 SET_ERR_TRANS_NOT_SUPP;
2037 return false;
2038 }
2039 KDbTransaction t = trans;
2040 if (!t.isActive()) { //try default tr.
2041 if (!d->default_trans.isActive()) {
2043 return true;
2044 }
2045 clearResult();
2046 m_result = KDbResult(ERR_NO_TRANSACTION_ACTIVE,
2047 tr("Transaction not started."));
2048 return false;
2049 }
2050 t = d->default_trans;
2051 d->default_trans = KDbTransaction(); //now: no default tr.
2052 }
2053 bool ret = true;
2054 if (!(d->driver->behavior()->features & KDbDriver::IgnoreTransactions))
2055 ret = drv_rollbackTransaction(t.m_data);
2056 if (t.m_data)
2057 t.m_data->setActive(false); //now this transaction if inactive
2058 if (!d->dontRemoveTransactions) //true=transaction obj will be later removed from list
2059 d->transactions.removeAt(d->transactions.indexOf(t));
2060 if (!ret && !m_result.isError())
2061 m_result = KDbResult(ERR_ROLLBACK_OR_COMMIT_TRANSACTION,
2062 tr("Error on rollback transaction."));
2063 return ret;
2064}
2065
2066#undef SET_ERR_TRANS_NOT_SUPP
2067#undef SET_BEGIN_TR_ERROR
2068
2069/*bool KDbConnection::duringTransaction()
2070{
2071 return drv_duringTransaction();
2072}*/
2073
2075{
2076 return d->default_trans;
2077}
2078
2080{
2081 if (!isDatabaseUsed())
2082 return;
2083 if (!(d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
2084 && (!trans.isActive() || !d->driver->transactionsSupported())) {
2085 return;
2086 }
2087 d->default_trans = trans;
2088}
2089
2091{
2092 return d->transactions;
2093}
2094
2096{
2097 return d->autoCommit;
2098}
2099
2101{
2102 if (d->autoCommit == on || d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
2103 return true;
2104 if (!drv_setAutoCommit(on))
2105 return false;
2106 d->autoCommit = on;
2107 return true;
2108}
2109
2111{
2112 if (!executeSql(KDbEscapedString("BEGIN")))
2113 return nullptr;
2114 return new KDbTransactionData(this);
2115}
2116
2121
2126
2128{
2129 return true;
2130}
2131
2133{
2134 if (sql.isEmpty())
2135 return nullptr;
2136 KDbCursor *c = prepareQuery(sql, options);
2137 if (!c)
2138 return nullptr;
2139 if (!c->open()) {//err - kill that
2140 m_result = c->result();
2141 CursorDeleter deleter(c);
2142 return nullptr;
2143 }
2144 return c;
2145}
2146
2148 KDbCursor::Options options)
2149{
2150 KDbCursor *c = prepareQuery(query, params, options);
2151 if (!c)
2152 return nullptr;
2153 if (!c->open()) {//err - kill that
2154 m_result = c->result();
2155 CursorDeleter deleter(c);
2156 return nullptr;
2157 }
2158 return c;
2159}
2160
2165
2170
2175
2177 KDbCursor::Options options)
2178{
2179 KDbCursor* cursor = prepareQuery(query, options);
2180 if (cursor)
2181 cursor->setQueryParameters(params);
2182 return cursor;
2183}
2184
2186{
2187 if (!cursor)
2188 return false;
2189 if (cursor->connection() != this) {//illegal call
2190 kdbWarning() << "Could not delete the cursor not owned by the same connection!";
2191 return false;
2192 }
2193 const bool ret = cursor->close();
2194 CursorDeleter deleter(cursor);
2195 return ret;
2196}
2197
2198//! @todo IMPORTANT: fix KDbConnection::setupObjectData() after refactoring
2200{
2201 if (data.count() < 5) {
2202 kdbWarning() << "Aborting, object data should have at least 5 elements, found" << data.count();
2203 return false;
2204 }
2205 bool ok;
2206 const int id = data[0].toInt(&ok);
2207 if (!ok)
2208 return false;
2209 object->setId(id);
2210 const QString name(data[2].toString());
2211 if (!KDb::isIdentifier(name)) {
2212 m_result = KDbResult(ERR_INVALID_IDENTIFIER,
2213 tr("Invalid object name \"%1\".").arg(name));
2214 return false;
2215 }
2216 object->setName(name);
2217 object->setCaption(data[3].toString());
2218 object->setDescription(data[4].toString());
2219
2220// kdbDebug()<<"@@@ KDbConnection::setupObjectData() == " << sdata.schemaDataDebugString();
2221 return true;
2222}
2223
2225{
2227 if (type == KDb::AnyObjectType) {
2228 if (true != querySingleRecord(KDbEscapedString("SELECT o_id, o_type, o_name, o_caption, "
2229 "o_desc FROM kexi__objects WHERE o_id=%1")
2230 .arg(d->driver->valueToSql(KDbField::Integer, id)),
2231 &data)) {
2232 return cancelled;
2233 }
2234 } else {
2235 if (true != querySingleRecord(KDbEscapedString("SELECT o_id, o_type, o_name, o_caption, o_desc "
2236 "FROM kexi__objects WHERE o_type=%1 AND o_id=%2")
2237 .arg(d->driver->valueToSql(KDbField::Integer, type))
2238 .arg(d->driver->valueToSql(KDbField::Integer, id)),
2239 &data))
2240 {
2241 return cancelled;
2242 }
2243 }
2244 return setupObjectData(data, object);
2245}
2246
2248{
2250 if (true != querySingleRecord(
2251 KDbEscapedString("SELECT o_id, o_type, o_name, o_caption, o_desc "
2252 "FROM kexi__objects WHERE o_type=%1 AND o_name=%2")
2253 .arg(d->driver->valueToSql(KDbField::Integer, type))
2254 .arg(escapeString(name)),
2255 &data))
2256 {
2257 return cancelled;
2258 }
2259 return setupObjectData(data, object);
2260}
2261
2262bool KDbConnection::storeObjectDataInternal(KDbObject* object, bool newObject)
2263{
2264 KDbTableSchema *ts = d->table(QLatin1String("kexi__objects"));
2265 if (!ts)
2266 return false;
2267 if (newObject) {
2268 int existingID;
2269 if (true == querySingleNumber(
2270 KDbEscapedString("SELECT o_id FROM kexi__objects WHERE o_type=%1 AND o_name=%2")
2271 .arg(d->driver->valueToSql(KDbField::Integer, object->type()))
2272 .arg(escapeString(object->name())), &existingID))
2273 {
2274 //we already have stored an object data with the same name and type:
2275 //just update it's properties as it would be existing object
2276 object->setId(existingID);
2277 newObject = false;
2278 }
2279 }
2280 if (newObject) {
2281 if (object->id() <= 0) {//get new ID
2283 QList<QByteArray>() << "o_type" << "o_name" << "o_caption" << "o_desc"));
2284 if (!fl) {
2285 return false;
2286 }
2288 = insertRecord(fl.data(), QVariant(object->type()), QVariant(object->name()),
2289 QVariant(object->caption()), QVariant(object->description()));
2290 if (!result) {
2291 return false;
2292 }
2293 //fetch newly assigned ID
2294//! @todo safe to cast it?
2295 quint64 obj_id = KDb::lastInsertedAutoIncValue(result, QLatin1String("o_id"), *ts);
2296 //kdbDebug() << "NEW obj_id == " << obj_id;
2297 if (obj_id == std::numeric_limits<quint64>::max()) {
2298 return false;
2299 }
2300 object->setId(obj_id);
2301 return true;
2302 } else {
2304 QList<QByteArray>() << "o_id" << "o_type" << "o_name" << "o_caption" << "o_desc"));
2305 return fl && insertRecord(fl.data(), QVariant(object->id()), QVariant(object->type()),
2306 QVariant(object->name()), QVariant(object->caption()),
2307 QVariant(object->description()));
2308 }
2309 }
2310 //existing object:
2311 return executeSql(
2312 KDbEscapedString("UPDATE kexi__objects SET o_type=%2, o_caption=%3, o_desc=%4 WHERE o_id=%1")
2313 .arg(d->driver->valueToSql(KDbField::Integer, object->id()))
2314 .arg(d->driver->valueToSql(KDbField::Integer, object->type()))
2315 .arg(escapeString(object->caption()))
2316 .arg(escapeString(object->description())));
2317}
2318
2320{
2321 return storeObjectDataInternal(object, false);
2322}
2323
2325{
2326 return storeObjectDataInternal(object, true);
2327}
2328
2330{
2331 return escapingType == KDb::KDbEscaping
2333}
2334
2336 KDbQuerySchema* query,
2337 const QList<QVariant>* params)
2338{
2339 Q_ASSERT(!sql.isEmpty() || query);
2340 clearResult();
2341 if (!sql.isEmpty()) {
2342 return executeQuery(sql);
2343 }
2344 if (!query) {
2345 return nullptr;
2346 }
2347 if (params) {
2348 return executeQuery(query, *params);
2349 }
2350 return executeQuery(query);
2351}
2352
2354 KDbQuerySchema *query,
2355 const QList<QVariant> *params,
2356 QueryRecordOptions options)
2357{
2358 Q_ASSERT(sql || query);
2359 if (sql) {
2360 //! @todo does not work with non-SQL data sources
2361 m_result.setSql(d->driver->addLimitTo1(*sql, options & QueryRecordOption::AddLimitTo1));
2362 }
2363 KDbCursor *cursor = executeQueryInternal(m_result.sql(), query, params);
2364 if (!cursor) {
2365 kdbWarning() << "!querySingleRecordInternal() " << m_result.sql();
2366 return false;
2367 }
2368 if (!cursor->moveFirst() || cursor->eof() || !cursor->storeCurrentRecord(data)) {
2369 const tristate result = cursor->result().isError() ? tristate(false) : tristate(cancelled);
2370 // kdbDebug() << "!cursor->moveFirst() || cursor->eof() || cursor->storeCurrentRecord(data)
2371 // "
2372 // "m_result.sql()=" << m_result.sql();
2373 m_result = cursor->result();
2374 deleteCursor(cursor);
2375 return result;
2376 }
2377 return deleteCursor(cursor);
2378}
2379
2381 QueryRecordOptions options)
2382{
2383 return querySingleRecordInternal(data, &sql, nullptr, nullptr, options);
2384}
2385
2387 QueryRecordOptions options)
2388{
2389 return querySingleRecordInternal(data, nullptr, query, nullptr, options);
2390}
2391
2393 const QList<QVariant> &params, QueryRecordOptions options)
2394{
2395 return querySingleRecordInternal(data, nullptr, query, &params, options);
2396}
2397
2399{
2400 if (column >= cursor->fieldCount()) {
2401 m_result = KDbResult(ERR_CURSOR_RECORD_FETCHING,
2402 tr("Column \"%1\" does not exist in the query.").arg(column));
2403 return false;
2404 }
2405 return true;
2406}
2407
2409 KDbQuerySchema *query,
2410 const QList<QVariant> *params, int column,
2411 QueryRecordOptions options)
2412{
2413 Q_ASSERT(sql || query);
2414 if (sql) {
2415 //! @todo does not work with non-SQL data sources
2416 m_result.setSql(d->driver->addLimitTo1(*sql, options & QueryRecordOption::AddLimitTo1));
2417 }
2418 KDbCursor *cursor = executeQueryInternal(m_result.sql(), query, params);
2419 if (!cursor) {
2420 kdbWarning() << "!querySingleStringInternal()" << m_result.sql();
2421 return false;
2422 }
2423 if (!cursor->moveFirst() || cursor->eof()) {
2424 const tristate result = cursor->result().isError() ? tristate(false) : tristate(cancelled);
2425 // kdbDebug() << "!cursor->moveFirst() || cursor->eof()" << m_result.sql();
2426 deleteCursor(cursor);
2427 return result;
2428 }
2429 if (!checkIfColumnExists(cursor, column)) {
2430 deleteCursor(cursor);
2431 return false;
2432 }
2433 if (value) {
2434 *value = cursor->value(column).toString();
2435 }
2436 return deleteCursor(cursor);
2437}
2438
2440 QueryRecordOptions options)
2441{
2442 return querySingleStringInternal(&sql, value, nullptr, nullptr, column, options);
2443}
2444
2446 QueryRecordOptions options)
2447{
2448 return querySingleStringInternal(nullptr, value, query, nullptr, column, options);
2449}
2450
2452 const QList<QVariant> &params, int column,
2453 QueryRecordOptions options)
2454{
2455 return querySingleStringInternal(nullptr, value, query, &params, column, options);
2456}
2457
2459 KDbQuerySchema *query,
2460 const QList<QVariant> *params, int column,
2461 QueryRecordOptions options)
2462{
2463 QString str;
2464 const tristate result = querySingleStringInternal(sql, &str, query, params, column, options);
2465 if (result != true)
2466 return result;
2467 bool ok;
2468 const int _number = str.toInt(&ok);
2469 if (!ok)
2470 return false;
2471 if (number) {
2472 *number = _number;
2473 }
2474 return true;
2475}
2476
2478 QueryRecordOptions options)
2479{
2480 return querySingleNumberInternal(&sql, number, nullptr, nullptr, column, options);
2481}
2482
2484 QueryRecordOptions options)
2485{
2486 return querySingleNumberInternal(nullptr, number, query, nullptr, column, options);
2487}
2488
2490 const QList<QVariant> &params, int column,
2491 QueryRecordOptions options)
2492{
2493 return querySingleNumberInternal(nullptr, number, query, &params, column, options);
2494}
2495
2497 KDbQuerySchema *query, const QList<QVariant> *params,
2498 int column, bool (*filterFunction)(const QString &))
2499{
2500 if (sql) {
2501 m_result.setSql(*sql);
2502 }
2503 KDbCursor *cursor = executeQueryInternal(m_result.sql(), query, params);
2504 if (!cursor) {
2505 kdbWarning() << "!queryStringListInternal() " << m_result.sql();
2506 return false;
2507 }
2508 cursor->moveFirst();
2509 if (cursor->result().isError()) {
2510 m_result = cursor->result();
2511 deleteCursor(cursor);
2512 return false;
2513 }
2514 if (!cursor->eof() && !checkIfColumnExists(cursor, column)) {
2515 deleteCursor(cursor);
2516 return false;
2517 }
2518 if (list) {
2519 list->clear();
2520 }
2521 QStringList listResult;
2522 while (!cursor->eof()) {
2523 const QString str(cursor->value(column).toString());
2524 if (!filterFunction || filterFunction(str)) {
2525 listResult.append(str);
2526 }
2527 if (!cursor->moveNext() && cursor->result().isError()) {
2528 m_result = cursor->result();
2529 deleteCursor(cursor);
2530 return false;
2531 }
2532 }
2533 if (list) {
2534 *list = listResult;
2535 }
2536 return deleteCursor(cursor);
2537}
2538
2540 int column)
2541{
2542 return queryStringListInternal(&sql, list, nullptr, nullptr, column, nullptr);
2543}
2544
2546{
2547 return queryStringListInternal(nullptr, list, query, nullptr, column, nullptr);
2548}
2549
2551 const QList<QVariant>& params, int column)
2552{
2553 return queryStringListInternal(nullptr, list, query, &params, column, nullptr);
2554}
2555
2557{
2558 // optimization
2559 if (d->driver->behavior()->SELECT_1_SUBQUERY_SUPPORTED) {
2560 // this is at least for sqlite
2561 if ((options & QueryRecordOption::AddLimitTo1) && sql.left(6).toUpper() == "SELECT") {
2562 m_result.setSql(d->driver->addLimitTo1("SELECT 1 FROM (" + sql + ')'));
2563 } else {
2564 m_result.setSql(sql);
2565 }
2566 } else {
2567 if ((options & QueryRecordOption::AddLimitTo1) && sql.startsWith("SELECT")) {
2568 m_result.setSql(d->driver->addLimitTo1(sql));
2569 } else {
2570 m_result.setSql(sql);
2571 }
2572 }
2573 KDbCursor *cursor = executeQuery(m_result.sql());
2574 if (!cursor) {
2575 kdbWarning() << "!executeQuery()" << m_result.sql();
2576 return cancelled;
2577 }
2578 if (!cursor->moveFirst() || cursor->eof()) {
2579 //kdbWarning() << "!cursor->moveFirst() || cursor->eof()" << m_result.sql();
2580 m_result = cursor->result();
2581 deleteCursor(cursor);
2582 return m_result.isError() ? cancelled : tristate(false);
2583 }
2584 return deleteCursor(cursor) ? tristate(true) : cancelled;
2585}
2586
2588{
2590 KDbEscapedString sql;
2591 if (!builder.generateSelectStatement(&sql, table)) {
2592 return cancelled;
2593 }
2594 const tristate result = resultExists(sql);
2595 if (~result) {
2596 return cancelled;
2597 }
2598 return result == false;
2599}
2600
2601//! Used by addFieldPropertyToExtendedTableSchemaData()
2602static void createExtendedTableSchemaMainElementIfNeeded(
2603 QDomDocument* doc, QDomElement* extendedTableSchemaMainEl,
2604 bool* extendedTableSchemaStringIsEmpty)
2605{
2606 if (!*extendedTableSchemaStringIsEmpty)
2607 return;
2608 //init document
2609 *extendedTableSchemaMainEl = doc->createElement(QLatin1String("EXTENDED_TABLE_SCHEMA"));
2610 doc->appendChild(*extendedTableSchemaMainEl);
2611 extendedTableSchemaMainEl->setAttribute(QLatin1String("version"),
2612 QString::number(KDB_EXTENDED_TABLE_SCHEMA_VERSION));
2613 *extendedTableSchemaStringIsEmpty = false;
2614}
2615
2616//! Used by addFieldPropertyToExtendedTableSchemaData()
2617static void createExtendedTableSchemaFieldElementIfNeeded(QDomDocument* doc,
2618 QDomElement* extendedTableSchemaMainEl, const QString& fieldName, QDomElement* extendedTableSchemaFieldEl,
2619 bool append = true)
2620{
2621 if (!extendedTableSchemaFieldEl->isNull())
2622 return;
2623 *extendedTableSchemaFieldEl = doc->createElement(QLatin1String("field"));
2624 if (append)
2625 extendedTableSchemaMainEl->appendChild(*extendedTableSchemaFieldEl);
2626 extendedTableSchemaFieldEl->setAttribute(QLatin1String("name"), fieldName);
2627}
2628
2629/*! @internal used by storeExtendedTableSchemaData()
2630 Creates DOM node for @a propertyName and @a propertyValue.
2631 Creates enclosing EXTENDED_TABLE_SCHEMA element if EXTENDED_TABLE_SCHEMA is true.
2632 Updates extendedTableSchemaStringIsEmpty and extendedTableSchemaMainEl afterwards.
2633 If extendedTableSchemaFieldEl is null, creates <field> element (with optional
2634 "custom" attribute is @a custom is false). */
2635static void addFieldPropertyToExtendedTableSchemaData(
2636 const KDbField& f, const QByteArray &propertyName, const QVariant& propertyValue,
2637 QDomDocument* doc, QDomElement* extendedTableSchemaMainEl,
2638 QDomElement* extendedTableSchemaFieldEl,
2639 bool* extendedTableSchemaStringIsEmpty,
2640 bool custom = false)
2641{
2642 createExtendedTableSchemaMainElementIfNeeded(doc,
2643 extendedTableSchemaMainEl, extendedTableSchemaStringIsEmpty);
2644 createExtendedTableSchemaFieldElementIfNeeded(
2645 doc, extendedTableSchemaMainEl, f.name(), extendedTableSchemaFieldEl);
2646
2647 //create <property>
2648 QDomElement extendedTableSchemaFieldPropertyEl = doc->createElement(QLatin1String("property"));
2649 extendedTableSchemaFieldEl->appendChild(extendedTableSchemaFieldPropertyEl);
2650 if (custom)
2651 extendedTableSchemaFieldPropertyEl.setAttribute(QLatin1String("custom"), QLatin1String("true"));
2652 extendedTableSchemaFieldPropertyEl.setAttribute(QLatin1String("name"), QLatin1String(propertyName));
2653 QDomElement extendedTableSchemaFieldPropertyValueEl;
2654 switch (propertyValue.type()) {
2655 case QVariant::String:
2656 extendedTableSchemaFieldPropertyValueEl = doc->createElement(QLatin1String("string"));
2657 break;
2659 extendedTableSchemaFieldPropertyValueEl = doc->createElement(QLatin1String("cstring"));
2660 break;
2661 case QVariant::Int:
2662 case QVariant::Double:
2663 case QVariant::UInt:
2664 case QVariant::LongLong:
2666 extendedTableSchemaFieldPropertyValueEl = doc->createElement(QLatin1String("number"));
2667 break;
2668 case QVariant::Bool:
2669 extendedTableSchemaFieldPropertyValueEl = doc->createElement(QLatin1String("bool"));
2670 break;
2671 default:
2672//! @todo add more QVariant types
2673 kdbCritical() << "addFieldPropertyToExtendedTableSchemaData(): impl. error";
2674 }
2675 extendedTableSchemaFieldPropertyEl.appendChild(extendedTableSchemaFieldPropertyValueEl);
2676 extendedTableSchemaFieldPropertyValueEl.appendChild(
2677 doc->createTextNode(propertyValue.toString()));
2678}
2679
2681{
2682//! @todo future: save in older versions if neeed
2683 QDomDocument doc(QLatin1String("EXTENDED_TABLE_SCHEMA"));
2684 QDomElement extendedTableSchemaMainEl;
2685 bool extendedTableSchemaStringIsEmpty = true;
2686
2687 //for each field:
2688 foreach(KDbField* f, *tableSchema->fields()) {
2689 QDomElement extendedTableSchemaFieldEl;
2690 const KDbField::Type type = f->type(); // cache: evaluating type of expressions can be expensive
2691 if (f->visibleDecimalPlaces() >= 0/*nondefault*/ && KDb::supportsVisibleDecimalPlacesProperty(type)) {
2692 addFieldPropertyToExtendedTableSchemaData(
2693 *f, "visibleDecimalPlaces", f->visibleDecimalPlaces(), &doc,
2694 &extendedTableSchemaMainEl, &extendedTableSchemaFieldEl,
2695 &extendedTableSchemaStringIsEmpty);
2696 }
2697 if (type == KDbField::Text) {
2699 addFieldPropertyToExtendedTableSchemaData(
2700 *f, "maxLengthIsDefault", true, &doc,
2701 &extendedTableSchemaMainEl, &extendedTableSchemaFieldEl,
2702 &extendedTableSchemaStringIsEmpty);
2703 }
2704 }
2705
2706 // boolean field with "not null"
2707
2708 // add custom properties
2709 const KDbField::CustomPropertiesMap customProperties(f->customProperties());
2710 for (KDbField::CustomPropertiesMap::ConstIterator itCustom = customProperties.constBegin();
2711 itCustom != customProperties.constEnd(); ++itCustom) {
2712 addFieldPropertyToExtendedTableSchemaData(
2713 *f, itCustom.key(), itCustom.value(), &doc,
2714 &extendedTableSchemaMainEl, &extendedTableSchemaFieldEl, &extendedTableSchemaStringIsEmpty,
2715 /*custom*/true);
2716 }
2717 // save lookup table specification, if present
2718 KDbLookupFieldSchema *lookupFieldSchema = tableSchema->lookupFieldSchema(*f);
2719 if (lookupFieldSchema) {
2720 createExtendedTableSchemaFieldElementIfNeeded(
2721 &doc, &extendedTableSchemaMainEl, f->name(), &extendedTableSchemaFieldEl, false/* !append */);
2722 lookupFieldSchema->saveToDom(&doc, &extendedTableSchemaFieldEl);
2723
2724 if (extendedTableSchemaFieldEl.hasChildNodes()) {
2725 // this element provides the definition, so let's append it now
2726 createExtendedTableSchemaMainElementIfNeeded(&doc, &extendedTableSchemaMainEl,
2727 &extendedTableSchemaStringIsEmpty);
2728 extendedTableSchemaMainEl.appendChild(extendedTableSchemaFieldEl);
2729 }
2730 }
2731 }
2732
2733 // Store extended schema information (see ExtendedTableSchemaInformation in Kexi Wiki)
2734 if (extendedTableSchemaStringIsEmpty) {
2735#ifdef KDB_DEBUG_GUI
2736 KDb::alterTableActionDebugGUI(QLatin1String("** Extended table schema REMOVED."));
2737#endif
2738 if (!removeDataBlock(tableSchema->id(), QLatin1String("extended_schema")))
2739 return false;
2740 } else {
2741#ifdef KDB_DEBUG_GUI
2742 KDb::alterTableActionDebugGUI(
2743 QLatin1String("** Extended table schema set to:\n") + doc.toString(4));
2744#endif
2745 if (!storeDataBlock(tableSchema->id(), doc.toString(1), QLatin1String("extended_schema")))
2746 return false;
2747 }
2748 return true;
2749}
2750
2752{
2753#define loadExtendedTableSchemaData_ERR \
2754 { m_result = KDbResult(tr("Error while loading extended table schema.", \
2755 "Extended schema for a table: loading error")); \
2756 return false; }
2757#define loadExtendedTableSchemaData_ERR2(details) \
2758 { m_result = KDbResult(details); \
2759 m_result.setMessageTitle(tr("Error while loading extended table schema.", \
2760 "Extended schema for a table: loading error")); \
2761 return false; }
2762#define loadExtendedTableSchemaData_ERR3(data) \
2763 { m_result = KDbResult(tr("Invalid XML data: %1").arg(data.left(1024))); \
2764 m_result.setMessageTitle(tr("Error while loading extended table schema.", \
2765 "Extended schema for a table: loading error")); \
2766 return false; }
2767
2768 // Load extended schema information, if present (see ExtendedTableSchemaInformation in Kexi Wiki)
2769 QString extendedTableSchemaString;
2771 &extendedTableSchemaString, QLatin1String("extended_schema"));
2772 if (!res)
2773 loadExtendedTableSchemaData_ERR;
2774 // extendedTableSchemaString will be just empty if there is no such data block
2775
2776 if (extendedTableSchemaString.isEmpty())
2777 return true;
2778
2779 QDomDocument doc;
2780 QString errorMsg;
2781 int errorLine, errorColumn;
2782 if (!doc.setContent(extendedTableSchemaString, &errorMsg, &errorLine, &errorColumn)) {
2783 loadExtendedTableSchemaData_ERR2(
2784 tr("Error in XML data: \"%1\" in line %2, column %3.\nXML data: %4")
2785 .arg(errorMsg).arg(errorLine).arg(errorColumn).arg(extendedTableSchemaString.left(1024)));
2786 }
2787
2788//! @todo look at the current format version (KDB_EXTENDED_TABLE_SCHEMA_VERSION)
2789
2790 if (doc.doctype().name() != QLatin1String("EXTENDED_TABLE_SCHEMA"))
2791 loadExtendedTableSchemaData_ERR3(extendedTableSchemaString);
2792
2793 QDomElement docEl = doc.documentElement();
2794 if (docEl.tagName() != QLatin1String("EXTENDED_TABLE_SCHEMA"))
2795 loadExtendedTableSchemaData_ERR3(extendedTableSchemaString);
2796
2797 for (QDomNode n = docEl.firstChild(); !n.isNull(); n = n.nextSibling()) {
2798 QDomElement fieldEl = n.toElement();
2799 if (fieldEl.tagName() == QLatin1String("field")) {
2800 KDbField *f = tableSchema->field(fieldEl.attribute(QLatin1String("name")));
2801 if (f) {
2802 //set properties of the field:
2803//! @todo more properties
2804 for (QDomNode propNode = fieldEl.firstChild();
2805 !propNode.isNull(); propNode = propNode.nextSibling())
2806 {
2807 const QDomElement propEl = propNode.toElement();
2808 bool ok;
2809 int intValue;
2810 if (propEl.tagName() == QLatin1String("property")) {
2811 QByteArray propertyName = propEl.attribute(QLatin1String("name")).toLatin1();
2812 if (propEl.attribute(QLatin1String("custom")) == QLatin1String("true")) {
2813 //custom property
2814 const QVariant v(KDb::loadPropertyValueFromDom(propEl.firstChild(), &ok));
2815 if (ok) {
2816 f->setCustomProperty(propertyName, v);
2817 }
2818 }
2819 else if (propertyName == "visibleDecimalPlaces") {
2821 intValue = KDb::loadIntPropertyValueFromDom(propEl.firstChild(), &ok);
2822 if (ok)
2823 f->setVisibleDecimalPlaces(intValue);
2824 }
2825 }
2826 else if (propertyName == "maxLengthIsDefault") {
2827 if (f->type() == KDbField::Text) {
2828 const bool maxLengthIsDefault
2830 if (ok) {
2833 }
2834 }
2835 }
2836//! @todo more properties...
2837 } else if (propEl.tagName() == QLatin1String("lookup-column")) {
2838 KDbLookupFieldSchema *lookupFieldSchema = KDbLookupFieldSchema::loadFromDom(propEl);
2839 if (lookupFieldSchema) {
2840 kdbDebug() << f->name() << *lookupFieldSchema;
2841 tableSchema->setLookupFieldSchema(f->name(), lookupFieldSchema);
2842 }
2843 }
2844 }
2845 } else {
2846 kdbWarning() << "no such field:" << fieldEl.attribute(QLatin1String("name"))
2847 << "in table:" << tableSchema->name();
2848 }
2849 }
2850 }
2851
2852 return true;
2853}
2854
2856{
2857 bool ok = true;
2858 int f_int_type = data.at(1).toInt(&ok);
2859 if (f_int_type <= KDbField::InvalidType || f_int_type > KDbField::LastType)
2860 ok = false;
2861 if (!ok)
2862 return nullptr;
2863 KDbField::Type f_type = (KDbField::Type)f_int_type;
2864 const int f_len = qMax(0, data.at(3).toInt(&ok)); // defined limit
2865 if (!ok) {
2866 return nullptr;
2867 }
2868//! @todo load maxLengthStrategy info to see if the maxLength is the default
2869
2870 int f_prec = data.at(4).toInt(&ok);
2871 if (!ok)
2872 return nullptr;
2873 KDbField::Constraints f_constr = (KDbField::Constraints)data.at(5).toInt(&ok);
2874 if (!ok)
2875 return nullptr;
2876 KDbField::Options f_opts = (KDbField::Options)data.at(6).toInt(&ok);
2877 if (!ok)
2878 return nullptr;
2879
2880 QString name(data.at(2).toString());
2881 if (!KDb::isIdentifier(name)) {
2882 name = KDb::stringToIdentifier(name);
2883 }
2884
2885 KDbField *f = new KDbField(
2886 name, f_type, f_constr, f_opts, f_len, f_prec);
2887
2888 QVariant defaultVariant = data.at(7);
2889 if (defaultVariant.isValid()) {
2890 defaultVariant = KDb::stringToVariant(defaultVariant.toString(), KDbField::variantType(f_type), &ok);
2891 if (ok) {
2892 f->setDefaultValue(defaultVariant);
2893 } else {
2894 kdbWarning() << "problem with KDb::stringToVariant(" << defaultVariant << ')';
2895 ok = true; //problem with defaultValue is not critical
2896 }
2897 }
2898
2899 f->setCaption(data.at(9).toString());
2900 f->setDescription(data.at(10).toString());
2901 return f;
2902}
2903
2905{
2906 KDbTableSchema *t = d->table(tableName);
2907 if (t || tableName.isEmpty()) {
2908 return t;
2909 }
2910 //not found: retrieve schema
2912 clearResult();
2913 if (true != loadObjectData(KDb::TableObjectType, tableName, newTable.data())) {
2914 return nullptr;
2915 }
2916 return d->setupTableSchema(newTable.take());
2917}
2918
2920{
2921 KDbTableSchema *t = d->table(tableId);
2922 if (t)
2923 return t;
2924 //not found: retrieve schema
2926 clearResult();
2927 if (true != loadObjectData(KDb::TableObjectType, tableId, newTable.data())) {
2928 return nullptr;
2929 }
2930 return d->setupTableSchema(newTable.take());
2931}
2932
2933tristate KDbConnection::loadDataBlock(int objectID, QString* dataString, const QString& dataID)
2934{
2935 if (objectID <= 0)
2936 return false;
2937 return querySingleString(
2938 KDbEscapedString("SELECT o_data FROM kexi__objectdata WHERE o_id=%1 AND ")
2939 .arg(d->driver->valueToSql(KDbField::Integer, objectID))
2941 QLatin1String("o_sub_id"),
2942 dataID.isEmpty() ? QVariant() : QVariant(dataID))),
2943 dataString);
2944}
2945
2946bool KDbConnection::storeDataBlock(int objectID, const QString &dataString, const QString& dataID)
2947{
2948 if (objectID <= 0)
2949 return false;
2950 KDbEscapedString sql(
2951 KDbEscapedString("SELECT kexi__objectdata.o_id FROM kexi__objectdata WHERE o_id=%1")
2952 .arg(d->driver->valueToSql(KDbField::Integer, objectID)));
2953 KDbEscapedString sql_sub(KDb::sqlWhere(d->driver, KDbField::Text, QLatin1String("o_sub_id"),
2954 dataID.isEmpty() ? QVariant() : QVariant(dataID)));
2955
2956 const tristate result = resultExists(sql + " AND " + sql_sub);
2957 if (~result) {
2958 return false;
2959 }
2960 if (result == true) {
2961 return executeSql(KDbEscapedString("UPDATE kexi__objectdata SET o_data=%1 WHERE o_id=%2 AND ")
2962 .arg(d->driver->valueToSql(KDbField::LongText, dataString))
2963 .arg(d->driver->valueToSql(KDbField::Integer, objectID))
2964 + sql_sub);
2965 }
2966 return executeSql(
2967 KDbEscapedString("INSERT INTO kexi__objectdata (o_id, o_data, o_sub_id) VALUES (")
2968 + KDbEscapedString::number(objectID) + ',' + d->driver->valueToSql(KDbField::LongText, dataString)
2969 + ',' + d->driver->valueToSql(KDbField::Text, dataID) + ')');
2970}
2971
2972bool KDbConnection::copyDataBlock(int sourceObjectID, int destObjectID, const QString &dataID)
2973{
2974 if (sourceObjectID <= 0 || destObjectID <= 0)
2975 return false;
2976 if (sourceObjectID == destObjectID)
2977 return true;
2978 if (!removeDataBlock(destObjectID, dataID)) // remove before copying
2979 return false;
2981 "INSERT INTO kexi__objectdata SELECT %1, t.o_data, t.o_sub_id "
2982 "FROM kexi__objectdata AS t WHERE o_id=%2")
2983 .arg(d->driver->valueToSql(KDbField::Integer, destObjectID))
2984 .arg(d->driver->valueToSql(KDbField::Integer, sourceObjectID));
2985 if (!dataID.isEmpty()) {
2986 sql += KDbEscapedString(" AND ") + KDb::sqlWhere(d->driver, KDbField::Text,
2987 QLatin1String("o_sub_id"), dataID);
2988 }
2989 return executeSql(sql);
2990}
2991
2992bool KDbConnection::removeDataBlock(int objectID, const QString& dataID)
2993{
2994 if (objectID <= 0)
2995 return false;
2996 if (dataID.isEmpty())
2997 return KDb::deleteRecords(this, QLatin1String("kexi__objectdata"),
2998 QLatin1String("o_id"), QString::number(objectID));
2999 else
3000 return KDb::deleteRecords(this, QLatin1String("kexi__objectdata"),
3001 QLatin1String("o_id"), KDbField::Integer, objectID,
3002 QLatin1String("o_sub_id"), KDbField::Text, dataID);
3003}
3004
3006{
3007 QString queryName = aQueryName.toLower();
3008 KDbQuerySchema *q = d->query(queryName);
3009 if (q || queryName.isEmpty()) {
3010 return q;
3011 }
3012 //not found: retrieve schema
3014 clearResult();
3015 if (true != loadObjectData(KDb::QueryObjectType, aQueryName, newQuery.data())) {
3016 return nullptr;
3017 }
3018 return d->setupQuerySchema(newQuery.take());
3019}
3020
3022{
3023 KDbQuerySchema *q = d->query(queryId);
3024 if (q)
3025 return q;
3026 //not found: retrieve schema
3028 clearResult();
3029 if (true != loadObjectData(KDb::QueryObjectType, queryId, newQuery.data())) {
3030 return nullptr;
3031 }
3032 return d->setupQuerySchema(newQuery.take());
3033}
3034
3036{
3037 KDbQuerySchema* oldQuery = querySchema(queryName);
3038 if (!oldQuery)
3039 return false;
3040 d->setQueryObsolete(oldQuery);
3041 return true;
3042}
3043
3045{
3046 return d->driver->escapeIdentifier(id);
3047}
3048
3049bool KDbConnection::isInternalTableSchema(const QString& tableName)
3050{
3051 KDbTableSchema* schema = d->table(tableName);
3052 return (schema && schema->isInternal())
3053 // these are here for compatiblility because we're no longer instantiate
3054 // them but can exist in projects created with previous Kexi versions:
3055 || tableName == QLatin1String("kexi__final") || tableName == QLatin1String("kexi__useractions");
3056}
3057
3059{
3060 if (table && d) {
3061 d->takeTable(table);
3062 }
3063}
3064
3066{
3067 if (!d->availableDatabaseName.isEmpty()) {
3068 return d->availableDatabaseName;
3069 }
3070 return d->driver->behavior()->ALWAYS_AVAILABLE_DATABASE_NAME;
3071}
3072
3074{
3075 d->availableDatabaseName = dbName;
3076}
3077
3078//! @internal used in updateRecord(), insertRecord(),
3079inline static void updateRecordDataWithNewValues(
3080 KDbConnection *conn, KDbQuerySchema* query, KDbRecordData* data,
3082 QHash<KDbQueryColumnInfo*, int>* columnsOrderExpanded)
3083{
3084 *columnsOrderExpanded
3085 = query->columnsOrder(conn, KDbQuerySchema::ColumnsOrderMode::ExpandedList);
3087 for (KDbRecordEditBuffer::DbHash::ConstIterator it = b.constBegin();it != b.constEnd();++it) {
3088 columnsOrderExpandedIt = columnsOrderExpanded->constFind(it.key());
3089 if (columnsOrderExpandedIt == columnsOrderExpanded->constEnd()) {
3090 kdbWarning() << "(KDbConnection) \"now also assign new value in memory\" step"
3091 "- could not find item" << it.key()->aliasOrName();
3092 continue;
3093 }
3094 (*data)[ columnsOrderExpandedIt.value() ] = it.value();
3095 }
3096}
3097
3099{
3100// Each SQL identifier needs to be escaped in the generated query.
3101// kdbDebug() << *query;
3102
3103 clearResult();
3104 //--get PKEY
3105 if (buf->dbBuffer().isEmpty()) {
3106 kdbDebug() << " -- NO CHANGES DATA!";
3107 return true;
3108 }
3109 KDbTableSchema *mt = query->masterTable();
3110 if (!mt) {
3111 kdbWarning() << " -- NO MASTER TABLE!";
3112 m_result = KDbResult(ERR_UPDATE_NO_MASTER_TABLE,
3113 tr("Could not update record because there is no master table defined."));
3114 return false;
3115 }
3116 KDbIndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : nullptr;
3117 if (!useRecordId && !pkey) {
3118 kdbWarning() << " -- NO MASTER TABLE's PKEY!";
3119 m_result = KDbResult(ERR_UPDATE_NO_MASTER_TABLES_PKEY,
3120 tr("Could not update record because master table has no primary key defined."));
3121//! @todo perhaps we can try to update without using PKEY?
3122 return false;
3123 }
3124 //update the record:
3125 KDbEscapedString sql;
3126 sql.reserve(4096);
3127 sql = KDbEscapedString("UPDATE ") + escapeIdentifier(mt->name()) + " SET ";
3128 KDbEscapedString sqlset, sqlwhere;
3129 sqlset.reserve(1024);
3130 sqlwhere.reserve(1024);
3131 KDbRecordEditBuffer::DbHash b = buf->dbBuffer();
3132
3133 //gather the fields which are updated ( have values in KDbRecordEditBuffer)
3134 KDbFieldList affectedFields;
3135 for (KDbRecordEditBuffer::DbHash::ConstIterator it = b.constBegin();it != b.constEnd();++it) {
3136 if (it.key()->field()->table() != mt)
3137 continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
3138 if (!sqlset.isEmpty())
3139 sqlset += ',';
3140 KDbField* currentField = it.key()->field();
3141 const bool affectedFieldsAddOk = affectedFields.addField(currentField);
3142 Q_ASSERT(affectedFieldsAddOk);
3143 sqlset += KDbEscapedString(escapeIdentifier(currentField->name())) + '=' +
3144 d->driver->valueToSql(currentField, it.value());
3145 }
3146 if (pkey) {
3147 //kdbDebug() << pkey->fieldCount() << " ? " << query->pkeyFieldCount();
3148 if (pkey->fieldCount() != query->pkeyFieldCount(this)) { //sanity check
3149 kdbWarning() << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!";
3150 m_result = KDbResult(ERR_UPDATE_NO_ENTIRE_MASTER_TABLES_PKEY,
3151 tr("Could not update record because it does not contain entire primary key of master table."));
3152 return false;
3153 }
3154 if (!pkey->fields()->isEmpty()) {
3155 int i = 0;
3156 const QVector<int> pkeyFieldsOrder(query->pkeyFieldsOrder(this));
3157 for (KDbField *f : qAsConst(*pkey->fields())) {
3158 if (!sqlwhere.isEmpty())
3159 sqlwhere += " AND ";
3160 const QVariant val(data->at(pkeyFieldsOrder.at(i)));
3161 if (val.isNull() || !val.isValid()) {
3162 m_result = KDbResult(ERR_UPDATE_NULL_PKEY_FIELD,
3163 tr("Primary key's field \"%1\" cannot be empty.").arg(f->name()));
3164 //js todo: pass the field's name somewhere!
3165 return false;
3166 }
3167 sqlwhere += KDbEscapedString(escapeIdentifier(f->name())) + '=' +
3168 d->driver->valueToSql(f, val);
3169 i++;
3170 }
3171 }
3172 } else { //use RecordId
3173 sqlwhere = KDbEscapedString(escapeIdentifier(d->driver->behavior()->ROW_ID_FIELD_NAME)) + '='
3174 + d->driver->valueToSql(KDbField::BigInteger, (*data)[data->size() - 1]);
3175 }
3176 sql += (sqlset + " WHERE " + sqlwhere);
3177 //kdbDebug() << " -- SQL == " << ((sql.length() > 400) ? (sql.left(400) + "[.....]") : sql);
3178
3179 // preprocessing before update
3180 if (!drv_beforeUpdate(mt->name(), &affectedFields))
3181 return false;
3182
3183 bool res = executeSql(sql);
3184
3185 // postprocessing after update
3186 if (!drv_afterUpdate(mt->name(), &affectedFields))
3187 return false;
3188
3189 if (!res) {
3190 m_result = KDbResult(ERR_UPDATE_SERVER_ERROR,
3191 tr("Record updating on the server failed."));
3192 return false;
3193 }
3194 //success: now also assign new values in memory:
3195 QHash<KDbQueryColumnInfo*, int> columnsOrderExpanded;
3196 updateRecordDataWithNewValues(this, query, data, b, &columnsOrderExpanded);
3197 return true;
3198}
3199
3200bool KDbConnection::insertRecord(KDbQuerySchema* query, KDbRecordData* data, KDbRecordEditBuffer* buf, bool getRecordId)
3201{
3202// Each SQL identifier needs to be escaped in the generated query.
3203 clearResult();
3204 //--get PKEY
3205 /*disabled: there may be empty records (with autoinc)
3206 if (buf.dbBuffer().isEmpty()) {
3207 kdbDebug() << " -- NO CHANGES DATA!";
3208 return true; }*/
3209 KDbTableSchema *mt = query->masterTable();
3210 if (!mt) {
3211 kdbWarning() << " -- NO MASTER TABLE!";
3212 m_result = KDbResult(ERR_INSERT_NO_MASTER_TABLE,
3213 tr("Could not insert record because there is no master table specified."));
3214 return false;
3215 }
3216 KDbIndexSchema *pkey
3217 = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : nullptr;
3218 if (!getRecordId && !pkey) {
3219 kdbWarning() << " -- WARNING: NO MASTER TABLE's PKEY";
3220 }
3221
3222 KDbEscapedString sqlcols, sqlvals;
3223 sqlcols.reserve(1024);
3224 sqlvals.reserve(1024);
3225
3226 //insert the record:
3227 KDbEscapedString sql;
3228 sql.reserve(4096);
3229 sql = KDbEscapedString("INSERT INTO ") + escapeIdentifier(mt->name()) + " (";
3230 KDbRecordEditBuffer::DbHash b = buf->dbBuffer();
3231
3232 // add default values, if available (for any column without value explicitly set)
3233 const KDbQueryColumnInfo::Vector fieldsExpanded(
3234 query->fieldsExpanded(this, KDbQuerySchema::FieldsExpandedMode::Unique));
3235 int fieldsExpandedCount = fieldsExpanded.count();
3236 for (int i = 0; i < fieldsExpandedCount; i++) {
3237 KDbQueryColumnInfo *ci = fieldsExpanded.at(i);
3238 if (ci->field() && KDb::isDefaultValueAllowed(*ci->field())
3239 && !ci->field()->defaultValue().isNull()
3240 && !b.contains(ci))
3241 {
3242 //kdbDebug() << "adding default value" << ci->field->defaultValue().toString() << "for column" << ci->field->name();
3243 b.insert(ci, ci->field()->defaultValue());
3244 }
3245 }
3246
3247 //collect fields which have values in KDbRecordEditBuffer
3248 KDbFieldList affectedFields;
3249
3250 if (b.isEmpty()) {
3251 // empty record inserting requested:
3252 if (!getRecordId && !pkey) {
3253 kdbWarning() << "MASTER TABLE's PKEY REQUIRED FOR INSERTING EMPTY RECORDS: INSERT CANCELLED";
3254 m_result = KDbResult(ERR_INSERT_NO_MASTER_TABLES_PKEY,
3255 tr("Could not insert record because master table has no primary key specified."));
3256 return false;
3257 }
3258 if (pkey) {
3259 const QVector<int> pkeyFieldsOrder(query->pkeyFieldsOrder(this));
3260 // kdbDebug() << pkey->fieldCount() << " ? " << query->pkeyFieldCount();
3261 if (pkey->fieldCount() != query->pkeyFieldCount(this)) { // sanity check
3262 kdbWarning() << "NO ENTIRE MASTER TABLE's PKEY SPECIFIED!";
3263 m_result = KDbResult(ERR_INSERT_NO_ENTIRE_MASTER_TABLES_PKEY,
3264 tr("Could not insert record because it does not contain "
3265 "entire master table's primary key."));
3266 return false;
3267 }
3268 }
3269 //at least one value is needed for VALUES section: find it and set to NULL:
3270 KDbField *anyField = mt->anyNonPKField();
3271 if (!anyField) {
3272 if (!pkey) {
3273 kdbWarning() << "WARNING: NO FIELD AVAILABLE TO SET IT TO NULL";
3274 return false;
3275 } else {
3276 //try to set NULL in pkey field (could not work for every SQL engine!)
3277 anyField = pkey->fields()->first();
3278 }
3279 }
3280 sqlcols += escapeIdentifier(anyField->name());
3281 sqlvals += d->driver->valueToSql(anyField, QVariant()/*NULL*/);
3282 const bool affectedFieldsAddOk = affectedFields.addField(anyField);
3283 Q_ASSERT(affectedFieldsAddOk);
3284 } else {
3285 // non-empty record inserting requested:
3286 for (KDbRecordEditBuffer::DbHash::ConstIterator it = b.constBegin();it != b.constEnd();++it) {
3287 if (it.key()->field()->table() != mt)
3288 continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
3289 if (!sqlcols.isEmpty()) {
3290 sqlcols += ',';
3291 sqlvals += ',';
3292 }
3293 KDbField* currentField = it.key()->field();
3294 const bool affectedFieldsAddOk = affectedFields.addField(currentField);
3295 Q_ASSERT(affectedFieldsAddOk);
3296 sqlcols += escapeIdentifier(currentField->name());
3297 sqlvals += d->driver->valueToSql(currentField, it.value());
3298 }
3299 }
3300 sql += (sqlcols + ") VALUES (" + sqlvals + ')');
3301// kdbDebug() << " -- SQL == " << sql;
3302
3303 // low-level insert
3304 QSharedPointer<KDbSqlResult> result = insertRecordInternal(mt->name(), &affectedFields, sql);
3305 if (!result) {
3306 m_result = KDbResult(ERR_INSERT_SERVER_ERROR,
3307 tr("Record inserting on the server failed."));
3308 return false;
3309 }
3310 //success: now also assign a new value in memory:
3311 QHash<KDbQueryColumnInfo*, int> columnsOrderExpanded;
3312 updateRecordDataWithNewValues(this, query, data, b, &columnsOrderExpanded);
3313
3314 //fetch autoincremented values
3315 KDbQueryColumnInfo::List *aif_list = query->autoIncrementFields(this);
3316 quint64 recordId = 0;
3317 if (pkey && !aif_list->isEmpty()) {
3318 //! @todo now only if PKEY is present, this should also work when there's no PKEY
3319 KDbQueryColumnInfo *id_columnInfo = aif_list->first();
3320 //! @todo safe to cast it?
3321 quint64 last_id
3322 = KDb::lastInsertedAutoIncValue(result, id_columnInfo->field()->name(),
3323 id_columnInfo->field()->table()->name(), &recordId);
3324 if (last_id == std::numeric_limits<quint64>::max()) {
3325 //! @todo show error
3326//! @todo remove just inserted record. How? Using ROLLBACK?
3327 return false;
3328 }
3329 KDbRecordData aif_data;
3330 KDbEscapedString getAutoIncForInsertedValue("SELECT "
3331 + query->autoIncrementSqlFieldsList(this)
3332 + " FROM "
3333 + escapeIdentifier(id_columnInfo->field()->table()->name())
3334 + " WHERE "
3335 + escapeIdentifier(id_columnInfo->field()->name()) + '='
3336 + QByteArray::number(last_id));
3337 if (true != querySingleRecord(getAutoIncForInsertedValue, &aif_data)) {
3338 //! @todo show error
3339 return false;
3340 }
3341 int i = 0;
3342 foreach(KDbQueryColumnInfo *ci, *aif_list) {
3343// kdbDebug() << "AUTOINCREMENTED FIELD" << fi->field->name() << "==" << aif_data[i].toInt();
3344 ((*data)[ columnsOrderExpanded.value(ci)]
3345 = aif_data.value(i)).convert(ci->field()->variantType()); //cast to get proper type
3346 i++;
3347 }
3348 } else {
3349 recordId = result->lastInsertRecordId();
3350// kdbDebug() << "new recordId ==" << recordId;
3351 if (d->driver->behavior()->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
3352 kdbWarning() << "d->driver->behavior()->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE";
3353 return false;
3354 }
3355 }
3356 if (getRecordId && /*sanity check*/data->size() > fieldsExpanded.size()) {
3357// kdbDebug() << "new ROWID ==" << ROWID;
3358 (*data)[data->size() - 1] = recordId;
3359 }
3360 return true;
3361}
3362
3363bool KDbConnection::deleteRecord(KDbQuerySchema* query, KDbRecordData* data, bool useRecordId)
3364{
3365// Each SQL identifier needs to be escaped in the generated query.
3366 clearResult();
3367 KDbTableSchema *mt = query->masterTable();
3368 if (!mt) {
3369 kdbWarning() << " -- NO MASTER TABLE!";
3370 m_result = KDbResult(ERR_DELETE_NO_MASTER_TABLE,
3371 tr("Could not delete record because there is no master table specified."));
3372 return false;
3373 }
3374 KDbIndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : nullptr;
3375
3376//! @todo allow to delete from a table without pkey
3377 if (!useRecordId && !pkey) {
3378 kdbWarning() << " -- WARNING: NO MASTER TABLE's PKEY";
3379 m_result = KDbResult(ERR_DELETE_NO_MASTER_TABLES_PKEY,
3380 tr("Could not delete record because there is no primary key for master table specified."));
3381 return false;
3382 }
3383
3384 //update the record:
3385 KDbEscapedString sql;
3386 sql.reserve(4096);
3387 sql = KDbEscapedString("DELETE FROM ") + escapeIdentifier(mt->name()) + " WHERE ";
3388 KDbEscapedString sqlwhere;
3389 sqlwhere.reserve(1024);
3390
3391 if (pkey) {
3392 const QVector<int> pkeyFieldsOrder(query->pkeyFieldsOrder(this));
3393 //kdbDebug() << pkey->fieldCount() << " ? " << query->pkeyFieldCount();
3394 if (pkey->fieldCount() != query->pkeyFieldCount(this)) { //sanity check
3395 kdbWarning() << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!";
3396 m_result = KDbResult(ERR_DELETE_NO_ENTIRE_MASTER_TABLES_PKEY,
3397 tr("Could not delete record because it does not contain entire master table's primary key."));
3398 return false;
3399 }
3400 int i = 0;
3401 foreach(KDbField *f, *pkey->fields()) {
3402 if (!sqlwhere.isEmpty())
3403 sqlwhere += " AND ";
3404 QVariant val(data->at(pkeyFieldsOrder.at(i)));
3405 if (val.isNull() || !val.isValid()) {
3406 m_result = KDbResult(ERR_DELETE_NULL_PKEY_FIELD,
3407 tr("Primary key's field \"%1\" cannot be empty.").arg(f->name()));
3408//js todo: pass the field's name somewhere!
3409 return false;
3410 }
3411 sqlwhere += KDbEscapedString(escapeIdentifier(f->name())) + '=' +
3412 d->driver->valueToSql(f, val);
3413 i++;
3414 }
3415 } else {//use RecordId
3416 sqlwhere = KDbEscapedString(escapeIdentifier(d->driver->behavior()->ROW_ID_FIELD_NAME)) + '='
3417 + d->driver->valueToSql(KDbField::BigInteger, (*data)[data->size() - 1]);
3418 }
3419 sql += sqlwhere;
3420 //kdbDebug() << " -- SQL == " << sql;
3421
3422 if (!executeSql(sql)) {
3423 m_result = KDbResult(ERR_DELETE_SERVER_ERROR,
3424 tr("Record deletion on the server failed."));
3425 return false;
3426 }
3427 return true;
3428}
3429
3431{
3432 clearResult();
3433 KDbTableSchema *mt = query->masterTable();
3434 if (!mt) {
3435 kdbWarning() << " -- NO MASTER TABLE!";
3436 return false;
3437 }
3438 KDbIndexSchema *pkey = mt->primaryKey();
3439 if (!pkey || pkey->fields()->isEmpty()) {
3440 kdbWarning() << "-- WARNING: NO MASTER TABLE's PKEY";
3441 }
3442 KDbEscapedString sql = KDbEscapedString("DELETE FROM ") + escapeIdentifier(mt->name());
3443 //kdbDebug() << "-- SQL == " << sql;
3444
3445 if (!executeSql(sql)) {
3446 m_result = KDbResult(ERR_DELETE_SERVER_ERROR,
3447 tr("Record deletion on the server failed."));
3448 return false;
3449 }
3450 return true;
3451}
3452
3454{
3455 int count = -1; //will be changed only on success of querySingleNumber()
3456 const tristate result = querySingleNumber(
3457 KDbEscapedString("SELECT COUNT() FROM (") + sql + ") AS kdb__subquery", &count);
3458 if (~result) {
3459 count = 0;
3460 }
3461 return count;
3462}
3463
3465{
3466 //! @todo does not work with non-SQL data sources
3467 int count = -1; // will be changed only on success of querySingleNumber()
3468 const tristate result = querySingleNumber(
3469 KDbEscapedString("SELECT COUNT(*) FROM ") + escapeIdentifier(tableSchema.name()), &count);
3470 if (~result) {
3471 count = 0;
3472 }
3473 return count;
3474}
3475
3477{
3478//! @todo does not work with non-SQL data sources
3479 int count = -1; //will be changed only on success of querySingleNumber()
3481 KDbEscapedString subSql;
3482 if (!builder.generateSelectStatement(&subSql, querySchema, params)) {
3483 return -1;
3484 }
3485 const tristate result = querySingleNumber(
3486 KDbEscapedString("SELECT COUNT(*) FROM (") + subSql + ") AS kdb__subquery", &count);
3487 if (~result) {
3488 count = 0;
3489 }
3490 return count;
3491}
3492
3494{
3495 if (tableOrQuery) {
3496 if (tableOrQuery->table())
3497 return recordCount(*tableOrQuery->table());
3498 if (tableOrQuery->query())
3499 return recordCount(tableOrQuery->query(), params);
3500 }
3501 return -1;
3502}
3503
3505{
3506 return &d->options;
3507}
3508
3510{
3511 d->cursors.insert(cursor);
3512}
3513
3515{
3516 if (d && !d->cursors.isEmpty()) { // checking because this may be called from ~KDbConnection()
3517 d->cursors.remove(cursor);
3518 }
3519}
3520
3522 KDbFieldList* fields, const QStringList& whereFieldNames)
3523{
3524//! @todo move to ConnectionInterface just like we moved execute() and prepare() to KDbPreparedStatementInterface...
3526 if (!iface)
3527 return KDbPreparedStatement();
3528 return KDbPreparedStatement(iface, type, fields, whereFieldNames);
3529}
3530
3532 return result().errorSql().isEmpty() ? m_result.sql() : result().errorSql();
3533}
3534
3536{
3537 return d->driver->escapeString(str);
3538}
3539
3540//! @todo extraMessages
3541#if 0
3542static const char *extraMessages[] = {
3543 QT_TRANSLATE_NOOP("KDbConnection", "Unknown error.")
3544};
3545#endif
Database specific connection data, e.g. host, port.
Generic options for a single connection. The options are accessible using key/value pairs....
void setCaption(const QByteArray &name, const QString &caption)
Sets caption for option name to caption.
void insert(const QByteArray &name, const QVariant &value, const QString &caption=QString())
Inserts option with a given name, value and caption.
void setReadOnly(bool set)
void setValue(const QByteArray &name, const QVariant &value)
Sets value for option name to value.
bool operator==(const KDbConnectionOptions &other) const
void remove(const QByteArray &name)
Removes option with a given name if exists.
Provides database connection, allowing queries and data modification.
bool connect()
Connects to driver with given parameters.
virtual QStringList drv_getTableNames(bool *ok)
LOW LEVEL METHOD.
void removeMe(KDbTableSchema *ts)
@ DropDestination
Drop destination table if exists.
bool updateRecord(KDbQuerySchema *query, KDbRecordData *data, KDbRecordEditBuffer *buf, bool useRecordId=false)
virtual KDbEscapedString escapeString(const QString &str) const
static QStringList kdbSystemTableNames()
bool removeObject(int objId)
tristate loadObjectData(int type, int id, KDbObject *object)
tristate loadDataBlock(int objectID, QString *dataString, const QString &dataID=QString())
KDbField * setupField(const KDbRecordData &data)
virtual bool drv_setAutoCommit(bool on)
KDbTableSchema * copyTable(const KDbTableSchema &tableSchema, const KDbObject &newData)
void takeCursor(KDbCursor *cursor)
Used by KDbCursor class.
bool databaseExists(const QString &dbName, bool ignoreErrors=true)
bool queryStringList(const KDbEscapedString &sql, QStringList *list, int column=0)
bool removeDataBlock(int objectID, const QString &dataID=QString())
~KDbConnection() override
virtual bool drv_closeDatabase()=0
virtual KDbPreparedStatementInterface * prepareStatementInternal()=0
bool storeMainFieldSchema(KDbField *field)
void setAvailableDatabaseName(const QString &dbName)
bool setQuerySchemaObsolete(const QString &queryName)
void addCursor(KDbCursor *cursor)
Used by KDbCursor class.
QList< int > tableIds(bool *ok=nullptr)
KDbProperties databaseProperties() const
KDbPreparedStatement prepareStatement(KDbPreparedStatement::Type type, KDbFieldList *fields, const QStringList &whereFieldNames=QStringList())
bool commitAutoCommitTransaction(const KDbTransaction &trans)
tristate querySingleString(const KDbEscapedString &sql, QString *value, int column=0, QueryRecordOptions options=QueryRecordOption::Default)
virtual bool drv_getServerVersion(KDbServerVersionInfo *version)=0
KDbCursor * executeQuery(const KDbEscapedString &sql, KDbCursor::Options options=KDbCursor::Option::None)
virtual bool drv_alterTableName(KDbTableSchema *tableSchema, const QString &newName)
bool closeDatabase()
Closes currently used database for this connection.
virtual bool drv_isDatabaseUsed() const
virtual bool drv_executeSql(const KDbEscapedString &sql)=0
Executes query for a raw SQL statement sql without returning resulting records.
virtual bool drv_useDatabase(const QString &dbName=QString(), bool *cancelled=nullptr, KDbMessageHandler *msgHandler=nullptr)=0
void setDefaultTransaction(const KDbTransaction &trans)
Sets default transaction.
QStringList tableNames(bool alsoSystemTables=false, bool *ok=nullptr)
bool executeSql(const KDbEscapedString &sql)
Executes a new native (raw, backend-specific) SQL query.
bool autoCommit() const
virtual KDbSqlResult * drv_prepareSql(const KDbEscapedString &sql)=0
Prepares query for a raw SQL statement sql with possibility of returning records.
tristate querySingleStringInternal(const KDbEscapedString *sql, QString *value, KDbQuerySchema *query, const QList< QVariant > *params, int column, QueryRecordOptions options)
virtual bool drv_disconnect()=0
virtual QString escapeIdentifier(const QString &id) const
Identifier escaping function in the associated KDbDriver.
virtual bool drv_createDatabase(const QString &dbName=QString())=0
bool storeDataBlock(int objectID, const QString &dataString, const QString &dataID=QString())
KDbConnectionData data() const
tristate dropTable(KDbTableSchema *tableSchema)
virtual QString anyAvailableDatabaseName()
virtual KDbTransactionData * drv_beginTransaction()
KDbServerVersionInfo serverVersion() const
virtual bool drv_createTable(const KDbTableSchema &tableSchema)
Creates table using tableSchema information.
tristate alterTable(KDbTableSchema *tableSchema, KDbTableSchema *newTableSchema)
virtual bool drv_databaseExists(const QString &dbName, bool ignoreErrors=true)
tristate isEmpty(KDbTableSchema *table)
virtual bool drv_copyTableData(const KDbTableSchema &tableSchema, const KDbTableSchema &destinationTableSchema)
KDbField * findSystemFieldName(const KDbFieldList &fieldlist)
bool isConnected() const
bool dropDatabase(const QString &dbName=QString())
Drops database with name dbName.
QStringList databaseNames(bool also_system_db=false)
virtual KDbCursor * prepareQuery(const KDbEscapedString &sql, KDbCursor::Options options=KDbCursor::Option::None)=0
bool deleteCursor(KDbCursor *cursor)
bool checkIfColumnExists(KDbCursor *cursor, int column)
bool rollbackTransaction(KDbTransaction trans=KDbTransaction(), KDbTransaction::CommitOptions options=KDbTransaction::CommitOptions())
Rolls back specified transaction for this connection.
virtual bool drv_dropDatabase(const QString &dbName=QString())=0
bool createTable(KDbTableSchema *tableSchema, CreateTableOptions options=CreateTableOption::Default)
Creates a new table.
virtual bool drv_rollbackTransaction(KDbTransactionData *trans)
KDbTransaction beginTransaction()
Starts a new database transaction.
bool loadExtendedTableSchemaData(KDbTableSchema *tableSchema)
bool deleteAllRecords(KDbQuerySchema *query)
QStringList objectNames(int objectType=KDb::AnyObjectType, bool *ok=nullptr)
QSharedPointer< KDbSqlResult > prepareSql(const KDbEscapedString &sql)
Prepares execution of a new native (raw, backend-specific) SQL query.
virtual bool drv_dropTable(const QString &tableName)
QList< int > objectIds(int objectType, bool *ok=nullptr)
bool disconnect()
Disconnects from driver with given parameters.
bool checkIsDatabaseUsed()
tristate querySingleRecordInternal(KDbRecordData *data, const KDbEscapedString *sql, KDbQuerySchema *query, const QList< QVariant > *params, QueryRecordOptions options)
KDbDriver * driver() const
bool storeObjectData(KDbObject *object)
tristate querySingleRecord(const KDbEscapedString &sql, KDbRecordData *data, QueryRecordOptions options=QueryRecordOption::Default)
KDbTableSchema * tableSchema(int tableId)
friend class KDbTableSchema
for removeMe()
bool storeExtendedTableSchemaData(KDbTableSchema *tableSchema)
KDbCursor * executeQueryInternal(const KDbEscapedString &sql, KDbQuerySchema *query, const QList< QVariant > *params)
bool dropQuery(KDbQuerySchema *querySchema)
bool isDatabaseUsed() const
virtual bool drv_connect()=0
bool copyDataBlock(int sourceObjectID, int destObjectID, const QString &dataID=QString())
tristate querySingleNumber(const KDbEscapedString &sql, int *number, int column=0, QueryRecordOptions options=QueryRecordOption::Default)
bool createDatabase(const QString &dbName)
Creates new database with name dbName, using this connection.
QString currentDatabase() const
Get the name of the current database.
@ DropDestination
Drop destination table if exists.
bool useDatabase(const QString &dbName=QString(), bool kexiCompatible=true, bool *cancelled=nullptr, KDbMessageHandler *msgHandler=nullptr)
Opens an existing database specified by dbName.
virtual bool drv_beforeInsert(const QString &tableName, KDbFieldList *fields)
virtual bool drv_afterInsert(const QString &tableName, KDbFieldList *fields)
int recordCount(const KDbEscapedString &sql)
Returns number of records returned by given SQL statement.
bool setupObjectData(const KDbRecordData &data, KDbObject *object)
virtual KDbEscapedString recentSqlString() const
Return recently used SQL string.
bool beginAutoCommitTransaction(KDbTransactionGuard *tg)
KDbVersionInfo databaseVersion() const
QList< int > queryIds(bool *ok=nullptr)
bool alterTableName(KDbTableSchema *tableSchema, const QString &newName, AlterTableNameOptions options=AlterTableNameOption::Default)
Alters name of table.
bool rollbackAutoCommitTransaction(const KDbTransaction &trans)
tristate resultExists(const KDbEscapedString &sql, QueryRecordOptions options=QueryRecordOption::Default)
@ AddLimitTo1
Adds a "LIMIT 1" clause to the query for optimization purposes (it should not include one already)
virtual bool drv_afterUpdate(const QString &tableName, KDbFieldList *fields)
bool deleteRecord(KDbQuerySchema *query, KDbRecordData *data, bool useRecordId=false)
QList< KDbTransaction > transactions()
Returns set of handles of currently active transactions.
tristate dropTableInternal(KDbTableSchema *tableSchema, bool alsoRemoveSchema)
bool useTemporaryDatabaseIfNeeded(QString *name)
virtual bool drv_beforeUpdate(const QString &tableName, KDbFieldList *fields)
QSharedPointer< KDbSqlResult > insertRecordInternal(const QString &tableSchemaName, KDbFieldList *fields, const KDbEscapedString &sql)
KDbQuerySchema * querySchema(int queryId)
bool storeNewObjectData(KDbObject *object)
bool setAutoCommit(bool on)
bool commitTransaction(KDbTransaction transaction=KDbTransaction(), KDbTransaction::CommitOptions options=KDbTransaction::CommitOptions())
Commits specified transaction for this connection.
virtual bool drv_commitTransaction(KDbTransactionData *trans)
KDbTransaction defaultTransaction() const
Returns handle of default transaction for this connection.
virtual tristate drv_containsTable(const QString &tableName)=0
virtual bool drv_getDatabasesList(QStringList *list)
tristate querySingleNumberInternal(const KDbEscapedString *sql, int *number, KDbQuerySchema *query, const QList< QVariant > *params, int column, QueryRecordOptions options)
tristate containsTable(const QString &tableName)
KDbConnectionOptions * options()
bool queryStringListInternal(const KDbEscapedString *sql, QStringList *list, KDbQuerySchema *query, const QList< QVariant > *params, int column, bool(*filterFunction)(const QString &))
Provides database cursor functionality.
Definition KDbCursor.h:69
virtual bool moveNext()
bool open()
KDbRecordData * storeCurrentRecord() const
virtual QVariant value(int i)=0
int fieldCount() const
Definition KDbCursor.h:167
bool moveFirst()
void setQueryParameters(const QList< QVariant > &params)
Sets query parameters params for this cursor.
bool eof() const
Definition KDbCursor.h:147
KDbConnection * connection()
virtual bool close()
Database driver's abstraction.
Definition KDbDriver.h:50
@ IgnoreTransactions
Definition KDbDriver.h:79
@ SingleTransactions
single trasactions are only supported
Definition KDbDriver.h:58
@ MultipleTransactions
multiple concurrent trasactions are supported (this implies !SingleTransactions)
Definition KDbDriver.h:61
Specialized string for escaping.
bool isValid() const
bool addField(KDbField *field)
virtual KDbField * field(int id)
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())
KDbField::ListIterator fieldsIterator() const
int fieldCount() const
KDbField::ListIterator fieldsIteratorConstEnd() const
KDbEscapedString sqlFieldsList(KDbConnection *conn, const QString &separator=QLatin1String(","), const QString &tableOrAlias=QString(), KDb::IdentifierEscapingType escapingType=KDb::DriverEscaping) const
KDbField::List * fields()
Meta-data for a field.
Definition KDbField.h:72
MaxLengthStrategy maxLengthStrategy() const
Definition KDbField.cpp:655
void setDescription(const QString &description)
Definition KDbField.cpp:336
int precision() const
Definition KDbField.cpp:286
KDbTableSchema * table()
Definition KDbField.cpp:585
QString name() const
Definition KDbField.cpp:256
QVariant::Type variantType() const
Converts field's type to QVariant equivalent as accurate as possible.
Definition KDbField.h:368
CustomPropertiesMap customProperties() const
static QVariant::Type variantType(Type type)
Converts type type to QVariant equivalent as accurate as possible.
Definition KDbField.cpp:387
void setCaption(const QString &caption)
Definition KDbField.cpp:321
QList< KDbField * >::ConstIterator ListIterator
iterator for list of fields
Definition KDbField.h:79
int maxLength() const
Definition KDbField.cpp:665
@ DefaultMaxLength
Default maximum text length defined globally by the application.
Definition KDbField.h:430
@ DefinedMaxLength
Used if setMaxLength() was called to set specific maximum value or to unlimited (0).
Definition KDbField.h:432
QString caption() const
Definition KDbField.cpp:316
QString description() const
Definition KDbField.cpp:331
void setDefaultValue(const QVariant &def)
Definition KDbField.cpp:711
void setMaxLengthStrategy(MaxLengthStrategy strategy)
Definition KDbField.cpp:660
int scale() const
Definition KDbField.cpp:291
@ Integer
Definition KDbField.h:90
@ BigInteger
Definition KDbField.h:91
@ LongText
Definition KDbField.h:99
bool isFPNumericType() const
Definition KDbField.h:335
void setVisibleDecimalPlaces(int p)
Definition KDbField.cpp:692
Type type() const
Definition KDbField.cpp:379
Constraints constraints() const
Definition KDbField.cpp:301
int visibleDecimalPlaces() const
Definition KDbField.cpp:296
int order() const
Definition KDbField.cpp:306
QVariant defaultValue() const
Definition KDbField.cpp:281
void setCustomProperty(const QByteArray &propertyName, const QVariant &value)
Sets value value for custom property propertyName.
Options options() const
Definition KDbField.cpp:261
Provides information about database index that can be created for a database table.
Provides information about lookup field's setup.
void saveToDom(QDomDocument *doc, QDomElement *parentEl)
static KDbLookupFieldSchema * loadFromDom(const QDomElement &lookupEl)
A builder for generating various types of native SQL statements.
bool generateCreateTableStatement(KDbEscapedString *target, const KDbTableSchema &tableSchema) const
bool generateSelectStatement(KDbEscapedString *target, KDbQuerySchema *querySchema, const KDbSelectStatementOptions &options, const QList< QVariant > &parameters=QList< QVariant >()) const
QString description
QString caption
Prepared statement interface for backend-dependent implementations.
Prepared database command for optimizing sequences of multiple database actions.
Type
Defines type of the prepared statement.
A set of storable database properties.
Helper class that assigns additional information for the column in a query.
KDbQuerySchema provides information about database query.
@ ExpandedList
A map for expanded list is created.
@ Unique
Unique list of fields is returned.
Structure for storing single record with type information.
QVariant value(int i) const
provides data for single edited database record
bool isError() const
Definition KDbResult.cpp:64
void prependMessage(int code, const QString &message)
Sets result code and prepends message to an existing message.
Definition KDbResult.cpp:80
KDbTableSchema * table() const
KDbQuerySchema * query() const
static tristate closeListeners(KDbConnection *conn, const KDbTableSchema *table, const QList< KDbTableSchemaChangeListener * > &except=QList< KDbTableSchemaChangeListener * >())
Closes all table schema listeners for table schema table except for the ones from the except list.
static void unregisterForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener, const KDbTableSchema *table)
Unregisters listener for receiving (listening) information about changes in table schema table.
void setConnection(KDbConnection *conn)
KDbLookupFieldSchema * lookupFieldSchema(const KDbField &field)
bool isInternal() const
KDbField * anyNonPKField()
void clear() override
KDbIndexSchema * primaryKey()
bool setLookupFieldSchema(const QString &fieldName, KDbLookupFieldSchema *lookupFieldSchema)
KDbQuerySchema * query()
Internal prototype for storing transaction handle for KDbTransaction object.
void setActive(bool set)
Sets "active" flag of this data.
KDbTransactionGuard class is a convenience class that simplifies handling transactions.
const KDbTransaction transaction() const
Returns transaction that is controlled by this guard.
void doNothing()
Deativates the transaction guard.
void setTransaction(const KDbTransaction &transaction)
Assigns transaction to this guard.
This class encapsulates a single database transaction.
@ IgnoreInactive
Do not return error for inactive or null transactions when requesting commit or rollback.
bool isActive() const
Returns true if transaction is active (i.e.
bool isNull() const
Returns true if this transaction is null.
void setValue(const QByteArray &name, const QVariant &value)
Sets value for property name to value.
Definition KDbUtils.cpp:686
Property property(const QByteArray &name) const
Definition KDbUtils.cpp:699
void remove(const QByteArray &name)
Removes property with a given name.
Definition KDbUtils.cpp:694
PropertySet & operator=(const PropertySet &other)
Assigns other to this property set and returns a reference to this property set.
Definition KDbUtils.cpp:646
void setCaption(const QByteArray &name, const QString &caption)
Sets caption for property name to caption.
Definition KDbUtils.cpp:678
void insert(const QByteArray &name, const QVariant &value, const QString &caption=QString())
Inserts property with a given name, value and caption.
Definition KDbUtils.cpp:660
bool operator==(const PropertySet &other) const
Definition KDbUtils.cpp:655
3-state logical type with three values: true, false and cancelled and convenient operators.
Type type(const QSqlDatabase &db)
std::optional< QSqlQuery > query(const QString &queryStatement)
KDB_EXPORT bool isIdentifier(const QString &s)
KDB_EXPORT QString escapeIdentifier(const QString &string)
Definition KDb.cpp:1334
KDB_EXPORT quint64 lastInsertedAutoIncValue(QSharedPointer< KDbSqlResult > result, const QString &autoIncrementFieldName, const QString &tableName, quint64 *recordId=nullptr)
Returns value of last inserted record for an autoincrement field.
Definition KDb.cpp:392
KDB_EXPORT bool deleteRecords(KDbConnection *conn, const QString &tableName, const QString &keyname, KDbField::Type keytype, const QVariant &keyval)
Deletes records using one generic criteria.
Definition KDb.cpp:342
KDB_EXPORT KDbEscapedString sqlWhere(KDbDriver *drv, KDbField::Type t, const QString &fieldName, const QVariant &value)
Definition KDb.cpp:440
KDB_EXPORT KDbVersionInfo version()
Definition KDb.cpp:336
KDB_EXPORT QString variantToString(const QVariant &v)
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
KDB_EXPORT bool supportsVisibleDecimalPlacesProperty(KDbField::Type type)
Definition KDb.cpp:623
KDB_EXPORT QString stringToIdentifier(const QString &s)
KDB_EXPORT int loadIntPropertyValueFromDom(const QDomNode &node, bool *ok)
Definition KDb.cpp:1097
@ AnyObjectType
helper
Definition KDbGlobal.h:132
KDB_EXPORT QVariant loadPropertyValueFromDom(const QDomNode &node, bool *ok)
Definition KDb.cpp:1123
KDB_EXPORT QVariant stringToVariant(const QString &s, QVariant::Type type, bool *ok)
KDB_EXPORT bool isDefaultValueAllowed(const KDbField &field)
const QList< QKeySequence > & copy()
bool operator==(const StyleDelim &l, const StyleDelim &r)
FeedPtr parse(const DocumentSource &src, const QString &formatHint=QString())
QByteArray number(double n, char format, int precision)
QString fromNativeSeparators(const QString &pathName)
QChar separator()
QDomElement createElement(const QString &tagName)
QDomText createTextNode(const QString &value)
QDomDocumentType doctype() const const
QDomElement documentElement() const const
ParseResult setContent(QAnyStringView text, ParseOptions options)
QString toString(int indent) const const
QString name() const const
QString attribute(const QString &name, const QString &defValue) const const
void setAttribute(const QString &name, const QString &value)
QString tagName() const const
QDomNode appendChild(const QDomNode &newChild)
QDomNode firstChild() const const
bool hasChildNodes() const const
bool isNull() const const
QDomElement toElement() const const
QString absoluteFilePath() const const
QString absolutePath() const const
bool exists(const QString &path)
QString fileName() const const
bool isFile() const const
bool isReadable() const const
bool isWritable() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
T value(const Key &key) const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
iterator end()
T & first()
bool isEmpty() const const
qsizetype size() const const
T value(qsizetype i) const const
bool hasNext() const const
T * data() const const
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool isEmpty() const const
QString left(qsizetype n) const const
QString number(double n, char format, int precision)
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
QString toLower() const const
QString trimmed() const const
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
CaseInsensitive
Type type() const const
bool isNull() const const
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const
QString toString() 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.