kexi

connection.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2007 Jaroslaw Staniek <js@iidea.pl>
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 <kexidb/connection.h>
21
22#include "error.h"
23#include "connection_p.h"
24#include "connectiondata.h"
25#include "driver.h"
26#include "driver_p.h"
27#include "schemadata.h"
28#include "tableschema.h"
29#include "relationship.h"
30#include "transaction.h"
31#include "cursor.h"
32#include "global.h"
33#include "roweditbuffer.h"
34#include "utils.h"
35#include "dbproperties.h"
36#include "lookupfieldschema.h"
37#include "parser/parser.h"
38
39#include <kexiutils/utils.h>
40#include <kexiutils/identifier.h>
41
42#include <tqdir.h>
43#include <tqfileinfo.h>
44#include <tqguardedptr.h>
45#include <tqdom.h>
46
47#include <tdelocale.h>
48#include <kdebug.h>
49
50#define KEXIDB_EXTENDED_TABLE_SCHEMA_VERSION 1
51
52//#define KEXIDB_LOOKUP_FIELD_TEST
53
54namespace KexiDB {
55
56Connection::SelectStatementOptions::SelectStatementOptions()
57 : identifierEscaping(Driver::EscapeDriver|Driver::EscapeAsNecessary)
58 , alsoRetrieveROWID(false)
59 , addVisibleLookupColumns(true)
60{
61}
62
63Connection::SelectStatementOptions::~SelectStatementOptions()
64{
65}
66
67//================================================
68
69ConnectionInternal::ConnectionInternal(Connection *conn)
70 : connection(conn)
71{
72}
73
74ConnectionInternal::~ConnectionInternal()
75{
76}
77
78//================================================
80class ConnectionPrivate
81{
82 public:
83 ConnectionPrivate(Connection* const conn, ConnectionData &conn_data)
84 : conn(conn)
85 , conn_data(&conn_data)
86 , tableSchemaChangeListeners(1009)
87 , m_parser(0)
88 , tables_byname(1009, false)
89 , queries_byname(1009, false)
90 , kexiDBSystemTables(101)
91 , dont_remove_transactions(false)
92 , skip_databaseExists_check_in_useDatabase(false)
93 , default_trans_started_inside(false)
94 , isConnected(false)
95 , autoCommit(true)
96 {
97 tableSchemaChangeListeners.setAutoDelete(true);
98 obsoleteQueries.setAutoDelete(true);
99
100 tables.setAutoDelete(true);
101 tables_byname.setAutoDelete(false);//tables is owner, not me
102 kexiDBSystemTables.setAutoDelete(true);//only system tables
103 queries.setAutoDelete(true);
104 queries_byname.setAutoDelete(false);//queries is owner, not me
105
106 //reasonable sizes: TODO
107 tables.resize(1009);
108 queries.resize(1009);
109 }
110 ~ConnectionPrivate()
111 {
112 delete m_parser;
113 }
114
115 void errorInvalidDBContents(const TQString& details) {
116 conn->setError( ERR_INVALID_DATABASE_CONTENTS, i18n("Invalid database contents. ")+details);
117 }
118
119 TQString strItIsASystemObject() const {
120 return i18n("It is a system object.");
121 }
122
123 inline Parser *parser() { return m_parser ? m_parser : (m_parser = new Parser(conn)); }
124
125 Connection* const conn;
126 TQGuardedPtr<ConnectionData> conn_data;
127
132 Transaction default_trans;
133 TQValueList<Transaction> transactions;
134
135 TQPtrDict< TQPtrList<Connection::TableSchemaChangeListenerInterface> > tableSchemaChangeListeners;
136
139 TQPtrList<QuerySchema> obsoleteQueries;
140
141
143 KexiDB::ServerVersionInfo serverVersion;
144
146 KexiDB::DatabaseVersionInfo databaseVersion;
147
148 Parser *m_parser;
149
151 TQIntDict<TableSchema> tables;
152 TQDict<TableSchema> tables_byname;
153 TQIntDict<QuerySchema> queries;
154 TQDict<QuerySchema> queries_byname;
155
157 TQPtrDict<TableSchema> kexiDBSystemTables;
158
160 DatabaseProperties* dbProperties;
161
162 TQString availableDatabaseName;
163 TQString usedDatabase;
164
167 bool dont_remove_transactions : 1;
168
171 bool skip_databaseExists_check_in_useDatabase : 1;
172
181 bool default_trans_started_inside : 1;
182
183 bool isConnected : 1;
184
185 bool autoCommit : 1;
186
188 bool readOnly : 1;
189};
190
191}//namespace KexiDB
192
193//================================================
194using namespace KexiDB;
195
197TQStringList KexiDB_kexiDBSystemTableNames;
198
200 : TQObject()
201 ,KexiDB::Object()
202 ,d(new ConnectionPrivate(this, conn_data))
203 ,m_driver(driver)
204 ,m_destructor_started(false)
205{
206 d->dbProperties = new DatabaseProperties(this);
207 m_cursors.setAutoDelete(true);
208// d->transactions.setAutoDelete(true);
209 //reasonable sizes: TODO
210 m_cursors.resize(101);
211// d->transactions.resize(101);//woohoo! so many transactions?
212 m_sql.reserve(0x4000);
213}
214
216{
217 disconnect();
218 //do not allow the driver to touch me: I will kill myself.
219 m_driver->d->connections.take( this );
220}
221
223{
224 m_destructor_started = true;
225// KexiDBDbg << "Connection::~Connection()" << endl;
226 delete d->dbProperties;
227 delete d;
228 d = 0;
229/* if (m_driver) {
230 if (m_is_connected) {
231 //delete own table schemas
232 d->tables.clear();
233 //delete own cursors:
234 m_cursors.clear();
235 }
236 //do not allow the driver to touch me: I will kill myself.
237 m_driver->m_connections.take( this );
238 }*/
239}
240
242{
243 return d->conn_data;
244}
245
247{
248 clearError();
249 if (d->isConnected) {
250 setError(ERR_ALREADY_CONNECTED, i18n("Connection already established.") );
251 return false;
252 }
253
254 d->serverVersion.clear();
255 if (!(d->isConnected = drv_connect(d->serverVersion))) {
256 setError(m_driver->isFileDriver() ?
257 i18n("Could not open \"%1\" project file.").arg(TQDir::convertSeparators(d->conn_data->fileName()))
258 : i18n("Could not connect to \"%1\" database server.").arg(d->conn_data->serverInfoString()) );
259 }
260 return d->isConnected;
261}
262
264{
265 return !d->usedDatabase.isEmpty() && d->isConnected && drv_isDatabaseUsed();
266}
267
269{
271 m_sql = TQString();
272}
273
275{
276 clearError();
277 if (!d->isConnected)
278 return true;
279
280 if (!closeDatabase())
281 return false;
282
283 bool ok = drv_disconnect();
284 if (ok)
285 d->isConnected = false;
286 return ok;
287}
288
290{
291 return d->isConnected;
292}
293
295{
296 if (d->isConnected) {
297 clearError();
298 return true;
299 }
300 setError(ERR_NO_CONNECTION, i18n("Not connected to the database server.") );
301 return false;
302}
303
305{
306 if (isDatabaseUsed()) {
307 clearError();
308 return true;
309 }
310 setError(ERR_NO_DB_USED, i18n("Currently no database is used.") );
311 return false;
312}
313
314TQStringList Connection::databaseNames(bool also_system_db)
315{
316 KexiDBDbg << "Connection::databaseNames("<<also_system_db<<")"<< endl;
317 if (!checkConnected())
318 return TQStringList();
319
320 TQString tmpdbName;
321 //some engines need to have opened any database before executing "create database"
322 if (!useTemporaryDatabaseIfNeeded(tmpdbName))
323 return TQStringList();
324
325 TQStringList list, non_system_list;
326
327 bool ret = drv_getDatabasesList( list );
328
329 if (!tmpdbName.isEmpty()) {
330 //whatever result is - now we have to close temporary opened database:
331 if (!closeDatabase())
332 return TQStringList();
333 }
334
335 if (!ret)
336 return TQStringList();
337
338 if (also_system_db)
339 return list;
340 //filter system databases:
341 for (TQStringList::ConstIterator it = list.constBegin(); it!=list.constEnd(); ++it) {
342 KexiDBDbg << "Connection::databaseNames(): " << *it << endl;
343 if (!m_driver->isSystemDatabaseName(*it)) {
344 KexiDBDbg << "add " << *it << endl;
345 non_system_list << (*it);
346 }
347 }
348 return non_system_list;
349}
350
351bool Connection::drv_getDatabasesList( TQStringList &list )
352{
353 list.clear();
354 return true;
355}
356
357bool Connection::drv_databaseExists( const TQString &dbName, bool ignoreErrors )
358{
359 TQStringList list = databaseNames(true);//also system
360 if (error()) {
361 return false;
362 }
363
364 if (list.find( dbName )==list.end()) {
365 if (!ignoreErrors)
366 setError(ERR_OBJECT_NOT_FOUND, i18n("The database \"%1\" does not exist.").arg(dbName));
367 return false;
368 }
369
370 return true;
371}
372
373bool Connection::databaseExists( const TQString &dbName, bool ignoreErrors )
374{
375// KexiDBDbg << "Connection::databaseExists(" << dbName << "," << ignoreErrors << ")" << endl;
376 if (!checkConnected())
377 return false;
378 clearError();
379
380 if (m_driver->isFileDriver()) {
381 //for file-based db: file must exists and be accessible
382//js: moved from useDatabase():
383 TQFileInfo file(d->conn_data->fileName());
384 if (!file.exists() || ( !file.isFile() && !file.isSymLink()) ) {
385 if (!ignoreErrors)
386 setError(ERR_OBJECT_NOT_FOUND, i18n("Database file \"%1\" does not exist.")
387 .arg(TQDir::convertSeparators(d->conn_data->fileName())) );
388 return false;
389 }
390 if (!file.isReadable()) {
391 if (!ignoreErrors)
392 setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not readable.")
393 .arg(TQDir::convertSeparators(d->conn_data->fileName())) );
394 return false;
395 }
396 if (!file.isWritable()) {
397 if (!ignoreErrors)
398 setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not writable.")
399 .arg(TQDir::convertSeparators(d->conn_data->fileName())) );
400 return false;
401 }
402 return true;
403 }
404
405 TQString tmpdbName;
406 //some engines need to have opened any database before executing "create database"
407 const bool orig_skip_databaseExists_check_in_useDatabase = d->skip_databaseExists_check_in_useDatabase;
408 d->skip_databaseExists_check_in_useDatabase = true;
409 bool ret = useTemporaryDatabaseIfNeeded(tmpdbName);
410 d->skip_databaseExists_check_in_useDatabase = orig_skip_databaseExists_check_in_useDatabase;
411 if (!ret)
412 return false;
413
414 ret = drv_databaseExists(dbName, ignoreErrors);
415
416 if (!tmpdbName.isEmpty()) {
417 //whatever result is - now we have to close temporary opened database:
418 if (!closeDatabase())
419 return false;
420 }
421
422 return ret;
423}
424
425#define createDatabase_CLOSE \
426 { if (!closeDatabase()) { \
427 setError(i18n("Database \"%1\" created but could not be closed after creation.").arg(dbName) ); \
428 return false; \
429 } }
430
431#define createDatabase_ERROR \
432 { createDatabase_CLOSE; return false; }
433
434
435bool Connection::createDatabase( const TQString &dbName )
436{
437 if (!checkConnected())
438 return false;
439
440 if (databaseExists( dbName )) {
441 setError(ERR_OBJECT_EXISTS, i18n("Database \"%1\" already exists.").arg(dbName) );
442 return false;
443 }
444 if (m_driver->isSystemDatabaseName( dbName )) {
445 setError(ERR_SYSTEM_NAME_RESERVED,
446 i18n("Cannot create database \"%1\". This name is reserved for system database.").arg(dbName) );
447 return false;
448 }
449 if (m_driver->isFileDriver()) {
450 //update connection data if filename differs
451 d->conn_data->setFileName( dbName );
452 }
453
454 TQString tmpdbName;
455 //some engines need to have opened any database before executing "create database"
456 if (!useTemporaryDatabaseIfNeeded(tmpdbName))
457 return false;
458
459 //low-level create
460 if (!drv_createDatabase( dbName )) {
461 setError(i18n("Error creating database \"%1\" on the server.").arg(dbName) );
462 closeDatabase();//sanity
463 return false;
464 }
465
466 if (!tmpdbName.isEmpty()) {
467 //whatever result is - now we have to close temporary opened database:
468 if (!closeDatabase())
469 return false;
470 }
471
472 if (!tmpdbName.isEmpty() || !m_driver->d->isDBOpenedAfterCreate) {
473 //db need to be opened
474 if (!useDatabase( dbName, false/*not yet kexi compatible!*/ )) {
475 setError(i18n("Database \"%1\" created but could not be opened.").arg(dbName) );
476 return false;
477 }
478 }
479 else {
480 //just for the rule
481 d->usedDatabase = dbName;
482 }
483
484 Transaction trans;
485 if (m_driver->transactionsSupported()) {
486 trans = beginTransaction();
487 if (!trans.active())
488 return false;
489 }
490//not needed since closeDatabase() rollbacks transaction: TransactionGuard trans_g(this);
491// if (error())
492// return false;
493
494 //-create system tables schema objects
496 return false;
497
498 //-physically create system tables
499 for (TQPtrDictIterator<TableSchema> it(d->kexiDBSystemTables); it.current(); ++it) {
500 if (!drv_createTable( it.current()->name() ))
501 createDatabase_ERROR;
502 }
503
504/* moved to KexiProject...
505
506 //-create default part info
507 TableSchema *ts;
508 if (!(ts = tableSchema("kexi__parts")))
509 createDatabase_ERROR;
510 FieldList *fl = ts->subList("p_id", "p_name", "p_mime", "p_url");
511 if (!fl)
512 createDatabase_ERROR;
513 if (!insertRecord(*fl, TQVariant(1), TQVariant("Tables"), TQVariant("kexi/table"), TQVariant("http://koffice.org/kexi/")))
514 createDatabase_ERROR;
515 if (!insertRecord(*fl, TQVariant(2), TQVariant("Queries"), TQVariant("kexi/query"), TQVariant("http://koffice.org/kexi/")))
516 createDatabase_ERROR;
517*/
518
519 //-insert KexiDB version info:
520 TableSchema *t_db = tableSchema("kexi__db");
521 if (!t_db)
522 createDatabase_ERROR;
523 if ( !insertRecord(*t_db, "kexidb_major_ver", KexiDB::version().major)
524 || !insertRecord(*t_db, "kexidb_minor_ver", KexiDB::version().minor))
525 createDatabase_ERROR;
526
527 if (trans.active() && !commitTransaction(trans))
528 createDatabase_ERROR;
529
530 createDatabase_CLOSE;
531 return true;
532}
533
534#undef createDatabase_CLOSE
535#undef createDatabase_ERROR
536
537bool Connection::useDatabase( const TQString &dbName, bool kexiCompatible, bool *cancelled, MessageHandler* msgHandler )
538{
539 if (cancelled)
540 *cancelled = false;
541 KexiDBDbg << "Connection::useDatabase(" << dbName << "," << kexiCompatible <<")" << endl;
542 if (!checkConnected())
543 return false;
544
545 if (dbName.isEmpty())
546 return false;
547 TQString my_dbName = dbName;
548// if (my_dbName.isEmpty()) {
549// const TQStringList& db_lst = databaseNames();
550// if (!db_lst.isEmpty())
551// my_dbName = db_lst.first();
552// }
553 if (d->usedDatabase == my_dbName)
554 return true; //already used
555
556 if (!d->skip_databaseExists_check_in_useDatabase) {
557 if (!databaseExists(my_dbName, false /*don't ignore errors*/))
558 return false; //database must exist
559 }
560
561 if (!d->usedDatabase.isEmpty() && !closeDatabase()) //close db if already used
562 return false;
563
564 d->usedDatabase = "";
565
566 if (!drv_useDatabase( my_dbName, cancelled, msgHandler )) {
567 if (cancelled && *cancelled)
568 return false;
569 TQString msg(i18n("Opening database \"%1\" failed.").arg( my_dbName ));
570 if (error())
571 setError( this, msg );
572 else
573 setError( msg );
574 return false;
575 }
576
577 //-create system tables schema objects
579 return false;
580
581 if (kexiCompatible && my_dbName.lower()!=anyAvailableDatabaseName().lower()) {
582 //-get global database information
583 int num;
584 bool ok;
585// static TQString notfound_str = i18n("\"%1\" database property not found");
586 num = d->dbProperties->value("kexidb_major_ver").toInt(&ok);
587 if (!ok)
588 return false;
589 d->databaseVersion.major = num;
590/* if (true!=querySingleNumber(
591 "select db_value from kexi__db where db_property=" + m_driver->escapeString(TQString("kexidb_major_ver")), num)) {
592 d->errorInvalidDBContents(notfound_str.arg("kexidb_major_ver"));
593 return false;
594 }*/
595 num = d->dbProperties->value("kexidb_minor_ver").toInt(&ok);
596 if (!ok)
597 return false;
598 d->databaseVersion.minor = num;
599/* if (true!=querySingleNumber(
600 "select db_value from kexi__db where db_property=" + m_driver->escapeString(TQString("kexidb_minor_ver")), num)) {
601 d->errorInvalidDBContents(notfound_str.arg("kexidb_minor_ver"));
602 return false;
603 }*/
604
605#if 0 //this is already checked in DriverManagerInternal::lookupDrivers()
606 //** error if major version does not match
607 if (m_driver->versionMajor()!=KexiDB::versionMajor()) {
608 setError(ERR_INCOMPAT_DATABASE_VERSION,
609 i18n("Database version (%1) does not match Kexi application's version (%2)")
610 .arg( TQString("%1.%2").arg(versionMajor()).arg(versionMinor()) )
611 .arg( TQString("%1.%2").arg(KexiDB::versionMajor()).arg(KexiDB::versionMinor()) ) );
612 return false;
613 }
614 if (m_driver->versionMinor()!=KexiDB::versionMinor()) {
615 //js TODO: COMPATIBILITY CODE HERE!
616 //js TODO: CONVERSION CODE HERE (or signal that conversion is needed)
617 }
618#endif
619 }
620 d->usedDatabase = my_dbName;
621 return true;
622}
623
625{
626 if (d->usedDatabase.isEmpty())
627 return true; //no db used
628 if (!checkConnected())
629 return true;
630
631 bool ret = true;
632
634 if (m_driver->transactionsSupported()) {
635 //rollback all transactions
636 TQValueList<Transaction>::ConstIterator it;
637 d->dont_remove_transactions=true; //lock!
638 for (it=d->transactions.constBegin(); it!= d->transactions.constEnd(); ++it) {
639 if (!rollbackTransaction(*it)) {//rollback as much as you can, don't stop on prev. errors
640 ret = false;
641 }
642 else {
643 KexiDBDbg << "Connection::closeDatabase(): transaction rolled back!" << endl;
644 KexiDBDbg << "Connection::closeDatabase(): trans.refcount==" <<
645 ((*it).m_data ? TQString::number((*it).m_data->refcount) : "(null)") << endl;
646 }
647 }
648 d->dont_remove_transactions=false; //unlock!
649 d->transactions.clear(); //free trans. data
650 }
651
652 //delete own cursors:
653 m_cursors.clear();
654 //delete own schemas
655 d->tables.clear();
656 d->kexiDBSystemTables.clear();
657 d->queries.clear();
658
659 if (!drv_closeDatabase())
660 return false;
661
662 d->usedDatabase = "";
663// KexiDBDbg << "Connection::closeDatabase(): " << ret << endl;
664 return ret;
665}
666
668{
669 return d->usedDatabase;
670}
671
673{
674 if (!m_driver->isFileDriver() && m_driver->beh->USING_DATABASE_REQUIRED_TO_CONNECT
675 && !isDatabaseUsed()) {
676 //we have no db used, but it is required by engine to have used any!
677 tmpdbName = anyAvailableDatabaseName();
678 if (tmpdbName.isEmpty()) {
679 setError(ERR_NO_DB_USED, i18n("Cannot find any database for temporary connection.") );
680 return false;
681 }
682 const bool orig_skip_databaseExists_check_in_useDatabase = d->skip_databaseExists_check_in_useDatabase;
683 d->skip_databaseExists_check_in_useDatabase = true;
684 bool ret = useDatabase(tmpdbName, false);
685 d->skip_databaseExists_check_in_useDatabase = orig_skip_databaseExists_check_in_useDatabase;
686 if (!ret) {
688 i18n("Error during starting temporary connection using \"%1\" database name.")
689 .arg(tmpdbName) );
690 return false;
691 }
692 }
693 return true;
694}
695
696bool Connection::dropDatabase( const TQString &dbName )
697{
698 if (!checkConnected())
699 return false;
700
701 TQString dbToDrop;
702 if (dbName.isEmpty() && d->usedDatabase.isEmpty()) {
703 if (!m_driver->isFileDriver()
704 || (m_driver->isFileDriver() && d->conn_data->fileName().isEmpty()) ) {
705 setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot drop database - name not specified.") );
706 return false;
707 }
708 //this is a file driver so reuse previously passed filename
709 dbToDrop = d->conn_data->fileName();
710 }
711 else {
712 if (dbName.isEmpty()) {
713 dbToDrop = d->usedDatabase;
714 } else {
715 if (m_driver->isFileDriver()) //lets get full path
716 dbToDrop = TQFileInfo(dbName).absFilePath();
717 else
718 dbToDrop = dbName;
719 }
720 }
721
722 if (dbToDrop.isEmpty()) {
723 setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot delete database - name not specified.") );
724 return false;
725 }
726
727 if (m_driver->isSystemDatabaseName( dbToDrop )) {
728 setError(ERR_SYSTEM_NAME_RESERVED, i18n("Cannot delete system database \"%1\".").arg(dbToDrop) );
729 return false;
730 }
731
732 if (isDatabaseUsed() && d->usedDatabase == dbToDrop) {
733 //we need to close database because cannot drop used this database
734 if (!closeDatabase())
735 return false;
736 }
737
738 TQString tmpdbName;
739 //some engines need to have opened any database before executing "drop database"
740 if (!useTemporaryDatabaseIfNeeded(tmpdbName))
741 return false;
742
743 //ok, now we have access to dropping
744 bool ret = drv_dropDatabase( dbToDrop );
745
746 if (!tmpdbName.isEmpty()) {
747 //whatever result is - now we have to close temporary opened database:
748 if (!closeDatabase())
749 return false;
750 }
751 return ret;
752}
753
754TQStringList Connection::objectNames(int objType, bool* ok)
755{
756 TQStringList list;
757
758 if (!checkIsDatabaseUsed()) {
759 if(ok)
760 *ok = false;
761 return list;
762 }
763
764 TQString sql;
765 if (objType==KexiDB::AnyObjectType)
766 sql = "SELECT o_name FROM kexi__objects";
767 else
768 sql = TQString::fromLatin1("SELECT o_name FROM kexi__objects WHERE o_type=%1").arg(objType);
769
770 Cursor *c = executeQuery(sql);
771 if (!c) {
772 if(ok)
773 *ok = false;
774 return list;
775 }
776
777 for (c->moveFirst(); !c->eof(); c->moveNext()) {
778 TQString name = c->value(0).toString();
779 if (KexiUtils::isIdentifier( name )) {
780 list.append(name);
781 }
782 }
783
784 if (!deleteCursor(c)) {
785 if(ok)
786 *ok = false;
787 return list;
788 }
789
790 if(ok)
791 *ok = true;
792 return list;
793}
794
795TQStringList Connection::tableNames(bool also_system_tables)
796{
797 bool ok = true;
798 TQStringList list = objectNames(TableObjectType, &ok);
799 if (also_system_tables && ok) {
801 }
802 return list;
803}
804
807{
808 if (KexiDB_kexiDBSystemTableNames.isEmpty()) {
809 KexiDB_kexiDBSystemTableNames
810 << "kexi__objects"
811 << "kexi__objectdata"
812 << "kexi__fields"
813// << "kexi__querydata"
814// << "kexi__queryfields"
815// << "kexi__querytables"
816 << "kexi__db"
817 ;
818 }
819 return KexiDB_kexiDBSystemTableNames;
820}
821
823{
824 return isConnected() ? &d->serverVersion : 0;
825}
826
828{
829 return isDatabaseUsed() ? &d->databaseVersion : 0;
830}
831
833{
834 return *d->dbProperties;
835}
836
837TQValueList<int> Connection::tableIds()
838{
839 return objectIds(KexiDB::TableObjectType);
840}
841
842TQValueList<int> Connection::queryIds()
843{
844 return objectIds(KexiDB::QueryObjectType);
845}
846
847TQValueList<int> Connection::objectIds(int objType)
848{
849 TQValueList<int> list;
850
851 if (!checkIsDatabaseUsed())
852 return list;
853
854 Cursor *c = executeQuery(
855 TQString::fromLatin1("SELECT o_id, o_name FROM kexi__objects WHERE o_type=%1").arg(objType));
856 if (!c)
857 return list;
858 for (c->moveFirst(); !c->eof(); c->moveNext())
859 {
860 TQString tname = c->value(1).toString(); //kexi__objects.o_name
861 if (KexiUtils::isIdentifier( tname )) {
862 list.append(c->value(0).toInt()); //kexi__objects.o_id
863 }
864 }
865
866 deleteCursor(c);
867
868 return list;
869}
870
871TQString Connection::createTableStatement( const KexiDB::TableSchema& tableSchema ) const
872{
873// Each SQL identifier needs to be escaped in the generated query.
874 TQString sql;
875 sql.reserve(4096);
876 sql = "CREATE TABLE " + escapeIdentifier(tableSchema.name()) + " (";
877 bool first=true;
878 Field::ListIterator it( tableSchema.m_fields );
879 Field *field;
880 for (;(field = it.current())!=0; ++it) {
881 if (first)
882 first = false;
883 else
884 sql += ", ";
885 TQString v = escapeIdentifier(field->name()) + " ";
886 const bool autoinc = field->isAutoIncrement();
887 const bool pk = field->isPrimaryKey() || (autoinc && m_driver->beh->AUTO_INCREMENT_REQUIRES_PK);
888//TODO: warning: ^^^^^ this allows only one autonumber per table when AUTO_INCREMENT_REQUIRES_PK==true!
889 if (autoinc && m_driver->beh->SPECIAL_AUTO_INCREMENT_DEF) {
890 if (pk)
891 v += m_driver->beh->AUTO_INCREMENT_TYPE + " " + m_driver->beh->AUTO_INCREMENT_PK_FIELD_OPTION;
892 else
893 v += m_driver->beh->AUTO_INCREMENT_TYPE + " " + m_driver->beh->AUTO_INCREMENT_FIELD_OPTION;
894 }
895 else {
896 if (autoinc && !m_driver->beh->AUTO_INCREMENT_TYPE.isEmpty())
897 v += m_driver->beh->AUTO_INCREMENT_TYPE;
898 else
899 v += m_driver->sqlTypeName(field->type(), field->precision());
900
901 if (field->isUnsigned())
902 v += (" " + m_driver->beh->UNSIGNED_TYPE_KEYWORD);
903
904 if (field->isFPNumericType() && field->precision()>0) {
905 if (field->scale()>0)
906 v += TQString::fromLatin1("(%1,%2)").arg(field->precision()).arg(field->scale());
907 else
908 v += TQString::fromLatin1("(%1)").arg(field->precision());
909 }
910 else if (field->type()==Field::Text && field->length()>0)
911 v += TQString::fromLatin1("(%1)").arg(field->length());
912
913 if (autoinc)
914 v += (" " +
915 (pk ? m_driver->beh->AUTO_INCREMENT_PK_FIELD_OPTION : m_driver->beh->AUTO_INCREMENT_FIELD_OPTION));
916 else
917 //TODO: here is automatically a single-field key created
918 if (pk)
919 v += " PRIMARY KEY";
920 if (!pk && field->isUniqueKey())
921 v += " UNIQUE";
923 if (!autoinc && !pk && field->isNotNull())
924 v += " NOT NULL"; //only add not null option if no autocommit is set
925 if (field->defaultValue().isValid()) {
926 TQString valToSQL( m_driver->valueToSQL( field, field->defaultValue() ) );
927 if (!valToSQL.isEmpty()) //for sanity
928 v += TQString::fromLatin1(" DEFAULT ") + valToSQL;
929 }
930 }
931 sql += v;
932 }
933 sql += ")";
934 return sql;
935}
936
937//yeah, it is very efficient:
938#define C_A(a) , const TQVariant& c ## a
939
940#define V_A0 m_driver->valueToSQL( tableSchema.field(0), c0 )
941#define V_A(a) +","+m_driver->valueToSQL( \
942 tableSchema.field(a) ? tableSchema.field(a)->type() : Field::Text, c ## a )
943
944// KexiDBDbg << "******** " << TQString("INSERT INTO ") +
945// escapeIdentifier(tableSchema.name()) +
946// " VALUES (" + vals + ")" <<endl;
947
948#define C_INS_REC(args, vals) \
949 bool Connection::insertRecord(KexiDB::TableSchema &tableSchema args) {\
950 return executeSQL( \
951 TQString("INSERT INTO ") + escapeIdentifier(tableSchema.name()) + " VALUES (" + vals + ")" \
952 ); \
953 }
954
955#define C_INS_REC_ALL \
956C_INS_REC( C_A(0), V_A0 ) \
957C_INS_REC( C_A(0) C_A(1), V_A0 V_A(1) ) \
958C_INS_REC( C_A(0) C_A(1) C_A(2), V_A0 V_A(1) V_A(2) ) \
959C_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) ) \
960C_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) ) \
961C_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) ) \
962C_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) ) \
963C_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) )
964
965C_INS_REC_ALL
966
967#undef V_A0
968#undef V_A
969#undef C_INS_REC
970
971#define V_A0 value += m_driver->valueToSQL( flist->first(), c0 );
972#define V_A( a ) value += ("," + m_driver->valueToSQL( flist->next(), c ## a ));
973//#define V_ALAST( a ) valueToSQL( flist->last(), c ## a )
974
975
976#define C_INS_REC(args, vals) \
977 bool Connection::insertRecord(FieldList& fields args) \
978 { \
979 TQString value; \
980 Field::List *flist = fields.fields(); \
981 vals \
982 return executeSQL( \
983 TQString("INSERT INTO ") + \
984 ((fields.fields()->first() && fields.fields()->first()->table()) ? \
985 escapeIdentifier(fields.fields()->first()->table()->name()) : \
986 "??") \
987 + "(" + fields.sqlFieldsList(m_driver) + ") VALUES (" + value + ")" \
988 ); \
989 }
990
991C_INS_REC_ALL
992
993#undef C_A
994#undef V_A
995#undef V_ALAST
996#undef C_INS_REC
997#undef C_INS_REC_ALL
998
999bool Connection::insertRecord(TableSchema &tableSchema, TQValueList<TQVariant>& values)
1000{
1001// Each SQL identifier needs to be escaped in the generated query.
1002 Field::List *fields = tableSchema.fields();
1003 Field *f = fields->first();
1004// TQString s_val;
1005// s_val.reserve(4096);
1006 m_sql = TQString();
1007 TQValueList<TQVariant>::ConstIterator it = values.constBegin();
1008// int i=0;
1009 while (f && (it!=values.end())) {
1010 if (m_sql.isEmpty())
1011 m_sql = TQString("INSERT INTO ") +
1013 " VALUES (";
1014 else
1015 m_sql += ",";
1016 m_sql += m_driver->valueToSQL( f, *it );
1017// KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl;
1018 ++it;
1019 f=fields->next();
1020 }
1021 m_sql += ")";
1022
1023// KexiDBDbg<<"******** "<< m_sql << endl;
1024 return executeSQL(m_sql);
1025}
1026
1027bool Connection::insertRecord(FieldList& fields, TQValueList<TQVariant>& values)
1028{
1029// Each SQL identifier needs to be escaped in the generated query.
1030 Field::List *flist = fields.fields();
1031 Field *f = flist->first();
1032 if (!f)
1033 return false;
1034// TQString s_val;
1035// s_val.reserve(4096);
1036 m_sql = TQString();
1037 TQValueList<TQVariant>::ConstIterator it = values.constBegin();
1038// int i=0;
1039 while (f && (it!=values.constEnd())) {
1040 if (m_sql.isEmpty())
1041 m_sql = TQString("INSERT INTO ") +
1042 escapeIdentifier(flist->first()->table()->name()) + "(" +
1043 fields.sqlFieldsList(m_driver) + ") VALUES (";
1044 else
1045 m_sql += ",";
1046 m_sql += m_driver->valueToSQL( f, *it );
1047// KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl;
1048 ++it;
1049 f=flist->next();
1050 }
1051 m_sql += ")";
1052
1053 return executeSQL(m_sql);
1054}
1055
1056bool Connection::executeSQL( const TQString& statement )
1057{
1058 m_sql = statement; //remember for error handling
1059 if (!drv_executeSQL( m_sql )) {
1060 m_errMsg = TQString(); //clear as this could be most probably jsut "Unknown error" string.
1061 m_errorSql = statement;
1062 setError(this, ERR_SQL_EXECUTION_ERROR, i18n("Error while executing SQL statement."));
1063 return false;
1064 }
1065 return true;
1066}
1067
1069 const TQValueList<TQVariant>& params,
1070 const SelectStatementOptions& options) const
1071{
1072//"SELECT FROM ..." is theoretically allowed "
1073//if (querySchema.fieldCount()<1)
1074// return TQString();
1075// Each SQL identifier needs to be escaped in the generated query.
1076
1077 if (!querySchema.statement().isEmpty())
1078 return querySchema.statement();
1079
1082 Field *f;
1083 uint number = 0;
1084 bool singleTable = querySchema.tables()->count() <= 1;
1085 if (singleTable) {
1086 //make sure we will have single table:
1087 for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) {
1088 if (querySchema.isColumnVisible(number) && f->table() && f->table()->lookupFieldSchema( *f )) {
1089 //uups, no, there's at least one left join
1090 singleTable = false;
1091 break;
1092 }
1093 }
1094 }
1095
1096 TQString sql; //final sql string
1097 sql.reserve(4096);
1098//unused TQString s_from_additional; //additional tables list needed for lookup fields
1099 TQString s_additional_joins; //additional joins needed for lookup fields
1100 TQString s_additional_fields; //additional fields to append to the fields list
1101 uint internalUniqueTableAliasNumber = 0; //used to build internalUniqueTableAliases
1102 uint internalUniqueQueryAliasNumber = 0; //used to build internalUniqueQueryAliases
1103 number = 0;
1104 TQPtrList<QuerySchema> subqueries_for_lookup_data; // subqueries will be added to FROM section
1105 TQString kexidb_subquery_prefix("__kexidb_subquery_");
1106 for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) {
1107 if (querySchema.isColumnVisible(number)) {
1108 if (!sql.isEmpty())
1109 sql += TQString::fromLatin1(", ");
1110
1111 if (f->isQueryAsterisk()) {
1112 if (!singleTable && static_cast<QueryAsterisk*>(f)->isSingleTableAsterisk()) //single-table *
1113 sql += escapeIdentifier(f->table()->name(), options.identifierEscaping) +
1114 TQString::fromLatin1(".*");
1115 else //all-tables * (or simplified table.* when there's only one table)
1116 sql += TQString::fromLatin1("*");
1117 }
1118 else {
1119 if (f->isExpression()) {
1120 sql += f->expression()->toString();
1121 }
1122 else {
1123 if (!f->table()) //sanity check
1124 return TQString();
1125
1126 TQString tableName;
1127 int tablePosition = querySchema.tableBoundToColumn(number);
1128 if (tablePosition>=0)
1129 tableName = querySchema.tableAlias(tablePosition);
1130 if (tableName.isEmpty())
1131 tableName = f->table()->name();
1132
1133 if (!singleTable) {
1134 sql += (escapeIdentifier(tableName, options.identifierEscaping) + ".");
1135 }
1136 sql += escapeIdentifier(f->name(), options.identifierEscaping);
1137 }
1138 TQString aliasString = TQString(querySchema.columnAlias(number));
1139 if (!aliasString.isEmpty())
1140 sql += (TQString::fromLatin1(" AS ") + aliasString);
1142 }
1143 LookupFieldSchema *lookupFieldSchema = (options.addVisibleLookupColumns && f->table())
1144 ? f->table()->lookupFieldSchema( *f ) : 0;
1145 if (lookupFieldSchema && lookupFieldSchema->boundColumn()>=0) {
1146 // Lookup field schema found
1147 // Now we also need to fetch "visible" value from the lookup table, not only the value of binding.
1148 // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken)
1149 // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField"
1150 LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource();
1151 if (rowSource.type()==LookupFieldSchema::RowSource::Table) {
1152 TableSchema *lookupTable = querySchema.connection()->tableSchema( rowSource.name() );
1153 FieldList* visibleColumns = 0;
1154 Field *boundField = 0;
1155 if (lookupTable
1156 && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
1157 && (visibleColumns = lookupTable->subList( lookupFieldSchema->visibleColumns() ))
1158 && (boundField = lookupTable->field( lookupFieldSchema->boundColumn() )))
1159 {
1160 //add LEFT OUTER JOIN
1161 if (!s_additional_joins.isEmpty())
1162 s_additional_joins += TQString::fromLatin1(" ");
1163 TQString internalUniqueTableAlias( TQString("__kexidb_") + lookupTable->name() + "_"
1164 + TQString::number(internalUniqueTableAliasNumber++) );
1165 s_additional_joins += TQString("LEFT OUTER JOIN %1 AS %2 ON %3.%4=%5.%6")
1166 .arg(escapeIdentifier(lookupTable->name(), options.identifierEscaping))
1167 .arg(internalUniqueTableAlias)
1168 .arg(escapeIdentifier(f->table()->name(), options.identifierEscaping))
1169 .arg(escapeIdentifier(f->name(), options.identifierEscaping))
1170 .arg(internalUniqueTableAlias)
1171 .arg(escapeIdentifier(boundField->name(), options.identifierEscaping));
1172
1173 //add visibleField to the list of SELECTed fields //if it is not yet present there
1174//not needed if (!querySchema.findTableField( visibleField->table()->name()+"."+visibleField->name() )) {
1175#if 0
1176 if (!querySchema.table( visibleField->table()->name() )) {
1177/* not true
1178 //table should be added after FROM
1179 if (!s_from_additional.isEmpty())
1180 s_from_additional += TQString::fromLatin1(", ");
1181 s_from_additional += escapeIdentifier(visibleField->table()->name(), options.identifierEscaping);
1182 */
1183 }
1184#endif
1185 if (!s_additional_fields.isEmpty())
1186 s_additional_fields += TQString::fromLatin1(", ");
1187// s_additional_fields += (internalUniqueTableAlias + "." //escapeIdentifier(visibleField->table()->name(), options.identifierEscaping) + "."
1188// escapeIdentifier(visibleField->name(), options.identifierEscaping));
1191 s_additional_fields += visibleColumns->sqlFieldsList(
1192 driver(), " || ' ' || ", internalUniqueTableAlias, options.identifierEscaping);
1193 }
1194 delete visibleColumns;
1195 }
1196 else if (rowSource.type()==LookupFieldSchema::RowSource::Query) {
1197 QuerySchema *lookupQuery = querySchema.connection()->querySchema( rowSource.name() );
1198 if (!lookupQuery) {
1199 KexiDBWarn << "Connection::selectStatement(): !lookupQuery" << endl;
1200 return TQString();
1201 }
1202 const QueryColumnInfo::Vector fieldsExpanded( lookupQuery->fieldsExpanded() );
1203 if ((uint)lookupFieldSchema->boundColumn() >= fieldsExpanded.count()) {
1204 KexiDBWarn << "Connection::selectStatement(): (uint)lookupFieldSchema->boundColumn() >= fieldsExpanded.count()" << endl;
1205 return TQString();
1206 }
1207 QueryColumnInfo *boundColumnInfo = fieldsExpanded.at( lookupFieldSchema->boundColumn() );
1208 if (!boundColumnInfo) {
1209 KexiDBWarn << "Connection::selectStatement(): !boundColumnInfo" << endl;
1210 return TQString();
1211 }
1212 Field *boundField = boundColumnInfo->field;
1213 if (!boundField) {
1214 KexiDBWarn << "Connection::selectStatement(): !boundField" << endl;
1215 return TQString();
1216 }
1217 //add LEFT OUTER JOIN
1218 if (!s_additional_joins.isEmpty())
1219 s_additional_joins += TQString::fromLatin1(" ");
1220 TQString internalUniqueQueryAlias(
1221 kexidb_subquery_prefix + lookupQuery->name() + "_"
1222 + TQString::number(internalUniqueQueryAliasNumber++) );
1223 s_additional_joins += TQString("LEFT OUTER JOIN (%1) AS %2 ON %3.%4=%5.%6")
1224 .arg(selectStatement( *lookupQuery, params, options ))
1225 .arg(internalUniqueQueryAlias)
1226 .arg(escapeIdentifier(f->table()->name(), options.identifierEscaping))
1227 .arg(escapeIdentifier(f->name(), options.identifierEscaping))
1228 .arg(internalUniqueQueryAlias)
1229 .arg(escapeIdentifier(boundColumnInfo->aliasOrName(), options.identifierEscaping));
1230
1231 if (!s_additional_fields.isEmpty())
1232 s_additional_fields += TQString::fromLatin1(", ");
1233 const TQValueList<uint> visibleColumns( lookupFieldSchema->visibleColumns() );
1234 TQString expression;
1235 foreach (TQValueList<uint>::ConstIterator, visibleColumnsIt, visibleColumns) {
1238 if (fieldsExpanded.count() <= (*visibleColumnsIt)) {
1239 KexiDBWarn << "Connection::selectStatement(): fieldsExpanded.count() <= (*visibleColumnsIt) : "
1240 << fieldsExpanded.count() << " <= " << *visibleColumnsIt << endl;
1241 return TQString();
1242 }
1243 if (!expression.isEmpty())
1244 expression += " || ' ' || ";
1245 expression += (internalUniqueQueryAlias + "." +
1246 escapeIdentifier(fieldsExpanded[*visibleColumnsIt]->aliasOrName(),
1247 options.identifierEscaping));
1248 }
1249 s_additional_fields += expression;
1250//subqueries_for_lookup_data.append(lookupQuery);
1251 }
1252 else {
1253 KexiDBWarn << "Connection::selectStatement(): unsupported row source type "
1254 << rowSource.typeName() << endl;
1255 return TQString();
1256 }
1257 }
1258 }
1259 }
1260
1261 //add lookup fields
1262 if (!s_additional_fields.isEmpty())
1263 sql += (TQString::fromLatin1(", ") + s_additional_fields);
1264
1265 if (options.alsoRetrieveROWID) { //append rowid column
1266 TQString s;
1267 if (!sql.isEmpty())
1268 s = TQString::fromLatin1(", ");
1270 s += (escapeIdentifier(querySchema.masterTable()->name())+".");
1271 s += m_driver->beh->ROW_ID_FIELD_NAME;
1272 sql += s;
1273 }
1274
1275 sql.prepend("SELECT ");
1277 if ((tables && !tables->isEmpty()) || !subqueries_for_lookup_data.isEmpty()) {
1278 sql += TQString::fromLatin1(" FROM ");
1279 TQString s_from;
1280 if (tables) {
1281 TableSchema *table;
1282 number = 0;
1283 for (TableSchema::ListIterator it(*tables); (table = it.current());
1284 ++it, number++)
1285 {
1286 if (!s_from.isEmpty())
1287 s_from += TQString::fromLatin1(", ");
1288 s_from += escapeIdentifier(table->name(), options.identifierEscaping);
1289 TQString aliasString = TQString(querySchema.tableAlias(number));
1290 if (!aliasString.isEmpty())
1291 s_from += (TQString::fromLatin1(" AS ") + aliasString);
1292 }
1293 /*unused if (!s_from_additional.isEmpty()) {//additional tables list needed for lookup fields
1294 if (!s_from.isEmpty())
1295 s_from += TQString::fromLatin1(", ");
1296 s_from += s_from_additional;
1297 }*/
1298 }
1299 // add subqueries for lookup data
1300 uint subqueries_for_lookup_data_counter = 0;
1301 for (TQPtrListIterator<QuerySchema> it(subqueries_for_lookup_data);
1302 subqueries_for_lookup_data.current(); ++it, subqueries_for_lookup_data_counter++)
1303 {
1304 if (!s_from.isEmpty())
1305 s_from += TQString::fromLatin1(", ");
1306 s_from += TQString::fromLatin1("(");
1307 s_from += selectStatement( *it.current(), params, options );
1308 s_from += TQString::fromLatin1(") AS %1%2")
1309 .arg(kexidb_subquery_prefix).arg(subqueries_for_lookup_data_counter);
1310 }
1311 sql += s_from;
1312 }
1313 TQString s_where;
1314 s_where.reserve(4096);
1315
1316 //JOINS
1317 if (!s_additional_joins.isEmpty()) {
1318 sql += TQString::fromLatin1(" ") + s_additional_joins + TQString::fromLatin1(" ");
1319 }
1320
1321//@todo: we're using WHERE for joins now; use INNER/LEFT/RIGHT JOIN later
1322
1323 //WHERE
1324 Relationship *rel;
1325 bool wasWhere = false; //for later use
1326 for (Relationship::ListIterator it(*querySchema.relationships()); (rel = it.current()); ++it) {
1327 if (s_where.isEmpty()) {
1328 wasWhere = true;
1329 }
1330 else
1331 s_where += TQString::fromLatin1(" AND ");
1332 Field::Pair *pair;
1333 TQString s_where_sub;
1334 for (TQPtrListIterator<Field::Pair> p_it(*rel->fieldPairs()); (pair = p_it.current()); ++p_it) {
1335 if (!s_where_sub.isEmpty())
1336 s_where_sub += TQString::fromLatin1(" AND ");
1337 s_where_sub += (
1338 escapeIdentifier(pair->first->table()->name(), options.identifierEscaping) +
1339 TQString::fromLatin1(".") +
1340 escapeIdentifier(pair->first->name(), options.identifierEscaping) +
1341 TQString::fromLatin1(" = ") +
1342 escapeIdentifier(pair->second->table()->name(), options.identifierEscaping) +
1343 TQString::fromLatin1(".") +
1344 escapeIdentifier(pair->second->name(), options.identifierEscaping));
1345 }
1346 if (rel->fieldPairs()->count()>1) {
1347 s_where_sub.prepend("(");
1348 s_where_sub += TQString::fromLatin1(")");
1349 }
1350 s_where += s_where_sub;
1351 }
1352 //EXPLICITLY SPECIFIED WHERE EXPRESSION
1354 QuerySchemaParameterValueListIterator paramValuesIt(*m_driver, params);
1355 QuerySchemaParameterValueListIterator *paramValuesItPtr = params.isEmpty() ? 0 : &paramValuesIt;
1356 if (wasWhere) {
1357//TODO: () are not always needed
1358 s_where = "(" + s_where + ") AND (" + querySchema.whereExpression()->toString(paramValuesItPtr) + ")";
1359 }
1360 else {
1361 s_where = querySchema.whereExpression()->toString(paramValuesItPtr);
1362 }
1363 }
1364 if (!s_where.isEmpty())
1365 sql += TQString::fromLatin1(" WHERE ") + s_where;
1367 //(use wasWhere here)
1368
1369 // ORDER BY
1370 TQString orderByString(
1371 querySchema.orderByColumnList().toSQLString(!singleTable/*includeTableName*/,
1372 driver(), options.identifierEscaping) );
1373 const TQValueVector<int> pkeyFieldsOrder( querySchema.pkeyFieldsOrder() );
1374 if (orderByString.isEmpty() && !pkeyFieldsOrder.isEmpty()) {
1375 //add automatic ORDER BY if there is no explicity defined (especially helps when there are complex JOINs)
1376 OrderByColumnList automaticPKOrderBy;
1377 const QueryColumnInfo::Vector fieldsExpanded( querySchema.fieldsExpanded() );
1378 foreach (TQValueVector<int>::ConstIterator, it, pkeyFieldsOrder) {
1379 if ((*it) < 0) // no field mentioned in this query
1380 continue;
1381 if ((*it) >= (int)fieldsExpanded.count()) {
1382 KexiDBWarn << "Connection::selectStatement(): ORDER BY: (*it) >= fieldsExpanded.count() - "
1383 << (*it) << " >= " << fieldsExpanded.count() << endl;
1384 continue;
1385 }
1386 QueryColumnInfo *ci = fieldsExpanded[ *it ];
1387 automaticPKOrderBy.appendColumn( *ci );
1388 }
1389 orderByString = automaticPKOrderBy.toSQLString(!singleTable/*includeTableName*/,
1390 driver(), options.identifierEscaping);
1391 }
1392 if (!orderByString.isEmpty())
1393 sql += (" ORDER BY " + orderByString);
1394
1395 //KexiDBDbg << sql << endl;
1396 return sql;
1397}
1398
1400 const SelectStatementOptions& options) const
1401{
1402 return selectStatement( *tableSchema.query(), options );
1403}
1404
1406{
1407 Field *f = fieldlist->fields()->first();
1408 while (f) {
1409 if (m_driver->isSystemFieldName( f->name() ))
1410 return f;
1411 f = fieldlist->fields()->next();
1412 }
1413 return 0;
1414}
1415
1416TQ_ULLONG Connection::lastInsertedAutoIncValue(const TQString& aiFieldName, const TQString& tableName,
1417 TQ_ULLONG* ROWID)
1418{
1419 TQ_ULLONG row_id = drv_lastInsertRowID();
1420 if (ROWID)
1421 *ROWID = row_id;
1422 if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
1423 return row_id;
1424 }
1425 RowData rdata;
1426 if (row_id<=0 || true!=querySingleRecord(
1427 TQString::fromLatin1("SELECT ") + tableName + TQString::fromLatin1(".") + aiFieldName + TQString::fromLatin1(" FROM ") + tableName
1428 + TQString::fromLatin1(" WHERE ") + m_driver->beh->ROW_ID_FIELD_NAME + TQString::fromLatin1("=") + TQString::number(row_id), rdata))
1429 {
1430// KexiDBDbg << "Connection::lastInsertedAutoIncValue(): row_id<=0 || true!=querySingleRecord()" << endl;
1431 return (TQ_ULLONG)-1; //ULL;
1432 }
1433 return rdata[0].toULongLong();
1434}
1435
1436TQ_ULLONG Connection::lastInsertedAutoIncValue(const TQString& aiFieldName,
1437 const KexiDB::TableSchema& table, TQ_ULLONG* ROWID)
1438{
1439 return lastInsertedAutoIncValue(aiFieldName,table.name(), ROWID);
1440}
1441
1443static FieldList* createFieldListForKexi__Fields(TableSchema *kexi__fieldsSchema)
1444{
1445 if (!kexi__fieldsSchema)
1446 return 0;
1447 return kexi__fieldsSchema->subList(
1448 "t_id",
1449 "f_type",
1450 "f_name",
1451 "f_length",
1452 "f_precision",
1453 "f_constraints",
1454 "f_options",
1455 "f_default",
1456 "f_order",
1457 "f_caption",
1458 "f_help"
1459 );
1460}
1461
1463void buildValuesForKexi__Fields(TQValueList<TQVariant>& vals, Field* f)
1464{
1465 vals.clear();
1466 vals
1467 << TQVariant(f->table()->id())
1468 << TQVariant(f->type())
1469 << TQVariant(f->name())
1470 << TQVariant(f->isFPNumericType() ? f->scale() : f->length())
1471 << TQVariant(f->isFPNumericType() ? f->precision() : 0)
1472 << TQVariant(f->constraints())
1473 << TQVariant(f->options())
1474 // KexiDB::variantToString() is needed here because the value can be of any TQVariant type,
1475 // depending on f->type()
1476 << (f->defaultValue().isNull()
1477 ? TQVariant() : TQVariant(KexiDB::variantToString( f->defaultValue() )))
1478 << TQVariant(f->order())
1479 << TQVariant(f->caption())
1480 << TQVariant(f->description());
1481}
1482
1483bool Connection::storeMainFieldSchema(Field *field)
1484{
1485 if (!field || !field->table())
1486 return false;
1487 FieldList *fl = createFieldListForKexi__Fields(d->tables_byname["kexi__fields"]);
1488 if (!fl)
1489 return false;
1490
1491 TQValueList<TQVariant> vals;
1492 buildValuesForKexi__Fields(vals, field);
1493 TQValueList<TQVariant>::ConstIterator valsIt = vals.constBegin();
1494 Field *f;
1495 bool first = true;
1496 TQString sql = "UPDATE kexi__fields SET ";
1497 for (Field::ListIterator it( fl->fieldsIterator() ); (f = it.current()); ++it, ++valsIt) {
1498 sql.append( (first ? TQString() : TQString(", ")) +
1499 f->name() + "=" + m_driver->valueToSQL( f, *valsIt ) );
1500 if (first)
1501 first = false;
1502 }
1503 delete fl;
1504
1505 sql.append(TQString(" WHERE t_id=") + TQString::number( field->table()->id() )
1506 + " AND f_name=" + m_driver->valueToSQL( Field::Text, field->name() ) );
1507 return executeSQL( sql );
1508}
1509
1510#define createTable_ERR \
1511 { KexiDBDbg << "Connection::createTable(): ERROR!" <<endl; \
1512 setError(this, i18n("Creating table failed.")); \
1513 rollbackAutoCommitTransaction(tg.transaction()); \
1514 return false; }
1515 //setError( errorNum(), i18n("Creating table failed.") + " " + errorMsg());
1516
1518
1525bool Connection::createTable( KexiDB::TableSchema* tableSchema, bool replaceExisting )
1526{
1528 return false;
1529
1530 //check if there are any fields
1531 if (tableSchema->fieldCount()<1) {
1532 clearError();
1533 setError(ERR_CANNOT_CREATE_EMPTY_OBJECT, i18n("Cannot create table without fields."));
1534 return false;
1535 }
1536 const bool internalTable = dynamic_cast<InternalTableSchema*>(tableSchema);
1537
1538 const TQString &tableName = tableSchema->name().lower();
1539
1540 if (!internalTable) {
1541 if (m_driver->isSystemObjectName( tableName )) {
1542 clearError();
1543 setError(ERR_SYSTEM_NAME_RESERVED, i18n("System name \"%1\" cannot be used as table name.")
1544 .arg(tableSchema->name()));
1545 return false;
1546 }
1547
1549 if (sys_field) {
1550 clearError();
1551 setError(ERR_SYSTEM_NAME_RESERVED,
1552 i18n("System name \"%1\" cannot be used as one of fields in \"%2\" table.")
1553 .arg(sys_field->name()).arg(tableName));
1554 return false;
1555 }
1556 }
1557
1558 bool previousSchemaStillKept = false;
1559
1560 KexiDB::TableSchema *existingTable = 0;
1561 if (replaceExisting) {
1562 //get previous table (do not retrieve, though)
1563 existingTable = d->tables_byname[tableName];
1564 if (existingTable) {
1565 if (existingTable == tableSchema) {
1566 clearError();
1567 setError(ERR_OBJECT_EXISTS,
1568 i18n("Could not create the same table \"%1\" twice.").arg(tableSchema->name()) );
1569 return false;
1570 }
1571//TODO(js): update any structure (e.g. queries) that depend on this table!
1572 if (existingTable->id()>0)
1573 tableSchema->m_id = existingTable->id(); //copy id from existing table
1574 previousSchemaStillKept = true;
1575 if (!dropTable( existingTable, false /*alsoRemoveSchema*/ ))
1576 return false;
1577 }
1578 }
1579 else {
1580 if (this->tableSchema( tableSchema->name() ) != 0) {
1581 clearError();
1582 setError(ERR_OBJECT_EXISTS, i18n("Table \"%1\" already exists.").arg(tableSchema->name()) );
1583 return false;
1584 }
1585 }
1586
1587/* if (replaceExisting) {
1588 //get previous table (do not retrieve, though)
1589 KexiDB::TableSchema *existingTable = d->tables_byname.take(name);
1590 if (oldTable) {
1591 }*/
1592
1595 return false;
1596
1598 createTable_ERR;
1599
1600 //add schema data to kexi__* tables
1601 if (!internalTable) {
1602 //update kexi__objects
1603 if (!storeObjectSchemaData( *tableSchema, true ))
1604 createTable_ERR;
1605
1606 TableSchema *ts = d->tables_byname["kexi__fields"];
1607 if (!ts)
1608 return false;
1609 //for sanity: remove field info (if any) for this table id
1610 if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id()))
1611 return false;
1612
1613 FieldList *fl = createFieldListForKexi__Fields(d->tables_byname["kexi__fields"]);
1614 if (!fl)
1615 return false;
1616
1617// int order = 0;
1618 Field *f;
1619 for (Field::ListIterator it( *tableSchema->fields() ); (f = it.current()); ++it/*, order++*/) {
1620 TQValueList<TQVariant> vals;
1621 buildValuesForKexi__Fields(vals, f);
1622 if (!insertRecord(*fl, vals ))
1623 createTable_ERR;
1624 }
1625 delete fl;
1626
1628 createTable_ERR;
1629 }
1630
1631 //finally:
1632/* if (replaceExisting) {
1633 if (existingTable) {
1634 d->tables.take(existingTable->id());
1635 delete existingTable;
1636 }
1637 }*/
1638
1640
1641 if (res) {
1642 if (internalTable) {
1643 //insert the internal table into structures
1644 insertInternalTableSchema(tableSchema);
1645 }
1646 else {
1647 if (previousSchemaStillKept) {
1648 //remove previous table schema
1649 removeTableSchemaInternal(tableSchema);
1650 }
1651 //store one schema object locally:
1652 d->tables.insert(tableSchema->id(), tableSchema);
1653 d->tables_byname.insert(tableSchema->name().lower(), tableSchema);
1654 }
1655 //ok, this table is not created by the connection
1656 tableSchema->m_conn = this;
1657 }
1658 return res;
1659}
1660
1661void Connection::removeTableSchemaInternal(TableSchema *tableSchema)
1662{
1663 d->tables_byname.remove(tableSchema->name());
1664 d->tables.remove(tableSchema->id());
1665}
1666
1668{
1669 clearError();
1670 //remove table schema from kexi__* tables
1671 if (!KexiDB::deleteRow(*this, d->tables_byname["kexi__objects"], "o_id", objId) //schema entry
1672 || !KexiDB::deleteRow(*this, d->tables_byname["kexi__objectdata"], "o_id", objId)) {//data blocks
1673 setError(ERR_DELETE_SERVER_ERROR, i18n("Could not remove object's data."));
1674 return false;
1675 }
1676 return true;
1677}
1678
1679bool Connection::drv_dropTable( const TQString& name )
1680{
1681 m_sql = "DROP TABLE " + escapeIdentifier(name);
1682 return executeSQL(m_sql);
1683}
1684
1686
1693{
1694 return dropTable( tableSchema, true );
1695}
1696
1697tristate Connection::dropTable( KexiDB::TableSchema* tableSchema, bool alsoRemoveSchema)
1698{
1699// Each SQL identifier needs to be escaped in the generated query.
1700 clearError();
1701 if (!tableSchema)
1702 return false;
1703
1704 TQString errmsg(i18n("Table \"%1\" cannot be removed.\n"));
1705 //be sure that we handle the correct TableSchema object:
1706 if (tableSchema->id() < 0
1707 || this->tableSchema(tableSchema->name())!=tableSchema
1708 || this->tableSchema(tableSchema->id())!=tableSchema)
1709 {
1710 setError(ERR_OBJECT_NOT_FOUND, errmsg.arg(tableSchema->name())
1711 +i18n("Unexpected name or identifier."));
1712 return false;
1713 }
1714
1715 tristate res = closeAllTableSchemaChangeListeners(*tableSchema);
1716 if (true!=res)
1717 return res;
1718
1719 //sanity checks:
1720 if (m_driver->isSystemObjectName( tableSchema->name() )) {
1721 setError(ERR_SYSTEM_NAME_RESERVED, errmsg.arg(tableSchema->name()) + d->strItIsASystemObject());
1722 return false;
1723 }
1724
1727 return false;
1728
1729 //for sanity we're checking if this table exists physically
1730 if (drv_containsTable(tableSchema->name())) {
1731 if (!drv_dropTable(tableSchema->name()))
1732 return false;
1733 }
1734
1735 TableSchema *ts = d->tables_byname["kexi__fields"];
1736 if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id())) //field entries
1737 return false;
1738
1739 //remove table schema from kexi__objects table
1740 if (!removeObject( tableSchema->id() )) {
1741 return false;
1742 }
1743
1744 if (alsoRemoveSchema) {
1746 tristate res = removeDataBlock( tableSchema->id(), "extended_schema");
1747 if (!res)
1748 return false;
1749 removeTableSchemaInternal(tableSchema);
1750 }
1752}
1753
1754tristate Connection::dropTable( const TQString& table )
1755{
1756 clearError();
1757 TableSchema* ts = tableSchema( table );
1758 if (!ts) {
1759 setError(ERR_OBJECT_NOT_FOUND, i18n("Table \"%1\" does not exist.")
1760 .arg(table));
1761 return false;
1762 }
1763 return dropTable(ts);
1764}
1765
1767{
1768 clearError();
1769 tristate res = closeAllTableSchemaChangeListeners(tableSchema);
1770 if (true!=res)
1771 return res;
1772
1773 if (&tableSchema == &newTableSchema) {
1774 setError(ERR_OBJECT_THE_SAME, i18n("Could not alter table \"%1\" using the same table.")
1775 .arg(tableSchema.name()));
1776 return false;
1777 }
1778//TODO(js): implement real altering
1779//TODO(js): update any structure (e.g. query) that depend on this table!
1780 bool ok, empty;
1781#if 0//TODO ucomment:
1782 empty = isEmpty( tableSchema, ok ) && ok;
1783#else
1784 empty = true;
1785#endif
1786 if (empty) {
1787 ok = createTable(&newTableSchema, true/*replace*/);
1788 }
1789 return ok;
1790}
1791
1792bool Connection::alterTableName(TableSchema& tableSchema, const TQString& newName, bool replace)
1793{
1794 clearError();
1795 if (&tableSchema!=d->tables[tableSchema.id()]) {
1796 setError(ERR_OBJECT_NOT_FOUND, i18n("Unknown table \"%1\"").arg(tableSchema.name()));
1797 return false;
1798 }
1799 if (newName.isEmpty() || !KexiUtils::isIdentifier(newName)) {
1800 setError(ERR_INVALID_IDENTIFIER, i18n("Invalid table name \"%1\"").arg(newName));
1801 return false;
1802 }
1803 const TQString oldTableName = tableSchema.name();
1804 const TQString newTableName = newName.lower().stripWhiteSpace();
1805 if (oldTableName.lower().stripWhiteSpace() == newTableName) {
1806 setError(ERR_OBJECT_THE_SAME, i18n("Could rename table \"%1\" using the same name.")
1807 .arg(newTableName));
1808 return false;
1809 }
1810//TODO: alter table name for server DB backends!
1811//TODO: what about objects (queries/forms) that use old name?
1812//TODO
1813 TableSchema *tableToReplace = this->tableSchema( newName );
1814 const bool destTableExists = tableToReplace != 0;
1815 const int origID = destTableExists ? tableToReplace->id() : -1; //will be reused in the new table
1816 if (!replace && destTableExists) {
1817 setError(ERR_OBJECT_EXISTS,
1818 i18n("Could not rename table \"%1\" to \"%2\". Table \"%3\" already exists.")
1819 .arg(tableSchema.name()).arg(newName).arg(newName));
1820 return false;
1821 }
1822
1823//helper:
1824#define alterTableName_ERR \
1825 tableSchema.setName(oldTableName) //restore old name
1826
1829 return false;
1830
1831 // drop the table replaced (with schema)
1832 if (destTableExists) {
1833 if (!replace) {
1834 return false;
1835 }
1836 if (!dropTable( newName )) {
1837 return false;
1838 }
1839
1840 // the new table owns the previous table's id:
1841 if (!executeSQL(TQString::fromLatin1("UPDATE kexi__objects SET o_id=%1 WHERE o_id=%2 AND o_type=%3")
1842 .arg(origID).arg(tableSchema.id()).arg((int)TableObjectType)))
1843 {
1844 return false;
1845 }
1846 if (!executeSQL(TQString::fromLatin1("UPDATE kexi__fields SET t_id=%1 WHERE t_id=%2")
1847 .arg(origID).arg(tableSchema.id())))
1848 {
1849 return false;
1850 }
1851 d->tables.take(tableSchema.id());
1852 d->tables.insert(origID, &tableSchema);
1853 //maintain table ID
1854 tableSchema.m_id = origID;
1855 }
1856
1857 if (!drv_alterTableName(tableSchema, newTableName)) {
1858 alterTableName_ERR;
1859 return false;
1860 }
1861
1862 // Update kexi__objects
1863 //TODO
1864 if (!executeSQL(TQString::fromLatin1("UPDATE kexi__objects SET o_name=%1 WHERE o_id=%2")
1865 .arg(m_driver->escapeString(tableSchema.name())).arg(tableSchema.id())))
1866 {
1867 alterTableName_ERR;
1868 return false;
1869 }
1870//TODO what about caption?
1871
1872 //restore old name: it will be changed soon!
1873 tableSchema.setName(oldTableName);
1874
1876 alterTableName_ERR;
1877 return false;
1878 }
1879
1880 //update tableSchema:
1881 d->tables_byname.take(tableSchema.name());
1882 tableSchema.setName(newTableName);
1883 d->tables_byname.insert(tableSchema.name(), &tableSchema);
1884 return true;
1885}
1886
1887bool Connection::drv_alterTableName(TableSchema& tableSchema, const TQString& newName)
1888{
1889 const TQString oldTableName = tableSchema.name();
1890 tableSchema.setName(newName);
1891
1892 if (!executeSQL(TQString::fromLatin1("ALTER TABLE %1 RENAME TO %2")
1893 .arg(escapeIdentifier(oldTableName)).arg(escapeIdentifier(newName))))
1894 {
1895 tableSchema.setName(oldTableName); //restore old name
1896 return false;
1897 }
1898 return true;
1899}
1900
1902{
1903 clearError();
1904 if (!querySchema)
1905 return false;
1906
1909 return false;
1910
1911/* TableSchema *ts = d->tables_byname["kexi__querydata"];
1912 if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
1913 return false;
1914
1915 ts = d->tables_byname["kexi__queryfields"];
1916 if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
1917 return false;
1918
1919 ts = d->tables_byname["kexi__querytables"];
1920 if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
1921 return false;*/
1922
1923 //remove query schema from kexi__objects table
1924 if (!removeObject( querySchema->id() )) {
1925 return false;
1926 }
1927
1928//TODO(js): update any structure that depend on this table!
1929 d->queries_byname.remove(querySchema->name());
1930 d->queries.remove(querySchema->id());
1931
1933}
1934
1935bool Connection::dropQuery( const TQString& query )
1936{
1937 clearError();
1938 QuerySchema* qs = querySchema( query );
1939 if (!qs) {
1940 setError(ERR_OBJECT_NOT_FOUND, i18n("Query \"%1\" does not exist.")
1941 .arg(query));
1942 return false;
1943 }
1944 return dropQuery(qs);
1945}
1946
1948{
1950 KexiDBDbg<<"******** "<<m_sql<<endl;
1951 return executeSQL(m_sql);
1952}
1953
1954bool Connection::drv_createTable( const TQString& tableSchemaName )
1955{
1956 TableSchema *ts = d->tables_byname[tableSchemaName];
1957 if (!ts)
1958 return false;
1959 return drv_createTable(*ts);
1960}
1961
1963{
1964 if ((m_driver->d->features & Driver::IgnoreTransactions)
1965 || !d->autoCommit)
1966 {
1968 return true;
1969 }
1970
1971 // commit current transaction (if present) for drivers
1972 // that allow single transaction per connection
1973 if (m_driver->d->features & Driver::SingleTransactions) {
1974 if (d->default_trans_started_inside) //only commit internally started transaction
1975 if (!commitTransaction(d->default_trans, true)) {
1977 return false; //we have a real error
1978 }
1979
1980 d->default_trans_started_inside = d->default_trans.isNull();
1981 if (!d->default_trans_started_inside) {
1982 tg.setTransaction( d->default_trans );
1983 tg.doNothing();
1984 return true; //reuse externally started transaction
1985 }
1986 }
1987 else if (!(m_driver->d->features & Driver::MultipleTransactions)) {
1989 return true; //no trans. supported at all - just return
1990 }
1992 return !error();
1993}
1994
1996{
1997 if (m_driver->d->features & Driver::IgnoreTransactions)
1998 return true;
1999 if (trans.isNull() || !m_driver->transactionsSupported())
2000 return true;
2001 if (m_driver->d->features & Driver::SingleTransactions) {
2002 if (!d->default_trans_started_inside) //only commit internally started transaction
2003 return true; //give up
2004 }
2005 return commitTransaction(trans, true);
2006}
2007
2009{
2010 if (trans.isNull() || !m_driver->transactionsSupported())
2011 return true;
2012 return rollbackTransaction(trans);
2013}
2014
2015#define SET_ERR_TRANS_NOT_SUPP \
2016 { setError(ERR_UNSUPPORTED_DRV_FEATURE, \
2017 i18n("Transactions are not supported for \"%1\" driver.").arg(m_driver->name() )); }
2018
2019#define SET_BEGIN_TR_ERROR \
2020 { if (!error()) \
2021 setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Begin transaction failed")); }
2022
2024{
2025 if (!checkIsDatabaseUsed())
2026 return Transaction::null;
2027 Transaction trans;
2028 if (m_driver->d->features & Driver::IgnoreTransactions) {
2029 //we're creating dummy transaction data here,
2030 //so it will look like active
2031 trans.m_data = new TransactionData(this);
2032 d->transactions.append(trans);
2033 return trans;
2034 }
2035 if (m_driver->d->features & Driver::SingleTransactions) {
2036 if (d->default_trans.active()) {
2037 setError(ERR_TRANSACTION_ACTIVE, i18n("Transaction already started.") );
2038 return Transaction::null;
2039 }
2040 if (!(trans.m_data = drv_beginTransaction())) {
2041 SET_BEGIN_TR_ERROR;
2042 return Transaction::null;
2043 }
2044 d->default_trans = trans;
2045 d->transactions.append(trans);
2046 return d->default_trans;
2047 }
2048 if (m_driver->d->features & Driver::MultipleTransactions) {
2049 if (!(trans.m_data = drv_beginTransaction())) {
2050 SET_BEGIN_TR_ERROR;
2051 return Transaction::null;
2052 }
2053 d->transactions.append(trans);
2054 return trans;
2055 }
2056
2057 SET_ERR_TRANS_NOT_SUPP;
2058 return Transaction::null;
2059}
2060
2061bool Connection::commitTransaction(const Transaction trans, bool ignore_inactive)
2062{
2063 if (!isDatabaseUsed())
2064 return false;
2065// if (!checkIsDatabaseUsed())
2066 //return false;
2067 if ( !m_driver->transactionsSupported()
2068 && !(m_driver->d->features & Driver::IgnoreTransactions))
2069 {
2070 SET_ERR_TRANS_NOT_SUPP;
2071 return false;
2072 }
2073 Transaction t = trans;
2074 if (!t.active()) { //try default tr.
2075 if (!d->default_trans.active()) {
2076 if (ignore_inactive)
2077 return true;
2078 clearError();
2079 setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") );
2080 return false;
2081 }
2082 t = d->default_trans;
2083 d->default_trans = Transaction::null; //now: no default tr.
2084 }
2085 bool ret = true;
2086 if (! (m_driver->d->features & Driver::IgnoreTransactions) )
2087 ret = drv_commitTransaction(t.m_data);
2088 if (t.m_data)
2089 t.m_data->m_active = false; //now this transaction if inactive
2090 if (!d->dont_remove_transactions) //true=transaction obj will be later removed from list
2091 d->transactions.remove(t);
2092 if (!ret && !error())
2093 setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on commit transaction"));
2094 return ret;
2095}
2096
2097bool Connection::rollbackTransaction(const Transaction trans, bool ignore_inactive)
2098{
2099 if (!isDatabaseUsed())
2100 return false;
2101// if (!checkIsDatabaseUsed())
2102// return false;
2103 if ( !m_driver->transactionsSupported()
2104 && !(m_driver->d->features & Driver::IgnoreTransactions))
2105 {
2106 SET_ERR_TRANS_NOT_SUPP;
2107 return false;
2108 }
2109 Transaction t = trans;
2110 if (!t.active()) { //try default tr.
2111 if (!d->default_trans.active()) {
2112 if (ignore_inactive)
2113 return true;
2114 clearError();
2115 setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") );
2116 return false;
2117 }
2118 t = d->default_trans;
2119 d->default_trans = Transaction::null; //now: no default tr.
2120 }
2121 bool ret = true;
2122 if (! (m_driver->d->features & Driver::IgnoreTransactions) )
2123 ret = drv_rollbackTransaction(t.m_data);
2124 if (t.m_data)
2125 t.m_data->m_active = false; //now this transaction if inactive
2126 if (!d->dont_remove_transactions) //true=transaction obj will be later removed from list
2127 d->transactions.remove(t);
2128 if (!ret && !error())
2129 setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on rollback transaction"));
2130 return ret;
2131}
2132
2133#undef SET_ERR_TRANS_NOT_SUPP
2134#undef SET_BEGIN_TR_ERROR
2135
2136/*bool Connection::duringTransaction()
2137{
2138 return drv_duringTransaction();
2139}*/
2140
2142{
2143 return d->default_trans;
2144}
2145
2147{
2148 if (!isDatabaseUsed())
2149 return;
2150// if (!checkIsDatabaseUsed())
2151 // return;
2152 if ( !(m_driver->d->features & Driver::IgnoreTransactions)
2153 && (!trans.active() || !m_driver->transactionsSupported()) )
2154 {
2155 return;
2156 }
2157 d->default_trans = trans;
2158}
2159
2160const TQValueList<Transaction>& Connection::transactions()
2161{
2162 return d->transactions;
2163}
2164
2166{
2167 return d->autoCommit;
2168}
2169
2171{
2172 if (d->autoCommit == on || m_driver->d->features & Driver::IgnoreTransactions)
2173 return true;
2174 if (!drv_setAutoCommit(on))
2175 return false;
2176 d->autoCommit = on;
2177 return true;
2178}
2179
2181{
2182 TQString old_sql = m_sql; //don't
2183 if (!executeSQL( "BEGIN" ))
2184 return 0;
2185 return new TransactionData(this);
2186}
2187
2189{
2190 return executeSQL( "COMMIT" );
2191}
2192
2194{
2195 return executeSQL( "ROLLBACK" );
2196}
2197
2199{
2200 return true;
2201}
2202
2203Cursor* Connection::executeQuery( const TQString& statement, uint cursor_options )
2204{
2205 if (statement.isEmpty())
2206 return 0;
2207 Cursor *c = prepareQuery( statement, cursor_options );
2208 if (!c)
2209 return 0;
2210 if (!c->open()) {//err - kill that
2211 setError(c);
2212 delete c;
2213 return 0;
2214 }
2215 return c;
2216}
2217
2218Cursor* Connection::executeQuery( QuerySchema& query, const TQValueList<TQVariant>& params,
2219 uint cursor_options )
2220{
2221 Cursor *c = prepareQuery( query, params, cursor_options );
2222 if (!c)
2223 return 0;
2224 if (!c->open()) {//err - kill that
2225 setError(c);
2226 delete c;
2227 return 0;
2228 }
2229 return c;
2230}
2231
2232Cursor* Connection::executeQuery( QuerySchema& query, uint cursor_options )
2233{
2234 return executeQuery(query, TQValueList<TQVariant>(), cursor_options);
2235}
2236
2237Cursor* Connection::executeQuery( TableSchema& table, uint cursor_options )
2238{
2239 return executeQuery( *table.query(), cursor_options );
2240}
2241
2242Cursor* Connection::prepareQuery( TableSchema& table, uint cursor_options )
2243{
2244 return prepareQuery( *table.query(), cursor_options );
2245}
2246
2247Cursor* Connection::prepareQuery( QuerySchema& query, const TQValueList<TQVariant>& params,
2248 uint cursor_options )
2249{
2250 Cursor* cursor = prepareQuery(query, cursor_options);
2251 if (cursor)
2252 cursor->setQueryParameters(params);
2253 return cursor;
2254}
2255
2257{
2258 if (!cursor)
2259 return false;
2260 if (cursor->connection()!=this) {//illegal call
2261 KexiDBWarn << "Connection::deleteCursor(): Cannot delete the cursor not owned by the same connection!" << endl;
2262 return false;
2263 }
2264 const bool ret = cursor->close();
2265 delete cursor;
2266 return ret;
2267}
2268
2270{
2271 //not found: retrieve schema
2272/* KexiDB::Cursor *cursor;
2273 if (!(cursor = executeQuery( TQString("select * from kexi__objects where o_id='%1'").arg(objId) )))
2274 return false;
2275 if (!cursor->moveFirst()) {
2276 deleteCursor(cursor);
2277 return false;
2278 }*/
2279 //if (!ok) {
2280 //deleteCursor(cursor);
2281 //return 0;
2282// }
2283 bool ok;
2284 sdata.m_id = data[0].toInt(&ok);
2285 if (!ok) {
2286 return false;
2287 }
2288 sdata.m_name = data[2].toString();
2289 if (!KexiUtils::isIdentifier( sdata.m_name )) {
2290 setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"").arg(sdata.m_name));
2291 return false;
2292 }
2293 sdata.m_caption = data[3].toString();
2294 sdata.m_desc = data[4].toString();
2295
2296// KexiDBDbg<<"@@@ Connection::setupObjectSchemaData() == " << sdata.schemaDataDebugString() << endl;
2297 return true;
2298}
2299
2301{
2302 RowData data;
2303 if (true!=querySingleRecord(TQString::fromLatin1(
2304 "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1")
2305 .arg(objectID), data))
2306 return cancelled;
2307 return setupObjectSchemaData( data, sdata );
2308}
2309
2310tristate Connection::loadObjectSchemaData( int objectType, const TQString& objectName, SchemaData &sdata )
2311{
2312 RowData data;
2313 if (true!=querySingleRecord(TQString::fromLatin1("SELECT o_id, o_type, o_name, o_caption, o_desc "
2314 "FROM kexi__objects WHERE o_type=%1 AND lower(o_name)=%2")
2315 .arg(objectType).arg(m_driver->valueToSQL(Field::Text, objectName.lower())), data))
2316 return cancelled;
2317 return setupObjectSchemaData( data, sdata );
2318}
2319
2320bool Connection::storeObjectSchemaData( SchemaData &sdata, bool newObject )
2321{
2322 TableSchema *ts = d->tables_byname["kexi__objects"];
2323 if (!ts)
2324 return false;
2325 if (newObject) {
2326 int existingID;
2327 if (true == querySingleNumber(TQString::fromLatin1(
2328 "SELECT o_id FROM kexi__objects WHERE o_type=%1 AND lower(o_name)=%2")
2329 .arg(sdata.type()).arg(m_driver->valueToSQL(Field::Text, sdata.name().lower())), existingID))
2330 {
2331 //we already have stored a schema data with the same name and type:
2332 //just update it's properties as it would be existing object
2333 sdata.m_id = existingID;
2334 newObject = false;
2335 }
2336 }
2337 if (newObject) {
2338 FieldList *fl;
2339 bool ok;
2340 if (sdata.id()<=0) {//get new ID
2341 fl = ts->subList("o_type", "o_name", "o_caption", "o_desc");
2342 ok = fl!=0;
2343 if (ok && !insertRecord(*fl, TQVariant(sdata.type()), TQVariant(sdata.name()),
2344 TQVariant(sdata.caption()), TQVariant(sdata.description()) ))
2345 ok = false;
2346 delete fl;
2347 if (!ok)
2348 return false;
2349 //fetch newly assigned ID
2351 int obj_id = (int)lastInsertedAutoIncValue("o_id",*ts);
2352 KexiDBDbg << "######## NEW obj_id == " << obj_id << endl;
2353 if (obj_id<=0)
2354 return false;
2355 sdata.m_id = obj_id;
2356 return true;
2357 } else {
2358 fl = ts->subList("o_id", "o_type", "o_name", "o_caption", "o_desc");
2359 ok = fl!=0;
2360 if (ok && !insertRecord(*fl, TQVariant(sdata.id()), TQVariant(sdata.type()), TQVariant(sdata.name()),
2361 TQVariant(sdata.caption()), TQVariant(sdata.description()) ))
2362 ok = false;
2363 delete fl;
2364 return ok;
2365 }
2366 }
2367 //existing object:
2368 return executeSQL(TQString("UPDATE kexi__objects SET o_type=%2, o_caption=%3, o_desc=%4 WHERE o_id=%1")
2369 .arg(sdata.id()).arg(sdata.type())
2370 .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.caption()))
2371 .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.description())) );
2372}
2373
2375 bool addLimitTo1)
2376{
2377 Q_ASSERT(sql || query);
2379 if (sql)
2380 m_sql = addLimitTo1 ? (*sql + " LIMIT 1") : *sql; // is this safe?
2381 KexiDB::Cursor *cursor;
2382 if (!(cursor = sql ? executeQuery( m_sql ) : executeQuery( *query ))) {
2383 KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
2384 return false;
2385 }
2386 if (!cursor->moveFirst() || cursor->eof()) {
2387 const tristate result = cursor->error() ? false : cancelled;
2388 KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() m_sql=" << m_sql << endl;
2389 setError(cursor);
2390 deleteCursor(cursor);
2391 return result;
2392 }
2393 cursor->storeCurrentRow(data);
2394 return deleteCursor(cursor);
2395}
2396
2397tristate Connection::querySingleRecord(const TQString& sql, RowData &data, bool addLimitTo1)
2398{
2399 return querySingleRecordInternal(data, &sql, 0, addLimitTo1);
2400}
2401
2403{
2404 return querySingleRecordInternal(data, 0, &query, addLimitTo1);
2405}
2406
2407bool Connection::checkIfColumnExists(Cursor *cursor, uint column)
2408{
2409 if (column >= cursor->fieldCount()) {
2410 setError(ERR_CURSOR_RECORD_FETCHING, i18n("Column %1 does not exist for the query.").arg(column));
2411 return false;
2412 }
2413 return true;
2414}
2415
2416tristate Connection::querySingleString(const TQString& sql, TQString &value, uint column, bool addLimitTo1)
2417{
2418 KexiDB::Cursor *cursor;
2419 m_sql = addLimitTo1 ? (sql + " LIMIT 1") : sql; // is this safe?;
2420 if (!(cursor = executeQuery( m_sql ))) {
2421 KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
2422 return false;
2423 }
2424 if (!cursor->moveFirst() || cursor->eof()) {
2425 const tristate result = cursor->error() ? false : cancelled;
2426 KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() " << m_sql << endl;
2427 deleteCursor(cursor);
2428 return result;
2429 }
2430 if (!checkIfColumnExists(cursor, column)) {
2431 deleteCursor(cursor);
2432 return false;
2433 }
2434 value = cursor->value(column).toString();
2435 return deleteCursor(cursor);
2436}
2437
2438tristate Connection::querySingleNumber(const TQString& sql, int &number, uint column, bool addLimitTo1)
2439{
2440 static TQString str;
2441 static bool ok;
2442 const tristate result = querySingleString(sql, str, column, addLimitTo1);
2443 if (result!=true)
2444 return result;
2445 number = str.toInt(&ok);
2446 return ok;
2447}
2448
2449bool Connection::queryStringList(const TQString& sql, TQStringList& list, uint column)
2450{
2451 KexiDB::Cursor *cursor;
2452 clearError();
2453 m_sql = sql;
2454 if (!(cursor = executeQuery( m_sql ))) {
2455 KexiDBWarn << "Connection::queryStringList(): !executeQuery() " << m_sql << endl;
2456 return false;
2457 }
2458 cursor->moveFirst();
2459 if (cursor->error()) {
2460 setError(cursor);
2461 deleteCursor(cursor);
2462 return false;
2463 }
2464 if (!cursor->eof() && !checkIfColumnExists(cursor, column)) {
2465 deleteCursor(cursor);
2466 return false;
2467 }
2468 list.clear();
2469 while (!cursor->eof()) {
2470 list.append( cursor->value(column).toString() );
2471 if (!cursor->moveNext() && cursor->error()) {
2472 setError(cursor);
2473 deleteCursor(cursor);
2474 return false;
2475 }
2476 }
2477 return deleteCursor(cursor);
2478}
2479
2480bool Connection::resultExists(const TQString& sql, bool &success, bool addLimitTo1)
2481{
2482 KexiDB::Cursor *cursor;
2483 //optimization
2484 if (m_driver->beh->SELECT_1_SUBQUERY_SUPPORTED) {
2485 //this is at least for sqlite
2486 if (addLimitTo1 && sql.left(6).upper() == "SELECT")
2487 m_sql = TQString("SELECT 1 FROM (") + sql + ") LIMIT 1"; // is this safe?;
2488 else
2489 m_sql = sql;
2490 }
2491 else {
2492 if (addLimitTo1 && sql.left(6).upper() == "SELECT")
2493 m_sql = sql + " LIMIT 1"; //not always safe!
2494 else
2495 m_sql = sql;
2496 }
2497 if (!(cursor = executeQuery( m_sql ))) {
2498 KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
2499 success = false;
2500 return false;
2501 }
2502 if (!cursor->moveFirst() || cursor->eof()) {
2503 success = !cursor->error();
2504 KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() " << m_sql << endl;
2505 setError(cursor);
2506 deleteCursor(cursor);
2507 return false;
2508 }
2509 success = deleteCursor(cursor);
2510 return true;
2511}
2512
2513bool Connection::isEmpty( TableSchema& table, bool &success )
2514{
2515 return !resultExists( selectStatement( *table.query() ), success );
2516}
2517
2519static void createExtendedTableSchemaMainElementIfNeeded(
2520 TQDomDocument& doc, TQDomElement& extendedTableSchemaMainEl,
2521 bool& extendedTableSchemaStringIsEmpty)
2522{
2523 if (!extendedTableSchemaStringIsEmpty)
2524 return;
2525 //init document
2526 extendedTableSchemaMainEl = doc.createElement("EXTENDED_TABLE_SCHEMA");
2527 doc.appendChild( extendedTableSchemaMainEl );
2528 extendedTableSchemaMainEl.setAttribute("version", TQString::number(KEXIDB_EXTENDED_TABLE_SCHEMA_VERSION));
2529 extendedTableSchemaStringIsEmpty = false;
2530}
2531
2533static void createExtendedTableSchemaFieldElementIfNeeded(TQDomDocument& doc,
2534 TQDomElement& extendedTableSchemaMainEl, const TQString& fieldName, TQDomElement& extendedTableSchemaFieldEl,
2535 bool append = true)
2536{
2537 if (!extendedTableSchemaFieldEl.isNull())
2538 return;
2539 extendedTableSchemaFieldEl = doc.createElement("field");
2540 if (append)
2541 extendedTableSchemaMainEl.appendChild( extendedTableSchemaFieldEl );
2542 extendedTableSchemaFieldEl.setAttribute("name", fieldName);
2543}
2544
2551static void addFieldPropertyToExtendedTableSchemaData(
2552 Field *f, const char* propertyName, const TQVariant& propertyValue,
2553 TQDomDocument& doc, TQDomElement& extendedTableSchemaMainEl,
2554 TQDomElement& extendedTableSchemaFieldEl,
2555 bool& extendedTableSchemaStringIsEmpty,
2556 bool custom = false )
2557{
2558 createExtendedTableSchemaMainElementIfNeeded(doc,
2559 extendedTableSchemaMainEl, extendedTableSchemaStringIsEmpty);
2560 createExtendedTableSchemaFieldElementIfNeeded(
2561 doc, extendedTableSchemaMainEl, f->name(), extendedTableSchemaFieldEl);
2562
2563 //create <property>
2564 TQDomElement extendedTableSchemaFieldPropertyEl = doc.createElement("property");
2565 extendedTableSchemaFieldEl.appendChild( extendedTableSchemaFieldPropertyEl );
2566 if (custom)
2567 extendedTableSchemaFieldPropertyEl.setAttribute("custom", "true");
2568 extendedTableSchemaFieldPropertyEl.setAttribute("name", propertyName);
2569 TQDomElement extendedTableSchemaFieldPropertyValueEl;
2570 switch (propertyValue.type()) {
2571 case TQVariant::String:
2572 extendedTableSchemaFieldPropertyValueEl = doc.createElement("string");
2573 break;
2574 case TQVariant::CString:
2575 extendedTableSchemaFieldPropertyValueEl = doc.createElement("cstring");
2576 break;
2577 case TQVariant::Int:
2578 case TQVariant::Double:
2579 case TQVariant::UInt:
2580 case TQVariant::LongLong:
2581 case TQVariant::ULongLong:
2582 extendedTableSchemaFieldPropertyValueEl = doc.createElement("number");
2583 break;
2584 case TQVariant::Bool:
2585 extendedTableSchemaFieldPropertyValueEl = doc.createElement("bool");
2586 break;
2587 default:
2589 KexiDBFatal << "addFieldPropertyToExtendedTableSchemaData(): impl. error" << endl;
2590 }
2591 extendedTableSchemaFieldPropertyEl.appendChild( extendedTableSchemaFieldPropertyValueEl );
2592 extendedTableSchemaFieldPropertyValueEl.appendChild(
2593 doc.createTextNode( propertyValue.toString() ) );
2594}
2595
2597{
2599 TQDomDocument doc("EXTENDED_TABLE_SCHEMA");
2600 TQDomElement extendedTableSchemaMainEl;
2601 bool extendedTableSchemaStringIsEmpty = true;
2602
2603 //for each field:
2604 Field *f;
2605 for (Field::ListIterator it( *tableSchema.fields() ); (f = it.current()); ++it) {
2606 TQDomElement extendedTableSchemaFieldEl;
2607 if (f->visibleDecimalPlaces()>=0/*nondefault*/ && KexiDB::supportsVisibleDecimalPlacesProperty(f->type())) {
2608 addFieldPropertyToExtendedTableSchemaData(
2609 f, "visibleDecimalPlaces", f->visibleDecimalPlaces(), doc,
2610 extendedTableSchemaMainEl, extendedTableSchemaFieldEl,
2611 extendedTableSchemaStringIsEmpty );
2612 }
2613 // boolean field with "not null"
2614
2615 // add custom properties
2616 const Field::CustomPropertiesMap customProperties(f->customProperties());
2617 foreach( Field::CustomPropertiesMap::ConstIterator, itCustom, customProperties ) {
2618 addFieldPropertyToExtendedTableSchemaData(
2619 f, itCustom.key(), itCustom.data(), doc,
2620 extendedTableSchemaMainEl, extendedTableSchemaFieldEl, extendedTableSchemaStringIsEmpty,
2621 /*custom*/true );
2622 }
2623 // save lookup table specification, if present
2624 LookupFieldSchema *lookupFieldSchema = tableSchema.lookupFieldSchema( *f );
2625 if (lookupFieldSchema) {
2626 createExtendedTableSchemaFieldElementIfNeeded(
2627 doc, extendedTableSchemaMainEl, f->name(), extendedTableSchemaFieldEl, false/* !append */);
2628 LookupFieldSchema::saveToDom(*lookupFieldSchema, doc, extendedTableSchemaFieldEl);
2629
2630 if (extendedTableSchemaFieldEl.hasChildNodes()) {
2631 // this element provides the definition, so let's append it now
2632 createExtendedTableSchemaMainElementIfNeeded(doc, extendedTableSchemaMainEl,
2633 extendedTableSchemaStringIsEmpty);
2634 extendedTableSchemaMainEl.appendChild( extendedTableSchemaFieldEl );
2635 }
2636 }
2637 }
2638
2639 // Store extended schema information (see ExtendedTableSchemaInformation in Kexi Wiki)
2640 if (extendedTableSchemaStringIsEmpty) {
2641#ifdef KEXI_DEBUG_GUI
2642 KexiUtils::addAlterTableActionDebug(TQString("** Extended table schema REMOVED."));
2643#endif
2644 if (!removeDataBlock( tableSchema.id(), "extended_schema"))
2645 return false;
2646 }
2647 else {
2648#ifdef KEXI_DEBUG_GUI
2649 KexiUtils::addAlterTableActionDebug(TQString("** Extended table schema set to:\n")+doc.toString(4));
2650#endif
2651 if (!storeDataBlock( tableSchema.id(), doc.toString(1), "extended_schema" ))
2652 return false;
2653 }
2654 return true;
2655}
2656
2658{
2659#define loadExtendedTableSchemaData_ERR \
2660 { setError(i18n("Error while loading extended table schema information.")); \
2661 return false; }
2662#define loadExtendedTableSchemaData_ERR2(details) \
2663 { setError(i18n("Error while loading extended table schema information."), details); \
2664 return false; }
2665#define loadExtendedTableSchemaData_ERR3(data) \
2666 { setError(i18n("Error while loading extended table schema information."), \
2667 i18n("Invalid XML data: ") + data.left(1024) ); \
2668 return false; }
2669
2670 // Load extended schema information, if present (see ExtendedTableSchemaInformation in Kexi Wiki)
2671 TQString extendedTableSchemaString;
2672 tristate res = loadDataBlock( tableSchema.id(), extendedTableSchemaString, "extended_schema" );
2673 if (!res)
2674 loadExtendedTableSchemaData_ERR;
2675 // extendedTableSchemaString will be just empty if there is no such data block
2676
2677#ifdef KEXIDB_LOOKUP_FIELD_TEST
2678//<temp. for LookupFieldSchema tests>
2679 if (tableSchema.name()=="cars") {
2680 LookupFieldSchema *lookupFieldSchema = new LookupFieldSchema();
2682 lookupFieldSchema->rowSource().setName("persons");
2683 lookupFieldSchema->setBoundColumn(0); //id
2684 lookupFieldSchema->setVisibleColumn(3); //surname
2685 tableSchema.setLookupFieldSchema( "owner", lookupFieldSchema );
2686 }
2687//</temp. for LookupFieldSchema tests>
2688#endif
2689
2690 if (extendedTableSchemaString.isEmpty())
2691 return true;
2692
2693 TQDomDocument doc;
2694 TQString errorMsg;
2695 int errorLine, errorColumn;
2696 if (!doc.setContent( extendedTableSchemaString, &errorMsg, &errorLine, &errorColumn ))
2697 loadExtendedTableSchemaData_ERR2( i18n("Error in XML data: \"%1\" in line %2, column %3.\nXML data: ")
2698 .arg(errorMsg).arg(errorLine).arg(errorColumn) + extendedTableSchemaString.left(1024));
2699
2701
2702 if (doc.doctype().name()!="EXTENDED_TABLE_SCHEMA")
2703 loadExtendedTableSchemaData_ERR3( extendedTableSchemaString );
2704
2705 TQDomElement docEl = doc.documentElement();
2706 if (docEl.tagName()!="EXTENDED_TABLE_SCHEMA")
2707 loadExtendedTableSchemaData_ERR3( extendedTableSchemaString );
2708
2709 for (TQDomNode n = docEl.firstChild(); !n.isNull(); n = n.nextSibling()) {
2710 TQDomElement fieldEl = n.toElement();
2711 if (fieldEl.tagName()=="field") {
2712 Field *f = tableSchema.field( fieldEl.attribute("name") );
2713 if (f) {
2714 //set properties of the field:
2716 for (TQDomNode propNode = fieldEl.firstChild();
2717 !propNode.isNull(); propNode = propNode.nextSibling())
2718 {
2719 TQDomElement propEl = propNode.toElement();
2720 bool ok;
2721 int intValue;
2722 if (propEl.tagName()=="property") {
2723 TQCString propertyName = propEl.attribute("name").latin1();
2724 if (propEl.attribute("custom")=="true") {
2725 //custom property
2726 f->setCustomProperty(propertyName,
2727 KexiDB::loadPropertyValueFromDom( propEl.firstChild() ));
2728 }
2729 else if (propertyName == "visibleDecimalPlaces"
2731 {
2732 intValue = KexiDB::loadIntPropertyValueFromDom( propEl.firstChild(), &ok );
2733 if (ok)
2734 f->setVisibleDecimalPlaces(intValue);
2735 }
2737 }
2738 else if (propEl.tagName()=="lookup-column") {
2739 LookupFieldSchema *lookupFieldSchema = LookupFieldSchema::loadFromDom(propEl);
2740 if (lookupFieldSchema)
2741 lookupFieldSchema->debug();
2742 tableSchema.setLookupFieldSchema( f->name(), lookupFieldSchema );
2743 }
2744 }
2745 }
2746 else {
2747 KexiDBWarn << "Connection::loadExtendedTableSchemaData(): no such field \""
2748 << fieldEl.attribute("name") << "\" in table \"" << tableSchema.name() << "\"" << endl;
2749 }
2750 }
2751 }
2752
2753 return true;
2754}
2755
2757{
2758 bool ok = true;
2759 int f_int_type = data.at(1).toInt(&ok);
2760 if (f_int_type<=Field::InvalidType || f_int_type>Field::LastType)
2761 ok = false;
2762 if (!ok)
2763 return 0;
2764 Field::Type f_type = (Field::Type)f_int_type;
2765 int f_len = TQMAX( 0, data.at(3).toInt(&ok) );
2766 if (!ok)
2767 return 0;
2768 int f_prec = data.at(4).toInt(&ok);
2769 if (!ok)
2770 return 0;
2771 int f_constr = data.at(5).toInt(&ok);
2772 if (!ok)
2773 return 0;
2774 int f_opts = data.at(6).toInt(&ok);
2775 if (!ok)
2776 return 0;
2777
2778 if (!KexiUtils::isIdentifier( data.at(2).toString() )) {
2779 setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"")
2780 .arg( data.at(2).toString() ));
2781 ok = false;
2782 return 0;
2783 }
2784
2785 Field *f = new Field(
2786 data.at(2).toString(), f_type, f_constr, f_opts, f_len, f_prec );
2787
2788 f->setDefaultValue( KexiDB::stringToVariant(data.at(7).toString(), Field::variantType( f_type ), ok) );
2789 if (!ok) {
2790 KexiDBWarn << "Connection::setupTableSchema() problem with KexiDB::stringToVariant("
2791 << data.at(7).toString() << ")" << endl;
2792 }
2793 ok = true; //problem with defaultValue is not critical
2794
2795 f->m_caption = data.at(9).toString();
2796 f->m_desc = data.at(10).toString();
2797 return f;
2798}
2799
2801{
2802 TableSchema *t = new TableSchema( this );
2803 if (!setupObjectSchemaData( data, *t )) {
2804 delete t;
2805 return 0;
2806 }
2807
2808 KexiDB::Cursor *cursor;
2809 if (!(cursor = executeQuery(
2810 TQString::fromLatin1("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
2811 "f_options, f_default, f_order, f_caption, f_help"
2812 " FROM kexi__fields WHERE t_id=%1 ORDER BY f_order").arg(t->m_id) )))
2813 {
2814 delete t;
2815 return 0;
2816 }
2817 if (!cursor->moveFirst()) {
2818 if (!cursor->error() && cursor->eof()) {
2819 setError(i18n("Table has no fields defined."));
2820 }
2821 deleteCursor(cursor);
2822 delete t;
2823 return 0;
2824 }
2825
2826 // For each field: load its schema
2827 RowData fieldData;
2828 bool ok = true;
2829 while (!cursor->eof()) {
2830// KexiDBDbg<<"@@@ f_name=="<<cursor->value(2).asCString()<<endl;
2831 cursor->storeCurrentRow(fieldData);
2832 Field *f = setupField(fieldData);
2833 if (!f) {
2834 ok = false;
2835 break;
2836 }
2837 t->addField(f);
2838 cursor->moveNext();
2839 }
2840
2841 if (!ok) {//error:
2842 deleteCursor(cursor);
2843 delete t;
2844 return 0;
2845 }
2846
2847 if (!deleteCursor(cursor)) {
2848 delete t;
2849 return 0;
2850 }
2851
2852 if (!loadExtendedTableSchemaData(*t)) {
2853 delete t;
2854 return 0;
2855 }
2856 //store locally:
2857 d->tables.insert(t->m_id, t);
2858 d->tables_byname.insert(t->m_name.lower(), t);
2859 return t;
2860}
2861
2862TableSchema* Connection::tableSchema( const TQString& tableName )
2863{
2864 TQString m_tableName = tableName.lower();
2865 TableSchema *t = d->tables_byname[m_tableName];
2866 if (t)
2867 return t;
2868 //not found: retrieve schema
2869 RowData data;
2870 if (true!=querySingleRecord(TQString::fromLatin1(
2871 "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE lower(o_name)='%1' AND o_type=%2")
2872 .arg(m_tableName).arg(KexiDB::TableObjectType), data))
2873 return 0;
2874
2875 return setupTableSchema(data);
2876}
2877
2879{
2880 TableSchema *t = d->tables[tableId];
2881 if (t)
2882 return t;
2883 //not found: retrieve schema
2884 RowData data;
2885 if (true!=querySingleRecord(TQString::fromLatin1(
2886 "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1")
2887 .arg(tableId), data))
2888 return 0;
2889
2890 return setupTableSchema(data);
2891}
2892
2893tristate Connection::loadDataBlock( int objectID, TQString &dataString, const TQString& dataID )
2894{
2895 if (objectID<=0)
2896 return false;
2897 return querySingleString(
2898 TQString("SELECT o_data FROM kexi__objectdata WHERE o_id=") + TQString::number(objectID)
2899 + " AND " + KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID),
2900 dataString );
2901}
2902
2903bool Connection::storeDataBlock( int objectID, const TQString &dataString, const TQString& dataID )
2904{
2905 if (objectID<=0)
2906 return false;
2907 TQString sql(TQString::fromLatin1("SELECT kexi__objectdata.o_id FROM kexi__objectdata WHERE o_id=%1").arg(objectID));
2908 TQString sql_sub( KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID) );
2909
2910 bool ok, exists;
2911 exists = resultExists(sql + " and " + sql_sub, ok);
2912 if (!ok)
2913 return false;
2914 if (exists) {
2915 return executeSQL( "UPDATE kexi__objectdata SET o_data="
2916 + m_driver->valueToSQL( KexiDB::Field::LongText, dataString )
2917 + " WHERE o_id=" + TQString::number(objectID) + " AND " + sql_sub );
2918 }
2919 return executeSQL(
2920 TQString::fromLatin1("INSERT INTO kexi__objectdata (o_id, o_data, o_sub_id) VALUES (")
2921 + TQString::number(objectID) +"," + m_driver->valueToSQL( KexiDB::Field::LongText, dataString )
2922 + "," + m_driver->valueToSQL( KexiDB::Field::Text, dataID ) + ")" );
2923}
2924
2925bool Connection::removeDataBlock( int objectID, const TQString& dataID)
2926{
2927 if (objectID<=0)
2928 return false;
2929 if (dataID.isEmpty())
2930 return KexiDB::deleteRow(*this, "kexi__objectdata", "o_id", TQString::number(objectID));
2931 else
2932 return KexiDB::deleteRow(*this, "kexi__objectdata",
2933 "o_id", KexiDB::Field::Integer, objectID, "o_sub_id", KexiDB::Field::Text, dataID);
2934}
2935
2937{
2938 bool ok = true;
2939 const int objID = data[0].toInt(&ok);
2940 if (!ok)
2941 return 0;
2942 TQString sqlText;
2943 if (!loadDataBlock( objID, sqlText, "sql" )) {
2944 setError(ERR_OBJECT_NOT_FOUND,
2945 i18n("Could not find definition for query \"%1\". Removing this query is recommended.")
2946 .arg(data[2].toString()));
2947 return 0;
2948 }
2949 d->parser()->parse( sqlText );
2950 KexiDB::QuerySchema *query = d->parser()->query();
2951 //error?
2952 if (!query) {
2953 setError(ERR_SQL_PARSE_ERROR,
2954 i18n("<p>Could not load definition for query \"%1\". "
2955 "SQL statement for this query is invalid:<br><tt>%2</tt></p>\n"
2956 "<p>You can open this query in Text View and correct it.</p>").arg(data[2].toString())
2957 .arg(d->parser()->statement()));
2958 return 0;
2959 }
2960 if (!setupObjectSchemaData( data, *query )) {
2961 delete query;
2962 return 0;
2963 }
2964 d->queries.insert(query->m_id, query);
2965 d->queries_byname.insert(query->m_name, query);
2966 return query;
2967}
2968
2969QuerySchema* Connection::querySchema( const TQString& queryName )
2970{
2971 TQString m_queryName = queryName.lower();
2972 QuerySchema *q = d->queries_byname[m_queryName];
2973 if (q)
2974 return q;
2975 //not found: retrieve schema
2976 RowData data;
2977 if (true!=querySingleRecord(TQString::fromLatin1(
2978 "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE lower(o_name)='%1' AND o_type=%2")
2979 .arg(m_queryName).arg(KexiDB::QueryObjectType), data))
2980 return 0;
2981
2982 return setupQuerySchema(data);
2983}
2984
2986{
2987 QuerySchema *q = d->queries[queryId];
2988 if (q)
2989 return q;
2990 //not found: retrieve schema
2991 clearError();
2992 RowData data;
2993 if (true!=querySingleRecord(TQString::fromLatin1(
2994 "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1").arg(queryId), data))
2995 return 0;
2996
2997 return setupQuerySchema(data);
2998}
2999
3000bool Connection::setQuerySchemaObsolete( const TQString& queryName )
3001{
3002 QuerySchema* oldQuery = querySchema( queryName );
3003 if (!oldQuery)
3004 return false;
3005 d->obsoleteQueries.append(oldQuery);
3006 d->queries_byname.take(queryName);
3007 d->queries.take(oldQuery->id());
3008 return true;
3009}
3010
3012{
3013 TableSchema *ts = new TableSchema(tsname.lower());
3014 insertInternalTableSchema( ts );
3015 return ts;
3016}
3017
3018bool Connection::isInternalTableSchema(const TQString& tableName)
3019{
3020 return (d->kexiDBSystemTables[ d->tables_byname[tableName] ])
3021 // these are here for compatiblility because we're no longer instantiate
3022 // them but can exist in projects created with previous Kexi versions:
3023 || tableName=="kexi__final" || tableName=="kexi__useractions";
3024}
3025
3026void Connection::insertInternalTableSchema(TableSchema *tableSchema)
3027{
3029 d->kexiDBSystemTables.insert(tableSchema, tableSchema);
3030 d->tables_byname.insert(tableSchema->name(), tableSchema);
3031}
3032
3035{
3036 if (!d->kexiDBSystemTables.isEmpty())
3037 return true; //already set up
3038
3039 TableSchema *t_objects = newKexiDBSystemTableSchema("kexi__objects");
3040 t_objects->addField( new Field("o_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) )
3041 .addField( new Field("o_type", Field::Byte, 0, Field::Unsigned) )
3042 .addField( new Field("o_name", Field::Text) )
3043 .addField( new Field("o_caption", Field::Text ) )
3044 .addField( new Field("o_desc", Field::LongText ) );
3045
3046 t_objects->debug();
3047
3048 TableSchema *t_objectdata = newKexiDBSystemTableSchema("kexi__objectdata");
3049 t_objectdata->addField( new Field("o_id", Field::Integer, Field::NotNull, Field::Unsigned) )
3050 .addField( new Field("o_data", Field::LongText) )
3051 .addField( new Field("o_sub_id", Field::Text) );
3052
3053 TableSchema *t_fields = newKexiDBSystemTableSchema("kexi__fields");
3054 t_fields->addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) )
3055 .addField( new Field("f_type", Field::Byte, 0, Field::Unsigned) )
3056 .addField( new Field("f_name", Field::Text ) )
3057 .addField( new Field("f_length", Field::Integer ) )
3058 .addField( new Field("f_precision", Field::Integer ) )
3059 .addField( new Field("f_constraints", Field::Integer ) )
3060 .addField( new Field("f_options", Field::Integer ) )
3061 .addField( new Field("f_default", Field::Text ) )
3062 //these are additional properties:
3063 .addField( new Field("f_order", Field::Integer ) )
3064 .addField( new Field("f_caption", Field::Text ) )
3065 .addField( new Field("f_help", Field::LongText ) );
3066
3067/* TableSchema *t_querydata = newKexiDBSystemTableSchema("kexi__querydata");
3068 t_querydata->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
3069 .addField( new Field("q_sql", Field::LongText ) )
3070 .addField( new Field("q_valid", Field::Boolean ) );
3071
3072 TableSchema *t_queryfields = newKexiDBSystemTableSchema("kexi__queryfields");
3073 t_queryfields->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
3074 .addField( new Field("f_order", Field::Integer ) )
3075 .addField( new Field("f_id", Field::Integer ) )
3076 .addField( new Field("f_tab_asterisk", Field::Integer, 0, Field::Unsigned) )
3077 .addField( new Field("f_alltab_asterisk", Field::Boolean) );
3078
3079 TableSchema *t_querytables = newKexiDBSystemTableSchema("kexi__querytables");
3080 t_querytables->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
3081 .addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) )
3082 .addField( new Field("t_order", Field::Integer, 0, Field::Unsigned) );*/
3083
3084 TableSchema *t_db = newKexiDBSystemTableSchema("kexi__db");
3085 t_db->addField( new Field("db_property", Field::Text, Field::NoConstraints, Field::NoOptions, 32 ) )
3086 .addField( new Field("db_value", Field::LongText ) );
3087
3088/* moved to KexiProject...
3089 TableSchema *t_parts = newKexiDBSystemTableSchema("kexi__parts");
3090 t_parts->addField( new Field("p_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) )
3091 .addField( new Field("p_name", Field::Text) )
3092 .addField( new Field("p_mime", Field::Text ) )
3093 .addField( new Field("p_url", Field::Text ) );
3094*/
3095
3096/*UNUSED
3097 TableSchema *t_final = newKexiDBSystemTableSchema("kexi__final");
3098 t_final->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) )
3099 .addField( new Field("property", Field::LongText ) )
3100 .addField( new Field("value", Field::BLOB) );
3101
3102 TableSchema *t_useractions = newKexiDBSystemTableSchema("kexi__useractions");
3103 t_useractions->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) )
3104 .addField( new Field("scope", Field::Integer ) )
3105 .addField( new Field("name", Field::LongText ) )
3106 .addField( new Field("text", Field::LongText ) )
3107 .addField( new Field("icon", Field::LongText ) )
3108 .addField( new Field("method", Field::Integer ) )
3109 .addField( new Field("arguments", Field::LongText) );
3110*/
3111 return true;
3112}
3113
3115{
3116 if (ts && !m_destructor_started) {
3117 d->tables.take(ts->id());
3118 d->tables_byname.take(ts->name());
3119 }
3120}
3121
3123{
3124 if (!d->availableDatabaseName.isEmpty()) {
3125 return d->availableDatabaseName;
3126 }
3127 return m_driver->beh->ALWAYS_AVAILABLE_DATABASE_NAME;
3128}
3129
3130void Connection::setAvailableDatabaseName(const TQString& dbName)
3131{
3132 d->availableDatabaseName = dbName;
3133}
3134
3136inline void updateRowDataWithNewValues(QuerySchema &query, RowData& data, KexiDB::RowEditBuffer::DBMap& b,
3137 TQMap<QueryColumnInfo*,int>& columnsOrderExpanded)
3138{
3139 columnsOrderExpanded = query.columnsOrder(QuerySchema::ExpandedList);
3140 TQMap<QueryColumnInfo*,int>::ConstIterator columnsOrderExpandedIt;
3141 for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
3142 columnsOrderExpandedIt = columnsOrderExpanded.find( it.key() );
3143 if (columnsOrderExpandedIt == columnsOrderExpanded.constEnd()) {
3144 KexiDBWarn << "(Connection) updateRowDataWithNewValues(): \"now also assign new value in memory\" step "
3145 "- could not find item '" << it.key()->aliasOrName() << "'" << endl;
3146 continue;
3147 }
3148 data[ columnsOrderExpandedIt.data() ] = it.data();
3149 }
3150}
3151
3152bool Connection::updateRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool useROWID)
3153{
3154// Each SQL identifier needs to be escaped in the generated query.
3155// query.debug();
3156
3157 KexiDBDbg << "Connection::updateRow.." << endl;
3158 clearError();
3159 //--get PKEY
3160 if (buf.dbBuffer().isEmpty()) {
3161 KexiDBDbg << " -- NO CHANGES DATA!" << endl;
3162 return true;
3163 }
3164 TableSchema *mt = query.masterTable();
3165 if (!mt) {
3166 KexiDBWarn << " -- NO MASTER TABLE!" << endl;
3167 setError(ERR_UPDATE_NO_MASTER_TABLE,
3168 i18n("Could not update row because there is no master table defined."));
3169 return false;
3170 }
3171 IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
3172 if (!useROWID && !pkey) {
3173 KexiDBWarn << " -- NO MASTER TABLE's PKEY!" << endl;
3174 setError(ERR_UPDATE_NO_MASTER_TABLES_PKEY,
3175 i18n("Could not update row because master table has no primary key defined."));
3177 return false;
3178 }
3179 //update the record:
3180 m_sql = "UPDATE " + escapeIdentifier(mt->name()) + " SET ";
3181 TQString sqlset, sqlwhere;
3182 sqlset.reserve(1024);
3183 sqlwhere.reserve(1024);
3184 KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer();
3185 for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
3186 if (it.key()->field->table()!=mt)
3187 continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
3188 if (!sqlset.isEmpty())
3189 sqlset+=",";
3190 sqlset += (escapeIdentifier(it.key()->field->name()) + "=" +
3191 m_driver->valueToSQL(it.key()->field,it.data()));
3192 }
3193 if (pkey) {
3194 const TQValueVector<int> pkeyFieldsOrder( query.pkeyFieldsOrder() );
3195 KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
3196 if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
3197 KexiDBWarn << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
3198 setError(ERR_UPDATE_NO_ENTIRE_MASTER_TABLES_PKEY,
3199 i18n("Could not update row because it does not contain entire master table's primary key."));
3200 return false;
3201 }
3202 if (!pkey->fields()->isEmpty()) {
3203 uint i=0;
3204 for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) {
3205 if (!sqlwhere.isEmpty())
3206 sqlwhere+=" AND ";
3207 TQVariant val = data[ pkeyFieldsOrder[i] ];
3208 if (val.isNull() || !val.isValid()) {
3209 setError(ERR_UPDATE_NULL_PKEY_FIELD,
3210 i18n("Primary key's field \"%1\" cannot be empty.").arg(it.current()->name()));
3211 //js todo: pass the field's name somewhere!
3212 return false;
3213 }
3214 sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" +
3215 m_driver->valueToSQL( it.current(), val ) );
3216 }
3217 }
3218 }
3219 else {//use ROWID
3220 sqlwhere = ( escapeIdentifier(m_driver->beh->ROW_ID_FIELD_NAME) + "="
3221 + m_driver->valueToSQL(Field::BigInteger, data[data.size()-1]));
3222 }
3223 m_sql += (sqlset + " WHERE " + sqlwhere);
3224 KexiDBDbg << " -- SQL == " << ((m_sql.length() > 400) ? (m_sql.left(400)+"[.....]") : m_sql) << endl;
3225
3226 if (!executeSQL(m_sql)) {
3227 setError(ERR_UPDATE_SERVER_ERROR, i18n("Row updating on the server failed."));
3228 return false;
3229 }
3230
3231 if (m_driver->beh->ROW_ID_FIELD_NAME == "xmin")
3232 data[data.size()-1]=drv_lastInsertRowID();
3233
3234 //success: now also assign new values in memory:
3235 TQMap<QueryColumnInfo*,int> columnsOrderExpanded;
3236 updateRowDataWithNewValues(query, data, b, columnsOrderExpanded);
3237 return true;
3238}
3239
3240bool Connection::insertRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool getROWID)
3241{
3242// Each SQL identifier needs to be escaped in the generated query.
3243 KexiDBDbg << "Connection::updateRow.." << endl;
3244 clearError();
3245 //--get PKEY
3246 /*disabled: there may be empty rows (with autoinc)
3247 if (buf.dbBuffer().isEmpty()) {
3248 KexiDBDbg << " -- NO CHANGES DATA!" << endl;
3249 return true; }*/
3250 TableSchema *mt = query.masterTable();
3251 if (!mt) {
3252 KexiDBWarn << " -- NO MASTER TABLE!" << endl;
3253 setError(ERR_INSERT_NO_MASTER_TABLE,
3254 i18n("Could not insert row because there is no master table defined."));
3255 return false;
3256 }
3257 IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
3258 if (!getROWID && !pkey)
3259 KexiDBWarn << " -- WARNING: NO MASTER TABLE's PKEY" << endl;
3260
3261 TQString sqlcols, sqlvals;
3262 sqlcols.reserve(1024);
3263 sqlvals.reserve(1024);
3264
3265 //insert the record:
3266 m_sql = "INSERT INTO " + escapeIdentifier(mt->name()) + " (";
3267 KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer();
3268
3269 // add default values, if available (for any column without value explicitly set)
3270 const QueryColumnInfo::Vector fieldsExpanded( query.fieldsExpanded( QuerySchema::Unique ) );
3271 for (uint i=0; i<fieldsExpanded.count(); i++) {
3272 QueryColumnInfo *ci = fieldsExpanded.at(i);
3273 if (ci->field && KexiDB::isDefaultValueAllowed(ci->field)
3274 && !ci->field->defaultValue().isNull()
3275 && !b.contains( ci ))
3276 {
3277 KexiDBDbg << "Connection::insertRow(): adding default value '" << ci->field->defaultValue().toString()
3278 << "' for column '" << ci->field->name() << "'" << endl;
3279 b.insert( ci, ci->field->defaultValue() );
3280 }
3281 }
3282
3283 if (b.isEmpty()) {
3284 // empty row inserting requested:
3285 if (!getROWID && !pkey) {
3286 KexiDBWarn << "MASTER TABLE's PKEY REQUIRED FOR INSERTING EMPTY ROWS: INSERT CANCELLED" << endl;
3287 setError(ERR_INSERT_NO_MASTER_TABLES_PKEY,
3288 i18n("Could not insert row because master table has no primary key defined."));
3289 return false;
3290 }
3291 if (pkey) {
3292 const TQValueVector<int> pkeyFieldsOrder( query.pkeyFieldsOrder() );
3293// KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
3294 if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
3295 KexiDBWarn << "NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
3296 setError(ERR_INSERT_NO_ENTIRE_MASTER_TABLES_PKEY,
3297 i18n("Could not insert row because it does not contain entire master table's primary key.")
3298 .arg(query.name()));
3299 return false;
3300 }
3301 }
3302 //at least one value is needed for VALUES section: find it and set to NULL:
3303 Field *anyField = mt->anyNonPKField();
3304 if (!anyField) {
3305 if (!pkey) {
3306 KexiDBWarn << "WARNING: NO FIELD AVAILABLE TO SET IT TO NULL" << endl;
3307 return false;
3308 }
3309 else {
3310 //try to set NULL in pkey field (could not work for every SQL engine!)
3311 anyField = pkey->fields()->first();
3312 }
3313 }
3314 sqlcols += escapeIdentifier(anyField->name());
3315 sqlvals += m_driver->valueToSQL(anyField,TQVariant()/*NULL*/);
3316 }
3317 else {
3318 // non-empty row inserting requested:
3319 for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
3320 if (it.key()->field->table()!=mt)
3321 continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
3322 if (!sqlcols.isEmpty()) {
3323 sqlcols+=",";
3324 sqlvals+=",";
3325 }
3326 sqlcols += escapeIdentifier(it.key()->field->name());
3327 sqlvals += m_driver->valueToSQL(it.key()->field,it.data());
3328 }
3329 }
3330 m_sql += (sqlcols + ") VALUES (" + sqlvals + ")");
3331// KexiDBDbg << " -- SQL == " << m_sql << endl;
3332
3333 bool res = executeSQL(m_sql);
3334
3335 if (!res) {
3336 setError(ERR_INSERT_SERVER_ERROR, i18n("Row inserting on the server failed."));
3337 return false;
3338 }
3339 //success: now also assign a new value in memory:
3340 TQMap<QueryColumnInfo*,int> columnsOrderExpanded;
3341 updateRowDataWithNewValues(query, data, b, columnsOrderExpanded);
3342
3343 //fetch autoincremented values
3344 QueryColumnInfo::List *aif_list = query.autoIncrementFields();
3345 TQ_ULLONG ROWID = 0;
3346 if (pkey && !aif_list->isEmpty()) {
3348 QueryColumnInfo *id_columnInfo = aif_list->first();
3350 TQ_ULLONG last_id = lastInsertedAutoIncValue(
3351 id_columnInfo->field->name(), id_columnInfo->field->table()->name(), &ROWID);
3352 if (last_id==(TQ_ULLONG)-1 || last_id<=0) {
3355 return false;
3356 }
3357 RowData aif_data;
3358 TQString getAutoIncForInsertedValue = TQString::fromLatin1("SELECT ")
3359 + query.autoIncrementSQLFieldsList(m_driver)
3360 + TQString::fromLatin1(" FROM ")
3361 + escapeIdentifier(id_columnInfo->field->table()->name())
3362 + TQString::fromLatin1(" WHERE ")
3363 + escapeIdentifier(id_columnInfo->field->name()) + "="
3364 + TQString::number(last_id);
3365 if (true!=querySingleRecord(getAutoIncForInsertedValue, aif_data)) {
3367 return false;
3368 }
3369 QueryColumnInfo::ListIterator ci_it(*aif_list);
3370 QueryColumnInfo *ci;
3371 for (uint i=0; (ci = ci_it.current()); ++ci_it, i++) {
3372// KexiDBDbg << "Connection::insertRow(): AUTOINCREMENTED FIELD " << fi->field->name() << " == "
3373// << aif_data[i].toInt() << endl;
3374 ( data[ columnsOrderExpanded[ ci ] ] = aif_data[i] ).cast( ci->field->variantType() ); //cast to get proper type
3375 }
3376 }
3377 else {
3378 ROWID = drv_lastInsertRowID();
3379// KexiDBDbg << "Connection::insertRow(): new ROWID == " << (uint)ROWID << endl;
3380 if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
3381 KexiDBWarn << "Connection::insertRow(): m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE" << endl;
3382 return false;
3383 }
3384 }
3385 if (getROWID && /*sanity check*/data.size() > fieldsExpanded.size()) {
3386// KexiDBDbg << "Connection::insertRow(): new ROWID == " << (uint)ROWID << endl;
3387 data[data.size()-1] = ROWID;
3388 }
3389 return true;
3390}
3391
3392bool Connection::deleteRow(QuerySchema &query, RowData& data, bool useROWID)
3393{
3394// Each SQL identifier needs to be escaped in the generated query.
3395 KexiDBWarn << "Connection::deleteRow.." << endl;
3396 clearError();
3397 TableSchema *mt = query.masterTable();
3398 if (!mt) {
3399 KexiDBWarn << " -- NO MASTER TABLE!" << endl;
3400 setError(ERR_DELETE_NO_MASTER_TABLE,
3401 i18n("Could not delete row because there is no master table defined.")
3402 .arg(query.name()));
3403 return false;
3404 }
3405 IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
3406
3408 if (!useROWID && !pkey) {
3409 KexiDBWarn << " -- WARNING: NO MASTER TABLE's PKEY" << endl;
3410 setError(ERR_DELETE_NO_MASTER_TABLES_PKEY,
3411 i18n("Could not delete row because there is no primary key for master table defined."));
3412 return false;
3413 }
3414
3415 //update the record:
3416 m_sql = "DELETE FROM " + escapeIdentifier(mt->name()) + " WHERE ";
3417 TQString sqlwhere;
3418 sqlwhere.reserve(1024);
3419
3420 if (pkey) {
3421 const TQValueVector<int> pkeyFieldsOrder( query.pkeyFieldsOrder() );
3422 KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
3423 if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
3424 KexiDBWarn << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
3425 setError(ERR_DELETE_NO_ENTIRE_MASTER_TABLES_PKEY,
3426 i18n("Could not delete row because it does not contain entire master table's primary key."));
3427 return false;
3428 }
3429 uint i=0;
3430 for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) {
3431 if (!sqlwhere.isEmpty())
3432 sqlwhere+=" AND ";
3433 TQVariant val = data[ pkeyFieldsOrder[i] ];
3434 if (val.isNull() || !val.isValid()) {
3435 setError(ERR_DELETE_NULL_PKEY_FIELD, i18n("Primary key's field \"%1\" cannot be empty.")
3436 .arg(it.current()->name()));
3437//js todo: pass the field's name somewhere!
3438 return false;
3439 }
3440 sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" +
3441 m_driver->valueToSQL( it.current(), val ) );
3442 }
3443 }
3444 else {//use ROWID
3445 sqlwhere = ( escapeIdentifier(m_driver->beh->ROW_ID_FIELD_NAME) + "="
3446 + m_driver->valueToSQL(Field::BigInteger, data[data.size()-1]));
3447 }
3448 m_sql += sqlwhere;
3449 KexiDBDbg << " -- SQL == " << m_sql << endl;
3450
3451 if (!executeSQL(m_sql)) {
3452 setError(ERR_DELETE_SERVER_ERROR, i18n("Row deletion on the server failed."));
3453 return false;
3454 }
3455 return true;
3456}
3457
3459{
3460 clearError();
3461 TableSchema *mt = query.masterTable();
3462 if (!mt) {
3463 KexiDBWarn << " -- NO MASTER TABLE!" << endl;
3464 return false;
3465 }
3466 IndexSchema *pkey = mt->primaryKey();
3467 if (!pkey || pkey->fields()->isEmpty())
3468 KexiDBWarn << "Connection::deleteAllRows -- WARNING: NO MASTER TABLE's PKEY" << endl;
3469
3470 m_sql = "DELETE FROM " + escapeIdentifier(mt->name());
3471
3472 KexiDBDbg << " -- SQL == " << m_sql << endl;
3473
3474 if (!executeSQL(m_sql)) {
3475 setError(ERR_DELETE_SERVER_ERROR, i18n("Row deletion on the server failed."));
3476 return false;
3477 }
3478 return true;
3479}
3480
3481void Connection::registerForTableSchemaChanges(TableSchemaChangeListenerInterface& listener,
3482 TableSchema &schema)
3483{
3484 TQPtrList<TableSchemaChangeListenerInterface>* listeners = d->tableSchemaChangeListeners[&schema];
3485 if (!listeners) {
3486 listeners = new TQPtrList<TableSchemaChangeListenerInterface>();
3487 d->tableSchemaChangeListeners.insert(&schema, listeners);
3488 }
3489//TODO: inefficient
3490 if (listeners->findRef( &listener )==-1)
3491 listeners->append( &listener );
3492}
3493
3494void Connection::unregisterForTableSchemaChanges(TableSchemaChangeListenerInterface& listener,
3495 TableSchema &schema)
3496{
3497 TQPtrList<TableSchemaChangeListenerInterface>* listeners = d->tableSchemaChangeListeners[&schema];
3498 if (!listeners)
3499 return;
3500//TODO: inefficient
3501 listeners->remove( &listener );
3502}
3503
3504void Connection::unregisterForTablesSchemaChanges(TableSchemaChangeListenerInterface& listener)
3505{
3506 for (TQPtrDictIterator< TQPtrList<TableSchemaChangeListenerInterface> > it(d->tableSchemaChangeListeners);
3507 it.current(); ++it)
3508 {
3509 if (-1!=it.current()->find(&listener))
3510 it.current()->take();
3511 }
3512}
3513
3514TQPtrList<Connection::TableSchemaChangeListenerInterface>*
3515Connection::tableSchemaChangeListeners(TableSchema& tableSchema) const
3516{
3517 KexiDBDbg << d->tableSchemaChangeListeners.count() << endl;
3518 return d->tableSchemaChangeListeners[&tableSchema];
3519}
3520
3521tristate Connection::closeAllTableSchemaChangeListeners(TableSchema& tableSchema)
3522{
3523 TQPtrList<Connection::TableSchemaChangeListenerInterface> *listeners = d->tableSchemaChangeListeners[&tableSchema];
3524 if (!listeners)
3525 return true;
3526 TQPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> tmpListeners(*listeners); //safer copy
3527 tristate res = true;
3528 //try to close every window
3529 for (TQPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> it(tmpListeners);
3530 it.current() && res==true; ++it)
3531 {
3532 res = it.current()->closeListener();
3533 }
3534 return res;
3535}
3536
3537/*PreparedStatement::Ptr Connection::prepareStatement(PreparedStatement::StatementType,
3538 TableSchema&)
3539{
3540 //safe?
3541 return 0;
3542}*/
3543
3544void Connection::setReadOnly(bool set)
3545{
3546 if (d->isConnected)
3547 return; //sanity
3548 d->readOnly = set;
3549}
3550
3552{
3553 return d->readOnly;
3554}
3555
3556#include "connection.moc"
virtual TQString toString(QuerySchemaParameterValueListIterator *params=0)=0
Database specific connection data, e.g. host, port.
options used in selectStatement()
Definition connection.h:672
int identifierEscaping
A mode for escaping identifier, Driver::EscapeDriver|Driver::EscapeAsNecessary by default.
Definition connection.h:678
bool alsoRetrieveROWID
True if ROWID should be also retrieved. False by default.
Definition connection.h:681
Provides database connection, allowing queries and data modification.
Definition connection.h:62
TQValueList< int > queryIds()
TQStringList objectNames(int objType=KexiDB::AnyObjectType, bool *ok=0)
virtual bool drv_executeSQL(const TQString &statement)=0
TQString escapeIdentifier(const TQString &id, int escaping=Driver::EscapeDriver|Driver::EscapeAsNecessary) const
Identifier escaping function in the associated Driver.
TQ_ULLONG lastInsertedAutoIncValue(const TQString &aiFieldName, const TQString &tableName, TQ_ULLONG *ROWID=0)
bool commitTransaction(Transaction trans=Transaction::null, bool ignore_inactive=false)
bool beginAutoCommitTransaction(TransactionGuard &tg)
DatabaseProperties & databaseProperties()
TQString createTableStatement(const TableSchema &tableSchema) const
bool deleteAllRows(QuerySchema &query)
TableSchema * tableSchema(int tableId)
bool autoCommit() const
bool deleteCursor(Cursor *cursor)
bool rollbackTransaction(Transaction trans=Transaction::null, bool ignore_inactive=false)
bool removeObject(uint objId)
KexiDB::DatabaseVersionInfo * databaseVersion() const
bool rollbackAutoCommitTransaction(const Transaction &trans)
bool executeSQL(const TQString &statement)
const TQValueList< Transaction > & transactions()
virtual bool drv_createTable(const TableSchema &tableSchema)
tristate querySingleRecordInternal(RowData &data, const TQString *sql, QuerySchema *query, bool addLimitTo1=true)
friend class KexiDB::DatabaseProperties
for setError()
bool createTable(TableSchema *tableSchema, bool replaceExisting=false)
Creates a table according to the given schema.
bool isDatabaseUsed() const
TableSchema * setupTableSchema(const RowData &data)
TQValueList< int > tableIds()
tristate loadDataBlock(int objectID, TQString &dataString, const TQString &dataID)
TQValueList< int > objectIds(int objType)
bool useDatabase(const TQString &dbName, bool kexiCompatible=true, bool *cancelled=0, MessageHandler *msgHandler=0)
Opens an existing database specified by dbName.
bool closeDatabase()
Closes currently used database for this connection.
TQPtrDict< KexiDB::Cursor > m_cursors
cursors created for this connection
static const TQStringList & kexiDBSystemTableNames()
bool createDatabase(const TQString &dbName)
Creates new database with name dbName, using this connection.
bool loadExtendedTableSchemaData(TableSchema &tableSchema)
void setAvailableDatabaseName(const TQString &dbName)
bool queryStringList(const TQString &sql, TQStringList &list, uint column=0)
virtual TQ_ULLONG drv_lastInsertRowID()=0
virtual bool drv_isDatabaseUsed() const
Definition connection.h:934
virtual Cursor * prepareQuery(const TQString &statement, uint cursor_options=0)=0
KexiDB::ServerVersionInfo * serverVersion() const
virtual bool drv_createDatabase(const TQString &dbName=TQString())=0
TableSchema * newKexiDBSystemTableSchema(const TQString &tsname)
virtual bool drv_getDatabasesList(TQStringList &list)
bool storeExtendedTableSchemaData(TableSchema &tableSchema)
bool insertRow(QuerySchema &query, RowData &data, RowEditBuffer &buf, bool getROWID=false)
bool isEmpty(TableSchema &table, bool &success)
void registerForTableSchemaChanges(TableSchemaChangeListenerInterface &listener, TableSchema &schema)
Register listener for receiving (listening) information about changes in TableSchema object.
Field * findSystemFieldName(FieldList *fieldlist)
virtual bool drv_connect(KexiDB::ServerVersionInfo &version)=0
virtual bool drv_commitTransaction(TransactionData *trans)
bool setQuerySchemaObsolete(const TQString &queryName)
virtual TQString anyAvailableDatabaseName()
virtual bool drv_disconnect()=0
bool storeObjectSchemaData(SchemaData &sdata, bool newObject)
TQString selectStatement(QuerySchema &querySchema, const TQValueList< TQVariant > &params, const SelectStatementOptions &options=SelectStatementOptions()) const
tristate querySingleString(const TQString &sql, TQString &value, uint column=0, bool addLimitTo1=true)
QuerySchema * querySchema(int queryId)
void removeMe(TableSchema *ts)
tristate loadObjectSchemaData(int objectID, SchemaData &sdata)
TQString currentDatabase() const
Get the name of the current database.
virtual bool isReadOnly() const
bool updateRow(QuerySchema &query, RowData &data, RowEditBuffer &buf, bool useROWID=false)
virtual bool drv_dropDatabase(const TQString &dbName=TQString())=0
bool dropDatabase(const TQString &dbName=TQString())
Drops database with name dbName.
Cursor * executeQuery(const TQString &statement, uint cursor_options=0)
void setDefaultTransaction(const Transaction &trans)
virtual bool drv_setAutoCommit(bool on)
virtual bool drv_closeDatabase()=0
bool removeDataBlock(int objectID, const TQString &dataID=TQString())
bool alterTableName(TableSchema &tableSchema, const TQString &newName, bool replace=false)
bool disconnect()
Disconnects from driver with given parameters.
bool deleteRow(QuerySchema &query, RowData &data, bool useROWID=false)
bool databaseExists(const TQString &dbName, bool ignoreErrors=true)
virtual bool drv_containsTable(const TQString &tableName)=0
bool commitAutoCommitTransaction(const Transaction &trans)
bool setupObjectSchemaData(const RowData &data, SchemaData &sdata)
virtual void clearError()
virtual TransactionData * drv_beginTransaction()
bool isConnected() const
Transaction & defaultTransaction() const
tristate querySingleNumber(const TQString &sql, int &number, uint column=0, bool addLimitTo1=true)
bool resultExists(const TQString &sql, bool &success, bool addLimitTo1=true)
TQStringList tableNames(bool also_system_tables=false)
bool setAutoCommit(bool on)
virtual bool drv_rollbackTransaction(TransactionData *trans)
bool useTemporaryDatabaseIfNeeded(TQString &tmpdbName)
bool dropQuery(QuerySchema *querySchema)
Driver * driver() const
Definition connection.h:78
bool setupKexiDBSystemSchema()
Creates kexi__* tables.
tristate querySingleRecord(const TQString &sql, RowData &data, bool addLimitTo1=true)
Transaction beginTransaction()
Creates new transaction handle and starts a new transaction.
KexiDB::Field * setupField(const RowData &data)
TQStringList databaseNames(bool also_system_db=false)
virtual bool drv_useDatabase(const TQString &dbName=TQString(), bool *cancelled=0, MessageHandler *msgHandler=0)=0
tristate dropTable(TableSchema *tableSchema)
Drops a table corresponding to the name in the given schema.
virtual bool drv_alterTableName(TableSchema &tableSchema, const TQString &newName)
virtual bool drv_databaseExists(const TQString &dbName, bool ignoreErrors=true)
QuerySchema * setupQuerySchema(const RowData &data)
friend class KexiDB::TableSchema
for removeMe()
Connection(Driver *driver, ConnectionData &conn_data)
ConnectionData * data() const
virtual bool drv_dropTable(const TQString &name)
bool storeDataBlock(int objectID, const TQString &dataString, const TQString &dataID=TQString())
bool connect()
Connects to driver with given parameters.
tristate alterTable(TableSchema &tableSchema, TableSchema &newTableSchema)
Provides database cursor functionality.
Definition cursor.h:67
void setQueryParameters(const TQValueList< TQVariant > &params)
Sets query parameters params for this cursor.
Definition cursor.cpp:563
virtual bool moveNext()
Definition cursor.cpp:292
virtual bool close()
Definition cursor.cpp:178
virtual TQVariant value(uint i)=0
virtual void storeCurrentRow(RowData &data) const =0
bool eof() const
Definition cursor.cpp:339
uint fieldCount() const
Definition cursor.h:165
Connection * connection() const
Definition cursor.h:81
bool moveFirst()
Definition cursor.cpp:205
A set of storable database properties.
Contains database version information about a Kexi-compatible database. The version is stored as inte...
Definition global.h:109
Generic database abstraction.
Definition driver.h:67
bool transactionsSupported() const
Definition driver.cpp:155
virtual TQString valueToSQL(uint ftype, const TQVariant &v) const
Definition driver.cpp:250
@ IgnoreTransactions
Definition driver.h:111
@ SingleTransactions
single trasactions are only supported
Definition driver.h:90
@ MultipleTransactions
multiple concurrent trasactions are supported (this implies !SingleTransactions)
Definition driver.h:93
virtual TQString escapeString(const TQString &str) const =0
bool isFileDriver() const
Definition driver.cpp:149
bool isSystemFieldName(const TQString &n) const
Definition driver.cpp:243
virtual bool isSystemObjectName(const TQString &n) const
Definition driver.cpp:230
virtual bool isSystemDatabaseName(const TQString &n) const =0
virtual TQString sqlTypeName(int id_t, int p=0) const
Definition driver.cpp:170
FieldList * subList(const TQString &n1, const TQString &n2=TQString(), const TQString &n3=TQString(), const TQString &n4=TQString(), const TQString &n5=TQString(), const TQString &n6=TQString(), const TQString &n7=TQString(), const TQString &n8=TQString(), const TQString &n9=TQString(), const TQString &n10=TQString(), const TQString &n11=TQString(), const TQString &n12=TQString(), const TQString &n13=TQString(), const TQString &n14=TQString(), const TQString &n15=TQString(), const TQString &n16=TQString(), const TQString &n17=TQString(), const TQString &n18=TQString())
FieldList & addField(Field *field)
uint fieldCount() const
Definition fieldlist.h:58
Field * field(uint id)
Definition fieldlist.h:76
TQString sqlFieldsList(Driver *driver, const TQString &separator=TQString::fromLatin1(","), const TQString &tableAlias=TQString(), int drvEscaping=Driver::EscapeDriver|Driver::EscapeAsNecessary)
Meta-data for a field.
Definition field.h:73
bool isPrimaryKey() const
Definition field.h:234
KexiDB::BaseExpr * expression()
Definition field.h:521
TQPair< Field *, Field * > Pair
fields pair
Definition field.h:78
bool isNotNull() const
Definition field.h:243
Type type() const
Definition field.cpp:134
void setVisibleDecimalPlaces(int p)
Definition field.cpp:381
TQVariant::Type variantType() const
Converts field's type to TQVariant equivalent as accurate as possible.
Definition field.h:291
const CustomPropertiesMap customProperties() const
Definition field.h:559
static TQVariant::Type variantType(uint type)
Converts type type to TQVariant equivalent as accurate as possible.
Definition field.cpp:141
void setDefaultValue(const TQVariant &def)
Definition field.cpp:396
TQPtrList< Field > List
list of fields
Definition field.h:75
bool isFPNumericType() const
Definition field.h:266
bool isAutoIncrement() const
Definition field.h:231
TQPtrListIterator< Field > ListIterator
iterator for list of fields
Definition field.h:77
void setCustomProperty(const TQCString &propertyName, const TQVariant &value)
Sets value value for custom property propertyName.
Definition field.cpp:655
virtual bool isQueryAsterisk() const
Definition field.h:510
bool isExpression() const
Definition field.h:537
uint scale() const
Definition field.h:343
int visibleDecimalPlaces() const
Definition field.h:361
TQMap< TQCString, TQVariant > CustomPropertiesMap
A data type used for handling custom properties of a field.
Definition field.h:556
virtual TableSchema * table() const
Definition field.cpp:300
uint precision() const
Definition field.h:334
TQVariant defaultValue() const
Definition field.h:326
bool isUnsigned() const
if the type has the unsigned attribute
Definition field.h:384
bool isUniqueKey() const
Definition field.h:237
uint length() const
Definition field.h:330
Provides information about database index that can be created for a database table.
Definition indexschema.h:44
Row source information that can be specified for the lookup field schema.
@ Table
table as lookup row source
@ Query
named query as lookup row source
Provides information about lookup field's setup.
static LookupFieldSchema * loadFromDom(const TQDomElement &lookupEl)
static void saveToDom(LookupFieldSchema &lookupSchema, TQDomDocument &doc, TQDomElement &parentEl)
TQValueList< uint > visibleColumns() const
bool error() const
Definition object.h:38
int errorNum() const
Definition object.h:46
TQString m_sql
used to store of actually executed SQL statement
Definition object.h:161
virtual void clearError()
Definition object.cpp:131
virtual void setError(int code=ERR_OTHER, const TQString &msg=TQString())
Definition object.cpp:53
const TQString & errorMsg() const
Definition object.h:42
KexiDB::OrderByColumnList provides list of sorted columns for a query schema.
void appendColumn(QueryColumnInfo &columnInfo, bool ascending=true)
TQString toSQLString(bool includeTableNames=true, Driver *drv=0, int identifierEscaping=Driver::EscapeDriver|Driver::EscapeAsNecessary) const
Parser for SQL statements.
Definition parser.h:113
KexiDB::QueryAsterisk class encapsulates information about single asterisk in query definition.
bool isSingleTableAsterisk() const
Helper class that assigns additional information for the column in a query.
Definition queryschema.h:49
TQCString aliasOrName() const
Definition queryschema.h:59
An iteratof for a list of values of query schema parameters providing Allows to iterate over paramete...
KexiDB::QuerySchema provides information about database query.
TableSchema * masterTable() const
TQCString columnAlias(uint position) const
Relationship::List * relationships() const
Connection * connection() const
TQString autoIncrementSQLFieldsList(Driver *driver)
OrderByColumnList & orderByColumnList() const
TableSchema * table(const TQString &tableName) const
@ Unique
Unique list of fields is returned.
TQMap< QueryColumnInfo *, int > columnsOrder(ColumnsOrderOptions options=ExpandedList)
QueryColumnInfo::Vector fieldsExpanded(FieldsExpandedOptions options=Default)
TQValueVector< int > pkeyFieldsOrder()
bool isColumnVisible(uint position) const
@ ExpandedList
A map for expanded list is created.
QueryColumnInfo::List * autoIncrementFields()
int tableBoundToColumn(uint columnPosition) const
TableSchema::List * tables() const
TQCString tableAlias(uint position) const
TQString statement() const
BaseExpr * whereExpression() const
provides data for single edited database row KexiDB::RowEditBuffer provides data for single edited ro...
Contains version information about a database backend.
Definition global.h:126
QuerySchema * query()
IndexSchema * primaryKey() const
Definition tableschema.h:76
TQPtrListIterator< TableSchema > ListIterator
Iterator for tables list.
Definition tableschema.h:46
TQPtrList< TableSchema > List
Type of tables list.
Definition tableschema.h:45
void setKexiDBSystem(bool set)
LookupFieldSchema * lookupFieldSchema(const Field &field) const
bool setLookupFieldSchema(const TQString &fieldName, LookupFieldSchema *lookupFieldSchema)
Helper class for using inside methods for given connection.
const Transaction transaction() const
void setTransaction(const Transaction &trans)
This class encapsulates transaction handle.
Definition transaction.h:60
static const Transaction null
Definition transaction.h:85
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition tristate.h:100
High-level database connectivity library with database backend drivers.
KEXI_DB_EXPORT bool isDefaultValueAllowed(Field *field)
KEXI_DB_EXPORT TQVariant stringToVariant(const TQString &s, TQVariant::Type type, bool &ok)
KEXI_DB_EXPORT bool supportsVisibleDecimalPlacesProperty(Field::Type type)
KEXI_DB_EXPORT TQVariant loadPropertyValueFromDom(const TQDomNode &node)
KEXI_DB_EXPORT DatabaseVersionInfo version()
Definition global.cpp:55
TQValueVector< TQVariant > RowData
structure for storing single record with type information
Definition connection.h:47
KEXI_DB_EXPORT TQString sqlWhere(Driver *drv, Field::Type t, const TQString fieldName, const TQVariant value)
KEXI_DB_EXPORT bool deleteRow(Connection &conn, TableSchema *table, const TQString &keyname, const TQString &keyval)
for convenience
KEXI_DB_EXPORT int loadIntPropertyValueFromDom(const TQDomNode &node, bool *ok)
KEXI_DB_EXPORT TQString variantToString(const TQVariant &v)
@ AnyObjectType
helper
Definition global.h:149
type * findParent(TQObject *o, const char *className)
KEXIUTILS_EXPORT bool isIdentifier(const TQString &s)