File indexing completed on 2024-03-24 04:43:06
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> ¶ms, QueryRecordOptions options) 2394 { 2395 return querySingleRecordInternal(data, nullptr, query, ¶ms, 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> ¶ms, int column, 2453 QueryRecordOptions options) 2454 { 2455 return querySingleStringInternal(nullptr, value, query, ¶ms, 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> ¶ms, int column, 2491 QueryRecordOptions options) 2492 { 2493 return querySingleNumberInternal(nullptr, number, query, ¶ms, 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, ¶ms, 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