File indexing completed on 2024-04-21 15:30:11

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2017 Jarosław Staniek <staniek@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KDbConnection.h"
0021 #include "KDbConnection_p.h"
0022 #include "KDbCursor.h"
0023 #include "KDbDriverBehavior.h"
0024 #include "KDbDriverMetaData.h"
0025 #include "KDbDriver_p.h"
0026 #include "KDbLookupFieldSchema.h"
0027 #include "KDbNativeStatementBuilder.h"
0028 #include "KDbQuerySchema.h"
0029 #include "KDbQuerySchema_p.h"
0030 #include "KDbRecordData.h"
0031 #include "KDbRecordEditBuffer.h"
0032 #include "KDbRelationship.h"
0033 #include "KDbSqlRecord.h"
0034 #include "KDbSqlResult.h"
0035 #include "KDbTableOrQuerySchema.h"
0036 #include "KDbTableSchemaChangeListener.h"
0037 #include "KDbTransactionData.h"
0038 #include "KDbTransactionGuard.h"
0039 #include "kdb_debug.h"
0040 
0041 #include <QDir>
0042 #include <QFileInfo>
0043 #include <QDomDocument>
0044 
0045 /*! Version number of extended table schema.
0046 
0047   List of changes:
0048   * 2: (Kexi 2.5.0) Added maxLengthIsDefault property (type: bool, if true, KDbField::maxLengthStrategy() == KDbField::DefaultMaxLength)
0049   * 1: (Kexi 1.x) Initial version
0050 */
0051 #define KDB_EXTENDED_TABLE_SCHEMA_VERSION 2
0052 
0053 KDbConnectionInternal::KDbConnectionInternal(KDbConnection *conn)
0054         : connection(conn)
0055 {
0056 }
0057 
0058 class CursorDeleter
0059 {
0060 public:
0061     explicit CursorDeleter(KDbCursor *cursor) {
0062         delete cursor;
0063     }
0064 };
0065 
0066 //================================================
0067 
0068 class Q_DECL_HIDDEN KDbConnectionOptions::Private
0069 {
0070 public:
0071     Private() : connection(nullptr) {}
0072     Private(const Private &other) {
0073         copy(other);
0074     }
0075 #define KDbConnectionOptionsPrivateArgs(o) std::tie(o.connection)
0076     void copy(const Private &other) {
0077         KDbConnectionOptionsPrivateArgs((*this)) = KDbConnectionOptionsPrivateArgs(other);
0078     }
0079     bool operator==(const Private &other) const {
0080         return KDbConnectionOptionsPrivateArgs((*this)) == KDbConnectionOptionsPrivateArgs(other);
0081     }
0082     KDbConnection *connection;
0083 };
0084 
0085 KDbConnectionOptions::KDbConnectionOptions()
0086  : d(new Private)
0087 {
0088     KDbUtils::PropertySet::insert("readOnly", false, tr("Read only", "Read only connection"));
0089 }
0090 
0091 KDbConnectionOptions::KDbConnectionOptions(const KDbConnectionOptions &other)
0092  : KDbUtils::PropertySet(other)
0093  , d(new Private(*other.d))
0094 {
0095 }
0096 
0097 KDbConnectionOptions::~KDbConnectionOptions()
0098 {
0099     delete d;
0100 }
0101 
0102 KDbConnectionOptions& KDbConnectionOptions::operator=(const KDbConnectionOptions &other)
0103 {
0104     if (this != &other) {
0105         KDbUtils::PropertySet::operator=(other);
0106         d->copy(*other.d);
0107     }
0108     return *this;
0109 }
0110 
0111 bool KDbConnectionOptions::operator==(const KDbConnectionOptions &other) const
0112 {
0113     return KDbUtils::PropertySet::operator==(other) && *d == *other.d;
0114 }
0115 
0116 bool KDbConnectionOptions::isReadOnly() const
0117 {
0118     return property("readOnly").value().toBool();
0119 }
0120 
0121 void KDbConnectionOptions::insert(const QByteArray &name, const QVariant &value,
0122                                   const QString &caption)
0123 {
0124     if (name == "readOnly") {
0125         setReadOnly(value.toBool());
0126         return;
0127     }
0128     QString realCaption;
0129     if (property(name).caption().isEmpty()) { // don't allow to change the caption
0130         realCaption = caption;
0131     }
0132     KDbUtils::PropertySet::insert(name, value, realCaption);
0133 }
0134 
0135 void KDbConnectionOptions::setCaption(const QByteArray &name, const QString &caption)
0136 {
0137     if (name == "readOnly") {
0138         return;
0139     }
0140     KDbUtils::PropertySet::setCaption(name, caption);
0141 }
0142 
0143 void KDbConnectionOptions::setValue(const QByteArray &name, const QVariant &value)
0144 {
0145     if (name == "readOnly") {
0146         setReadOnly(value.toBool());
0147         return;
0148     }
0149     KDbUtils::PropertySet::setValue(name, value);
0150 }
0151 
0152 void KDbConnectionOptions::remove(const QByteArray &name)
0153 {
0154     if (name == "readOnly") {
0155         return;
0156     }
0157     KDbUtils::PropertySet::remove(name);
0158 }
0159 
0160 void KDbConnectionOptions::setReadOnly(bool set)
0161 {
0162     if (d->connection && d->connection->isConnected()) {
0163         return; //sanity
0164     }
0165     KDbUtils::PropertySet::setValue("readOnly", set);
0166 }
0167 
0168 void KDbConnectionOptions::setConnection(KDbConnection *connection)
0169 {
0170     d->connection = connection;
0171 }
0172 
0173 //================================================
0174 
0175 KDbConnectionPrivate::KDbConnectionPrivate(KDbConnection* const conn, KDbDriver *drv, const KDbConnectionData& _connData,
0176                   const KDbConnectionOptions &_options)
0177         : conn(conn)
0178         , connData(_connData)
0179         , options(_options)
0180         , driver(drv)
0181         , dbProperties(conn)
0182 {
0183     options.setConnection(conn);
0184 }
0185 
0186 KDbConnectionPrivate::~KDbConnectionPrivate()
0187 {
0188     options.setConnection(nullptr);
0189     deleteAllCursors();
0190     delete m_parser;
0191     qDeleteAll(tableSchemaChangeListeners);
0192     qDeleteAll(obsoleteQueries);
0193 }
0194 
0195 void KDbConnectionPrivate::deleteAllCursors()
0196 {
0197     QSet<KDbCursor*> cursorsToDelete(cursors);
0198     cursors.clear();
0199     for(KDbCursor* c : cursorsToDelete) {
0200         CursorDeleter deleter(c);
0201     }
0202 }
0203 
0204 void KDbConnectionPrivate::errorInvalidDBContents(const QString& details)
0205 {
0206     conn->m_result = KDbResult(ERR_INVALID_DATABASE_CONTENTS,
0207                                KDbConnection::tr("Invalid database contents. %1").arg(details));
0208 }
0209 
0210 QString KDbConnectionPrivate::strItIsASystemObject() const
0211 {
0212     return KDbConnection::tr("It is a system object.");
0213 }
0214 
0215 void KDbConnectionPrivate::setupKDbSystemSchema()
0216 {
0217     if (!m_internalKDbTables.isEmpty()) {
0218         return; //already set up
0219     }
0220     {
0221         KDbInternalTableSchema *t_objects = new KDbInternalTableSchema(QLatin1String("kexi__objects"));
0222         t_objects->addField(new KDbField(QLatin1String("o_id"),
0223                                       KDbField::Integer, KDbField::PrimaryKey | KDbField::AutoInc, KDbField::Unsigned));
0224         t_objects->addField(new KDbField(QLatin1String("o_type"), KDbField::Byte, {}, KDbField::Unsigned));
0225         t_objects->addField(new KDbField(QLatin1String("o_name"), KDbField::Text));
0226         t_objects->addField(new KDbField(QLatin1String("o_caption"), KDbField::Text));
0227         t_objects->addField(new KDbField(QLatin1String("o_desc"), KDbField::LongText));
0228         //kdbDebug() << *t_objects;
0229         insertTable(t_objects);
0230     }
0231     {
0232         KDbInternalTableSchema *t_objectdata = new KDbInternalTableSchema(QLatin1String("kexi__objectdata"));
0233         t_objectdata->addField(new KDbField(QLatin1String("o_id"),
0234                                          KDbField::Integer, KDbField::NotNull, KDbField::Unsigned));
0235         t_objectdata->addField(new KDbField(QLatin1String("o_data"), KDbField::LongText));
0236         t_objectdata->addField(new KDbField(QLatin1String("o_sub_id"), KDbField::Text));
0237         insertTable(t_objectdata);
0238     }
0239     {
0240         KDbInternalTableSchema *t_fields = new KDbInternalTableSchema(QLatin1String("kexi__fields"));
0241         t_fields->addField(new KDbField(QLatin1String("t_id"), KDbField::Integer, {}, KDbField::Unsigned));
0242         t_fields->addField(new KDbField(QLatin1String("f_type"), KDbField::Byte, {}, KDbField::Unsigned));
0243         t_fields->addField(new KDbField(QLatin1String("f_name"), KDbField::Text));
0244         t_fields->addField(new KDbField(QLatin1String("f_length"), KDbField::Integer));
0245         t_fields->addField(new KDbField(QLatin1String("f_precision"), KDbField::Integer));
0246         t_fields->addField(new KDbField(QLatin1String("f_constraints"), KDbField::Integer));
0247         t_fields->addField(new KDbField(QLatin1String("f_options"), KDbField::Integer));
0248         t_fields->addField(new KDbField(QLatin1String("f_default"), KDbField::Text));
0249         //these are additional properties:
0250         t_fields->addField(new KDbField(QLatin1String("f_order"), KDbField::Integer));
0251         t_fields->addField(new KDbField(QLatin1String("f_caption"), KDbField::Text));
0252         t_fields->addField(new KDbField(QLatin1String("f_help"), KDbField::LongText));
0253         insertTable(t_fields);
0254     }
0255     {
0256         KDbInternalTableSchema *t_db = new KDbInternalTableSchema(QLatin1String("kexi__db"));
0257         t_db->addField(new KDbField(QLatin1String("db_property"),
0258                                  KDbField::Text, KDbField::NoConstraints, KDbField::NoOptions, 32));
0259         t_db->addField(new KDbField(QLatin1String("db_value"), KDbField::LongText));
0260         insertTable(t_db);
0261     }
0262 }
0263 
0264 void KDbConnectionPrivate::insertTable(KDbTableSchema* tableSchema)
0265 {
0266     KDbInternalTableSchema* internalTable = dynamic_cast<KDbInternalTableSchema*>(tableSchema);
0267     if (internalTable) {
0268         m_internalKDbTables.insert(internalTable);
0269     } else {
0270         m_tables.insert(tableSchema->id(), tableSchema);
0271     }
0272     m_tablesByName.insert(tableSchema->name(), tableSchema);
0273 }
0274 
0275 void KDbConnectionPrivate::removeTable(int id)
0276 {
0277     QScopedPointer<KDbTableSchema> toDelete(m_tables.take(id));
0278     if (!toDelete) {
0279         kdbWarning() << "Could not find table to delete with id=" << id;
0280         return;
0281     }
0282     KDbTableSchemaChangeListener::unregisterForChanges(conn, toDelete.data());
0283     const int count = m_tablesByName.remove(toDelete->name());
0284     Q_ASSERT_X(count == 1, "KDbConnectionPrivate::removeTable", "Table to remove not found");
0285 }
0286 
0287 void KDbConnectionPrivate::takeTable(KDbTableSchema* tableSchema)
0288 {
0289     if (m_tables.isEmpty()) {
0290         return;
0291     }
0292     m_tables.take(tableSchema->id());
0293     m_tablesByName.take(tableSchema->name());
0294 }
0295 
0296 void KDbConnectionPrivate::renameTable(KDbTableSchema* tableSchema, const QString& newName)
0297 {
0298     m_tablesByName.take(tableSchema->name());
0299     tableSchema->setName(newName);
0300     m_tablesByName.insert(tableSchema->name(), tableSchema);
0301 }
0302 
0303 void KDbConnectionPrivate::changeTableId(KDbTableSchema* tableSchema, int newId)
0304 {
0305     m_tables.take(tableSchema->id());
0306     m_tables.insert(newId, tableSchema);
0307 }
0308 
0309 void KDbConnectionPrivate::clearTables()
0310 {
0311     m_tablesByName.clear();
0312     qDeleteAll(m_internalKDbTables);
0313     m_internalKDbTables.clear();
0314     QHash<int, KDbTableSchema*> tablesToDelete(m_tables);
0315     m_tables.clear();
0316     qDeleteAll(tablesToDelete);
0317 }
0318 
0319 void KDbConnectionPrivate::insertQuery(KDbQuerySchema* query)
0320 {
0321     m_queries.insert(query->id(), query);
0322     m_queriesByName.insert(query->name(), query);
0323 }
0324 
0325 void KDbConnectionPrivate::removeQuery(KDbQuerySchema* querySchema)
0326 {
0327     m_queriesByName.remove(querySchema->name());
0328     m_queries.remove(querySchema->id());
0329     delete querySchema;
0330 }
0331 
0332 void KDbConnectionPrivate::setQueryObsolete(KDbQuerySchema* query)
0333 {
0334     obsoleteQueries.insert(query);
0335     m_queriesByName.take(query->name());
0336     m_queries.take(query->id());
0337 }
0338 
0339 void KDbConnectionPrivate::clearQueries()
0340 {
0341     qDeleteAll(m_queries);
0342     m_queries.clear();
0343 }
0344 
0345 KDbTableSchema* KDbConnectionPrivate::setupTableSchema(KDbTableSchema *table)
0346 {
0347     Q_ASSERT(table);
0348     QScopedPointer<KDbTableSchema> newTable(table);
0349     KDbCursor *cursor;
0350     if (!(cursor = conn->executeQuery(
0351             KDbEscapedString("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
0352                              "f_options, f_default, f_order, f_caption, f_help "
0353                              "FROM kexi__fields WHERE t_id=%1 ORDER BY f_order")
0354                             .arg(driver->valueToSql(KDbField::Integer, table->id())))))
0355     {
0356         return nullptr;
0357     }
0358     if (!cursor->moveFirst()) {
0359         if (!cursor->result().isError() && cursor->eof()) {
0360             conn->m_result = KDbResult(tr("Table has no fields defined."));
0361         }
0362         conn->deleteCursor(cursor);
0363         return nullptr;
0364     }
0365 
0366     // For each field: load its schema
0367     KDbRecordData fieldData;
0368     bool ok = true;
0369     while (!cursor->eof()) {
0370 //  kdbDebug()<<"@@@ f_name=="<<cursor->value(2).asCString();
0371         if (!cursor->storeCurrentRecord(&fieldData)) {
0372             ok = false;
0373             break;
0374         }
0375         KDbField *f = conn->setupField(fieldData);
0376         if (!f || !table->addField(f)) {
0377             ok = false;
0378             break;
0379         }
0380         cursor->moveNext();
0381     }
0382 
0383     if (!ok) {//error:
0384         conn->deleteCursor(cursor);
0385         return nullptr;
0386     }
0387 
0388     if (!conn->deleteCursor(cursor)) {
0389         return nullptr;
0390     }
0391 
0392     if (!conn->loadExtendedTableSchemaData(table)) {
0393         return nullptr;
0394     }
0395     //store locally:
0396     insertTable(table);
0397     return newTable.take();
0398 }
0399 
0400 KDbQuerySchema* KDbConnectionPrivate::setupQuerySchema(KDbQuerySchema *query)
0401 {
0402     Q_ASSERT(query);
0403     QScopedPointer<KDbQuerySchema> newQuery(query);
0404     QString sql;
0405     if (!conn->loadDataBlock(query->id(), &sql, QLatin1String("sql"))) {
0406         conn->m_result = KDbResult(
0407             ERR_OBJECT_NOT_FOUND,
0408             tr("Could not find definition for query \"%1\". Deleting this query is recommended.")
0409                 .arg(query->name()));
0410         return nullptr;
0411     }
0412     const QString queryName(query->name());
0413     if (!parser()->parse(KDbEscapedString(sql), query)) {
0414         newQuery.take(); // query is destroyed by the parser
0415         conn->m_result = KDbResult(
0416             ERR_SQL_PARSE_ERROR, tr("<p>Could not load definition for query \"%1\". "
0417                                     "SQL statement for this query is invalid:<br><tt>%2</tt></p>\n"
0418                                     "<p>This query can be edited only in Text View.</p>")
0419                                      .arg(queryName, sql));
0420         return nullptr;
0421     }
0422     insertQuery(query);
0423     return newQuery.take();
0424 }
0425 
0426 KDbQuerySchemaFieldsExpanded *KDbConnectionPrivate::fieldsExpanded(const KDbQuerySchema *query)
0427 {
0428     return m_fieldsExpandedCache[query];
0429 }
0430 
0431 void KDbConnectionPrivate::insertFieldsExpanded(const KDbQuerySchema *query, KDbQuerySchemaFieldsExpanded *cache)
0432 {
0433     m_fieldsExpandedCache.insert(query, cache);
0434 }
0435 
0436 void KDbConnectionPrivate::removeFieldsExpanded(const KDbQuerySchema *query)
0437 {
0438     //kdbDebug() << "**CACHE REMOVE**" << query;
0439     m_fieldsExpandedCache.remove(query);
0440 }
0441 
0442 //================================================
0443 
0444 namespace {
0445 //! @internal static: list of internal KDb system table names
0446 class SystemTables : public QStringList
0447 {
0448 public:
0449     SystemTables()
0450         : QStringList({
0451             QLatin1String("kexi__objects"),
0452             QLatin1String("kexi__objectdata"),
0453             QLatin1String("kexi__fields"),
0454             QLatin1String("kexi__db")})
0455     {}
0456 };
0457 }
0458 
0459 Q_GLOBAL_STATIC(SystemTables, g_kdbSystemTableNames)
0460 
0461 KDbConnection::KDbConnection(KDbDriver *driver, const KDbConnectionData& connData,
0462                              const KDbConnectionOptions &options)
0463         : d(new KDbConnectionPrivate(this, driver, connData, options))
0464 {
0465     if (d->connData.driverId().isEmpty()) {
0466         d->connData.setDriverId(d->driver->metaData()->id());
0467     }
0468 }
0469 
0470 void KDbConnection::destroy()
0471 {
0472     disconnect();
0473     //do not allow the driver to touch me: I will kill myself.
0474     d->driver->d->connections.remove(this);
0475 }
0476 
0477 KDbConnection::~KDbConnection()
0478 {
0479     KDbConnectionPrivate *thisD = d;
0480     d = nullptr; // make sure d is nullptr before destructing
0481     delete thisD;
0482 }
0483 
0484 KDbConnectionData KDbConnection::data() const
0485 {
0486     return d->connData;
0487 }
0488 
0489 KDbDriver* KDbConnection::driver() const
0490 {
0491     return d->driver;
0492 }
0493 
0494 bool KDbConnection::connect()
0495 {
0496     clearResult();
0497     if (d->isConnected) {
0498         m_result = KDbResult(ERR_ALREADY_CONNECTED,
0499                              tr("Connection already established."));
0500         return false;
0501     }
0502 
0503     d->serverVersion.clear();
0504     if (!(d->isConnected = drv_connect())) {
0505         if (m_result.code() == ERR_NONE) {
0506             m_result.setCode(ERR_OTHER);
0507         }
0508         m_result.setMessage(d->driver->metaData()->isFileBased() ?
0509                     tr("Could not open \"%1\" project file.")
0510                        .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName()))
0511                  :  tr("Could not connect to \"%1\" database server.")
0512                        .arg(d->connData.toUserVisibleString()));
0513     }
0514     if (d->isConnected && !d->driver->behavior()->USING_DATABASE_REQUIRED_TO_CONNECT) {
0515         if (!drv_getServerVersion(&d->serverVersion))
0516             return false;
0517     }
0518     return d->isConnected;
0519 }
0520 
0521 bool KDbConnection::isDatabaseUsed() const
0522 {
0523     return !d->usedDatabase.isEmpty() && d->isConnected && drv_isDatabaseUsed();
0524 }
0525 
0526 void KDbConnection::clearResult()
0527 {
0528     KDbResultable::clearResult();
0529 }
0530 
0531 bool KDbConnection::disconnect()
0532 {
0533     clearResult();
0534     if (!d->isConnected)
0535         return true;
0536 
0537     if (!closeDatabase())
0538         return false;
0539 
0540     bool ok = drv_disconnect();
0541     if (ok)
0542         d->isConnected = false;
0543     return ok;
0544 }
0545 
0546 bool KDbConnection::isConnected() const
0547 {
0548     return d->isConnected;
0549 }
0550 
0551 bool KDbConnection::checkConnected()
0552 {
0553     if (d->isConnected) {
0554         clearResult();
0555         return true;
0556     }
0557     m_result = KDbResult(ERR_NO_CONNECTION,
0558                          tr("Not connected to the database server."));
0559     return false;
0560 }
0561 
0562 bool KDbConnection::checkIsDatabaseUsed()
0563 {
0564     if (isDatabaseUsed()) {
0565         clearResult();
0566         return true;
0567     }
0568     m_result = KDbResult(ERR_NO_DB_USED,
0569                          tr("Currently no database is used."));
0570     return false;
0571 }
0572 
0573 QStringList KDbConnection::databaseNames(bool also_system_db)
0574 {
0575     //kdbDebug() << also_system_db;
0576     if (!checkConnected())
0577         return QStringList();
0578 
0579     QString tmpdbName;
0580     //some engines need to have opened any database before executing "create database"
0581     if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
0582         return QStringList();
0583 
0584     QStringList list;
0585     bool ret = drv_getDatabasesList(&list);
0586 
0587     if (!tmpdbName.isEmpty()) {
0588         //whatever result is - now we have to close temporary opened database:
0589         if (!closeDatabase())
0590             return QStringList();
0591     }
0592 
0593     if (!ret)
0594         return QStringList();
0595 
0596     if (also_system_db)
0597         return list;
0598     //filter system databases:
0599     for (QMutableListIterator<QString> it(list); it.hasNext();) {
0600         if (d->driver->isSystemDatabaseName(it.next())) {
0601             it.remove();
0602         }
0603     }
0604     return list;
0605 }
0606 
0607 bool KDbConnection::drv_getDatabasesList(QStringList* list)
0608 {
0609     list->clear();
0610     return true;
0611 }
0612 
0613 bool KDbConnection::drv_databaseExists(const QString &dbName, bool ignoreErrors)
0614 {
0615     QStringList list = databaseNames(true);//also system
0616     if (m_result.isError()) {
0617         return false;
0618     }
0619 
0620     if (list.indexOf(dbName) == -1) {
0621         if (!ignoreErrors)
0622             m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
0623                                  tr("The database \"%1\" does not exist.").arg(dbName));
0624         return false;
0625     }
0626 
0627     return true;
0628 }
0629 
0630 bool KDbConnection::databaseExists(const QString &dbName, bool ignoreErrors)
0631 {
0632 // kdbDebug() << dbName << ignoreErrors;
0633     if (d->driver->behavior()->CONNECTION_REQUIRED_TO_CHECK_DB_EXISTENCE && !checkConnected())
0634         return false;
0635     clearResult();
0636 
0637     if (d->driver->metaData()->isFileBased()) {
0638         //for file-based db: file must exists and be accessible
0639         QFileInfo file(d->connData.databaseName());
0640         if (!file.exists() || (!file.isFile() && !file.isSymLink())) {
0641             if (!ignoreErrors)
0642                 m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
0643                                      tr("The database file \"%1\" does not exist.")
0644                                         .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
0645             return false;
0646         }
0647         if (!file.isReadable()) {
0648             if (!ignoreErrors)
0649                 m_result = KDbResult(ERR_ACCESS_RIGHTS,
0650                                      tr("Database file \"%1\" is not readable.")
0651                                         .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
0652             return false;
0653         }
0654         if (!d->options.isReadOnly() && !file.isWritable()) {
0655             if (!ignoreErrors)
0656                 m_result = KDbResult(ERR_ACCESS_RIGHTS,
0657                                      tr("Database file \"%1\" is not writable.")
0658                                         .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
0659             return false;
0660         }
0661         return true;
0662     }
0663 
0664     QString tmpdbName;
0665     //some engines need to have opened any database before executing "create database"
0666     const bool orig_skipDatabaseExistsCheckInUseDatabase = d->skipDatabaseExistsCheckInUseDatabase;
0667     d->skipDatabaseExistsCheckInUseDatabase = true;
0668     bool ret = useTemporaryDatabaseIfNeeded(&tmpdbName);
0669     d->skipDatabaseExistsCheckInUseDatabase = orig_skipDatabaseExistsCheckInUseDatabase;
0670     if (!ret)
0671         return false;
0672 
0673     ret = drv_databaseExists(dbName, ignoreErrors);
0674 
0675     if (!tmpdbName.isEmpty()) {
0676         //whatever result is - now we have to close temporary opened database:
0677         if (!closeDatabase())
0678             return false;
0679     }
0680 
0681     return ret;
0682 }
0683 
0684 #define createDatabase_CLOSE \
0685     { if (!closeDatabase()) { \
0686             m_result = KDbResult(KDbConnection::tr("Database \"%1\" has been created but " \
0687                                  "could not be closed after creation.").arg(dbName)); \
0688             return false; \
0689         } }
0690 
0691 #define createDatabase_ERROR \
0692     { createDatabase_CLOSE; return false; }
0693 
0694 
0695 bool KDbConnection::createDatabase(const QString &dbName)
0696 {
0697     if (d->driver->behavior()->CONNECTION_REQUIRED_TO_CREATE_DB && !checkConnected())
0698         return false;
0699 
0700     if (databaseExists(dbName)) {
0701         m_result = KDbResult(ERR_OBJECT_EXISTS,
0702                              tr("Database \"%1\" already exists.").arg(dbName));
0703         return false;
0704     }
0705     if (d->driver->isSystemDatabaseName(dbName)) {
0706         m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
0707                              tr("Could not create database \"%1\". This name is reserved for system database.").arg(dbName));
0708         return false;
0709     }
0710     if (d->driver->metaData()->isFileBased()) {
0711         //update connection data if filename differs
0712         if (QFileInfo(dbName).isAbsolute()) {
0713             d->connData.setDatabaseName(dbName);
0714         }
0715         else {
0716             d->connData.setDatabaseName(
0717                 QFileInfo(d->connData.databaseName()).absolutePath()
0718                 + QDir::separator() +  QFileInfo(dbName).fileName());
0719         }
0720     }
0721 
0722     QString tmpdbName;
0723     //some engines need to have opened any database before executing "create database"
0724     if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
0725         return false;
0726 
0727     //low-level create
0728     if (!drv_createDatabase(dbName)) {
0729         m_result.prependMessage(tr("Error creating database \"%1\" on the server.").arg(dbName));
0730         (void)closeDatabase();//sanity
0731         return false;
0732     }
0733 
0734     if (!tmpdbName.isEmpty()) {
0735         //whatever result is - now we have to close temporary opened database:
0736         if (!closeDatabase())
0737             return false;
0738     }
0739 
0740     if (!tmpdbName.isEmpty() || !d->driver->behavior()->IS_DB_OPEN_AFTER_CREATE) {
0741         //db need to be opened
0742         if (!useDatabase(dbName, false/*not yet kexi compatible!*/)) {
0743             m_result = KDbResult(tr("Database \"%1\" has been created but could not be opened.").arg(dbName));
0744             return false;
0745         }
0746     } else {
0747         //just for the rule
0748         d->usedDatabase = dbName;
0749         d->isConnected = true;
0750     }
0751 
0752     KDbTransaction trans;
0753     if (d->driver->transactionsSupported()) {
0754         trans = beginTransaction();
0755         if (!trans.isActive())
0756             return false;
0757     }
0758 
0759     //-create system tables schema objects
0760     d->setupKDbSystemSchema();
0761 
0762     //-physically create internal KDb tables
0763     foreach(KDbInternalTableSchema* t, d->internalKDbTables()) {
0764         if (!drv_createTable(*t))
0765             createDatabase_ERROR;
0766     }
0767 
0768     //-insert KDb version info:
0769     // (for compatibility with Kexi expect the legacy kexidb_major_ver/kexidb_minor_ver values)
0770     KDbTableSchema *table = d->table(QLatin1String("kexi__db"));
0771     if (!table)
0772         createDatabase_ERROR;
0773     if (!insertRecord(table, QLatin1String("kexidb_major_ver"), KDb::version().major())
0774             || !insertRecord(table, QLatin1String("kexidb_minor_ver"), KDb::version().minor()))
0775         createDatabase_ERROR;
0776 
0777     if (trans.isActive() && !commitTransaction(trans))
0778         createDatabase_ERROR;
0779 
0780     createDatabase_CLOSE;
0781     return true;
0782 }
0783 
0784 #undef createDatabase_CLOSE
0785 #undef createDatabase_ERROR
0786 
0787 bool KDbConnection::useDatabase(const QString &dbName, bool kexiCompatible, bool *cancelled, KDbMessageHandler* msgHandler)
0788 {
0789     if (cancelled)
0790         *cancelled = false;
0791     //kdbDebug() << dbName << kexiCompatible;
0792     if (!checkConnected())
0793         return false;
0794 
0795     QString my_dbName;
0796     if (dbName.isEmpty())
0797         my_dbName = d->connData.databaseName();
0798     else
0799         my_dbName = dbName;
0800     if (my_dbName.isEmpty())
0801         return false;
0802 
0803     if (d->usedDatabase == my_dbName)
0804         return true; //already used
0805 
0806     if (!d->skipDatabaseExistsCheckInUseDatabase) {
0807         if (!databaseExists(my_dbName, false /*don't ignore errors*/))
0808             return false; //database must exist
0809     }
0810 
0811     if (!d->usedDatabase.isEmpty() && !closeDatabase()) //close db if already used
0812         return false;
0813 
0814     d->usedDatabase.clear();
0815 
0816     if (!drv_useDatabase(my_dbName, cancelled, msgHandler)) {
0817         if (cancelled && *cancelled)
0818             return false;
0819         QString msg(tr("Opening database \"%1\" failed.").arg(my_dbName));
0820         m_result.prependMessage(msg);
0821         return false;
0822     }
0823     if (d->serverVersion.isNull() && d->driver->behavior()->USING_DATABASE_REQUIRED_TO_CONNECT) {
0824         // get version just now, it was not possible earlier
0825         if (!drv_getServerVersion(&d->serverVersion))
0826             return false;
0827     }
0828 
0829     //-create system tables schema objects
0830     d->setupKDbSystemSchema();
0831 
0832     if (kexiCompatible && my_dbName.compare(anyAvailableDatabaseName(), Qt::CaseInsensitive) != 0) {
0833         //-get global database information
0834         bool ok;
0835         const int major = d->dbProperties.value(QLatin1String("kexidb_major_ver")).toInt(&ok);
0836         if (!ok) {
0837             m_result = d->dbProperties.result();
0838             return false;
0839         }
0840         const int minor = d->dbProperties.value(QLatin1String("kexidb_minor_ver")).toInt(&ok);
0841         if (!ok) {
0842             m_result = d->dbProperties.result();
0843             return false;
0844         }
0845         d->databaseVersion.setMajor(major);
0846         d->databaseVersion.setMinor(minor);
0847     }
0848     d->usedDatabase = my_dbName;
0849     return true;
0850 }
0851 
0852 bool KDbConnection::closeDatabase()
0853 {
0854     if (d->usedDatabase.isEmpty())
0855         return true; //no db used
0856     if (!checkConnected())
0857         return true;
0858 
0859     bool ret = true;
0860 
0861     /*! @todo (js) add CLEVER algorithm here for nested transactions */
0862     if (d->driver->transactionsSupported()) {
0863         //rollback all transactions
0864         d->dontRemoveTransactions = true; //lock!
0865         foreach(const KDbTransaction& tr, d->transactions) {
0866             if (!rollbackTransaction(tr)) {//rollback as much as you can, don't stop on prev. errors
0867                 ret = false;
0868             } else {
0869                 kdbDebug() << "transaction rolled back!";
0870                 kdbDebug() << "trans.refcount=="
0871                            << (tr.m_data ? QString::number(tr.m_data->refcount())
0872                                          : QLatin1String("(null)"));
0873             }
0874         }
0875         d->dontRemoveTransactions = false; //unlock!
0876         d->transactions.clear(); //free trans. data
0877     }
0878 
0879     //delete own cursors:
0880     d->deleteAllCursors();
0881     //delete own schemas
0882     d->clearTables();
0883     d->clearQueries();
0884 
0885     if (!drv_closeDatabase())
0886         return false;
0887 
0888     d->usedDatabase.clear();
0889     return ret;
0890 }
0891 
0892 QString KDbConnection::currentDatabase() const
0893 {
0894     return d->usedDatabase;
0895 }
0896 
0897 bool KDbConnection::useTemporaryDatabaseIfNeeded(QString* name)
0898 {
0899     if (d->driver->behavior()->USE_TEMPORARY_DATABASE_FOR_CONNECTION_IF_NEEDED && !isDatabaseUsed()) {
0900         //we have no db used, but it is required by engine to have used any!
0901         *name = anyAvailableDatabaseName();
0902         if (name->isEmpty()) {
0903             m_result = KDbResult(ERR_NO_DB_USED,
0904                                  tr("Could not find any database for temporary connection."));
0905             return false;
0906         }
0907         const bool orig_skipDatabaseExistsCheckInUseDatabase = d->skipDatabaseExistsCheckInUseDatabase;
0908         d->skipDatabaseExistsCheckInUseDatabase = true;
0909         bool ret = useDatabase(*name, false);
0910         d->skipDatabaseExistsCheckInUseDatabase = orig_skipDatabaseExistsCheckInUseDatabase;
0911         if (!ret) {
0912             m_result = KDbResult(m_result.code(),
0913                                  tr("Error during starting temporary connection using \"%1\" database name.").arg(*name));
0914             return false;
0915         }
0916     }
0917     return true;
0918 }
0919 
0920 bool KDbConnection::dropDatabase(const QString &dbName)
0921 {
0922     if (d->driver->behavior()->CONNECTION_REQUIRED_TO_DROP_DB && !checkConnected())
0923         return false;
0924 
0925     QString dbToDrop;
0926     if (dbName.isEmpty() && d->usedDatabase.isEmpty()) {
0927         if (!d->driver->metaData()->isFileBased()
0928                 || (d->driver->metaData()->isFileBased() && d->connData.databaseName().isEmpty()))
0929         {
0930             m_result = KDbResult(ERR_NO_NAME_SPECIFIED,
0931                                  tr("Could not delete database. Name is not specified."));
0932             return false;
0933         }
0934         //this is a file driver so reuse previously passed filename
0935         dbToDrop = d->connData.databaseName();
0936     } else {
0937         if (dbName.isEmpty()) {
0938             dbToDrop = d->usedDatabase;
0939         } else {
0940             if (d->driver->metaData()->isFileBased()) //lets get full path
0941                 dbToDrop = QFileInfo(dbName).absoluteFilePath();
0942             else
0943                 dbToDrop = dbName;
0944         }
0945     }
0946 
0947     if (dbToDrop.isEmpty()) {
0948         m_result = KDbResult(ERR_NO_NAME_SPECIFIED,
0949                              tr("Could not delete database. Name is not specified."));
0950         return false;
0951     }
0952 
0953     if (d->driver->isSystemDatabaseName(dbToDrop)) {
0954         m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
0955                              tr("Could not delete system database \"%1\".").arg(dbToDrop));
0956         return false;
0957     }
0958 
0959     if (isDatabaseUsed() && d->usedDatabase == dbToDrop) {
0960         //we need to close database because cannot drop used this database
0961         if (!closeDatabase())
0962             return false;
0963     }
0964 
0965     QString tmpdbName;
0966     //some engines need to have opened any database before executing "drop database"
0967     if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
0968         return false;
0969 
0970     //ok, now we have access to dropping
0971     bool ret = drv_dropDatabase(dbToDrop);
0972 
0973     if (!tmpdbName.isEmpty()) {
0974         //whatever result is - now we have to close temporary opened database:
0975         if (!closeDatabase())
0976             return false;
0977     }
0978     return ret;
0979 }
0980 
0981 QStringList KDbConnection::objectNames(int objectType, bool* ok)
0982 {
0983     if (!checkIsDatabaseUsed()) {
0984         if (ok) {
0985             *ok = false;
0986         }
0987         return QStringList();
0988     }
0989     KDbEscapedString sql;
0990     if (objectType == KDb::AnyObjectType) {
0991         sql = "SELECT o_name FROM kexi__objects ORDER BY o_id";
0992     } else {
0993         sql = KDbEscapedString("SELECT o_name FROM kexi__objects WHERE o_type=%1"
0994                                " ORDER BY o_id").arg(d->driver->valueToSql(KDbField::Integer, objectType));
0995     }
0996     QStringList list;
0997     const bool success = queryStringListInternal(&sql, &list, nullptr, nullptr, 0, KDb::isIdentifier);
0998     if (ok) {
0999         *ok = success;
1000     }
1001     if (!success) {
1002         m_result.prependMessage(tr("Could not retrieve list of object names."));
1003     }
1004     return list;
1005 }
1006 
1007 QStringList 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 
1039 tristate KDbConnection::containsTable(const QString &tableName)
1040 {
1041     return drv_containsTable(tableName);
1042 }
1043 
1044 QStringList KDbConnection::kdbSystemTableNames()
1045 {
1046     return *g_kdbSystemTableNames;
1047 }
1048 
1049 KDbServerVersionInfo KDbConnection::serverVersion() const
1050 {
1051     return isConnected() ? d->serverVersion : KDbServerVersionInfo();
1052 }
1053 
1054 KDbVersionInfo KDbConnection::databaseVersion() const
1055 {
1056     return isDatabaseUsed() ? d->databaseVersion : KDbVersionInfo();
1057 }
1058 
1059 KDbProperties KDbConnection::databaseProperties() const
1060 {
1061     return d->dbProperties;
1062 }
1063 
1064 QList<int> KDbConnection::tableIds(bool* ok)
1065 {
1066     return objectIds(KDb::TableObjectType, ok);
1067 }
1068 
1069 QList<int> KDbConnection::queryIds(bool* ok)
1070 {
1071     return objectIds(KDb::QueryObjectType, ok);
1072 }
1073 
1074 QList<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 
1119 QSharedPointer<KDbSqlResult> KDbConnection::insertRecordInternal(const QString &tableSchemaName,
1120                                                                  KDbFieldList *fields,
1121                                                                  const KDbEscapedString &sql)
1122 {
1123     QSharedPointer<KDbSqlResult> res;
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 
1167 C_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 
1191 C_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 
1199 QSharedPointer<KDbSqlResult> KDbConnection::insertRecord(KDbTableSchema *tableSchema,
1200                                                          const QList<QVariant> &values)
1201 {
1202 // Each SQL identifier needs to be escaped in the generated query.
1203     QSharedPointer<KDbSqlResult> res;
1204     const KDbField::List *flist = tableSchema->fields();
1205     if (flist->isEmpty()) {
1206         return res;
1207     }
1208     KDbField::ListIterator fieldsIt(flist->constBegin());
1209     QList<QVariant>::ConstIterator it = values.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);
1228     res = insertRecordInternal(tableSchema->name(), tableSchema, sql);
1229     return res;
1230 }
1231 
1232 QSharedPointer<KDbSqlResult> KDbConnection::insertRecord(KDbFieldList *fields,
1233                                                          const QList<QVariant> &values)
1234 {
1235 // Each SQL identifier needs to be escaped in the generated query.
1236     QSharedPointer<KDbSqlResult> res;
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);
1244     QList<QVariant>::ConstIterator it = values.constBegin();
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 
1268 inline 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 
1280 QSharedPointer<KDbSqlResult> KDbConnection::prepareSql(const KDbEscapedString& sql)
1281 {
1282     m_result.setSql(sql);
1283     return QSharedPointer<KDbSqlResult>(drv_prepareSql(sql));
1284 }
1285 
1286 bool KDbConnection::executeSql(const KDbEscapedString& sql)
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 
1303 KDbField* KDbConnection::findSystemFieldName(const KDbFieldList& fieldlist)
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()
1313 static KDbFieldList* createFieldListForKexi__Fields(KDbTableSchema *kexi__fieldsSchema)
1314 {
1315     if (!kexi__fieldsSchema)
1316         return nullptr;
1317     return kexi__fieldsSchema->subList(
1318                QList<QByteArray>()
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 
1333 static 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().
1342 static 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()
1357         ? QVariant() : QVariant(KDb::variantToString(f->defaultValue())))
1358     << QVariant(f->order())
1359     << QVariant(f->caption())
1360     << QVariant(f->description());
1361 }
1362 
1363 bool KDbConnection::storeMainFieldSchema(KDbField *field)
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);
1373     QList<QVariant>::ConstIterator valsIt = vals.constBegin();
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 
1397 bool KDbConnection::createTable(KDbTableSchema* tableSchema, CreateTableOptions options)
1398 {
1399     if (!tableSchema || !checkIsDatabaseUsed())
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 
1421         KDbField *sys_field = findSystemFieldName(*tableSchema);
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;
1434     if (options & CreateTableOption::DropDestination) {
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     }
1459     KDbTransactionGuard tg;
1460     if (!beginAutoCommitTransaction(&tg))
1461         return false;
1462 
1463     if (internalTable) {
1464         if (!drv_containsTable(internalTable->name())) { // internal table may exist
1465             if (!drv_createTable(*tableSchema)) {
1466                 createTable_ERR;
1467             }
1468         }
1469     } else {
1470         if (!drv_createTable(*tableSchema)) {
1471             createTable_ERR;
1472         }
1473     }
1474 
1475     //add the object data to kexi__* tables
1476     if (!internalTable) {
1477         //update kexi__objects
1478         if (!storeNewObjectData(tableSchema))
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 
1500         if (!storeExtendedTableSchemaData(tableSchema))
1501             createTable_ERR;
1502     }
1503     bool res = commitAutoCommitTransaction(tg.transaction());
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
1514         tableSchema->setConnection(this);
1515     }
1516     return res;
1517 }
1518 
1519 KDbTableSchema *KDbConnection::copyTable(const KDbTableSchema &tableSchema, const KDbObject &newData)
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,
1534         CreateTableOptions(CreateTableOption::Default) & ~CreateTableOptions(CreateTableOption::DropDestination)))
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 
1547 KDbTableSchema *KDbConnection::copyTable(const QString &tableName, const KDbObject &newData)
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 
1559 bool KDbConnection::drv_copyTableData(const KDbTableSchema &tableSchema,
1560                                    const KDbTableSchema &destinationTableSchema)
1561 {
1562     KDbEscapedString sql = KDbEscapedString("INSERT INTO %1 SELECT * FROM %2")
1563                 .arg(escapeIdentifier(destinationTableSchema.name()))
1564                 .arg(escapeIdentifier(tableSchema.name()));
1565     return executeSql(sql);
1566 }
1567 
1568 bool KDbConnection::removeObject(int objId)
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 
1585 bool KDbConnection::drv_dropTable(const QString& tableName)
1586 {
1587     return executeSql(KDbEscapedString("DROP TABLE %1").arg(escapeIdentifier(tableName)));
1588 }
1589 
1590 tristate KDbConnection::dropTable(KDbTableSchema* tableSchema)
1591 {
1592     return dropTableInternal(tableSchema, true);
1593 }
1594 
1595 tristate KDbConnection::dropTableInternal(KDbTableSchema* tableSchema, bool alsoRemoveSchema)
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 
1613     tristate res = KDbTableSchemaChangeListener::closeListeners(this, tableSchema);
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 
1626     KDbTransactionGuard tg;
1627     if (!beginAutoCommitTransaction(&tg))
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) {
1636         if (!drv_dropTable(tableSchema->name()))
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     }
1656     return commitAutoCommitTransaction(tg.transaction());
1657 }
1658 
1659 tristate KDbConnection::dropTable(const QString& tableName)
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 
1671 tristate KDbConnection::alterTable(KDbTableSchema* tableSchema, KDbTableSchema* newTableSchema)
1672 {
1673     clearResult();
1674     tristate res = KDbTableSchemaChangeListener::closeListeners(this, tableSchema);
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
1695                              | KDbConnection::CreateTableOption::DropDestination);
1696     }
1697     return ok;
1698 }
1699 
1700 bool 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 
1738     KDbTransactionGuard tg;
1739     if (!beginAutoCommitTransaction(&tg))
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 
1788     if (!commitAutoCommitTransaction(tg.transaction())) {
1789         alterTableName_ERR;
1790         return false;
1791     }
1792 
1793     //update tableSchema:
1794     d->renameTable(tableSchema, newTableName);
1795     return true;
1796 }
1797 
1798 bool KDbConnection::drv_alterTableName(KDbTableSchema* tableSchema, const QString& newName)
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)),
1805                          KDbEscapedString(escapeIdentifier(newName)))))
1806     {
1807         tableSchema->setName(oldTableName); //restore old name
1808         return false;
1809     }
1810     return true;
1811 }
1812 
1813 bool KDbConnection::dropQuery(KDbQuerySchema* querySchema)
1814 {
1815     clearResult();
1816     if (!querySchema)
1817         return false;
1818 
1819     KDbTransactionGuard tg;
1820     if (!beginAutoCommitTransaction(&tg))
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);
1830     return commitAutoCommitTransaction(tg.transaction());
1831 }
1832 
1833 bool 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 
1845 QStringList KDbConnection::drv_getTableNames(bool *ok)
1846 {
1847     Q_ASSERT(ok);
1848     QStringList tableNames;
1849     const KDbEscapedString sql(d->driver->behavior()->GET_TABLE_NAMES_SQL);
1850     if (sql.isEmpty()) {
1851         *ok = false;
1852         return QStringList();
1853     }
1854     QSharedPointer<KDbSqlResult> result = prepareSql(sql);
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 
1874 bool KDbConnection::drv_createTable(const KDbTableSchema& tableSchema)
1875 {
1876     const KDbNativeStatementBuilder builder(this, KDb::DriverEscaping);
1877     KDbEscapedString sql;
1878     if (!builder.generateCreateTableStatement(&sql,tableSchema)) {
1879         return false;
1880     }
1881     //kdbDebug() << "******** " << sql;
1882     return executeSql(sql);
1883 }
1884 
1885 bool KDbConnection::drv_createTable(const QString& tableName)
1886 {
1887     KDbTableSchema *ts = tableSchema(tableName);
1888     if (!ts)
1889         return false;
1890     return drv_createTable(*ts);
1891 }
1892 
1893 bool KDbConnection::beginAutoCommitTransaction(KDbTransactionGuard* tg)
1894 {
1895     if ((d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
1896             || !d->autoCommit) {
1897         tg->setTransaction(KDbTransaction());
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
1905             if (!commitTransaction(d->default_trans, KDbTransaction::CommitOption::IgnoreInactive)) {
1906                 tg->setTransaction(KDbTransaction());
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)) {
1917         tg->setTransaction(KDbTransaction());
1918         return true; //no trans. supported at all - just return
1919     }
1920     tg->setTransaction(beginTransaction());
1921     return !m_result.isError();
1922 }
1923 
1924 bool KDbConnection::commitAutoCommitTransaction(const KDbTransaction& trans)
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     }
1934     return commitTransaction(trans, KDbTransaction::CommitOption::IgnoreInactive);
1935 }
1936 
1937 bool KDbConnection::rollbackAutoCommitTransaction(const KDbTransaction& trans)
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 
1953 KDbTransaction KDbConnection::beginTransaction()
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 
1992 bool KDbConnection::commitTransaction(const KDbTransaction trans,
1993                                       KDbTransaction::CommitOptions options)
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()) {
2005             if (options & KDbTransaction::CommitOption::IgnoreInactive) {
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 
2029 bool KDbConnection::rollbackTransaction(const KDbTransaction trans,
2030                                         KDbTransaction::CommitOptions options)
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()) {
2042             if (options & KDbTransaction::CommitOption::IgnoreInactive) {
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 
2074 KDbTransaction KDbConnection::defaultTransaction() const
2075 {
2076     return d->default_trans;
2077 }
2078 
2079 void KDbConnection::setDefaultTransaction(const KDbTransaction& trans)
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 
2090 QList<KDbTransaction> KDbConnection::transactions()
2091 {
2092     return d->transactions;
2093 }
2094 
2095 bool KDbConnection::autoCommit() const
2096 {
2097     return d->autoCommit;
2098 }
2099 
2100 bool KDbConnection::setAutoCommit(bool on)
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 
2110 KDbTransactionData* KDbConnection::drv_beginTransaction()
2111 {
2112     if (!executeSql(KDbEscapedString("BEGIN")))
2113         return nullptr;
2114     return new KDbTransactionData(this);
2115 }
2116 
2117 bool KDbConnection::drv_commitTransaction(KDbTransactionData *)
2118 {
2119     return executeSql(KDbEscapedString("COMMIT"));
2120 }
2121 
2122 bool KDbConnection::drv_rollbackTransaction(KDbTransactionData *)
2123 {
2124     return executeSql(KDbEscapedString("ROLLBACK"));
2125 }
2126 
2127 bool KDbConnection::drv_setAutoCommit(bool /*on*/)
2128 {
2129     return true;
2130 }
2131 
2132 KDbCursor* KDbConnection::executeQuery(const KDbEscapedString& sql, KDbCursor::Options options)
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 
2147 KDbCursor* KDbConnection::executeQuery(KDbQuerySchema* query, const QList<QVariant>& params,
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 
2161 KDbCursor* KDbConnection::executeQuery(KDbQuerySchema* query, KDbCursor::Options options)
2162 {
2163     return executeQuery(query, QList<QVariant>(), options);
2164 }
2165 
2166 KDbCursor* KDbConnection::executeQuery(KDbTableSchema* table, KDbCursor::Options options)
2167 {
2168     return executeQuery(table->query(), options);
2169 }
2170 
2171 KDbCursor* KDbConnection::prepareQuery(KDbTableSchema* table, KDbCursor::Options options)
2172 {
2173     return prepareQuery(table->query(), options);
2174 }
2175 
2176 KDbCursor* KDbConnection::prepareQuery(KDbQuerySchema* query, const QList<QVariant>& params,
2177                                        KDbCursor::Options options)
2178 {
2179     KDbCursor* cursor = prepareQuery(query, options);
2180     if (cursor)
2181         cursor->setQueryParameters(params);
2182     return cursor;
2183 }
2184 
2185 bool KDbConnection::deleteCursor(KDbCursor *cursor)
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
2199 bool KDbConnection::setupObjectData(const KDbRecordData &data, KDbObject *object)
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 
2224 tristate KDbConnection::loadObjectData(int type, int id, KDbObject* object)
2225 {
2226     KDbRecordData data;
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 
2247 tristate KDbConnection::loadObjectData(int type, const QString& name, KDbObject* object)
2248 {
2249     KDbRecordData data;
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 
2262 bool 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
2282             QScopedPointer<KDbFieldList> fl(ts->subList(
2283                 QList<QByteArray>() << "o_type" << "o_name" << "o_caption" << "o_desc"));
2284             if (!fl) {
2285                 return false;
2286             }
2287             QSharedPointer<KDbSqlResult> result
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 {
2303             QScopedPointer<KDbFieldList> fl(ts->subList(
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 
2319 bool KDbConnection::storeObjectData(KDbObject* object)
2320 {
2321     return storeObjectDataInternal(object, false);
2322 }
2323 
2324 bool KDbConnection::storeNewObjectData(KDbObject* object)
2325 {
2326     return storeObjectDataInternal(object, true);
2327 }
2328 
2329 QString KDbConnection::escapeIdentifier(const QString& id, KDb::IdentifierEscapingType escapingType) const
2330 {
2331     return escapingType == KDb::KDbEscaping
2332         ? KDb::escapeIdentifier(id) : escapeIdentifier(id);
2333 }
2334 
2335 KDbCursor* KDbConnection::executeQueryInternal(const KDbEscapedString& sql,
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 
2353 tristate KDbConnection::querySingleRecordInternal(KDbRecordData *data, const KDbEscapedString *sql,
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 
2380 tristate KDbConnection::querySingleRecord(const KDbEscapedString &sql, KDbRecordData *data,
2381                                           QueryRecordOptions options)
2382 {
2383     return querySingleRecordInternal(data, &sql, nullptr, nullptr, options);
2384 }
2385 
2386 tristate KDbConnection::querySingleRecord(KDbQuerySchema *query, KDbRecordData *data,
2387                                           QueryRecordOptions options)
2388 {
2389     return querySingleRecordInternal(data, nullptr, query, nullptr, options);
2390 }
2391 
2392 tristate KDbConnection::querySingleRecord(KDbQuerySchema *query, KDbRecordData *data,
2393                                           const QList<QVariant> &params, QueryRecordOptions options)
2394 {
2395     return querySingleRecordInternal(data, nullptr, query, &params, options);
2396 }
2397 
2398 bool KDbConnection::checkIfColumnExists(KDbCursor *cursor, int column)
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 
2408 tristate KDbConnection::querySingleStringInternal(const KDbEscapedString *sql, QString *value,
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 
2439 tristate KDbConnection::querySingleString(const KDbEscapedString &sql, QString *value, int column,
2440                                           QueryRecordOptions options)
2441 {
2442     return querySingleStringInternal(&sql, value, nullptr, nullptr, column, options);
2443 }
2444 
2445 tristate KDbConnection::querySingleString(KDbQuerySchema *query, QString *value, int column,
2446                                           QueryRecordOptions options)
2447 {
2448     return querySingleStringInternal(nullptr, value, query, nullptr, column, options);
2449 }
2450 
2451 tristate KDbConnection::querySingleString(KDbQuerySchema *query, QString *value,
2452                                           const QList<QVariant> &params, int column,
2453                                           QueryRecordOptions options)
2454 {
2455     return querySingleStringInternal(nullptr, value, query, &params, column, options);
2456 }
2457 
2458 tristate KDbConnection::querySingleNumberInternal(const KDbEscapedString *sql, int *number,
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 
2477 tristate KDbConnection::querySingleNumber(const KDbEscapedString &sql, int *number, int column,
2478                                           QueryRecordOptions options)
2479 {
2480     return querySingleNumberInternal(&sql, number, nullptr, nullptr, column, options);
2481 }
2482 
2483 tristate KDbConnection::querySingleNumber(KDbQuerySchema *query, int *number, int column,
2484                                           QueryRecordOptions options)
2485 {
2486     return querySingleNumberInternal(nullptr, number, query, nullptr, column, options);
2487 }
2488 
2489 tristate KDbConnection::querySingleNumber(KDbQuerySchema *query, int *number,
2490                                           const QList<QVariant> &params, int column,
2491                                           QueryRecordOptions options)
2492 {
2493     return querySingleNumberInternal(nullptr, number, query, &params, column, options);
2494 }
2495 
2496 bool KDbConnection::queryStringListInternal(const KDbEscapedString *sql, QStringList *list,
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 
2539 bool KDbConnection::queryStringList(const KDbEscapedString& sql, QStringList* list,
2540                                     int column)
2541 {
2542     return queryStringListInternal(&sql, list, nullptr, nullptr, column, nullptr);
2543 }
2544 
2545 bool KDbConnection::queryStringList(KDbQuerySchema* query, QStringList* list, int column)
2546 {
2547     return queryStringListInternal(nullptr, list, query, nullptr, column, nullptr);
2548 }
2549 
2550 bool KDbConnection::queryStringList(KDbQuerySchema* query, QStringList* list,
2551                                     const QList<QVariant>& params, int column)
2552 {
2553     return queryStringListInternal(nullptr, list, query, &params, column, nullptr);
2554 }
2555 
2556 tristate KDbConnection::resultExists(const KDbEscapedString &sql, QueryRecordOptions options)
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 
2587 tristate KDbConnection::isEmpty(KDbTableSchema* table)
2588 {
2589     const KDbNativeStatementBuilder builder(this, KDb::DriverEscaping);
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()
2602 static 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()
2617 static 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). */
2635 static 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;
2658     case QVariant::ByteArray:
2659         extendedTableSchemaFieldPropertyValueEl = doc->createElement(QLatin1String("cstring"));
2660         break;
2661     case QVariant::Int:
2662     case QVariant::Double:
2663     case QVariant::UInt:
2664     case QVariant::LongLong:
2665     case QVariant::ULongLong:
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 
2680 bool KDbConnection::storeExtendedTableSchemaData(KDbTableSchema* tableSchema)
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) {
2698             if (f->maxLengthStrategy() == KDbField::DefaultMaxLength) {
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 
2751 bool KDbConnection::loadExtendedTableSchemaData(KDbTableSchema* tableSchema)
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;
2770     tristate res = loadDataBlock(tableSchema->id(),
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") {
2820                             if (KDb::supportsVisibleDecimalPlacesProperty(f->type())) {
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
2829                                     = KDb::loadPropertyValueFromDom(propEl.firstChild(), &ok).toBool();
2830                                 if (ok) {
2831                                     f->setMaxLengthStrategy(
2832                                         maxLengthIsDefault ? KDbField::DefaultMaxLength : KDbField::DefinedMaxLength);
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 
2855 KDbField* KDbConnection::setupField(const KDbRecordData &data)
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 
2904 KDbTableSchema* KDbConnection::tableSchema(const QString& tableName)
2905 {
2906     KDbTableSchema *t = d->table(tableName);
2907     if (t || tableName.isEmpty()) {
2908         return t;
2909     }
2910     //not found: retrieve schema
2911     QScopedPointer<KDbTableSchema> newTable(new KDbTableSchema);
2912     clearResult();
2913     if (true != loadObjectData(KDb::TableObjectType, tableName, newTable.data())) {
2914         return nullptr;
2915     }
2916     return d->setupTableSchema(newTable.take());
2917 }
2918 
2919 KDbTableSchema* KDbConnection::tableSchema(int tableId)
2920 {
2921     KDbTableSchema *t = d->table(tableId);
2922     if (t)
2923         return t;
2924     //not found: retrieve schema
2925     QScopedPointer<KDbTableSchema> newTable(new KDbTableSchema);
2926     clearResult();
2927     if (true != loadObjectData(KDb::TableObjectType, tableId, newTable.data())) {
2928         return nullptr;
2929     }
2930     return d->setupTableSchema(newTable.take());
2931 }
2932 
2933 tristate 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))
2940                                 + KDbEscapedString(KDb::sqlWhere(d->driver, KDbField::Text,
2941                                                 QLatin1String("o_sub_id"),
2942                                                 dataID.isEmpty() ? QVariant() : QVariant(dataID))),
2943                dataString);
2944 }
2945 
2946 bool 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 
2972 bool 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;
2980     KDbEscapedString sql = KDbEscapedString(
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 
2992 bool 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 
3005 KDbQuerySchema* KDbConnection::querySchema(const QString& aQueryName)
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
3013     QScopedPointer<KDbQuerySchema> newQuery(new KDbQuerySchema);
3014     clearResult();
3015     if (true != loadObjectData(KDb::QueryObjectType, aQueryName, newQuery.data())) {
3016         return nullptr;
3017     }
3018     return d->setupQuerySchema(newQuery.take());
3019 }
3020 
3021 KDbQuerySchema* KDbConnection::querySchema(int queryId)
3022 {
3023     KDbQuerySchema *q = d->query(queryId);
3024     if (q)
3025         return q;
3026     //not found: retrieve schema
3027     QScopedPointer<KDbQuerySchema> newQuery(new KDbQuerySchema);
3028     clearResult();
3029     if (true != loadObjectData(KDb::QueryObjectType, queryId, newQuery.data())) {
3030         return nullptr;
3031     }
3032     return d->setupQuerySchema(newQuery.take());
3033 }
3034 
3035 bool KDbConnection::setQuerySchemaObsolete(const QString& queryName)
3036 {
3037     KDbQuerySchema* oldQuery = querySchema(queryName);
3038     if (!oldQuery)
3039         return false;
3040     d->setQueryObsolete(oldQuery);
3041     return true;
3042 }
3043 
3044 QString KDbConnection::escapeIdentifier(const QString& id) const
3045 {
3046     return d->driver->escapeIdentifier(id);
3047 }
3048 
3049 bool 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 
3058 void KDbConnection::removeMe(KDbTableSchema *table)
3059 {
3060     if (table && d) {
3061         d->takeTable(table);
3062     }
3063 }
3064 
3065 QString KDbConnection::anyAvailableDatabaseName()
3066 {
3067     if (!d->availableDatabaseName.isEmpty()) {
3068         return d->availableDatabaseName;
3069     }
3070     return d->driver->behavior()->ALWAYS_AVAILABLE_DATABASE_NAME;
3071 }
3072 
3073 void KDbConnection::setAvailableDatabaseName(const QString& dbName)
3074 {
3075     d->availableDatabaseName = dbName;
3076 }
3077 
3078 //! @internal used in updateRecord(), insertRecord(),
3079 inline static void updateRecordDataWithNewValues(
3080         KDbConnection *conn, KDbQuerySchema* query, KDbRecordData* data,
3081         const KDbRecordEditBuffer::DbHash& b,
3082         QHash<KDbQueryColumnInfo*, int>* columnsOrderExpanded)
3083 {
3084     *columnsOrderExpanded
3085         = query->columnsOrder(conn, KDbQuerySchema::ColumnsOrderMode::ExpandedList);
3086     QHash<KDbQueryColumnInfo*, int>::ConstIterator columnsOrderExpandedIt;
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 
3098 bool KDbConnection::updateRecord(KDbQuerySchema* query, KDbRecordData* data, KDbRecordEditBuffer* buf, bool useRecordId)
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 
3200 bool 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 
3363 bool 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 
3430 bool KDbConnection::deleteAllRecords(KDbQuerySchema* query)
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 
3453 int KDbConnection::recordCount(const KDbEscapedString& sql)
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 
3464 int KDbConnection::recordCount(const KDbTableSchema& tableSchema)
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 
3476 int KDbConnection::recordCount(KDbQuerySchema* querySchema, const QList<QVariant>& params)
3477 {
3478 //! @todo does not work with non-SQL data sources
3479     int count = -1; //will be changed only on success of querySingleNumber()
3480     KDbNativeStatementBuilder builder(this, KDb::DriverEscaping);
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 
3493 int KDbConnection::recordCount(KDbTableOrQuerySchema* tableOrQuery, const QList<QVariant>& params)
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 
3504 KDbConnectionOptions* KDbConnection::options()
3505 {
3506     return &d->options;
3507 }
3508 
3509 void KDbConnection::addCursor(KDbCursor* cursor)
3510 {
3511     d->cursors.insert(cursor);
3512 }
3513 
3514 void KDbConnection::takeCursor(KDbCursor* cursor)
3515 {
3516     if (d && !d->cursors.isEmpty()) { // checking because this may be called from ~KDbConnection()
3517         d->cursors.remove(cursor);
3518     }
3519 }
3520 
3521 KDbPreparedStatement KDbConnection::prepareStatement(KDbPreparedStatement::Type type,
3522     KDbFieldList* fields, const QStringList& whereFieldNames)
3523 {
3524 //! @todo move to ConnectionInterface just like we moved execute() and prepare() to KDbPreparedStatementInterface...
3525     KDbPreparedStatementInterface *iface = prepareStatementInternal();
3526     if (!iface)
3527         return KDbPreparedStatement();
3528     return KDbPreparedStatement(iface, type, fields, whereFieldNames);
3529 }
3530 
3531 KDbEscapedString KDbConnection::recentSqlString() const {
3532     return result().errorSql().isEmpty() ? m_result.sql() : result().errorSql();
3533 }
3534 
3535 KDbEscapedString KDbConnection::escapeString(const QString& str) const
3536 {
3537     return d->driver->escapeString(str);
3538 }
3539 
3540 //! @todo extraMessages
3541 #if 0
3542 static const char *extraMessages[] = {
3543     QT_TRANSLATE_NOOP("KDbConnection", "Unknown error.")
3544 };
3545 #endif