KDb

KDbConnection.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2003-2017 JarosÅ‚aw Staniek <[email protected]>
3 
4  This program is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this program; see the file COPYING. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "KDbConnection.h"
21 #include "KDbConnection_p.h"
22 #include "KDbCursor.h"
23 #include "KDbDriverBehavior.h"
24 #include "KDbDriverMetaData.h"
25 #include "KDbDriver_p.h"
26 #include "KDbLookupFieldSchema.h"
27 #include "KDbNativeStatementBuilder.h"
28 #include "KDbQuerySchema.h"
29 #include "KDbQuerySchema_p.h"
30 #include "KDbRecordData.h"
31 #include "KDbRecordEditBuffer.h"
32 #include "KDbRelationship.h"
33 #include "KDbSqlRecord.h"
34 #include "KDbSqlResult.h"
35 #include "KDbTableOrQuerySchema.h"
36 #include "KDbTableSchemaChangeListener.h"
37 #include "KDbTransactionData.h"
38 #include "KDbTransactionGuard.h"
39 #include "kdb_debug.h"
40 
41 #include <QDir>
42 #include <QFileInfo>
43 #include <QDomDocument>
44 
45 /*! Version number of extended table schema.
46 
47  List of changes:
48  * 2: (Kexi 2.5.0) Added maxLengthIsDefault property (type: bool, if true, KDbField::maxLengthStrategy() == KDbField::DefaultMaxLength)
49  * 1: (Kexi 1.x) Initial version
50 */
51 #define KDB_EXTENDED_TABLE_SCHEMA_VERSION 2
52 
53 KDbConnectionInternal::KDbConnectionInternal(KDbConnection *conn)
54  : connection(conn)
55 {
56 }
57 
58 class CursorDeleter
59 {
60 public:
61  explicit CursorDeleter(KDbCursor *cursor) {
62  delete cursor;
63  }
64 };
65 
66 //================================================
67 
68 class Q_DECL_HIDDEN KDbConnectionOptions::Private
69 {
70 public:
71  Private() : connection(nullptr) {}
72  Private(const Private &other) {
73  copy(other);
74  }
75 #define KDbConnectionOptionsPrivateArgs(o) std::tie(o.connection)
76  void copy(const Private &other) {
77  KDbConnectionOptionsPrivateArgs((*this)) = KDbConnectionOptionsPrivateArgs(other);
78  }
79  bool operator==(const Private &other) const {
80  return KDbConnectionOptionsPrivateArgs((*this)) == KDbConnectionOptionsPrivateArgs(other);
81  }
82  KDbConnection *connection;
83 };
84 
85 KDbConnectionOptions::KDbConnectionOptions()
86  : d(new Private)
87 {
88  KDbUtils::PropertySet::insert("readOnly", false, tr("Read only", "Read only connection"));
89 }
90 
91 KDbConnectionOptions::KDbConnectionOptions(const KDbConnectionOptions &other)
92  : KDbUtils::PropertySet(other)
93  , d(new Private(*other.d))
94 {
95 }
96 
97 KDbConnectionOptions::~KDbConnectionOptions()
98 {
99  delete d;
100 }
101 
102 KDbConnectionOptions& KDbConnectionOptions::operator=(const KDbConnectionOptions &other)
103 {
104  if (this != &other) {
106  d->copy(*other.d);
107  }
108  return *this;
109 }
110 
112 {
113  return KDbUtils::PropertySet::operator==(other) && *d == *other.d;
114 }
115 
117 {
118  return property("readOnly").value().toBool();
119 }
120 
121 void KDbConnectionOptions::insert(const QByteArray &name, const QVariant &value,
122  const QString &caption)
123 {
124  if (name == "readOnly") {
125  setReadOnly(value.toBool());
126  return;
127  }
128  QString realCaption;
129  if (property(name).caption().isEmpty()) { // don't allow to change the caption
130  realCaption = caption;
131  }
132  KDbUtils::PropertySet::insert(name, value, realCaption);
133 }
134 
135 void KDbConnectionOptions::setCaption(const QByteArray &name, const QString &caption)
136 {
137  if (name == "readOnly") {
138  return;
139  }
140  KDbUtils::PropertySet::setCaption(name, caption);
141 }
142 
143 void KDbConnectionOptions::setValue(const QByteArray &name, const QVariant &value)
144 {
145  if (name == "readOnly") {
146  setReadOnly(value.toBool());
147  return;
148  }
149  KDbUtils::PropertySet::setValue(name, value);
150 }
151 
153 {
154  if (name == "readOnly") {
155  return;
156  }
158 }
159 
161 {
162  if (d->connection && d->connection->isConnected()) {
163  return; //sanity
164  }
165  KDbUtils::PropertySet::setValue("readOnly", set);
166 }
167 
168 void KDbConnectionOptions::setConnection(KDbConnection *connection)
169 {
170  d->connection = connection;
171 }
172 
173 //================================================
174 
175 KDbConnectionPrivate::KDbConnectionPrivate(KDbConnection* const conn, KDbDriver *drv, const KDbConnectionData& _connData,
176  const KDbConnectionOptions &_options)
177  : conn(conn)
178  , connData(_connData)
179  , options(_options)
180  , driver(drv)
181  , dbProperties(conn)
182 {
183  options.setConnection(conn);
184 }
185 
186 KDbConnectionPrivate::~KDbConnectionPrivate()
187 {
188  options.setConnection(nullptr);
189  deleteAllCursors();
190  delete m_parser;
191  qDeleteAll(tableSchemaChangeListeners);
192  qDeleteAll(obsoleteQueries);
193 }
194 
195 void KDbConnectionPrivate::deleteAllCursors()
196 {
197  QSet<KDbCursor*> cursorsToDelete(cursors);
198  cursors.clear();
199  for(KDbCursor* c : cursorsToDelete) {
200  CursorDeleter deleter(c);
201  }
202 }
203 
204 void KDbConnectionPrivate::errorInvalidDBContents(const QString& details)
205 {
206  conn->m_result = KDbResult(ERR_INVALID_DATABASE_CONTENTS,
207  KDbConnection::tr("Invalid database contents. %1").arg(details));
208 }
209 
210 QString KDbConnectionPrivate::strItIsASystemObject() const
211 {
212  return KDbConnection::tr("It is a system object.");
213 }
214 
215 void KDbConnectionPrivate::setupKDbSystemSchema()
216 {
217  if (!m_internalKDbTables.isEmpty()) {
218  return; //already set up
219  }
220  {
221  KDbInternalTableSchema *t_objects = new KDbInternalTableSchema(QLatin1String("kexi__objects"));
222  t_objects->addField(new KDbField(QLatin1String("o_id"),
223  KDbField::Integer, KDbField::PrimaryKey | KDbField::AutoInc, KDbField::Unsigned));
224  t_objects->addField(new KDbField(QLatin1String("o_type"), KDbField::Byte, nullptr, KDbField::Unsigned));
225  t_objects->addField(new KDbField(QLatin1String("o_name"), KDbField::Text));
226  t_objects->addField(new KDbField(QLatin1String("o_caption"), KDbField::Text));
227  t_objects->addField(new KDbField(QLatin1String("o_desc"), KDbField::LongText));
228  //kdbDebug() << *t_objects;
229  insertTable(t_objects);
230  }
231  {
232  KDbInternalTableSchema *t_objectdata = new KDbInternalTableSchema(QLatin1String("kexi__objectdata"));
233  t_objectdata->addField(new KDbField(QLatin1String("o_id"),
234  KDbField::Integer, KDbField::NotNull, KDbField::Unsigned));
235  t_objectdata->addField(new KDbField(QLatin1String("o_data"), KDbField::LongText));
236  t_objectdata->addField(new KDbField(QLatin1String("o_sub_id"), KDbField::Text));
237  insertTable(t_objectdata);
238  }
239  {
240  KDbInternalTableSchema *t_fields = new KDbInternalTableSchema(QLatin1String("kexi__fields"));
241  t_fields->addField(new KDbField(QLatin1String("t_id"), KDbField::Integer, nullptr, KDbField::Unsigned));
242  t_fields->addField(new KDbField(QLatin1String("f_type"), KDbField::Byte, nullptr, KDbField::Unsigned));
243  t_fields->addField(new KDbField(QLatin1String("f_name"), KDbField::Text));
244  t_fields->addField(new KDbField(QLatin1String("f_length"), KDbField::Integer));
245  t_fields->addField(new KDbField(QLatin1String("f_precision"), KDbField::Integer));
246  t_fields->addField(new KDbField(QLatin1String("f_constraints"), KDbField::Integer));
247  t_fields->addField(new KDbField(QLatin1String("f_options"), KDbField::Integer));
248  t_fields->addField(new KDbField(QLatin1String("f_default"), KDbField::Text));
249  //these are additional properties:
250  t_fields->addField(new KDbField(QLatin1String("f_order"), KDbField::Integer));
251  t_fields->addField(new KDbField(QLatin1String("f_caption"), KDbField::Text));
252  t_fields->addField(new KDbField(QLatin1String("f_help"), KDbField::LongText));
253  insertTable(t_fields);
254  }
255  {
257  t_db->addField(new KDbField(QLatin1String("db_property"),
258  KDbField::Text, KDbField::NoConstraints, KDbField::NoOptions, 32));
259  t_db->addField(new KDbField(QLatin1String("db_value"), KDbField::LongText));
260  insertTable(t_db);
261  }
262 }
263 
264 void KDbConnectionPrivate::insertTable(KDbTableSchema* tableSchema)
265 {
266  KDbInternalTableSchema* internalTable = dynamic_cast<KDbInternalTableSchema*>(tableSchema);
267  if (internalTable) {
268  m_internalKDbTables.insert(internalTable);
269  } else {
270  m_tables.insert(tableSchema->id(), tableSchema);
271  }
272  m_tablesByName.insert(tableSchema->name(), tableSchema);
273 }
274 
275 void KDbConnectionPrivate::removeTable(int id)
276 {
277  QScopedPointer<KDbTableSchema> toDelete(m_tables.take(id));
278  if (!toDelete) {
279  kdbWarning() << "Could not find table to delete with id=" << id;
280  return;
281  }
283  const int count = m_tablesByName.remove(toDelete->name());
284  Q_ASSERT_X(count == 1, "KDbConnectionPrivate::removeTable", "Table to remove not found");
285 }
286 
287 void KDbConnectionPrivate::takeTable(KDbTableSchema* tableSchema)
288 {
289  if (m_tables.isEmpty()) {
290  return;
291  }
292  m_tables.take(tableSchema->id());
293  m_tablesByName.take(tableSchema->name());
294 }
295 
296 void KDbConnectionPrivate::renameTable(KDbTableSchema* tableSchema, const QString& newName)
297 {
298  m_tablesByName.take(tableSchema->name());
299  tableSchema->setName(newName);
300  m_tablesByName.insert(tableSchema->name(), tableSchema);
301 }
302 
303 void KDbConnectionPrivate::changeTableId(KDbTableSchema* tableSchema, int newId)
304 {
305  m_tables.take(tableSchema->id());
306  m_tables.insert(newId, tableSchema);
307 }
308 
309 void KDbConnectionPrivate::clearTables()
310 {
311  m_tablesByName.clear();
312  qDeleteAll(m_internalKDbTables);
313  m_internalKDbTables.clear();
314  QHash<int, KDbTableSchema*> tablesToDelete(m_tables);
315  m_tables.clear();
316  qDeleteAll(tablesToDelete);
317 }
318 
319 void KDbConnectionPrivate::insertQuery(KDbQuerySchema* query)
320 {
321  m_queries.insert(query->id(), query);
322  m_queriesByName.insert(query->name(), query);
323 }
324 
325 void KDbConnectionPrivate::removeQuery(KDbQuerySchema* querySchema)
326 {
327  m_queriesByName.remove(querySchema->name());
328  m_queries.remove(querySchema->id());
329  delete querySchema;
330 }
331 
332 void KDbConnectionPrivate::setQueryObsolete(KDbQuerySchema* query)
333 {
334  obsoleteQueries.insert(query);
335  m_queriesByName.take(query->name());
336  m_queries.take(query->id());
337 }
338 
339 void KDbConnectionPrivate::clearQueries()
340 {
341  qDeleteAll(m_queries);
342  m_queries.clear();
343 }
344 
345 KDbTableSchema* KDbConnectionPrivate::setupTableSchema(KDbTableSchema *table)
346 {
347  Q_ASSERT(table);
348  QScopedPointer<KDbTableSchema> newTable(table);
349  KDbCursor *cursor;
350  if (!(cursor = conn->executeQuery(
351  KDbEscapedString("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
352  "f_options, f_default, f_order, f_caption, f_help "
353  "FROM kexi__fields WHERE t_id=%1 ORDER BY f_order")
354  .arg(driver->valueToSql(KDbField::Integer, table->id())))))
355  {
356  return nullptr;
357  }
358  if (!cursor->moveFirst()) {
359  if (!cursor->result().isError() && cursor->eof()) {
360  conn->m_result = KDbResult(tr("Table has no fields defined."));
361  }
362  conn->deleteCursor(cursor);
363  return nullptr;
364  }
365 
366  // For each field: load its schema
367  KDbRecordData fieldData;
368  bool ok = true;
369  while (!cursor->eof()) {
370 // kdbDebug()<<"@@@ f_name=="<<cursor->value(2).asCString();
371  if (!cursor->storeCurrentRecord(&fieldData)) {
372  ok = false;
373  break;
374  }
375  KDbField *f = conn->setupField(fieldData);
376  if (!f || !table->addField(f)) {
377  ok = false;
378  break;
379  }
380  cursor->moveNext();
381  }
382 
383  if (!ok) {//error:
384  conn->deleteCursor(cursor);
385  return nullptr;
386  }
387 
388  if (!conn->deleteCursor(cursor)) {
389  return nullptr;
390  }
391 
392  if (!conn->loadExtendedTableSchemaData(table)) {
393  return nullptr;
394  }
395  //store locally:
396  insertTable(table);
397  return newTable.take();
398 }
399 
400 KDbQuerySchema* KDbConnectionPrivate::setupQuerySchema(KDbQuerySchema *query)
401 {
402  Q_ASSERT(query);
403  QScopedPointer<KDbQuerySchema> newQuery(query);
404  QString sql;
405  if (!conn->loadDataBlock(query->id(), &sql, QLatin1String("sql"))) {
406  conn->m_result = KDbResult(
407  ERR_OBJECT_NOT_FOUND,
408  tr("Could not find definition for query \"%1\". Deleting this query is recommended.")
409  .arg(query->name()));
410  return nullptr;
411  }
412  const QString queryName(query->name());
413  if (!parser()->parse(KDbEscapedString(sql), query)) {
414  newQuery.take(); // query is destroyed by the parser
415  conn->m_result = KDbResult(
416  ERR_SQL_PARSE_ERROR, tr("<p>Could not load definition for query \"%1\". "
417  "SQL statement for this query is invalid:<br><tt>%2</tt></p>\n"
418  "<p>This query can be edited only in Text View.</p>")
419  .arg(queryName, sql));
420  return nullptr;
421  }
422  insertQuery(query);
423  return newQuery.take();
424 }
425 
426 KDbQuerySchemaFieldsExpanded *KDbConnectionPrivate::fieldsExpanded(const KDbQuerySchema *query)
427 {
428  return m_fieldsExpandedCache[query];
429 }
430 
431 void KDbConnectionPrivate::insertFieldsExpanded(const KDbQuerySchema *query, KDbQuerySchemaFieldsExpanded *cache)
432 {
433  m_fieldsExpandedCache.insert(query, cache);
434 }
435 
436 void KDbConnectionPrivate::removeFieldsExpanded(const KDbQuerySchema *query)
437 {
438  //kdbDebug() << "**CACHE REMOVE**" << query;
439  m_fieldsExpandedCache.remove(query);
440 }
441 
442 //================================================
443 
444 namespace {
445 //! @internal static: list of internal KDb system table names
446 class SystemTables : public QStringList
447 {
448 public:
449  SystemTables()
450  : QStringList({
451  QLatin1String("kexi__objects"),
452  QLatin1String("kexi__objectdata"),
453  QLatin1String("kexi__fields"),
454  QLatin1String("kexi__db")})
455  {}
456 };
457 }
458 
459 Q_GLOBAL_STATIC(SystemTables, g_kdbSystemTableNames)
460 
462  const KDbConnectionOptions &options)
463  : d(new KDbConnectionPrivate(this, driver, connData, options))
464 {
465  if (d->connData.driverId().isEmpty()) {
466  d->connData.setDriverId(d->driver->metaData()->id());
467  }
468 }
469 
471 {
472  disconnect();
473  //do not allow the driver to touch me: I will kill myself.
474  d->driver->d->connections.remove(this);
475 }
476 
478 {
479  KDbConnectionPrivate *thisD = d;
480  d = nullptr; // make sure d is nullptr before destructing
481  delete thisD;
482 }
483 
485 {
486  return d->connData;
487 }
488 
490 {
491  return d->driver;
492 }
493 
495 {
496  clearResult();
497  if (d->isConnected) {
498  m_result = KDbResult(ERR_ALREADY_CONNECTED,
499  tr("Connection already established."));
500  return false;
501  }
502 
503  d->serverVersion.clear();
504  if (!(d->isConnected = drv_connect())) {
505  if (m_result.code() == ERR_NONE) {
506  m_result.setCode(ERR_OTHER);
507  }
508  m_result.setMessage(d->driver->metaData()->isFileBased() ?
509  tr("Could not open \"%1\" project file.")
510  .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName()))
511  : tr("Could not connect to \"%1\" database server.")
512  .arg(d->connData.toUserVisibleString()));
513  }
514  if (d->isConnected && !d->driver->behavior()->USING_DATABASE_REQUIRED_TO_CONNECT) {
515  if (!drv_getServerVersion(&d->serverVersion))
516  return false;
517  }
518  return d->isConnected;
519 }
520 
522 {
523  return !d->usedDatabase.isEmpty() && d->isConnected && drv_isDatabaseUsed();
524 }
525 
527 {
528  KDbResultable::clearResult();
529 }
530 
532 {
533  clearResult();
534  if (!d->isConnected)
535  return true;
536 
537  if (!closeDatabase())
538  return false;
539 
540  bool ok = drv_disconnect();
541  if (ok)
542  d->isConnected = false;
543  return ok;
544 }
545 
547 {
548  return d->isConnected;
549 }
550 
552 {
553  if (d->isConnected) {
554  clearResult();
555  return true;
556  }
557  m_result = KDbResult(ERR_NO_CONNECTION,
558  tr("Not connected to the database server."));
559  return false;
560 }
561 
563 {
564  if (isDatabaseUsed()) {
565  clearResult();
566  return true;
567  }
568  m_result = KDbResult(ERR_NO_DB_USED,
569  tr("Currently no database is used."));
570  return false;
571 }
572 
574 {
575  //kdbDebug() << also_system_db;
576  if (!checkConnected())
577  return QStringList();
578 
579  QString tmpdbName;
580  //some engines need to have opened any database before executing "create database"
581  if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
582  return QStringList();
583 
584  QStringList list;
585  bool ret = drv_getDatabasesList(&list);
586 
587  if (!tmpdbName.isEmpty()) {
588  //whatever result is - now we have to close temporary opened database:
589  if (!closeDatabase())
590  return QStringList();
591  }
592 
593  if (!ret)
594  return QStringList();
595 
596  if (also_system_db)
597  return list;
598  //filter system databases:
599  for (QMutableListIterator<QString> it(list); it.hasNext();) {
600  if (d->driver->isSystemDatabaseName(it.next())) {
601  it.remove();
602  }
603  }
604  return list;
605 }
606 
608 {
609  list->clear();
610  return true;
611 }
612 
613 bool KDbConnection::drv_databaseExists(const QString &dbName, bool ignoreErrors)
614 {
615  QStringList list = databaseNames(true);//also system
616  if (m_result.isError()) {
617  return false;
618  }
619 
620  if (list.indexOf(dbName) == -1) {
621  if (!ignoreErrors)
622  m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
623  tr("The database \"%1\" does not exist.").arg(dbName));
624  return false;
625  }
626 
627  return true;
628 }
629 
630 bool KDbConnection::databaseExists(const QString &dbName, bool ignoreErrors)
631 {
632 // kdbDebug() << dbName << ignoreErrors;
633  if (d->driver->behavior()->CONNECTION_REQUIRED_TO_CHECK_DB_EXISTENCE && !checkConnected())
634  return false;
635  clearResult();
636 
637  if (d->driver->metaData()->isFileBased()) {
638  //for file-based db: file must exists and be accessible
639  QFileInfo file(d->connData.databaseName());
640  if (!file.exists() || (!file.isFile() && !file.isSymLink())) {
641  if (!ignoreErrors)
642  m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
643  tr("The database file \"%1\" does not exist.")
644  .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
645  return false;
646  }
647  if (!file.isReadable()) {
648  if (!ignoreErrors)
649  m_result = KDbResult(ERR_ACCESS_RIGHTS,
650  tr("Database file \"%1\" is not readable.")
651  .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
652  return false;
653  }
654  if (!d->options.isReadOnly() && !file.isWritable()) {
655  if (!ignoreErrors)
656  m_result = KDbResult(ERR_ACCESS_RIGHTS,
657  tr("Database file \"%1\" is not writable.")
658  .arg(QDir::fromNativeSeparators(QFileInfo(d->connData.databaseName()).fileName())));
659  return false;
660  }
661  return true;
662  }
663 
664  QString tmpdbName;
665  //some engines need to have opened any database before executing "create database"
666  const bool orig_skipDatabaseExistsCheckInUseDatabase = d->skipDatabaseExistsCheckInUseDatabase;
667  d->skipDatabaseExistsCheckInUseDatabase = true;
668  bool ret = useTemporaryDatabaseIfNeeded(&tmpdbName);
669  d->skipDatabaseExistsCheckInUseDatabase = orig_skipDatabaseExistsCheckInUseDatabase;
670  if (!ret)
671  return false;
672 
673  ret = drv_databaseExists(dbName, ignoreErrors);
674 
675  if (!tmpdbName.isEmpty()) {
676  //whatever result is - now we have to close temporary opened database:
677  if (!closeDatabase())
678  return false;
679  }
680 
681  return ret;
682 }
683 
684 #define createDatabase_CLOSE \
685  { if (!closeDatabase()) { \
686  m_result = KDbResult(KDbConnection::tr("Database \"%1\" has been created but " \
687  "could not be closed after creation.").arg(dbName)); \
688  return false; \
689  } }
690 
691 #define createDatabase_ERROR \
692  { createDatabase_CLOSE; return false; }
693 
694 
696 {
697  if (d->driver->behavior()->CONNECTION_REQUIRED_TO_CREATE_DB && !checkConnected())
698  return false;
699 
700  if (databaseExists(dbName)) {
701  m_result = KDbResult(ERR_OBJECT_EXISTS,
702  tr("Database \"%1\" already exists.").arg(dbName));
703  return false;
704  }
705  if (d->driver->isSystemDatabaseName(dbName)) {
706  m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
707  tr("Could not create database \"%1\". This name is reserved for system database.").arg(dbName));
708  return false;
709  }
710  if (d->driver->metaData()->isFileBased()) {
711  //update connection data if filename differs
712  if (QFileInfo(dbName).isAbsolute()) {
713  d->connData.setDatabaseName(dbName);
714  }
715  else {
716  d->connData.setDatabaseName(
717  QFileInfo(d->connData.databaseName()).absolutePath()
718  + QDir::separator() + QFileInfo(dbName).fileName());
719  }
720  }
721 
722  QString tmpdbName;
723  //some engines need to have opened any database before executing "create database"
724  if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
725  return false;
726 
727  //low-level create
728  if (!drv_createDatabase(dbName)) {
729  m_result.prependMessage(tr("Error creating database \"%1\" on the server.").arg(dbName));
730  (void)closeDatabase();//sanity
731  return false;
732  }
733 
734  if (!tmpdbName.isEmpty()) {
735  //whatever result is - now we have to close temporary opened database:
736  if (!closeDatabase())
737  return false;
738  }
739 
740  if (!tmpdbName.isEmpty() || !d->driver->behavior()->IS_DB_OPEN_AFTER_CREATE) {
741  //db need to be opened
742  if (!useDatabase(dbName, false/*not yet kexi compatible!*/)) {
743  m_result = KDbResult(tr("Database \"%1\" has been created but could not be opened.").arg(dbName));
744  return false;
745  }
746  } else {
747  //just for the rule
748  d->usedDatabase = dbName;
749  d->isConnected = true;
750  }
751 
752  KDbTransaction trans;
753  if (d->driver->transactionsSupported()) {
754  trans = beginTransaction();
755  if (!trans.isActive())
756  return false;
757  }
758 
759  //-create system tables schema objects
760  d->setupKDbSystemSchema();
761 
762  //-physically create internal KDb tables
763  foreach(KDbInternalTableSchema* t, d->internalKDbTables()) {
764  if (!drv_createTable(*t))
765  createDatabase_ERROR;
766  }
767 
768  //-insert KDb version info:
769  // (for compatibility with Kexi expect the legacy kexidb_major_ver/kexidb_minor_ver values)
770  KDbTableSchema *table = d->table(QLatin1String("kexi__db"));
771  if (!table)
772  createDatabase_ERROR;
773  if (!insertRecord(table, QLatin1String("kexidb_major_ver"), KDb::version().major())
774  || !insertRecord(table, QLatin1String("kexidb_minor_ver"), KDb::version().minor()))
775  createDatabase_ERROR;
776 
777  if (trans.isActive() && !commitTransaction(trans))
778  createDatabase_ERROR;
779 
780  createDatabase_CLOSE;
781  return true;
782 }
783 
784 #undef createDatabase_CLOSE
785 #undef createDatabase_ERROR
786 
787 bool KDbConnection::useDatabase(const QString &dbName, bool kexiCompatible, bool *cancelled, KDbMessageHandler* msgHandler)
788 {
789  if (cancelled)
790  *cancelled = false;
791  //kdbDebug() << dbName << kexiCompatible;
792  if (!checkConnected())
793  return false;
794 
795  QString my_dbName;
796  if (dbName.isEmpty())
797  my_dbName = d->connData.databaseName();
798  else
799  my_dbName = dbName;
800  if (my_dbName.isEmpty())
801  return false;
802 
803  if (d->usedDatabase == my_dbName)
804  return true; //already used
805 
806  if (!d->skipDatabaseExistsCheckInUseDatabase) {
807  if (!databaseExists(my_dbName, false /*don't ignore errors*/))
808  return false; //database must exist
809  }
810 
811  if (!d->usedDatabase.isEmpty() && !closeDatabase()) //close db if already used
812  return false;
813 
814  d->usedDatabase.clear();
815 
816  if (!drv_useDatabase(my_dbName, cancelled, msgHandler)) {
817  if (cancelled && *cancelled)
818  return false;
819  QString msg(tr("Opening database \"%1\" failed.").arg(my_dbName));
820  m_result.prependMessage(msg);
821  return false;
822  }
823  if (d->serverVersion.isNull() && d->driver->behavior()->USING_DATABASE_REQUIRED_TO_CONNECT) {
824  // get version just now, it was not possible earlier
825  if (!drv_getServerVersion(&d->serverVersion))
826  return false;
827  }
828 
829  //-create system tables schema objects
830  d->setupKDbSystemSchema();
831 
832  if (kexiCompatible && my_dbName.compare(anyAvailableDatabaseName(), Qt::CaseInsensitive) != 0) {
833  //-get global database information
834  bool ok;
835  const int major = d->dbProperties.value(QLatin1String("kexidb_major_ver")).toInt(&ok);
836  if (!ok) {
837  m_result = d->dbProperties.result();
838  return false;
839  }
840  const int minor = d->dbProperties.value(QLatin1String("kexidb_minor_ver")).toInt(&ok);
841  if (!ok) {
842  m_result = d->dbProperties.result();
843  return false;
844  }
845  d->databaseVersion.setMajor(major);
846  d->databaseVersion.setMinor(minor);
847  }
848  d->usedDatabase = my_dbName;
849  return true;
850 }
851 
853 {
854  if (d->usedDatabase.isEmpty())
855  return true; //no db used
856  if (!checkConnected())
857  return true;
858 
859  bool ret = true;
860 
861  /*! @todo (js) add CLEVER algorithm here for nested transactions */
862  if (d->driver->transactionsSupported()) {
863  //rollback all transactions
864  d->dontRemoveTransactions = true; //lock!
865  foreach(const KDbTransaction& tr, d->transactions) {
866  if (!rollbackTransaction(tr)) {//rollback as much as you can, don't stop on prev. errors
867  ret = false;
868  } else {
869  kdbDebug() << "transaction rolled back!";
870  kdbDebug() << "trans.refcount=="
871  << (tr.m_data ? QString::number(tr.m_data->refcount())
872  : QLatin1String("(null)"));
873  }
874  }
875  d->dontRemoveTransactions = false; //unlock!
876  d->transactions.clear(); //free trans. data
877  }
878 
879  //delete own cursors:
880  d->deleteAllCursors();
881  //delete own schemas
882  d->clearTables();
883  d->clearQueries();
884 
885  if (!drv_closeDatabase())
886  return false;
887 
888  d->usedDatabase.clear();
889  return ret;
890 }
891 
893 {
894  return d->usedDatabase;
895 }
896 
898 {
899  if (d->driver->behavior()->USE_TEMPORARY_DATABASE_FOR_CONNECTION_IF_NEEDED && !isDatabaseUsed()) {
900  //we have no db used, but it is required by engine to have used any!
901  *name = anyAvailableDatabaseName();
902  if (name->isEmpty()) {
903  m_result = KDbResult(ERR_NO_DB_USED,
904  tr("Could not find any database for temporary connection."));
905  return false;
906  }
907  const bool orig_skipDatabaseExistsCheckInUseDatabase = d->skipDatabaseExistsCheckInUseDatabase;
908  d->skipDatabaseExistsCheckInUseDatabase = true;
909  bool ret = useDatabase(*name, false);
910  d->skipDatabaseExistsCheckInUseDatabase = orig_skipDatabaseExistsCheckInUseDatabase;
911  if (!ret) {
912  m_result = KDbResult(m_result.code(),
913  tr("Error during starting temporary connection using \"%1\" database name.").arg(*name));
914  return false;
915  }
916  }
917  return true;
918 }
919 
921 {
922  if (d->driver->behavior()->CONNECTION_REQUIRED_TO_DROP_DB && !checkConnected())
923  return false;
924 
925  QString dbToDrop;
926  if (dbName.isEmpty() && d->usedDatabase.isEmpty()) {
927  if (!d->driver->metaData()->isFileBased()
928  || (d->driver->metaData()->isFileBased() && d->connData.databaseName().isEmpty()))
929  {
930  m_result = KDbResult(ERR_NO_NAME_SPECIFIED,
931  tr("Could not delete database. Name is not specified."));
932  return false;
933  }
934  //this is a file driver so reuse previously passed filename
935  dbToDrop = d->connData.databaseName();
936  } else {
937  if (dbName.isEmpty()) {
938  dbToDrop = d->usedDatabase;
939  } else {
940  if (d->driver->metaData()->isFileBased()) //lets get full path
941  dbToDrop = QFileInfo(dbName).absoluteFilePath();
942  else
943  dbToDrop = dbName;
944  }
945  }
946 
947  if (dbToDrop.isEmpty()) {
948  m_result = KDbResult(ERR_NO_NAME_SPECIFIED,
949  tr("Could not delete database. Name is not specified."));
950  return false;
951  }
952 
953  if (d->driver->isSystemDatabaseName(dbToDrop)) {
954  m_result = KDbResult(ERR_SYSTEM_NAME_RESERVED,
955  tr("Could not delete system database \"%1\".").arg(dbToDrop));
956  return false;
957  }
958 
959  if (isDatabaseUsed() && d->usedDatabase == dbToDrop) {
960  //we need to close database because cannot drop used this database
961  if (!closeDatabase())
962  return false;
963  }
964 
965  QString tmpdbName;
966  //some engines need to have opened any database before executing "drop database"
967  if (!useTemporaryDatabaseIfNeeded(&tmpdbName))
968  return false;
969 
970  //ok, now we have access to dropping
971  bool ret = drv_dropDatabase(dbToDrop);
972 
973  if (!tmpdbName.isEmpty()) {
974  //whatever result is - now we have to close temporary opened database:
975  if (!closeDatabase())
976  return false;
977  }
978  return ret;
979 }
980 
981 QStringList KDbConnection::objectNames(int objectType, bool* ok)
982 {
983  if (!checkIsDatabaseUsed()) {
984  if (ok) {
985  *ok = false;
986  }
987  return QStringList();
988  }
989  KDbEscapedString sql;
990  if (objectType == KDb::AnyObjectType) {
991  sql = "SELECT o_name FROM kexi__objects ORDER BY o_id";
992  } else {
993  sql = KDbEscapedString("SELECT o_name FROM kexi__objects WHERE o_type=%1"
994  " ORDER BY o_id").arg(d->driver->valueToSql(KDbField::Integer, objectType));
995  }
996  QStringList list;
997  const bool success = queryStringListInternal(&sql, &list, nullptr, nullptr, 0, KDb::isIdentifier);
998  if (ok) {
999  *ok = success;
1000  }
1001  if (!success) {
1002  m_result.prependMessage(tr("Could not retrieve list of object names."));
1003  }
1004  return list;
1005 }
1006 
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 : list) {
1032  if (physicalTableNamesSet.contains(name.toLower())) {
1033  result += name;
1034  }
1035  }
1036  return result;
1037 }
1038 
1040 {
1041  return drv_containsTable(tableName);
1042 }
1043 
1045 {
1046  return *g_kdbSystemTableNames;
1047 }
1048 
1050 {
1051  return isConnected() ? d->serverVersion : KDbServerVersionInfo();
1052 }
1053 
1055 {
1056  return isDatabaseUsed() ? d->databaseVersion : KDbVersionInfo();
1057 }
1058 
1060 {
1061  return d->dbProperties;
1062 }
1063 
1065 {
1066  return objectIds(KDb::TableObjectType, ok);
1067 }
1068 
1070 {
1071  return objectIds(KDb::QueryObjectType, ok);
1072 }
1073 
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 
1120  KDbFieldList *fields,
1121  const KDbEscapedString &sql)
1122 {
1124  if (!drv_beforeInsert(tableSchemaName,fields )) {
1125  return res;
1126  }
1127  res = prepareSql(sql);
1128  if (!res || res->lastResult().isError()) {
1129  res.clear();
1130  return res;
1131  }
1132  if (!drv_afterInsert(tableSchemaName, fields)) {
1133  res.clear();
1134  return res;
1135  }
1136  {
1137  // Fetching is needed to perform real execution at least for some backends.
1138  // Also we are not expecting record but let's delete if there's any.
1139  QSharedPointer<KDbSqlRecord> record = res->fetchRecord();
1140  Q_UNUSED(record)
1141  }
1142  if (res->lastResult().isError()) {
1143  res.clear();
1144  }
1145  return res;
1146 }
1147 
1148 #define C_INS_REC(args, vals) \
1149  QSharedPointer<KDbSqlResult> KDbConnection::insertRecord(KDbTableSchema* tableSchema args) { \
1150  return insertRecordInternal(tableSchema->name(), tableSchema, \
1151  KDbEscapedString("INSERT INTO ") + escapeIdentifier(tableSchema->name()) \
1152  + " (" \
1153  + tableSchema->sqlFieldsList(this) \
1154  + ") VALUES (" + vals + ')'); \
1155  }
1156 
1157 #define C_INS_REC_ALL \
1158  C_INS_REC( C_A(0), V_A0 ) \
1159  C_INS_REC( C_A(0) C_A(1), V_A0 V_A(1) ) \
1160  C_INS_REC( C_A(0) C_A(1) C_A(2), V_A0 V_A(1) V_A(2) ) \
1161  C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3), V_A0 V_A(1) V_A(2) V_A(3) ) \
1162  C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) ) \
1163  C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) ) \
1164  C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5) C_A(6), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) V_A(6) ) \
1165  C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5) C_A(6) C_A(7), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) V_A(6) V_A(7) )
1166 
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.
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);
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.
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 
1281 {
1282  m_result.setSql(sql);
1284 }
1285 
1287 {
1288  m_result.setSql(sql);
1289  if (!checkSql(sql, &m_result)) {
1290  return false;
1291  }
1292  if (!drv_executeSql(sql)) {
1293  m_result.setMessage(QString()); //clear as this could be most probably just "Unknown error" string.
1294  m_result.setErrorSql(sql);
1295  m_result.prependMessage(ERR_SQL_EXECUTION_ERROR,
1296  tr("Error while executing SQL statement."));
1297  kdbWarning() << m_result;
1298  return false;
1299  }
1300  return true;
1301 }
1302 
1304 {
1305  for (KDbField::ListIterator it(fieldlist.fieldsIterator()); it != fieldlist.fieldsIteratorConstEnd(); ++it) {
1306  if (d->driver->isSystemFieldName((*it)->name()))
1307  return *it;
1308  }
1309  return nullptr;
1310 }
1311 
1312 //! Creates a KDbField list for kexi__fields, for sanity. Used by createTable()
1313 static KDbFieldList* createFieldListForKexi__Fields(KDbTableSchema *kexi__fieldsSchema)
1314 {
1315  if (!kexi__fieldsSchema)
1316  return nullptr;
1317  return kexi__fieldsSchema->subList(
1319  << "t_id"
1320  << "f_type"
1321  << "f_name"
1322  << "f_length"
1323  << "f_precision"
1324  << "f_constraints"
1325  << "f_options"
1326  << "f_default"
1327  << "f_order"
1328  << "f_caption"
1329  << "f_help"
1330  );
1331 }
1332 
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 
1364 {
1365  if (!field || !field->table())
1366  return false;
1367  KDbFieldList *fl = createFieldListForKexi__Fields(d->table(QLatin1String("kexi__fields")));
1368  if (!fl)
1369  return false;
1370 
1371  QList<QVariant> vals;
1372  buildValuesForKexi__Fields(vals, field);
1374  bool first = true;
1375  KDbEscapedString sql("UPDATE kexi__fields SET ");
1376  foreach(KDbField *f, *fl->fields()) {
1377  sql.append((first ? QString() : QLatin1String(", ")) +
1378  f->name() + QLatin1Char('=') + d->driver->valueToSql(f, *valsIt));
1379  if (first)
1380  first = false;
1381  ++valsIt;
1382  }
1383  delete fl;
1384 
1385  sql.append(KDbEscapedString(" WHERE t_id=%1 AND f_name=%2")
1386  .arg(d->driver->valueToSql(KDbField::Integer, field->table()->id()))
1387  .arg(escapeString(field->name())));
1388  return executeSql(sql);
1389 }
1390 
1391 #define createTable_ERR \
1392  { kdbDebug() << "ERROR!"; \
1393  m_result.prependMessage(KDbConnection::tr("Creating table failed.")); \
1394  rollbackAutoCommitTransaction(tg.transaction()); \
1395  return false; }
1396 
1398 {
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;
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  }
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
1479  createTable_ERR;
1480 
1481  KDbTableSchema *ts = d->table(QLatin1String("kexi__fields"));
1482  if (!ts)
1483  return false;
1484  //for sanity: remove field info (if any) for this table id
1485  if (!KDb::deleteRecords(this, *ts, QLatin1String("t_id"), tableSchema->id()))
1486  return false;
1487 
1488  KDbFieldList *fl = createFieldListForKexi__Fields(ts);
1489  if (!fl)
1490  return false;
1491 
1492  foreach(KDbField *f, *tableSchema->fields()) {
1493  QList<QVariant> vals;
1494  buildValuesForKexi__Fields(vals, f);
1495  if (!insertRecord(fl, vals))
1496  createTable_ERR;
1497  }
1498  delete fl;
1499 
1501  createTable_ERR;
1502  }
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 
1520 {
1521  clearResult();
1522  if (this->tableSchema(tableSchema.name()) != &tableSchema) {
1523  m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1524  tr("Table \"%1\" does not exist.").arg(tableSchema.name()));
1525  return nullptr;
1526  }
1527  KDbTableSchema *copiedTable = new KDbTableSchema(tableSchema, false /* !copyId*/);
1528  // copy name, caption, description
1529  copiedTable->setName(newData.name());
1530  copiedTable->setCaption(newData.caption());
1531  copiedTable->setDescription(newData.description());
1532  // copy the structure and data
1533  if (!createTable(copiedTable,
1535  {
1536  delete copiedTable;
1537  return nullptr;
1538  }
1539  if (!drv_copyTableData(tableSchema, *copiedTable)) {
1540  dropTable(copiedTable);
1541  delete copiedTable;
1542  return nullptr;
1543  }
1544  return copiedTable;
1545 }
1546 
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 
1560  const KDbTableSchema &destinationTableSchema)
1561 {
1562  KDbEscapedString sql = KDbEscapedString("INSERT INTO %1 SELECT * FROM %2")
1563  .arg(escapeIdentifier(destinationTableSchema.name()))
1565  return executeSql(sql);
1566 }
1567 
1569 {
1570  clearResult();
1571  //remove table schema from kexi__* tables
1572  KDbTableSchema *kexi__objects = d->table(QLatin1String("kexi__objects"));
1573  KDbTableSchema *kexi__objectdata = d->table(QLatin1String("kexi__objectdata"));
1574  if (!kexi__objects || !kexi__objectdata
1575  || !KDb::deleteRecords(this, *kexi__objects, QLatin1String("o_id"), objId) //schema entry
1576  || !KDb::deleteRecords(this, *kexi__objectdata, QLatin1String("o_id"), objId)) //data blocks
1577  {
1578  m_result = KDbResult(ERR_DELETE_SERVER_ERROR,
1579  tr("Could not delete object's data."));
1580  return false;
1581  }
1582  return true;
1583 }
1584 
1586 {
1587  return executeSql(KDbEscapedString("DROP TABLE %1").arg(escapeIdentifier(tableName)));
1588 }
1589 
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 
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 
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  }
1657 }
1658 
1660 {
1661  clearResult();
1662  KDbTableSchema* ts = tableSchema(tableName);
1663  if (!ts) {
1664  m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
1665  tr("Table \"%1\" does not exist.").arg(tableName));
1666  return false;
1667  }
1668  return dropTable(ts);
1669 }
1670 
1672 {
1673  clearResult();
1675  if (true != res)
1676  return res;
1677 
1678  if (tableSchema == newTableSchema) {
1679  m_result = KDbResult(ERR_OBJECT_THE_SAME,
1680  tr("Could not alter table \"%1\" using the same table as destination.")
1681  .arg(tableSchema->name()));
1682  return false;
1683  }
1684 //! @todo (js) implement real altering
1685 //! @todo (js) update any structure (e.g. query) that depend on this table!
1686  bool ok = true;
1687  bool empty;
1688 #if 0 //! @todo uncomment:
1689  empty = isEmpty(tableSchema, ok) && ok;
1690 #else
1691  empty = true;
1692 #endif
1693  if (empty) {
1694  ok = createTable(newTableSchema, KDbConnection::CreateTableOption::Default
1696  }
1697  return ok;
1698 }
1699 
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 
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 
1789  alterTableName_ERR;
1790  return false;
1791  }
1792 
1793  //update tableSchema:
1794  d->renameTable(tableSchema, newTableName);
1795  return true;
1796 }
1797 
1799 {
1800  const QString oldTableName = tableSchema->name();
1801  tableSchema->setName(newName);
1802 
1803  if (!executeSql(KDbEscapedString("ALTER TABLE %1 RENAME TO %2")
1804  .arg(KDbEscapedString(escapeIdentifier(oldTableName)),
1805  KDbEscapedString(escapeIdentifier(newName)))))
1806  {
1807  tableSchema->setName(oldTableName); //restore old name
1808  return false;
1809  }
1810  return true;
1811 }
1812 
1814 {
1815  clearResult();
1816  if (!querySchema)
1817  return false;
1818 
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);
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 
1846 {
1847  Q_ASSERT(ok);
1849  const KDbEscapedString sql(d->driver->behavior()->GET_TABLE_NAMES_SQL);
1850  if (sql.isEmpty()) {
1851  *ok = false;
1852  return QStringList();
1853  }
1855  if (!result) {
1856  *ok = false;
1857  return QStringList();
1858  }
1859  Q_FOREVER {
1860  QSharedPointer<KDbSqlRecord> record = result->fetchRecord();
1861  if (!record) {
1862  if (result->lastResult().isError()) {
1863  *ok = false;
1864  return QStringList();
1865  }
1866  break;
1867  }
1868  tableNames.append(record->stringValue(0));
1869  }
1870  *ok = true;
1871  return tableNames;
1872 }
1873 
1875 {
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 
1886 {
1887  KDbTableSchema *ts = tableSchema(tableName);
1888  if (!ts)
1889  return false;
1890  return drv_createTable(*ts);
1891 }
1892 
1894 {
1895  if ((d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
1896  || !d->autoCommit) {
1898  return true;
1899  }
1900 
1901  // commit current transaction (if present) for drivers
1902  // that allow single transaction per connection
1903  if (d->driver->behavior()->features & KDbDriver::SingleTransactions) {
1904  if (d->defaultTransactionStartedInside) //only commit internally started transaction
1907  return false; //we have a real error
1908  }
1909 
1910  d->defaultTransactionStartedInside = d->default_trans.isNull();
1911  if (!d->defaultTransactionStartedInside) {
1912  tg->setTransaction(d->default_trans);
1913  tg->doNothing();
1914  return true; //reuse externally started transaction
1915  }
1916  } else if (!(d->driver->behavior()->features & KDbDriver::MultipleTransactions)) {
1918  return true; //no trans. supported at all - just return
1919  }
1921  return !m_result.isError();
1922 }
1923 
1925 {
1926  if (d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
1927  return true;
1928  if (trans.isNull() || !d->driver->transactionsSupported())
1929  return true;
1930  if (d->driver->behavior()->features & KDbDriver::SingleTransactions) {
1931  if (!d->defaultTransactionStartedInside) //only commit internally started transaction
1932  return true; //give up
1933  }
1935 }
1936 
1938 {
1939  if (trans.isNull() || !d->driver->transactionsSupported())
1940  return true;
1941  return rollbackTransaction(trans);
1942 }
1943 
1944 #define SET_ERR_TRANS_NOT_SUPP \
1945  { m_result = KDbResult(ERR_UNSUPPORTED_DRV_FEATURE, \
1946  KDbConnection::tr("Transactions are not supported for \"%1\" driver.").arg( d->driver->metaData()->name() )); }
1947 
1948 #define SET_BEGIN_TR_ERROR \
1949  { if (!m_result.isError()) \
1950  m_result = KDbResult(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, \
1951  KDbConnection::tr("Begin transaction failed.")); }
1952 
1954 {
1955  if (!checkIsDatabaseUsed())
1956  return KDbTransaction();
1957  KDbTransaction trans;
1958  if (d->driver->behavior()->features & KDbDriver::IgnoreTransactions) {
1959  //we're creating dummy transaction data here,
1960  //so it will look like active
1961  trans.m_data = new KDbTransactionData(this);
1962  d->transactions.append(trans);
1963  return trans;
1964  }
1965  if (d->driver->behavior()->features & KDbDriver::SingleTransactions) {
1966  if (d->default_trans.isActive()) {
1967  m_result = KDbResult(ERR_TRANSACTION_ACTIVE,
1968  tr("Transaction already started."));
1969  return KDbTransaction();
1970  }
1971  if (!(trans.m_data = drv_beginTransaction())) {
1972  SET_BEGIN_TR_ERROR;
1973  return KDbTransaction();
1974  }
1975  d->default_trans = trans;
1976  d->transactions.append(trans);
1977  return d->default_trans;
1978  }
1979  if (d->driver->behavior()->features & KDbDriver::MultipleTransactions) {
1980  if (!(trans.m_data = drv_beginTransaction())) {
1981  SET_BEGIN_TR_ERROR;
1982  return KDbTransaction();
1983  }
1984  d->transactions.append(trans);
1985  return trans;
1986  }
1987 
1988  SET_ERR_TRANS_NOT_SUPP;
1989  return KDbTransaction();
1990 }
1991 
1994 {
1995  if (!isDatabaseUsed())
1996  return false;
1997  if (!d->driver->transactionsSupported()
1998  && !(d->driver->behavior()->features & KDbDriver::IgnoreTransactions)) {
1999  SET_ERR_TRANS_NOT_SUPP;
2000  return false;
2001  }
2002  KDbTransaction t = trans;
2003  if (!t.isActive()) { //try default tr.
2004  if (!d->default_trans.isActive()) {
2006  return true;
2007  }
2008  clearResult();
2009  m_result = KDbResult(ERR_NO_TRANSACTION_ACTIVE,
2010  tr("Transaction not started."));
2011  return false;
2012  }
2013  t = d->default_trans;
2014  d->default_trans = KDbTransaction(); //now: no default tr.
2015  }
2016  bool ret = true;
2017  if (!(d->driver->behavior()->features & KDbDriver::IgnoreTransactions))
2018  ret = drv_commitTransaction(t.m_data);
2019  if (t.m_data)
2020  t.m_data->setActive(false); //now this transaction if inactive
2021  if (!d->dontRemoveTransactions) //true=transaction obj will be later removed from list
2022  d->transactions.removeAt(d->transactions.indexOf(t));
2023  if (!ret && !m_result.isError())
2024  m_result = KDbResult(ERR_ROLLBACK_OR_COMMIT_TRANSACTION,
2025  tr("Error on commit transaction."));
2026  return ret;
2027 }
2028 
2031 {
2032  if (!isDatabaseUsed())
2033  return false;
2034  if (!d->driver->transactionsSupported()
2035  && !(d->driver->behavior()->features & KDbDriver::IgnoreTransactions)) {
2036  SET_ERR_TRANS_NOT_SUPP;
2037  return false;
2038  }
2039  KDbTransaction t = trans;
2040  if (!t.isActive()) { //try default tr.
2041  if (!d->default_trans.isActive()) {
2043  return true;
2044  }
2045  clearResult();
2046  m_result = KDbResult(ERR_NO_TRANSACTION_ACTIVE,
2047  tr("Transaction not started."));
2048  return false;
2049  }
2050  t = d->default_trans;
2051  d->default_trans = KDbTransaction(); //now: no default tr.
2052  }
2053  bool ret = true;
2054  if (!(d->driver->behavior()->features & KDbDriver::IgnoreTransactions))
2055  ret = drv_rollbackTransaction(t.m_data);
2056  if (t.m_data)
2057  t.m_data->setActive(false); //now this transaction if inactive
2058  if (!d->dontRemoveTransactions) //true=transaction obj will be later removed from list
2059  d->transactions.removeAt(d->transactions.indexOf(t));
2060  if (!ret && !m_result.isError())
2061  m_result = KDbResult(ERR_ROLLBACK_OR_COMMIT_TRANSACTION,
2062  tr("Error on rollback transaction."));
2063  return ret;
2064 }
2065 
2066 #undef SET_ERR_TRANS_NOT_SUPP
2067 #undef SET_BEGIN_TR_ERROR
2068 
2069 /*bool KDbConnection::duringTransaction()
2070 {
2071  return drv_duringTransaction();
2072 }*/
2073 
2075 {
2076  return d->default_trans;
2077 }
2078 
2080 {
2081  if (!isDatabaseUsed())
2082  return;
2083  if (!(d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
2084  && (!trans.isActive() || !d->driver->transactionsSupported())) {
2085  return;
2086  }
2087  d->default_trans = trans;
2088 }
2089 
2091 {
2092  return d->transactions;
2093 }
2094 
2096 {
2097  return d->autoCommit;
2098 }
2099 
2101 {
2102  if (d->autoCommit == on || d->driver->behavior()->features & KDbDriver::IgnoreTransactions)
2103  return true;
2104  if (!drv_setAutoCommit(on))
2105  return false;
2106  d->autoCommit = on;
2107  return true;
2108 }
2109 
2111 {
2112  if (!executeSql(KDbEscapedString("BEGIN")))
2113  return nullptr;
2114  return new KDbTransactionData(this);
2115 }
2116 
2118 {
2119  return executeSql(KDbEscapedString("COMMIT"));
2120 }
2121 
2123 {
2124  return executeSql(KDbEscapedString("ROLLBACK"));
2125 }
2126 
2128 {
2129  return true;
2130 }
2131 
2133 {
2134  if (sql.isEmpty())
2135  return nullptr;
2136  KDbCursor *c = prepareQuery(sql, options);
2137  if (!c)
2138  return nullptr;
2139  if (!c->open()) {//err - kill that
2140  m_result = c->result();
2141  CursorDeleter deleter(c);
2142  return nullptr;
2143  }
2144  return c;
2145 }
2146 
2148  KDbCursor::Options options)
2149 {
2150  KDbCursor *c = prepareQuery(query, params, options);
2151  if (!c)
2152  return nullptr;
2153  if (!c->open()) {//err - kill that
2154  m_result = c->result();
2155  CursorDeleter deleter(c);
2156  return nullptr;
2157  }
2158  return c;
2159 }
2160 
2162 {
2163  return executeQuery(query, QList<QVariant>(), options);
2164 }
2165 
2167 {
2168  return executeQuery(table->query(), options);
2169 }
2170 
2172 {
2173  return prepareQuery(table->query(), options);
2174 }
2175 
2177  KDbCursor::Options options)
2178 {
2179  KDbCursor* cursor = prepareQuery(query, options);
2180  if (cursor)
2181  cursor->setQueryParameters(params);
2182  return cursor;
2183 }
2184 
2186 {
2187  if (!cursor)
2188  return false;
2189  if (cursor->connection() != this) {//illegal call
2190  kdbWarning() << "Could not delete the cursor not owned by the same connection!";
2191  return false;
2192  }
2193  const bool ret = cursor->close();
2194  CursorDeleter deleter(cursor);
2195  return ret;
2196 }
2197 
2198 //! @todo IMPORTANT: fix KDbConnection::setupObjectData() after refactoring
2200 {
2201  if (data.count() < 5) {
2202  kdbWarning() << "Aborting, object data should have at least 5 elements, found" << data.count();
2203  return false;
2204  }
2205  bool ok;
2206  const int id = data[0].toInt(&ok);
2207  if (!ok)
2208  return false;
2209  object->setId(id);
2210  const QString name(data[2].toString());
2211  if (!KDb::isIdentifier(name)) {
2212  m_result = KDbResult(ERR_INVALID_IDENTIFIER,
2213  tr("Invalid object name \"%1\".").arg(name));
2214  return false;
2215  }
2216  object->setName(name);
2217  object->setCaption(data[3].toString());
2218  object->setDescription(data[4].toString());
2219 
2220 // kdbDebug()<<"@@@ KDbConnection::setupObjectData() == " << sdata.schemaDataDebugString();
2221  return true;
2222 }
2223 
2225 {
2227  if (type == KDb::AnyObjectType) {
2228  if (true != querySingleRecord(KDbEscapedString("SELECT o_id, o_type, o_name, o_caption, "
2229  "o_desc FROM kexi__objects WHERE o_id=%1")
2230  .arg(d->driver->valueToSql(KDbField::Integer, id)),
2231  &data)) {
2232  return cancelled;
2233  }
2234  } else {
2235  if (true != querySingleRecord(KDbEscapedString("SELECT o_id, o_type, o_name, o_caption, o_desc "
2236  "FROM kexi__objects WHERE o_type=%1 AND o_id=%2")
2237  .arg(d->driver->valueToSql(KDbField::Integer, type))
2238  .arg(d->driver->valueToSql(KDbField::Integer, id)),
2239  &data))
2240  {
2241  return cancelled;
2242  }
2243  }
2244  return setupObjectData(data, object);
2245 }
2246 
2248 {
2250  if (true != querySingleRecord(
2251  KDbEscapedString("SELECT o_id, o_type, o_name, o_caption, o_desc "
2252  "FROM kexi__objects WHERE o_type=%1 AND o_name=%2")
2253  .arg(d->driver->valueToSql(KDbField::Integer, type))
2254  .arg(escapeString(name)),
2255  &data))
2256  {
2257  return cancelled;
2258  }
2259  return setupObjectData(data, object);
2260 }
2261 
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
2283  QList<QByteArray>() << "o_type" << "o_name" << "o_caption" << "o_desc"));
2284  if (!fl) {
2285  return false;
2286  }
2288  = insertRecord(fl.data(), QVariant(object->type()), QVariant(object->name()),
2289  QVariant(object->caption()), QVariant(object->description()));
2290  if (!result) {
2291  return false;
2292  }
2293  //fetch newly assigned ID
2294 //! @todo safe to cast it?
2295  quint64 obj_id = KDb::lastInsertedAutoIncValue(result, QLatin1String("o_id"), *ts);
2296  //kdbDebug() << "NEW obj_id == " << obj_id;
2297  if (obj_id == std::numeric_limits<quint64>::max()) {
2298  return false;
2299  }
2300  object->setId(obj_id);
2301  return true;
2302  } else {
2304  QList<QByteArray>() << "o_id" << "o_type" << "o_name" << "o_caption" << "o_desc"));
2305  return fl && insertRecord(fl.data(), QVariant(object->id()), QVariant(object->type()),
2306  QVariant(object->name()), QVariant(object->caption()),
2307  QVariant(object->description()));
2308  }
2309  }
2310  //existing object:
2311  return executeSql(
2312  KDbEscapedString("UPDATE kexi__objects SET o_type=%2, o_caption=%3, o_desc=%4 WHERE o_id=%1")
2313  .arg(d->driver->valueToSql(KDbField::Integer, object->id()))
2314  .arg(d->driver->valueToSql(KDbField::Integer, object->type()))
2315  .arg(escapeString(object->caption()))
2316  .arg(escapeString(object->description())));
2317 }
2318 
2320 {
2321  return storeObjectDataInternal(object, false);
2322 }
2323 
2325 {
2326  return storeObjectDataInternal(object, true);
2327 }
2328 
2330 {
2331  return escapingType == KDb::KDbEscaping
2333 }
2334 
2336  KDbQuerySchema* query,
2337  const QList<QVariant>* params)
2338 {
2339  Q_ASSERT(!sql.isEmpty() || query);
2340  clearResult();
2341  if (!sql.isEmpty()) {
2342  return executeQuery(sql);
2343  }
2344  if (!query) {
2345  return nullptr;
2346  }
2347  if (params) {
2348  return executeQuery(query, *params);
2349  }
2350  return executeQuery(query);
2351 }
2352 
2354  KDbQuerySchema *query,
2355  const QList<QVariant> *params,
2356  QueryRecordOptions options)
2357 {
2358  Q_ASSERT(sql || query);
2359  if (sql) {
2360  //! @todo does not work with non-SQL data sources
2361  m_result.setSql(d->driver->addLimitTo1(*sql, options & QueryRecordOption::AddLimitTo1));
2362  }
2363  KDbCursor *cursor = executeQueryInternal(m_result.sql(), query, params);
2364  if (!cursor) {
2365  kdbWarning() << "!querySingleRecordInternal() " << m_result.sql();
2366  return false;
2367  }
2368  if (!cursor->moveFirst() || cursor->eof() || !cursor->storeCurrentRecord(data)) {
2369  const tristate result = cursor->result().isError() ? tristate(false) : tristate(cancelled);
2370  // kdbDebug() << "!cursor->moveFirst() || cursor->eof() || cursor->storeCurrentRecord(data)
2371  // "
2372  // "m_result.sql()=" << m_result.sql();
2373  m_result = cursor->result();
2374  deleteCursor(cursor);
2375  return result;
2376  }
2377  return deleteCursor(cursor);
2378 }
2379 
2381  QueryRecordOptions options)
2382 {
2383  return querySingleRecordInternal(data, &sql, nullptr, nullptr, options);
2384 }
2385 
2387  QueryRecordOptions options)
2388 {
2389  return querySingleRecordInternal(data, nullptr, query, nullptr, options);
2390 }
2391 
2393  const QList<QVariant> &params, QueryRecordOptions options)
2394 {
2395  return querySingleRecordInternal(data, nullptr, query, &params, options);
2396 }
2397 
2399 {
2400  if (column >= cursor->fieldCount()) {
2401  m_result = KDbResult(ERR_CURSOR_RECORD_FETCHING,
2402  tr("Column \"%1\" does not exist in the query.").arg(column));
2403  return false;
2404  }
2405  return true;
2406 }
2407 
2409  KDbQuerySchema *query,
2410  const QList<QVariant> *params, int column,
2411  QueryRecordOptions options)
2412 {
2413  Q_ASSERT(sql || query);
2414  if (sql) {
2415  //! @todo does not work with non-SQL data sources
2416  m_result.setSql(d->driver->addLimitTo1(*sql, options & QueryRecordOption::AddLimitTo1));
2417  }
2418  KDbCursor *cursor = executeQueryInternal(m_result.sql(), query, params);
2419  if (!cursor) {
2420  kdbWarning() << "!querySingleStringInternal()" << m_result.sql();
2421  return false;
2422  }
2423  if (!cursor->moveFirst() || cursor->eof()) {
2424  const tristate result = cursor->result().isError() ? tristate(false) : tristate(cancelled);
2425  // kdbDebug() << "!cursor->moveFirst() || cursor->eof()" << m_result.sql();
2426  deleteCursor(cursor);
2427  return result;
2428  }
2429  if (!checkIfColumnExists(cursor, column)) {
2430  deleteCursor(cursor);
2431  return false;
2432  }
2433  if (value) {
2434  *value = cursor->value(column).toString();
2435  }
2436  return deleteCursor(cursor);
2437 }
2438 
2440  QueryRecordOptions options)
2441 {
2442  return querySingleStringInternal(&sql, value, nullptr, nullptr, column, options);
2443 }
2444 
2446  QueryRecordOptions options)
2447 {
2448  return querySingleStringInternal(nullptr, value, query, nullptr, column, options);
2449 }
2450 
2452  const QList<QVariant> &params, int column,
2453  QueryRecordOptions options)
2454 {
2455  return querySingleStringInternal(nullptr, value, query, &params, column, options);
2456 }
2457 
2459  KDbQuerySchema *query,
2460  const QList<QVariant> *params, int column,
2461  QueryRecordOptions options)
2462 {
2463  QString str;
2464  const tristate result = querySingleStringInternal(sql, &str, query, params, column, options);
2465  if (result != true)
2466  return result;
2467  bool ok;
2468  const int _number = str.toInt(&ok);
2469  if (!ok)
2470  return false;
2471  if (number) {
2472  *number = _number;
2473  }
2474  return true;
2475 }
2476 
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 
2484  QueryRecordOptions options)
2485 {
2486  return querySingleNumberInternal(nullptr, number, query, nullptr, column, options);
2487 }
2488 
2490  const QList<QVariant> &params, int column,
2491  QueryRecordOptions options)
2492 {
2493  return querySingleNumberInternal(nullptr, number, query, &params, column, options);
2494 }
2495 
2497  KDbQuerySchema *query, const QList<QVariant> *params,
2498  int column, bool (*filterFunction)(const QString &))
2499 {
2500  if (sql) {
2501  m_result.setSql(*sql);
2502  }
2503  KDbCursor *cursor = executeQueryInternal(m_result.sql(), query, params);
2504  if (!cursor) {
2505  kdbWarning() << "!queryStringListInternal() " << m_result.sql();
2506  return false;
2507  }
2508  cursor->moveFirst();
2509  if (cursor->result().isError()) {
2510  m_result = cursor->result();
2511  deleteCursor(cursor);
2512  return false;
2513  }
2514  if (!cursor->eof() && !checkIfColumnExists(cursor, column)) {
2515  deleteCursor(cursor);
2516  return false;
2517  }
2518  if (list) {
2519  list->clear();
2520  }
2521  QStringList listResult;
2522  while (!cursor->eof()) {
2523  const QString str(cursor->value(column).toString());
2524  if (!filterFunction || filterFunction(str)) {
2525  listResult.append(str);
2526  }
2527  if (!cursor->moveNext() && cursor->result().isError()) {
2528  m_result = cursor->result();
2529  deleteCursor(cursor);
2530  return false;
2531  }
2532  }
2533  if (list) {
2534  *list = listResult;
2535  }
2536  return deleteCursor(cursor);
2537 }
2538 
2540  int column)
2541 {
2542  return queryStringListInternal(&sql, list, nullptr, nullptr, column, nullptr);
2543 }
2544 
2546 {
2547  return queryStringListInternal(nullptr, list, query, nullptr, column, nullptr);
2548 }
2549 
2551  const QList<QVariant>& params, int column)
2552 {
2553  return queryStringListInternal(nullptr, list, query, &params, column, nullptr);
2554 }
2555 
2557 {
2558  // optimization
2559  if (d->driver->behavior()->SELECT_1_SUBQUERY_SUPPORTED) {
2560  // this is at least for sqlite
2561  if ((options & QueryRecordOption::AddLimitTo1) && sql.left(6).toUpper() == "SELECT") {
2562  m_result.setSql(d->driver->addLimitTo1("SELECT 1 FROM (" + sql + ')'));
2563  } else {
2564  m_result.setSql(sql);
2565  }
2566  } else {
2567  if ((options & QueryRecordOption::AddLimitTo1) && sql.startsWith("SELECT")) {
2568  m_result.setSql(d->driver->addLimitTo1(sql));
2569  } else {
2570  m_result.setSql(sql);
2571  }
2572  }
2573  KDbCursor *cursor = executeQuery(m_result.sql());
2574  if (!cursor) {
2575  kdbWarning() << "!executeQuery()" << m_result.sql();
2576  return cancelled;
2577  }
2578  if (!cursor->moveFirst() || cursor->eof()) {
2579  //kdbWarning() << "!cursor->moveFirst() || cursor->eof()" << m_result.sql();
2580  m_result = cursor->result();
2581  deleteCursor(cursor);
2582  return m_result.isError() ? cancelled : tristate(false);
2583  }
2584  return deleteCursor(cursor) ? tristate(true) : cancelled;
2585 }
2586 
2588 {
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 
2681 {
2682 //! @todo future: save in older versions if neeed
2683  QDomDocument doc(QLatin1String("EXTENDED_TABLE_SCHEMA"));
2684  QDomElement extendedTableSchemaMainEl;
2685  bool extendedTableSchemaStringIsEmpty = true;
2686 
2687  //for each field:
2688  foreach(KDbField* f, *tableSchema->fields()) {
2689  QDomElement extendedTableSchemaFieldEl;
2690  const KDbField::Type type = f->type(); // cache: evaluating type of expressions can be expensive
2691  if (f->visibleDecimalPlaces() >= 0/*nondefault*/ && KDb::supportsVisibleDecimalPlacesProperty(type)) {
2692  addFieldPropertyToExtendedTableSchemaData(
2693  *f, "visibleDecimalPlaces", f->visibleDecimalPlaces(), &doc,
2694  &extendedTableSchemaMainEl, &extendedTableSchemaFieldEl,
2695  &extendedTableSchemaStringIsEmpty);
2696  }
2697  if (type == KDbField::Text) {
2699  addFieldPropertyToExtendedTableSchemaData(
2700  *f, "maxLengthIsDefault", true, &doc,
2701  &extendedTableSchemaMainEl, &extendedTableSchemaFieldEl,
2702  &extendedTableSchemaStringIsEmpty);
2703  }
2704  }
2705 
2706  // boolean field with "not null"
2707 
2708  // add custom properties
2709  const KDbField::CustomPropertiesMap customProperties(f->customProperties());
2710  for (KDbField::CustomPropertiesMap::ConstIterator itCustom = customProperties.constBegin();
2711  itCustom != customProperties.constEnd(); ++itCustom) {
2712  addFieldPropertyToExtendedTableSchemaData(
2713  *f, itCustom.key(), itCustom.value(), &doc,
2714  &extendedTableSchemaMainEl, &extendedTableSchemaFieldEl, &extendedTableSchemaStringIsEmpty,
2715  /*custom*/true);
2716  }
2717  // save lookup table specification, if present
2718  KDbLookupFieldSchema *lookupFieldSchema = tableSchema->lookupFieldSchema(*f);
2719  if (lookupFieldSchema) {
2720  createExtendedTableSchemaFieldElementIfNeeded(
2721  &doc, &extendedTableSchemaMainEl, f->name(), &extendedTableSchemaFieldEl, false/* !append */);
2722  lookupFieldSchema->saveToDom(&doc, &extendedTableSchemaFieldEl);
2723 
2724  if (extendedTableSchemaFieldEl.hasChildNodes()) {
2725  // this element provides the definition, so let's append it now
2726  createExtendedTableSchemaMainElementIfNeeded(&doc, &extendedTableSchemaMainEl,
2727  &extendedTableSchemaStringIsEmpty);
2728  extendedTableSchemaMainEl.appendChild(extendedTableSchemaFieldEl);
2729  }
2730  }
2731  }
2732 
2733  // Store extended schema information (see ExtendedTableSchemaInformation in Kexi Wiki)
2734  if (extendedTableSchemaStringIsEmpty) {
2735 #ifdef KDB_DEBUG_GUI
2736  KDb::alterTableActionDebugGUI(QLatin1String("** Extended table schema REMOVED."));
2737 #endif
2738  if (!removeDataBlock(tableSchema->id(), QLatin1String("extended_schema")))
2739  return false;
2740  } else {
2741 #ifdef KDB_DEBUG_GUI
2742  KDb::alterTableActionDebugGUI(
2743  QLatin1String("** Extended table schema set to:\n") + doc.toString(4));
2744 #endif
2745  if (!storeDataBlock(tableSchema->id(), doc.toString(1), QLatin1String("extended_schema")))
2746  return false;
2747  }
2748  return true;
2749 }
2750 
2752 {
2753 #define loadExtendedTableSchemaData_ERR \
2754  { m_result = KDbResult(tr("Error while loading extended table schema.", \
2755  "Extended schema for a table: loading error")); \
2756  return false; }
2757 #define loadExtendedTableSchemaData_ERR2(details) \
2758  { m_result = KDbResult(details); \
2759  m_result.setMessageTitle(tr("Error while loading extended table schema.", \
2760  "Extended schema for a table: loading error")); \
2761  return false; }
2762 #define loadExtendedTableSchemaData_ERR3(data) \
2763  { m_result = KDbResult(tr("Invalid XML data: %1").arg(data.left(1024))); \
2764  m_result.setMessageTitle(tr("Error while loading extended table schema.", \
2765  "Extended schema for a table: loading error")); \
2766  return false; }
2767 
2768  // Load extended schema information, if present (see ExtendedTableSchemaInformation in Kexi Wiki)
2769  QString extendedTableSchemaString;
2771  &extendedTableSchemaString, QLatin1String("extended_schema"));
2772  if (!res)
2773  loadExtendedTableSchemaData_ERR;
2774  // extendedTableSchemaString will be just empty if there is no such data block
2775 
2776  if (extendedTableSchemaString.isEmpty())
2777  return true;
2778 
2779  QDomDocument doc;
2780  QString errorMsg;
2781  int errorLine, errorColumn;
2782  if (!doc.setContent(extendedTableSchemaString, &errorMsg, &errorLine, &errorColumn)) {
2783  loadExtendedTableSchemaData_ERR2(
2784  tr("Error in XML data: \"%1\" in line %2, column %3.\nXML data: %4")
2785  .arg(errorMsg).arg(errorLine).arg(errorColumn).arg(extendedTableSchemaString.left(1024)));
2786  }
2787 
2788 //! @todo look at the current format version (KDB_EXTENDED_TABLE_SCHEMA_VERSION)
2789 
2790  if (doc.doctype().name() != QLatin1String("EXTENDED_TABLE_SCHEMA"))
2791  loadExtendedTableSchemaData_ERR3(extendedTableSchemaString);
2792 
2793  QDomElement docEl = doc.documentElement();
2794  if (docEl.tagName() != QLatin1String("EXTENDED_TABLE_SCHEMA"))
2795  loadExtendedTableSchemaData_ERR3(extendedTableSchemaString);
2796 
2797  for (QDomNode n = docEl.firstChild(); !n.isNull(); n = n.nextSibling()) {
2798  QDomElement fieldEl = n.toElement();
2799  if (fieldEl.tagName() == QLatin1String("field")) {
2800  KDbField *f = tableSchema->field(fieldEl.attribute(QLatin1String("name")));
2801  if (f) {
2802  //set properties of the field:
2803 //! @todo more properties
2804  for (QDomNode propNode = fieldEl.firstChild();
2805  !propNode.isNull(); propNode = propNode.nextSibling())
2806  {
2807  const QDomElement propEl = propNode.toElement();
2808  bool ok;
2809  int intValue;
2810  if (propEl.tagName() == QLatin1String("property")) {
2811  QByteArray propertyName = propEl.attribute(QLatin1String("name")).toLatin1();
2812  if (propEl.attribute(QLatin1String("custom")) == QLatin1String("true")) {
2813  //custom property
2814  const QVariant v(KDb::loadPropertyValueFromDom(propEl.firstChild(), &ok));
2815  if (ok) {
2816  f->setCustomProperty(propertyName, v);
2817  }
2818  }
2819  else if (propertyName == "visibleDecimalPlaces") {
2821  intValue = KDb::loadIntPropertyValueFromDom(propEl.firstChild(), &ok);
2822  if (ok)
2823  f->setVisibleDecimalPlaces(intValue);
2824  }
2825  }
2826  else if (propertyName == "maxLengthIsDefault") {
2827  if (f->type() == KDbField::Text) {
2828  const bool maxLengthIsDefault
2829  = KDb::loadPropertyValueFromDom(propEl.firstChild(), &ok).toBool();
2830  if (ok) {
2833  }
2834  }
2835  }
2836 //! @todo more properties...
2837  } else if (propEl.tagName() == QLatin1String("lookup-column")) {
2838  KDbLookupFieldSchema *lookupFieldSchema = KDbLookupFieldSchema::loadFromDom(propEl);
2839  if (lookupFieldSchema) {
2840  kdbDebug() << f->name() << *lookupFieldSchema;
2841  tableSchema->setLookupFieldSchema(f->name(), lookupFieldSchema);
2842  }
2843  }
2844  }
2845  } else {
2846  kdbWarning() << "no such field:" << fieldEl.attribute(QLatin1String("name"))
2847  << "in table:" << tableSchema->name();
2848  }
2849  }
2850  }
2851 
2852  return true;
2853 }
2854 
2856 {
2857  bool ok = true;
2858  int f_int_type = data.at(1).toInt(&ok);
2859  if (f_int_type <= KDbField::InvalidType || f_int_type > KDbField::LastType)
2860  ok = false;
2861  if (!ok)
2862  return nullptr;
2863  KDbField::Type f_type = (KDbField::Type)f_int_type;
2864  const int f_len = qMax(0, data.at(3).toInt(&ok)); // defined limit
2865  if (!ok) {
2866  return nullptr;
2867  }
2868 //! @todo load maxLengthStrategy info to see if the maxLength is the default
2869 
2870  int f_prec = data.at(4).toInt(&ok);
2871  if (!ok)
2872  return nullptr;
2873  KDbField::Constraints f_constr = (KDbField::Constraints)data.at(5).toInt(&ok);
2874  if (!ok)
2875  return nullptr;
2876  KDbField::Options f_opts = (KDbField::Options)data.at(6).toInt(&ok);
2877  if (!ok)
2878  return nullptr;
2879 
2880  QString name(data.at(2).toString());
2881  if (!KDb::isIdentifier(name)) {
2882  name = KDb::stringToIdentifier(name);
2883  }
2884 
2885  KDbField *f = new KDbField(
2886  name, f_type, f_constr, f_opts, f_len, f_prec);
2887 
2888  QVariant defaultVariant = data.at(7);
2889  if (defaultVariant.isValid()) {
2890  defaultVariant = KDb::stringToVariant(defaultVariant.toString(), KDbField::variantType(f_type), &ok);
2891  if (ok) {
2892  f->setDefaultValue(defaultVariant);
2893  } else {
2894  kdbWarning() << "problem with KDb::stringToVariant(" << defaultVariant << ')';
2895  ok = true; //problem with defaultValue is not critical
2896  }
2897  }
2898 
2899  f->setCaption(data.at(9).toString());
2900  f->setDescription(data.at(10).toString());
2901  return f;
2902 }
2903 
2905 {
2906  KDbTableSchema *t = d->table(tableName);
2907  if (t || tableName.isEmpty()) {
2908  return t;
2909  }
2910  //not found: retrieve schema
2912  clearResult();
2913  if (true != loadObjectData(KDb::TableObjectType, tableName, newTable.data())) {
2914  return nullptr;
2915  }
2916  return d->setupTableSchema(newTable.take());
2917 }
2918 
2920 {
2921  KDbTableSchema *t = d->table(tableId);
2922  if (t)
2923  return t;
2924  //not found: retrieve schema
2926  clearResult();
2927  if (true != loadObjectData(KDb::TableObjectType, tableId, newTable.data())) {
2928  return nullptr;
2929  }
2930  return d->setupTableSchema(newTable.take());
2931 }
2932 
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))
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;
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 
3006 {
3007  QString queryName = aQueryName.toLower();
3008  KDbQuerySchema *q = d->query(queryName);
3009  if (q || queryName.isEmpty()) {
3010  return q;
3011  }
3012  //not found: retrieve schema
3014  clearResult();
3015  if (true != loadObjectData(KDb::QueryObjectType, aQueryName, newQuery.data())) {
3016  return nullptr;
3017  }
3018  return d->setupQuerySchema(newQuery.take());
3019 }
3020 
3022 {
3023  KDbQuerySchema *q = d->query(queryId);
3024  if (q)
3025  return q;
3026  //not found: retrieve schema
3028  clearResult();
3029  if (true != loadObjectData(KDb::QueryObjectType, queryId, newQuery.data())) {
3030  return nullptr;
3031  }
3032  return d->setupQuerySchema(newQuery.take());
3033 }
3034 
3036 {
3037  KDbQuerySchema* oldQuery = querySchema(queryName);
3038  if (!oldQuery)
3039  return false;
3040  d->setQueryObsolete(oldQuery);
3041  return true;
3042 }
3043 
3045 {
3046  return d->driver->escapeIdentifier(id);
3047 }
3048 
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 
3059 {
3060  if (table && d) {
3061  d->takeTable(table);
3062  }
3063 }
3064 
3066 {
3067  if (!d->availableDatabaseName.isEmpty()) {
3068  return d->availableDatabaseName;
3069  }
3070  return d->driver->behavior()->ALWAYS_AVAILABLE_DATABASE_NAME;
3071 }
3072 
3074 {
3075  d->availableDatabaseName = dbName;
3076 }
3077 
3078 //! @internal used in updateRecord(), insertRecord(),
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 
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  const QVector<int> pkeyFieldsOrder(query->pkeyFieldsOrder(this));
3148  //kdbDebug() << pkey->fieldCount() << " ? " << query->pkeyFieldCount();
3149  if (pkey->fieldCount() != query->pkeyFieldCount(this)) { //sanity check
3150  kdbWarning() << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!";
3151  m_result = KDbResult(ERR_UPDATE_NO_ENTIRE_MASTER_TABLES_PKEY,
3152  tr("Could not update record because it does not contain entire primary key of master table."));
3153  return false;
3154  }
3155  if (!pkey->fields()->isEmpty()) {
3156  int i = 0;
3157  foreach(KDbField *f, *pkey->fields()) {
3158  if (!sqlwhere.isEmpty())
3159  sqlwhere += " AND ";
3160  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 
3431 {
3432  clearResult();
3433  KDbTableSchema *mt = query->masterTable();
3434  if (!mt) {
3435  kdbWarning() << " -- NO MASTER TABLE!";
3436  return false;
3437  }
3438  KDbIndexSchema *pkey = mt->primaryKey();
3439  if (!pkey || pkey->fields()->isEmpty()) {
3440  kdbWarning() << "-- WARNING: NO MASTER TABLE's PKEY";
3441  }
3442  KDbEscapedString sql = KDbEscapedString("DELETE FROM ") + escapeIdentifier(mt->name());
3443  //kdbDebug() << "-- SQL == " << sql;
3444 
3445  if (!executeSql(sql)) {
3446  m_result = KDbResult(ERR_DELETE_SERVER_ERROR,
3447  tr("Record deletion on the server failed."));
3448  return false;
3449  }
3450  return true;
3451 }
3452 
3454 {
3455  int count = -1; //will be changed only on success of querySingleNumber()
3456  const tristate result = querySingleNumber(
3457  KDbEscapedString("SELECT COUNT() FROM (") + sql + ") AS kdb__subquery", &count);
3458  if (~result) {
3459  count = 0;
3460  }
3461  return count;
3462 }
3463 
3465 {
3466  //! @todo does not work with non-SQL data sources
3467  int count = -1; // will be changed only on success of querySingleNumber()
3468  const tristate result = querySingleNumber(
3469  KDbEscapedString("SELECT COUNT(*) FROM ") + escapeIdentifier(tableSchema.name()), &count);
3470  if (~result) {
3471  count = 0;
3472  }
3473  return count;
3474 }
3475 
3477 {
3478 //! @todo does not work with non-SQL data sources
3479  int count = -1; //will be changed only on success of querySingleNumber()
3481  KDbEscapedString subSql;
3482  if (!builder.generateSelectStatement(&subSql, querySchema, params)) {
3483  return -1;
3484  }
3485  const tristate result = querySingleNumber(
3486  KDbEscapedString("SELECT COUNT(*) FROM (") + subSql + ") AS kdb__subquery", &count);
3487  if (~result) {
3488  count = 0;
3489  }
3490  return count;
3491 }
3492 
3494 {
3495  if (tableOrQuery) {
3496  if (tableOrQuery->table())
3497  return recordCount(*tableOrQuery->table());
3498  if (tableOrQuery->query())
3499  return recordCount(tableOrQuery->query(), params);
3500  }
3501  return -1;
3502 }
3503 
3505 {
3506  return &d->options;
3507 }
3508 
3510 {
3511  d->cursors.insert(cursor);
3512 }
3513 
3515 {
3516  if (d && !d->cursors.isEmpty()) { // checking because this may be called from ~KDbConnection()
3517  d->cursors.remove(cursor);
3518  }
3519 }
3520 
3522  KDbFieldList* fields, const QStringList& whereFieldNames)
3523 {
3524 //! @todo move to ConnectionInterface just like we moved execute() and prepare() to KDbPreparedStatementInterface...
3526  if (!iface)
3527  return KDbPreparedStatement();
3528  return KDbPreparedStatement(iface, type, fields, whereFieldNames);
3529 }
3530 
3532  return result().errorSql().isEmpty() ? m_result.sql() : result().errorSql();
3533 }
3534 
3536 {
3537  return d->driver->escapeString(str);
3538 }
3539 
3540 //! @todo extraMessages
3541 #if 0
3542 static const char *extraMessages[] = {
3543  QT_TRANSLATE_NOOP("KDbConnection", "Unknown error.")
3544 };
3545 #endif
void setCaption(const QByteArray &name, const QString &caption)
Sets caption for property name to caption.
Definition: KDbUtils.cpp:677
void append(const T &value)
bool isNull() const const
bool deleteCursor(KDbCursor *cursor)
T & first()
QStringList objectNames(int objectType=KDb::AnyObjectType, bool *ok=nullptr)
KDB_EXPORT QVariant loadPropertyValueFromDom(const QDomNode &node, bool *ok)
Definition: KDb.cpp:1123
tristate containsTable(const QString &tableName)
bool createDatabase(const QString &dbName)
Creates new database with name dbName, using this connection.
Provides database cursor functionality.
Definition: KDbCursor.h:68
void saveToDom(QDomDocument *doc, QDomElement *parentEl)
KDB_EXPORT QString escapeIdentifier(const QString &string)
Definition: KDb.cpp:1334
QVariant defaultValue() const
Definition: KDbField.cpp:281
QString toString(int indent) const const
const T value(const Key &key) const const
void addCursor(KDbCursor *cursor)
Used by KDbCursor class.
bool useDatabase(const QString &dbName=QString(), bool kexiCompatible=true, bool *cancelled=nullptr, KDbMessageHandler *msgHandler=nullptr)
Opens an existing database specified by dbName.
virtual bool moveNext()
Definition: KDbCursor.cpp:351
tristate querySingleNumber(const KDbEscapedString &sql, int *number, int column=0, QueryRecordOptions options=QueryRecordOption::Default)
bool isValid() const const
@ IgnoreTransactions
Definition: KDbDriver.h:79
virtual bool drv_executeSql(const KDbEscapedString &sql)=0
Executes query for a raw SQL statement sql without returning resulting records.
QDomNode firstChild() const const
QDomElement toElement() const const
void insert(const QByteArray &name, const QVariant &value, const QString &caption=QString())
Inserts property with a given name, value and caption.
Definition: KDbUtils.cpp:659
Provides information about lookup field's setup.
KDbIndexSchema * primaryKey()
bool setAutoCommit(bool on)
QString number(int n, int base)
virtual bool drv_getDatabasesList(QStringList *list)
bool autoCommit() const
bool closeDatabase()
Closes currently used database for this connection.
void setQueryParameters(const QList< QVariant > &params)
Sets query parameters params for this cursor.
Definition: KDbCursor.cpp:612
CaseInsensitive
void setDefaultTransaction(const KDbTransaction &trans)
Sets default transaction.
void remove(const QByteArray &name)
Removes option with a given name if exists.
virtual bool drv_rollbackTransaction(KDbTransactionData *trans)
KDbQuerySchema * query()
A set of storable database properties.
Definition: KDbProperties.h:34
QString tagName() const const
void remove(const QByteArray &name)
Removes property with a given name.
Definition: KDbUtils.cpp:693
bool moveFirst()
Definition: KDbCursor.cpp:285
friend class KDbTableSchema
for removeMe()
int recordCount(const KDbEscapedString &sql)
Returns number of records returned by given SQL statement.
Type type(const QSqlDatabase &db)
KDbField::ListIterator fieldsIterator() const
QSharedPointer< KDbSqlResult > prepareSql(const KDbEscapedString &sql)
Prepares execution of a new native (raw, backend-specific) SQL query.
virtual KDbPreparedStatementInterface * prepareStatementInternal()=0
static QStringList kdbSystemTableNames()
virtual bool drv_isDatabaseUsed() const
KDbTableSchema * table()
Definition: KDbField.cpp:585
bool setupObjectData(const KDbRecordData &data, KDbObject *object)
virtual KDbField * field(int id)
tristate querySingleString(const KDbEscapedString &sql, QString *value, int column=0, QueryRecordOptions options=QueryRecordOption::Default)
QChar separator()
@ DropDestination
Drop destination table if exists.
@ Text
Definition: KDbField.h:98
void setValue(const QByteArray &name, const QVariant &value)
Sets value for property name to value.
Definition: KDbUtils.cpp:685
bool isNull() const const
KDbTableSchema * copyTable(const KDbTableSchema &tableSchema, const KDbObject &newData)
static void unregisterForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener, const KDbTableSchema *table)
Unregisters listener for receiving (listening) information about changes in table schema table.
virtual KDbCursor * prepareQuery(const KDbEscapedString &sql, KDbCursor::Options options=KDbCursor::Option::None)=0
A builder for generating various types of native SQL statements.
QString trimmed() const const
int fieldCount() const
Definition: KDbCursor.h:167
A database connectivity and creation framework.
QString name() const const
QDomText createTextNode(const QString &value)
KDbField * setupField(const KDbRecordData &data)
Specialized string for escaping.
virtual bool drv_commitTransaction(KDbTransactionData *trans)
virtual bool drv_afterUpdate(const QString &tableName, KDbFieldList *fields)
KDbTransaction beginTransaction()
Starts a new database transaction.
bool generateSelectStatement(KDbEscapedString *target, KDbQuerySchema *querySchema, const KDbSelectStatementOptions &options, const QList< QVariant > &parameters=QList< QVariant >()) const
bool beginAutoCommitTransaction(KDbTransactionGuard *tg)
QString name
QByteArray number(int n, int base)
QStringList tableNames(bool alsoSystemTables=false, bool *ok=nullptr)
virtual QString escapeIdentifier(const QString &id) const
Identifier escaping function in the associated KDbDriver.
QVector< QVariant > parse(const QString &message, const QDateTime &externalIssueDateTime=QDateTime())
Database driver's abstraction.
Definition: KDbDriver.h:49
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QByteArray toLatin1() const const
QList::const_iterator constBegin() const const
bool removeObject(int objId)
tristate querySingleNumberInternal(const KDbEscapedString *sql, int *number, KDbQuerySchema *query, const QList< QVariant > *params, int column, QueryRecordOptions options)
@ DriverEscaping
Identifiers are escaped by driver.
Definition: KDbGlobal.h:145
Options options() const
Definition: KDbField.cpp:261
bool queryStringList(const KDbEscapedString &sql, QStringList *list, int column=0)
bool commitTransaction(KDbTransaction transaction=KDbTransaction(), KDbTransaction::CommitOptions options=KDbTransaction::CommitOptions())
Commits specified transaction for this connection.
int visibleDecimalPlaces() const
Definition: KDbField.cpp:296
QDomElement createElement(const QString &tagName)
QString caption()
bool isActive() const
Returns true if transaction is active (i.e.
int scale() const
Definition: KDbField.cpp:291
bool isFPNumericType() const
Definition: KDbField.h:335
bool exists() const const
virtual bool drv_useDatabase(const QString &dbName=QString(), bool *cancelled=nullptr, KDbMessageHandler *msgHandler=nullptr)=0
void clear() override
KDbRecordData * storeCurrentRecord() const
Definition: KDbCursor.cpp:183
QString currentDatabase() const
Get the name of the current database.
bool setQuerySchemaObsolete(const QString &queryName)
bool storeNewObjectData(KDbObject *object)
const KDbTransaction transaction() const
Returns transaction that is controlled by this guard.
void setConnection(KDbConnection *conn)
bool isFile() const const
@ LastType
Definition: KDbField.h:102
virtual bool drv_disconnect()=0
KDbField * findSystemFieldName(const KDbFieldList &fieldlist)
QHash::iterator insert(const Key &key, const T &value)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
bool useTemporaryDatabaseIfNeeded(QString *name)
CustomPropertiesMap customProperties() const
Definition: KDbField.cpp:1053
void setVisibleDecimalPlaces(int p)
Definition: KDbField.cpp:692
@ DropDestination
Drop destination table if exists.
bool eof() const
Definition: KDbCursor.h:147
void setAttribute(const QString &name, const QString &value)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
tristate dropTableInternal(KDbTableSchema *tableSchema, bool alsoRemoveSchema)
void prependMessage(int code, const QString &message)
Sets result code and prepends message to an existing message.
Definition: KDbResult.cpp:80
QList< int > queryIds(bool *ok=nullptr)
QSharedPointer< KDbSqlResult > insertRecordInternal(const QString &tableSchemaName, KDbFieldList *fields, const KDbEscapedString &sql)
QList< int > tableIds(bool *ok=nullptr)
T * data() const const
KDB_EXPORT QVariant stringToVariant(const QString &s, QVariant::Type type, bool *ok)
Definition: KDb.cpp:1867
tristate isEmpty(KDbTableSchema *table)
typedef ConstIterator
bool checkIfColumnExists(KDbCursor *cursor, int column)
bool operator==(const Qt3DRender::QGraphicsApiFilter &reference, const Qt3DRender::QGraphicsApiFilter &sample)
tristate querySingleRecordInternal(KDbRecordData *data, const KDbEscapedString *sql, KDbQuerySchema *query, const QList< QVariant > *params, QueryRecordOptions options)
virtual bool drv_beforeUpdate(const QString &tableName, KDbFieldList *fields)
KDbConnectionData data() const
bool deleteAllRecords(KDbQuerySchema *query)
void setMaxLengthStrategy(MaxLengthStrategy strategy)
Definition: KDbField.cpp:660
QString absoluteFilePath() const const
bool hasNext() const const
bool storeExtendedTableSchemaData(KDbTableSchema *tableSchema)
bool executeSql(const KDbEscapedString &sql)
Executes a new native (raw, backend-specific) SQL query.
bool connect()
Connects to driver with given parameters.
bool databaseExists(const QString &dbName, bool ignoreErrors=true)
QVariant::Type type() const const
KDbQuerySchema * querySchema(int queryId)
virtual tristate drv_containsTable(const QString &tableName)=0
@ SingleTransactions
single trasactions are only supported
Definition: KDbDriver.h:58
QHash::const_iterator constBegin() const const
QHash::const_iterator constEnd() const const
QVariant::Type variantType() const
Converts field's type to QVariant equivalent as accurate as possible.
Definition: KDbField.h:368
void setReadOnly(bool set)
void doNothing()
Deativates the transaction guard.
@ Integer
Definition: KDbField.h:90
void setDescription(const QString &description)
Definition: KDbField.cpp:336
const T & at(int i) const const
KDbTransactionGuard class is a convenience class that simplifies handling transactions.
virtual bool drv_alterTableName(KDbTableSchema *tableSchema, const QString &newName)
KDB_EXPORT bool deleteRecords(KDbConnection *conn, const QString &tableName, const QString &keyname, KDbField::Type keytype, const QVariant &keyval)
Deletes records using one generic criteria.
Definition: KDb.cpp:342
KDB_EXPORT bool supportsVisibleDecimalPlacesProperty(KDbField::Type type)
Definition: KDb.cpp:623
bool isInternal() const
bool isEmpty() const const
KDB_EXPORT bool isDefaultValueAllowed(const KDbField &field)
Definition: KDb.cpp:1915
Constraints constraints() const
Definition: KDbField.cpp:301
PropertySet & operator=(const PropertySet &other)
Assigns other to this property set and returns a reference to this property set.
Definition: KDbUtils.cpp:645
KDB_EXPORT int loadIntPropertyValueFromDom(const QDomNode &node, bool *ok)
Definition: KDb.cpp:1097
KDbEscapedString sqlFieldsList(KDbConnection *conn, const QString &separator=QLatin1String(","), const QString &tableOrAlias=QString(), KDb::IdentifierEscapingType escapingType=KDb::DriverEscaping) const
Property property(const QByteArray &name) const
Definition: KDbUtils.cpp:698
@ IgnoreInactive
Do not return error for inactive or null transactions when requesting commit or rollback.
bool rollbackTransaction(KDbTransaction trans=KDbTransaction(), KDbTransaction::CommitOptions options=KDbTransaction::CommitOptions())
Rolls back specified transaction for this connection.
@ Unique
Unique list of fields is returned.
tristate dropTable(KDbTableSchema *tableSchema)
bool addField(KDbField *field)
@ ExpandedList
A map for expanded list is created.
void removeMe(KDbTableSchema *ts)
int maxLength() const
Definition: KDbField.cpp:665
int toInt(bool *ok) const const
virtual bool drv_closeDatabase()=0
int toInt(bool *ok, int base) const const
bool isValid() const
void setValue(const QByteArray &name, const QVariant &value)
Sets value for option name to value.
bool isEmpty() const const
QString fromNativeSeparators(const QString &pathName)
provides data for single edited database record
virtual bool drv_databaseExists(const QString &dbName, bool ignoreErrors=true)
int precision() const
Definition: KDbField.cpp:286
KDbCursor * executeQueryInternal(const KDbEscapedString &sql, KDbQuerySchema *query, const QList< QVariant > *params)
QVariant value(int i) const
void setAvailableDatabaseName(const QString &dbName)
KDbFieldList * subList(const QString &n1, const QString &n2=QString(), const QString &n3=QString(), const QString &n4=QString(), const QString &n5=QString(), const QString &n6=QString(), const QString &n7=QString(), const QString &n8=QString(), const QString &n9=QString(), const QString &n10=QString(), const QString &n11=QString(), const QString &n12=QString(), const QString &n13=QString(), const QString &n14=QString(), const QString &n15=QString(), const QString &n16=QString(), const QString &n17=QString(), const QString &n18=QString())
QList< KDbField * >::ConstIterator ListIterator
iterator for list of fields
Definition: KDbField.h:79
bool isError() const
Definition: KDbResult.cpp:64
bool hasChildNodes() const const
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition: KDbTristate.h:100
@ BigInteger
Definition: KDbField.h:91
Type type() const
Definition: KDbField.cpp:379
KDbCursor * executeQuery(const KDbEscapedString &sql, KDbCursor::Options options=KDbCursor::Option::None)
void insert(const QByteArray &name, const QVariant &value, const QString &caption=QString())
Inserts option with a given name, value and caption.
QHash::const_iterator constFind(const Key &key) const const
void setCaption(const QString &caption)
Definition: KDbField.cpp:321
int indexOf(QStringView str, int from) const const
tristate resultExists(const KDbEscapedString &sql, QueryRecordOptions options=QueryRecordOption::Default)
QDomElement documentElement() const const
tristate querySingleStringInternal(const KDbEscapedString *sql, QString *value, KDbQuerySchema *query, const QList< QVariant > *params, int column, QueryRecordOptions options)
virtual KDbEscapedString escapeString(const QString &str) const
bool rollbackAutoCommitTransaction(const KDbTransaction &trans)
QStringList databaseNames(bool also_system_db=false)
QString fileName() const const
virtual bool drv_connect()=0
virtual bool drv_beforeInsert(const QString &tableName, KDbFieldList *fields)
KDbTableSchema * tableSchema(int tableId)
bool toBool() const const
KDbField::List * fields()
QString description
bool contains(const T &value) const const
@ LongText
Definition: KDbField.h:99
@ Byte
Definition: KDbField.h:87
QString absolutePath() const const
bool isConnected() const
QDomDocumentType doctype() const const
void takeCursor(KDbCursor *cursor)
Used by KDbCursor class.
Provides information about database index that can be created for a database table.
bool setLookupFieldSchema(const QString &fieldName, KDbLookupFieldSchema *lookupFieldSchema)
bool copyDataBlock(int sourceObjectID, int destObjectID, const QString &dataID=QString())
@ DefinedMaxLength
Used if setMaxLength() was called to set specific maximum value or to unlimited (0).
Definition: KDbField.h:432
QString toLower() const const
virtual bool drv_dropTable(const QString &tableName)
virtual bool drv_setAutoCommit(bool on)
virtual bool drv_afterInsert(const QString &tableName, KDbFieldList *fields)
@ AnyObjectType
helper
Definition: KDbGlobal.h:132
void setCaption(const QByteArray &name, const QString &caption)
Sets caption for option name to caption.
KDbProperties databaseProperties() const
KDB_EXPORT bool isIdentifier(const QString &s)
Definition: KDb.cpp:2133
Type
Defines type of the prepared statement.
Prepared statement interface for backend-dependent implementations.
bool storeObjectData(KDbObject *object)
KDB_EXPORT quint64 lastInsertedAutoIncValue(QSharedPointer< KDbSqlResult > result, const QString &autoIncrementFieldName, const QString &tableName, quint64 *recordId=nullptr)
Returns value of last inserted record for an autoincrement field.
Definition: KDb.cpp:392
Structure for storing single record with type information.
Definition: KDbRecordData.h:36
QList::const_iterator constEnd() const const
KDB_EXPORT KDbVersionInfo version()
Definition: KDb.cpp:336
KDbQuerySchema provides information about database query.
@ AddLimitTo1
Adds a "LIMIT 1" clause to the query for optimization purposes (it should not include one already)
virtual bool drv_dropDatabase(const QString &dbName=QString())=0
KDB_EXPORT QString variantToString(const QVariant &v)
Definition: KDb.cpp:1856
bool isDatabaseUsed() const
void setDefaultValue(const QVariant &def)
Definition: KDbField.cpp:711
QDomNode appendChild(const QDomNode &newChild)
KDbPreparedStatement prepareStatement(KDbPreparedStatement::Type type, KDbFieldList *fields, const QStringList &whereFieldNames=QStringList())
static KDbLookupFieldSchema * loadFromDom(const QDomElement &lookupEl)
virtual QStringList drv_getTableNames(bool *ok)
LOW LEVEL METHOD.
bool createTable(KDbTableSchema *tableSchema, CreateTableOptions options=CreateTableOption::Default)
Creates a new table.
tristate querySingleRecord(const KDbEscapedString &sql, KDbRecordData *data, QueryRecordOptions options=QueryRecordOption::Default)
KDB_EXPORT KDbEscapedString sqlWhere(KDbDriver *drv, KDbField::Type t, const QString &fieldName, const QVariant &value)
Definition: KDb.cpp:440
QString left(int n) const const
tristate alterTable(KDbTableSchema *tableSchema, KDbTableSchema *newTableSchema)
virtual bool drv_createTable(const KDbTableSchema &tableSchema)
Creates table using tableSchema information.
void setCustomProperty(const QByteArray &propertyName, const QVariant &value)
Sets value value for custom property propertyName.
Definition: KDbField.cpp:1042
static QVariant::Type variantType(Type type)
Converts type type to QVariant equivalent as accurate as possible.
Definition: KDbField.cpp:387
KDbServerVersionInfo serverVersion() const
virtual QVariant value(int i)=0
bool isWritable() const const
KDbConnectionOptions * options()
bool isEmpty() const const
Database specific connection data, e.g. host, port.
bool deleteRecord(KDbQuerySchema *query, KDbRecordData *data, bool useRecordId=false)
bool queryStringListInternal(const KDbEscapedString *sql, QStringList *list, KDbQuerySchema *query, const QList< QVariant > *params, int column, bool(*filterFunction)(const QString &))
Prepared database command for optimizing sequences of multiple database actions.
~KDbConnection() override
bool disconnect()
Disconnects from driver with given parameters.
bool operator==(const KDbConnectionOptions &other) const
void clear()
bool checkIsDatabaseUsed()
Generic options for a single connection. The options are accessible using key/value pairs....
bool generateCreateTableStatement(KDbEscapedString *target, const KDbTableSchema &tableSchema) const
bool updateRecord(KDbQuerySchema *query, KDbRecordData *data, KDbRecordEditBuffer *buf, bool useRecordId=false)
QSet::iterator insert(const T &value)
Meta-data for a field.
Definition: KDbField.h:71
int count(const T &value) const const
tristate loadDataBlock(int objectID, QString *dataString, const QString &dataID=QString())
int fieldCount() const
KDbVersionInfo databaseVersion() const
virtual bool drv_getServerVersion(KDbServerVersionInfo *version)=0
MaxLengthStrategy maxLengthStrategy() const
Definition: KDbField.cpp:655
int size() const const
virtual bool close()
Definition: KDbCursor.cpp:256
static tristate closeListeners(KDbConnection *conn, const KDbTableSchema *table, const QList< KDbTableSchemaChangeListener * > &except=QList< KDbTableSchemaChangeListener * >())
Closes all table schema listeners for table schema table except for the ones from the except list.
virtual QString anyAvailableDatabaseName()
bool loadExtendedTableSchemaData(KDbTableSchema *tableSchema)
@ MultipleTransactions
multiple concurrent trasactions are supported (this implies !SingleTransactions)
Definition: KDbDriver.h:61
int compare(const QString &other, Qt::CaseSensitivity cs) const const
QString attribute(const QString &name, const QString &defValue) const const
virtual KDbTransactionData * drv_beginTransaction()
bool alterTableName(KDbTableSchema *tableSchema, const QString &newName, AlterTableNameOptions options=AlterTableNameOption::Default)
Alters name of table.
Provides database connection, allowing queries and data modification.
Definition: KDbConnection.h:51
KDbTransaction defaultTransaction() const
Returns handle of default transaction for this connection.
QString name() const
Definition: KDbField.cpp:256
bool contains(const Key &key) const const
KDbDriver * driver() const
bool removeDataBlock(int objectID, const QString &dataID=QString())
virtual bool drv_createDatabase(const QString &dbName=QString())=0
bool open()
Definition: KDbCursor.cpp:202
bool dropDatabase(const QString &dbName=QString())
Drops database with name dbName.
void setActive(bool set)
Sets "active" flag of this data.
Helper class that assigns additional information for the column in a query.
KDbTableSchema * table() const
tristate loadObjectData(int type, int id, KDbObject *object)
IdentifierEscapingType
Escaping type for identifiers.
Definition: KDbGlobal.h:144
KDbField::ListIterator fieldsIteratorConstEnd() const
bool storeMainFieldSchema(KDbField *field)
bool operator==(const PropertySet &other) const
Definition: KDbUtils.cpp:654
virtual bool drv_copyTableData(const KDbTableSchema &tableSchema, const KDbTableSchema &destinationTableSchema)
SystemTables
virtual KDbEscapedString recentSqlString() const
Return recently used SQL string.
QVector< V > values(const QMultiHash< K, V > &c)
QList< int > objectIds(int objectType, bool *ok=nullptr)
T value(int i) const const
KDbField * anyNonPKField()
bool dropQuery(KDbQuerySchema *querySchema)
Internal prototype for storing transaction handle for KDbTransaction object.
This class encapsulates a single database transaction.
const QList< QKeySequence > & copy()
QList< KDbTransaction > transactions()
Returns set of handles of currently active transactions.
QString caption
virtual KDbSqlResult * drv_prepareSql(const KDbEscapedString &sql)=0
Prepares query for a raw SQL statement sql with possibility of returning records.
bool isReadable() const const
KDbConnection * connection()
Definition: KDbCursor.cpp:148
@ KDbEscaping
Identifiers are escaped using KDb's generic convention.
Definition: KDbGlobal.h:146
void setTransaction(const KDbTransaction &transaction)
Assigns transaction to this guard.
QString toString() const const
bool commitAutoCommitTransaction(const KDbTransaction &trans)
KDB_EXPORT QString stringToIdentifier(const QString &s)
Definition: KDb.cpp:2166
bool isNull() const
Returns true if this transaction is null.
bool storeDataBlock(int objectID, const QString &dataString, const QString &dataID=QString())
KDbLookupFieldSchema * lookupFieldSchema(const KDbField &field)
@ DefaultMaxLength
Default maximum text length defined globally by the application.
Definition: KDbField.h:430
KDbQuerySchema * query() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:21:32 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.