kexi

connection.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this program; see the file COPYING.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <kexidb/connection.h>
00021 
00022 #include "error.h"
00023 #include "connection_p.h"
00024 #include "connectiondata.h"
00025 #include "driver.h"
00026 #include "driver_p.h"
00027 #include "schemadata.h"
00028 #include "tableschema.h"
00029 #include "relationship.h"
00030 #include "transaction.h"
00031 #include "cursor.h"
00032 #include "global.h"
00033 #include "roweditbuffer.h"
00034 #include "utils.h"
00035 #include "dbproperties.h"
00036 #include "lookupfieldschema.h"
00037 #include "parser/parser.h"
00038 
00039 #include <kexiutils/utils.h>
00040 #include <kexiutils/identifier.h>
00041 
00042 #include <qdir.h>
00043 #include <qfileinfo.h>
00044 #include <qguardedptr.h>
00045 #include <qdom.h>
00046 
00047 #include <klocale.h>
00048 #include <kdebug.h>
00049 
00050 #define KEXIDB_EXTENDED_TABLE_SCHEMA_VERSION 1
00051 
00052 //#define KEXIDB_LOOKUP_FIELD_TEST
00053 
00054 namespace KexiDB {
00055 
00056 Connection::SelectStatementOptions::SelectStatementOptions()
00057  : identifierEscaping(Driver::EscapeDriver|Driver::EscapeAsNecessary)
00058  , alsoRetrieveROWID(false)
00059 {
00060 }
00061 
00062 Connection::SelectStatementOptions::~SelectStatementOptions()
00063 {
00064 }
00065 
00066 //================================================
00067 
00068 ConnectionInternal::ConnectionInternal(Connection *conn)
00069  : connection(conn)
00070 {
00071 }
00072 
00073 ConnectionInternal::~ConnectionInternal()
00074 {
00075 }
00076 
00077 //================================================
00079 class ConnectionPrivate
00080 {
00081     public:
00082         ConnectionPrivate(Connection* const conn, ConnectionData &conn_data)
00083          : conn(conn)
00084          , conn_data(&conn_data)
00085          , tableSchemaChangeListeners(101)
00086          , m_parser(0)
00087          , tables_byname(101, false)
00088          , queries_byname(101, false)
00089          , kexiDBSystemTables(101)
00090          , dont_remove_transactions(false)
00091          , skip_databaseExists_check_in_useDatabase(false)
00092          , default_trans_started_inside(false)
00093          , isConnected(false)
00094          , autoCommit(true)
00095         {
00096             tableSchemaChangeListeners.setAutoDelete(true);
00097             obsoleteQueries.setAutoDelete(true);
00098 
00099             tables.setAutoDelete(true);
00100             tables_byname.setAutoDelete(false);//tables is owner, not me
00101             kexiDBSystemTables.setAutoDelete(true);//only system tables
00102             queries.setAutoDelete(true);
00103             queries_byname.setAutoDelete(false);//queries is owner, not me
00104 
00105             //reasonable sizes: TODO
00106             tables.resize(101);
00107             queries.resize(101);
00108         }
00109         ~ConnectionPrivate()
00110         {
00111             delete m_parser;
00112         }
00113 
00114         void errorInvalidDBContents(const QString& details) {
00115             conn->setError( ERR_INVALID_DATABASE_CONTENTS, i18n("Invalid database contents. ")+details);
00116         }
00117 
00118         QString strItIsASystemObject() const {
00119             return i18n("It is a system object.");
00120         }
00121 
00122         inline Parser *parser() { return m_parser ? m_parser : (m_parser = new Parser(conn)); }
00123 
00124         Connection* const conn; 
00125         QGuardedPtr<ConnectionData> conn_data; 
00126 
00131         Transaction default_trans;
00132         QValueList<Transaction> transactions;
00133 
00134         QPtrDict< QPtrList<Connection::TableSchemaChangeListenerInterface> > tableSchemaChangeListeners;
00135 
00138         QPtrList<QuerySchema> obsoleteQueries;
00139 
00140 
00142         KexiDB::ServerVersionInfo serverVersion;
00143 
00145         KexiDB::DatabaseVersionInfo databaseVersion;
00146 
00147         Parser *m_parser;
00148 
00150         QIntDict<TableSchema> tables;
00151         QDict<TableSchema> tables_byname;
00152         QIntDict<QuerySchema> queries;
00153         QDict<QuerySchema> queries_byname;
00154 
00156         QPtrDict<TableSchema> kexiDBSystemTables;
00157 
00159         DatabaseProperties* dbProperties;
00160 
00161         QString availableDatabaseName; 
00162         QString usedDatabase; 
00163 
00166         bool dont_remove_transactions : 1;
00167 
00170         bool skip_databaseExists_check_in_useDatabase : 1;
00171 
00180         bool default_trans_started_inside : 1;
00181 
00182         bool isConnected : 1;
00183 
00184         bool autoCommit : 1;
00185 
00187         bool readOnly : 1;
00188 };
00189 
00190 }//namespace KexiDB
00191 
00192 //================================================
00193 using namespace KexiDB;
00194 
00196 QStringList KexiDB_kexiDBSystemTableNames;
00197 
00198 Connection::Connection( Driver *driver, ConnectionData &conn_data )
00199     : QObject()
00200     ,KexiDB::Object()
00201     ,d(new ConnectionPrivate(this, conn_data))
00202     ,m_driver(driver)
00203     ,m_destructor_started(false)
00204 {
00205     d->dbProperties = new DatabaseProperties(this);
00206     m_cursors.setAutoDelete(true);
00207 //  d->transactions.setAutoDelete(true);
00208     //reasonable sizes: TODO
00209     m_cursors.resize(101);
00210 //  d->transactions.resize(101);//woohoo! so many transactions?
00211     m_sql.reserve(0x4000);
00212 }
00213 
00214 void Connection::destroy()
00215 {
00216     disconnect();
00217     //do not allow the driver to touch me: I will kill myself.
00218     m_driver->d->connections.take( this );
00219 }
00220 
00221 Connection::~Connection()
00222 {
00223     m_destructor_started = true;
00224 //  KexiDBDbg << "Connection::~Connection()" << endl;
00225     delete d->dbProperties;
00226     delete d;
00227     d = 0;
00228 /*  if (m_driver) {
00229         if (m_is_connected) {
00230             //delete own table schemas
00231             d->tables.clear();
00232             //delete own cursors:
00233             m_cursors.clear();
00234         }
00235         //do not allow the driver to touch me: I will kill myself.
00236         m_driver->m_connections.take( this );
00237     }*/
00238 }
00239 
00240 ConnectionData* Connection::data() const
00241 {
00242     return d->conn_data;
00243 }
00244 
00245 bool Connection::connect()
00246 {
00247     clearError();
00248     if (d->isConnected) {
00249         setError(ERR_ALREADY_CONNECTED, i18n("Connection already established.") );
00250         return false;
00251     }
00252 
00253     d->serverVersion.clear();
00254     if (!(d->isConnected = drv_connect(d->serverVersion))) {
00255         setError(m_driver->isFileDriver() ?
00256             i18n("Could not open \"%1\" project file.").arg(QDir::convertSeparators(d->conn_data->fileName()))
00257             : i18n("Could not connect to \"%1\" database server.").arg(d->conn_data->serverInfoString()) );
00258     }
00259     return d->isConnected;
00260 }
00261 
00262 bool Connection::isDatabaseUsed() const
00263 {
00264     return !d->usedDatabase.isEmpty() && d->isConnected && drv_isDatabaseUsed();
00265 }
00266 
00267 void Connection::clearError()
00268 {
00269     Object::clearError();
00270     m_sql = QString::null;
00271 }
00272 
00273 bool Connection::disconnect()
00274 {
00275     clearError();
00276     if (!d->isConnected)
00277         return true;
00278 
00279     if (!closeDatabase())
00280         return false;
00281 
00282     bool ok = drv_disconnect();
00283     if (ok)
00284         d->isConnected = false;
00285     return ok;
00286 }
00287 
00288 bool Connection::isConnected() const
00289 {
00290     return d->isConnected;
00291 }
00292 
00293 bool Connection::checkConnected()
00294 {
00295     if (d->isConnected) {
00296         clearError();
00297         return true;
00298     }
00299     setError(ERR_NO_CONNECTION, i18n("Not connected to the database server.") );
00300     return false;
00301 }
00302 
00303 bool Connection::checkIsDatabaseUsed()
00304 {
00305     if (isDatabaseUsed()) {
00306         clearError();
00307         return true;
00308     }
00309     setError(ERR_NO_DB_USED, i18n("Currently no database is used.") );
00310     return false;
00311 }
00312 
00313 QStringList Connection::databaseNames(bool also_system_db)
00314 {
00315     KexiDBDbg << "Connection::databaseNames("<<also_system_db<<")"<< endl;
00316     if (!checkConnected())
00317         return QStringList();
00318 
00319     QString tmpdbName;
00320     //some engines need to have opened any database before executing "create database"
00321     if (!useTemporaryDatabaseIfNeeded(tmpdbName))
00322         return QStringList();
00323 
00324     QStringList list, non_system_list;
00325 
00326     bool ret = drv_getDatabasesList( list );
00327 
00328     if (!tmpdbName.isEmpty()) {
00329         //whatever result is - now we have to close temporary opened database:
00330         if (!closeDatabase())
00331             return QStringList();
00332     }
00333 
00334     if (!ret)
00335         return QStringList();
00336 
00337     if (also_system_db)
00338         return list;
00339     //filter system databases:
00340     for (QStringList::ConstIterator it = list.constBegin(); it!=list.constEnd(); ++it) {
00341         KexiDBDbg << "Connection::databaseNames(): " << *it << endl;
00342         if (!m_driver->isSystemDatabaseName(*it)) {
00343             KexiDBDbg << "add " << *it << endl;
00344             non_system_list << (*it);
00345         }
00346     }
00347     return non_system_list;
00348 }
00349 
00350 bool Connection::drv_getDatabasesList( QStringList &list )
00351 {
00352     list.clear();
00353     return true;
00354 }
00355 
00356 bool Connection::drv_databaseExists( const QString &dbName, bool ignoreErrors )
00357 {
00358     QStringList list = databaseNames(true);//also system
00359     if (error()) {
00360         return false;
00361     }
00362 
00363     if (list.find( dbName )==list.end()) {
00364         if (!ignoreErrors)
00365             setError(ERR_OBJECT_NOT_FOUND, i18n("The database \"%1\" does not exist.").arg(dbName));
00366         return false;
00367     }
00368 
00369     return true;
00370 }
00371 
00372 bool Connection::databaseExists( const QString &dbName, bool ignoreErrors )
00373 {
00374 //  KexiDBDbg << "Connection::databaseExists(" << dbName << "," << ignoreErrors << ")" << endl;
00375     if (!checkConnected())
00376         return false;
00377     clearError();
00378 
00379     if (m_driver->isFileDriver()) {
00380         //for file-based db: file must exists and be accessible
00381 //js: moved from useDatabase():
00382         QFileInfo file(d->conn_data->fileName());
00383         if (!file.exists() || ( !file.isFile() && !file.isSymLink()) ) {
00384             if (!ignoreErrors)
00385                 setError(ERR_OBJECT_NOT_FOUND, i18n("Database file \"%1\" does not exist.")
00386                 .arg(QDir::convertSeparators(d->conn_data->fileName())) );
00387             return false;
00388         }
00389         if (!file.isReadable()) {
00390             if (!ignoreErrors)
00391                 setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not readable.")
00392                 .arg(QDir::convertSeparators(d->conn_data->fileName())) );
00393             return false;
00394         }
00395         if (!file.isWritable()) {
00396             if (!ignoreErrors)
00397                 setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not writable.")
00398                 .arg(QDir::convertSeparators(d->conn_data->fileName())) );
00399             return false;
00400         }
00401         return true;
00402     }
00403 
00404     QString tmpdbName;
00405     //some engines need to have opened any database before executing "create database"
00406     const bool orig_skip_databaseExists_check_in_useDatabase = d->skip_databaseExists_check_in_useDatabase;
00407     d->skip_databaseExists_check_in_useDatabase = true;
00408     bool ret = useTemporaryDatabaseIfNeeded(tmpdbName);
00409     d->skip_databaseExists_check_in_useDatabase = orig_skip_databaseExists_check_in_useDatabase;
00410     if (!ret)
00411         return false;
00412 
00413     ret = drv_databaseExists(dbName, ignoreErrors);
00414 
00415     if (!tmpdbName.isEmpty()) {
00416         //whatever result is - now we have to close temporary opened database:
00417         if (!closeDatabase())
00418             return false;
00419     }
00420 
00421     return ret;
00422 }
00423 
00424 #define createDatabase_CLOSE \
00425     { if (!closeDatabase()) { \
00426         setError(i18n("Database \"%1\" created but could not be closed after creation.").arg(dbName) ); \
00427         return false; \
00428     } }
00429 
00430 #define createDatabase_ERROR \
00431     { createDatabase_CLOSE; return false; }
00432 
00433 
00434 bool Connection::createDatabase( const QString &dbName )
00435 {
00436     if (!checkConnected())
00437         return false;
00438 
00439     if (databaseExists( dbName )) {
00440         setError(ERR_OBJECT_EXISTS, i18n("Database \"%1\" already exists.").arg(dbName) );
00441         return false;
00442     }
00443     if (m_driver->isSystemDatabaseName( dbName )) {
00444         setError(ERR_SYSTEM_NAME_RESERVED, 
00445             i18n("Cannot create database \"%1\". This name is reserved for system database.").arg(dbName) );
00446         return false;
00447     }
00448     if (m_driver->isFileDriver()) {
00449         //update connection data if filename differs
00450         d->conn_data->setFileName( dbName );
00451     }
00452 
00453     QString tmpdbName;
00454     //some engines need to have opened any database before executing "create database"
00455     if (!useTemporaryDatabaseIfNeeded(tmpdbName))
00456         return false;
00457 
00458     //low-level create
00459     if (!drv_createDatabase( dbName )) {
00460         setError(i18n("Error creating database \"%1\" on the server.").arg(dbName) );
00461         closeDatabase();//sanity
00462         return false;
00463     }
00464 
00465     if (!tmpdbName.isEmpty()) {
00466         //whatever result is - now we have to close temporary opened database:
00467         if (!closeDatabase())
00468             return false;
00469     }
00470 
00471     if (!tmpdbName.isEmpty() || !m_driver->d->isDBOpenedAfterCreate) {
00472         //db need to be opened
00473         if (!useDatabase( dbName, false/*not yet kexi compatible!*/ )) {
00474             setError(i18n("Database \"%1\" created but could not be opened.").arg(dbName) );
00475             return false;
00476         }
00477     }
00478     else {
00479         //just for the rule
00480         d->usedDatabase = dbName;
00481     }
00482 
00483     Transaction trans;
00484     if (m_driver->transactionsSupported()) {
00485         trans = beginTransaction();
00486         if (!trans.active())
00487             return false;
00488     }
00489 //not needed since closeDatabase() rollbacks transaction: TransactionGuard trans_g(this);
00490 //  if (error())
00491 //      return false;
00492 
00493     //-create system tables schema objects
00494     if (!setupKexiDBSystemSchema())
00495         return false;
00496 
00497     //-physically create system tables
00498     for (QPtrDictIterator<TableSchema> it(d->kexiDBSystemTables); it.current(); ++it) {
00499         if (!drv_createTable( it.current()->name() ))
00500             createDatabase_ERROR;
00501     }
00502 
00503 /* moved to KexiProject...
00504 
00505     //-create default part info
00506     TableSchema *ts;
00507     if (!(ts = tableSchema("kexi__parts")))
00508         createDatabase_ERROR;
00509     FieldList *fl = ts->subList("p_id", "p_name", "p_mime", "p_url");
00510     if (!fl)
00511         createDatabase_ERROR;
00512     if (!insertRecord(*fl, QVariant(1), QVariant("Tables"), QVariant("kexi/table"), QVariant("http://koffice.org/kexi/")))
00513         createDatabase_ERROR;
00514     if (!insertRecord(*fl, QVariant(2), QVariant("Queries"), QVariant("kexi/query"), QVariant("http://koffice.org/kexi/")))
00515         createDatabase_ERROR;
00516 */
00517 
00518     //-insert KexiDB version info:
00519     TableSchema *t_db = tableSchema("kexi__db");
00520     if (!t_db)
00521         createDatabase_ERROR;
00522     if ( !insertRecord(*t_db, "kexidb_major_ver", KexiDB::version().major)
00523         || !insertRecord(*t_db, "kexidb_minor_ver", KexiDB::version().minor))
00524         createDatabase_ERROR;
00525 
00526     if (trans.active() && !commitTransaction(trans))
00527         createDatabase_ERROR;
00528 
00529     createDatabase_CLOSE;
00530     return true;
00531 }
00532 
00533 #undef createDatabase_CLOSE
00534 #undef createDatabase_ERROR
00535 
00536 bool Connection::useDatabase( const QString &dbName, bool kexiCompatible, bool *cancelled, MessageHandler* msgHandler )
00537 {
00538     if (cancelled)
00539         *cancelled = false;
00540     KexiDBDbg << "Connection::useDatabase(" << dbName << "," << kexiCompatible <<")" << endl;
00541     if (!checkConnected())
00542         return false;
00543 
00544     if (dbName.isEmpty())
00545         return false;
00546     QString my_dbName = dbName;
00547 //  if (my_dbName.isEmpty()) {
00548 //      const QStringList& db_lst = databaseNames();
00549 //      if (!db_lst.isEmpty())
00550 //          my_dbName = db_lst.first();
00551 //  }
00552     if (d->usedDatabase == my_dbName)
00553         return true; //already used
00554 
00555     if (!d->skip_databaseExists_check_in_useDatabase) {
00556         if (!databaseExists(my_dbName, false /*don't ignore errors*/))
00557             return false; //database must exist
00558     }
00559 
00560     if (!d->usedDatabase.isEmpty() && !closeDatabase()) //close db if already used
00561         return false;
00562 
00563     d->usedDatabase = "";
00564 
00565     if (!drv_useDatabase( my_dbName, cancelled, msgHandler )) {
00566         if (cancelled && *cancelled)
00567             return false;
00568         QString msg(i18n("Opening database \"%1\" failed.").arg( my_dbName ));
00569         if (error())
00570             setError( this, msg );
00571         else
00572             setError( msg );
00573         return false;
00574     }
00575 
00576     //-create system tables schema objects
00577     if (!setupKexiDBSystemSchema())
00578         return false;
00579 
00580     if (kexiCompatible && my_dbName.lower()!=anyAvailableDatabaseName().lower()) {
00581         //-get global database information
00582         int num;
00583         bool ok;
00584 //      static QString notfound_str = i18n("\"%1\" database property not found");
00585         num = d->dbProperties->value("kexidb_major_ver").toInt(&ok);
00586         if (!ok)
00587             return false;
00588         d->databaseVersion.major = num;
00589 /*      if (true!=querySingleNumber(
00590             "select db_value from kexi__db where db_property=" + m_driver->escapeString(QString("kexidb_major_ver")), num)) {
00591             d->errorInvalidDBContents(notfound_str.arg("kexidb_major_ver"));
00592             return false;
00593         }*/
00594         num = d->dbProperties->value("kexidb_minor_ver").toInt(&ok);
00595         if (!ok)
00596             return false;
00597         d->databaseVersion.minor = num;
00598 /*      if (true!=querySingleNumber(
00599             "select db_value from kexi__db where db_property=" + m_driver->escapeString(QString("kexidb_minor_ver")), num)) {
00600             d->errorInvalidDBContents(notfound_str.arg("kexidb_minor_ver"));
00601             return false;
00602         }*/
00603 
00604 #if 0 //this is already checked in DriverManagerInternal::lookupDrivers()
00605         //** error if major version does not match
00606         if (m_driver->versionMajor()!=KexiDB::versionMajor()) {
00607             setError(ERR_INCOMPAT_DATABASE_VERSION,
00608                 i18n("Database version (%1) does not match Kexi application's version (%2)")
00609                 .arg( QString("%1.%2").arg(versionMajor()).arg(versionMinor()) )
00610                 .arg( QString("%1.%2").arg(KexiDB::versionMajor()).arg(KexiDB::versionMinor()) ) );
00611             return false;
00612         }
00613         if (m_driver->versionMinor()!=KexiDB::versionMinor()) {
00614             //js TODO: COMPATIBILITY CODE HERE!
00615             //js TODO: CONVERSION CODE HERE (or signal that conversion is needed)
00616         }
00617 #endif
00618     }
00619     d->usedDatabase = my_dbName;
00620     return true;
00621 }
00622 
00623 bool Connection::closeDatabase()
00624 {
00625     if (d->usedDatabase.isEmpty())
00626         return true; //no db used
00627     if (!checkConnected())
00628         return true;
00629 
00630     bool ret = true;
00631 
00633     if (m_driver->transactionsSupported()) {
00634         //rollback all transactions
00635         QValueList<Transaction>::ConstIterator it;
00636         d->dont_remove_transactions=true; //lock!
00637         for (it=d->transactions.constBegin(); it!= d->transactions.constEnd(); ++it) {
00638             if (!rollbackTransaction(*it)) {//rollback as much as you can, don't stop on prev. errors
00639                 ret = false;
00640             }
00641             else {
00642                 KexiDBDbg << "Connection::closeDatabase(): transaction rolled back!" << endl;
00643                 KexiDBDbg << "Connection::closeDatabase(): trans.refcount==" <<
00644                  ((*it).m_data ? QString::number((*it).m_data->refcount) : "(null)") << endl;
00645             }
00646         }
00647         d->dont_remove_transactions=false; //unlock!
00648         d->transactions.clear(); //free trans. data
00649     }
00650 
00651     //delete own cursors:
00652     m_cursors.clear();
00653     //delete own schemas
00654     d->tables.clear();
00655     d->kexiDBSystemTables.clear();
00656     d->queries.clear();
00657 
00658     if (!drv_closeDatabase())
00659         return false;
00660 
00661     d->usedDatabase = "";
00662 //  KexiDBDbg << "Connection::closeDatabase(): " << ret << endl;
00663     return ret;
00664 }
00665 
00666 QString Connection::currentDatabase() const
00667 {
00668     return d->usedDatabase;
00669 }
00670 
00671 bool Connection::useTemporaryDatabaseIfNeeded(QString &tmpdbName)
00672 {
00673     if (!m_driver->isFileDriver() && m_driver->beh->USING_DATABASE_REQUIRED_TO_CONNECT
00674      && !isDatabaseUsed()) {
00675         //we have no db used, but it is required by engine to have used any!
00676         tmpdbName = anyAvailableDatabaseName();
00677         if (tmpdbName.isEmpty()) {
00678             setError(ERR_NO_DB_USED, i18n("Cannot find any database for temporary connection.") );
00679             return false;
00680         }
00681         const bool orig_skip_databaseExists_check_in_useDatabase = d->skip_databaseExists_check_in_useDatabase;
00682         d->skip_databaseExists_check_in_useDatabase = true;
00683         bool ret = useDatabase(tmpdbName, false);
00684         d->skip_databaseExists_check_in_useDatabase = orig_skip_databaseExists_check_in_useDatabase;
00685         if (!ret) {
00686             setError(errorNum(), 
00687                 i18n("Error during starting temporary connection using \"%1\" database name.")
00688                 .arg(tmpdbName) );
00689             return false;
00690         }
00691     }
00692     return true;
00693 }
00694 
00695 bool Connection::dropDatabase( const QString &dbName )
00696 {
00697     if (!checkConnected())
00698         return false;
00699 
00700     QString dbToDrop;
00701     if (dbName.isEmpty() && d->usedDatabase.isEmpty()) {
00702         if (!m_driver->isFileDriver()
00703          || (m_driver->isFileDriver() && d->conn_data->fileName().isEmpty()) ) {
00704             setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot drop database - name not specified.") );
00705             return false;
00706         }
00707         //this is a file driver so reuse previously passed filename
00708         dbToDrop = d->conn_data->fileName();
00709     }
00710     else {
00711         if (dbName.isEmpty()) {
00712             dbToDrop = d->usedDatabase;
00713         } else {
00714             if (m_driver->isFileDriver()) //lets get full path
00715                 dbToDrop = QFileInfo(dbName).absFilePath();
00716             else
00717                 dbToDrop = dbName;
00718         }
00719     }
00720 
00721     if (dbToDrop.isEmpty()) {
00722         setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot delete database - name not specified.") );
00723         return false;
00724     }
00725 
00726     if (m_driver->isSystemDatabaseName( dbToDrop )) {
00727         setError(ERR_SYSTEM_NAME_RESERVED, i18n("Cannot delete system database \"%1\".").arg(dbToDrop) );
00728         return false;
00729     }
00730 
00731     if (isDatabaseUsed() && d->usedDatabase == dbToDrop) {
00732         //we need to close database because cannot drop used this database
00733         if (!closeDatabase())
00734             return false;
00735     }
00736 
00737     QString tmpdbName;
00738     //some engines need to have opened any database before executing "drop database"
00739     if (!useTemporaryDatabaseIfNeeded(tmpdbName))
00740         return false;
00741 
00742     //ok, now we have access to dropping
00743     bool ret = drv_dropDatabase( dbToDrop );
00744 
00745     if (!tmpdbName.isEmpty()) {
00746         //whatever result is - now we have to close temporary opened database:
00747         if (!closeDatabase())
00748             return false;
00749     }
00750     return ret;
00751 }
00752 
00753 QStringList Connection::objectNames(int objType, bool* ok)
00754 {
00755     QStringList list;
00756 
00757     if (!checkIsDatabaseUsed()) {
00758         if(ok) 
00759             *ok = false;
00760         return list;
00761     }
00762 
00763     QString sql;
00764     if (objType==KexiDB::AnyObjectType)
00765         sql = "SELECT o_name FROM kexi__objects";
00766     else
00767         sql = QString::fromLatin1("SELECT o_name FROM kexi__objects WHERE o_type=%1").arg(objType);
00768 
00769     Cursor *c = executeQuery(sql);
00770     if (!c) {
00771         if(ok) 
00772             *ok = false;
00773         return list;
00774     }
00775 
00776     for (c->moveFirst(); !c->eof(); c->moveNext()) {
00777         QString name = c->value(0).toString();
00778         if (KexiUtils::isIdentifier( name )) {
00779             list.append(name);
00780         }
00781     }
00782 
00783     if (!deleteCursor(c)) {
00784         if(ok)
00785             *ok = false;
00786         return list;
00787     }
00788 
00789     if(ok)
00790         *ok = true;
00791     return list;
00792 }
00793 
00794 QStringList Connection::tableNames(bool also_system_tables)
00795 {
00796     bool ok = true;
00797     QStringList list = objectNames(TableObjectType, &ok);
00798     if (also_system_tables && ok) {
00799         list += Connection::kexiDBSystemTableNames();
00800     }
00801     return list;
00802 }
00803 
00805 const QStringList& Connection::kexiDBSystemTableNames()
00806 {
00807     if (KexiDB_kexiDBSystemTableNames.isEmpty()) {
00808         KexiDB_kexiDBSystemTableNames
00809         << "kexi__objects"
00810         << "kexi__objectdata"
00811         << "kexi__fields"
00812 //      << "kexi__querydata"
00813 //      << "kexi__queryfields"
00814 //      << "kexi__querytables"
00815         << "kexi__db"
00816         ;
00817     }
00818     return KexiDB_kexiDBSystemTableNames;
00819 }
00820 
00821 KexiDB::ServerVersionInfo* Connection::serverVersion() const
00822 {
00823     return isConnected() ? &d->serverVersion : 0;
00824 }
00825 
00826 KexiDB::DatabaseVersionInfo* Connection::databaseVersion() const
00827 {
00828     return isDatabaseUsed() ? &d->databaseVersion : 0;
00829 }
00830 
00831 DatabaseProperties& Connection::databaseProperties()
00832 {
00833     return *d->dbProperties;
00834 }
00835 
00836 QValueList<int> Connection::tableIds()
00837 {
00838     return objectIds(KexiDB::TableObjectType);
00839 }
00840 
00841 QValueList<int> Connection::queryIds()
00842 {
00843     return objectIds(KexiDB::QueryObjectType);
00844 }
00845 
00846 QValueList<int> Connection::objectIds(int objType)
00847 {
00848     QValueList<int> list;
00849 
00850     if (!checkIsDatabaseUsed())
00851         return list;
00852 
00853     Cursor *c = executeQuery(
00854         QString::fromLatin1("SELECT o_id, o_name FROM kexi__objects WHERE o_type=%1").arg(objType));
00855     if (!c)
00856         return list;
00857     for (c->moveFirst(); !c->eof(); c->moveNext())
00858     {
00859         QString tname = c->value(1).toString(); //kexi__objects.o_name
00860         if (KexiUtils::isIdentifier( tname )) {
00861             list.append(c->value(0).toInt()); //kexi__objects.o_id
00862         }
00863     }
00864 
00865     deleteCursor(c);
00866 
00867     return list;
00868 }
00869 
00870 QString Connection::createTableStatement( const KexiDB::TableSchema& tableSchema ) const
00871 {
00872 // Each SQL identifier needs to be escaped in the generated query.
00873     QString sql;
00874     sql.reserve(4096);
00875     sql = "CREATE TABLE " + escapeIdentifier(tableSchema.name()) + " (";
00876     bool first=true;
00877     Field::ListIterator it( tableSchema.m_fields );
00878     Field *field;
00879     for (;(field = it.current())!=0; ++it) {
00880         if (first)
00881             first = false;
00882         else
00883             sql += ", ";
00884         QString v = escapeIdentifier(field->name()) + " ";
00885         const bool autoinc = field->isAutoIncrement();
00886         const bool pk = field->isPrimaryKey() || (autoinc && m_driver->beh->AUTO_INCREMENT_REQUIRES_PK);
00887 //TODO: warning: ^^^^^ this allows only ont autonumber per table when AUTO_INCREMENT_REQUIRES_PK==true!
00888         if (autoinc && m_driver->beh->SPECIAL_AUTO_INCREMENT_DEF) {
00889             if (pk)
00890                 v += m_driver->beh->AUTO_INCREMENT_TYPE + " " + m_driver->beh->AUTO_INCREMENT_PK_FIELD_OPTION;
00891             else
00892                 v += m_driver->beh->AUTO_INCREMENT_TYPE + " " + m_driver->beh->AUTO_INCREMENT_FIELD_OPTION;
00893         }
00894         else {
00895             if (autoinc && !m_driver->beh->AUTO_INCREMENT_TYPE.isEmpty())
00896                 v += m_driver->beh->AUTO_INCREMENT_TYPE;
00897             else
00898                 v += m_driver->sqlTypeName(field->type(), field->precision());
00899 
00900             if (field->isUnsigned())
00901                 v += (" " + m_driver->beh->UNSIGNED_TYPE_KEYWORD);
00902 
00903             if (field->isFPNumericType() && field->precision()>0) {
00904                 if (field->scale()>0)
00905                     v += QString::fromLatin1("(%1,%2)").arg(field->precision()).arg(field->scale());
00906                 else
00907                     v += QString::fromLatin1("(%1)").arg(field->precision());
00908             }
00909             else if (field->type()==Field::Text && field->length()>0)
00910                 v += QString::fromLatin1("(%1)").arg(field->length());
00911 
00912             if (autoinc)
00913                 v += (" " +
00914                 (pk ? m_driver->beh->AUTO_INCREMENT_PK_FIELD_OPTION : m_driver->beh->AUTO_INCREMENT_FIELD_OPTION));
00915             else
00916     //TODO: here is automatically a single-field key created
00917                 if (pk)
00918                     v += " PRIMARY KEY";
00919             if (!pk && field->isUniqueKey())
00920                 v += " UNIQUE";
00922             if (!autoinc && !pk && field->isNotNull())
00923                 v += " NOT NULL"; //only add not null option if no autocommit is set
00924             if (field->defaultValue().isValid())
00925                 v += QString::fromLatin1(" DEFAULT ") + m_driver->valueToSQL( field, field->defaultValue() );
00926         }
00927         sql += v;
00928     }
00929     sql += ")";
00930     return sql;
00931 }
00932 
00933 //yeah, it is very efficient:
00934 #define C_A(a) , const QVariant& c ## a
00935 
00936 #define V_A0 m_driver->valueToSQL( tableSchema.field(0), c0 )
00937 #define V_A(a) +","+m_driver->valueToSQL( \
00938     tableSchema.field(a) ? tableSchema.field(a)->type() : Field::Text, c ## a )
00939 
00940 //      KexiDBDbg << "******** " << QString("INSERT INTO ") + 
00941 //          escapeIdentifier(tableSchema.name()) + 
00942 //          " VALUES (" + vals + ")" <<endl; 
00943 
00944 #define C_INS_REC(args, vals) \
00945     bool Connection::insertRecord(KexiDB::TableSchema &tableSchema args) {\
00946         return executeSQL( \
00947          QString("INSERT INTO ") + escapeIdentifier(tableSchema.name()) + " VALUES (" + vals + ")" \
00948         ); \
00949     }
00950 
00951 #define C_INS_REC_ALL \
00952 C_INS_REC( C_A(0), V_A0 ) \
00953 C_INS_REC( C_A(0) C_A(1), V_A0 V_A(1) ) \
00954 C_INS_REC( C_A(0) C_A(1) C_A(2), V_A0 V_A(1) V_A(2) ) \
00955 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3), V_A0 V_A(1) V_A(2) V_A(3) ) \
00956 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) ) \
00957 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) ) \
00958 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5) C_A(6), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) V_A(6) ) \
00959 C_INS_REC( C_A(0) C_A(1) C_A(2) C_A(3) C_A(4) C_A(5) C_A(6) C_A(7), V_A0 V_A(1) V_A(2) V_A(3) V_A(4) V_A(5) V_A(6) V_A(7) )
00960 
00961 C_INS_REC_ALL
00962 
00963 #undef V_A0
00964 #undef V_A
00965 #undef C_INS_REC
00966 
00967 #define V_A0 value += m_driver->valueToSQL( flist->first(), c0 );
00968 #define V_A( a ) value += ("," + m_driver->valueToSQL( flist->next(), c ## a ));
00969 //#define V_ALAST( a ) valueToSQL( flist->last(), c ## a )
00970 
00971 
00972 #define C_INS_REC(args, vals) \
00973     bool Connection::insertRecord(FieldList& fields args) \
00974     { \
00975         QString value; \
00976         Field::List *flist = fields.fields(); \
00977         vals \
00978         return executeSQL( \
00979             QString("INSERT INTO ") + \
00980         ((fields.fields()->first() && fields.fields()->first()->table()) ? \
00981             escapeIdentifier(fields.fields()->first()->table()->name()) : \
00982             "??") \
00983         + "(" + fields.sqlFieldsList(m_driver) + ") VALUES (" + value + ")" \
00984         ); \
00985     }
00986 
00987 C_INS_REC_ALL
00988 
00989 #undef C_A
00990 #undef V_A
00991 #undef V_ALAST
00992 #undef C_INS_REC
00993 #undef C_INS_REC_ALL
00994 
00995 bool Connection::insertRecord(TableSchema &tableSchema, QValueList<QVariant>& values)
00996 {
00997 // Each SQL identifier needs to be escaped in the generated query.
00998     Field::List *fields = tableSchema.fields();
00999     Field *f = fields->first();
01000 //  QString s_val;
01001 //  s_val.reserve(4096);
01002     m_sql = QString::null;
01003     QValueList<QVariant>::ConstIterator it = values.constBegin();
01004 //  int i=0;
01005     while (f && (it!=values.end())) {
01006         if (m_sql.isEmpty())
01007             m_sql = QString("INSERT INTO ") +
01008                 escapeIdentifier(tableSchema.name()) +
01009                 " VALUES (";
01010         else
01011             m_sql += ",";
01012         m_sql += m_driver->valueToSQL( f, *it );
01013 //      KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl;
01014         ++it;
01015         f=fields->next();
01016     }
01017     m_sql += ")";
01018 
01019 //  KexiDBDbg<<"******** "<< m_sql << endl;
01020     return executeSQL(m_sql);
01021 }
01022 
01023 bool Connection::insertRecord(FieldList& fields, QValueList<QVariant>& values)
01024 {
01025 // Each SQL identifier needs to be escaped in the generated query.
01026     Field::List *flist = fields.fields();
01027     Field *f = flist->first();
01028     if (!f)
01029         return false;
01030 //  QString s_val;
01031 //  s_val.reserve(4096);
01032     m_sql = QString::null;
01033     QValueList<QVariant>::ConstIterator it = values.constBegin();
01034 //  int i=0;
01035     while (f && (it!=values.constEnd())) {
01036         if (m_sql.isEmpty())
01037             m_sql = QString("INSERT INTO ") +
01038                 escapeIdentifier(flist->first()->table()->name()) + "(" +
01039                 fields.sqlFieldsList(m_driver) + ") VALUES (";
01040         else
01041             m_sql += ",";
01042         m_sql += m_driver->valueToSQL( f, *it );
01043 //      KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl;
01044         ++it;
01045         f=flist->next();
01046     }
01047     m_sql += ")";
01048 
01049     return executeSQL(m_sql);
01050 }
01051 
01052 bool Connection::executeSQL( const QString& statement )
01053 {
01054     m_sql = statement; //remember for error handling
01055     if (!drv_executeSQL( m_sql )) {
01056         m_errMsg = QString::null; //clear as this could be most probably jsut "Unknown error" string.
01057         m_errorSql = statement;
01058         setError(this, ERR_SQL_EXECUTION_ERROR, i18n("Error while executing SQL statement."));
01059         return false;
01060     }
01061     return true;
01062 }
01063 
01064 QString Connection::selectStatement( KexiDB::QuerySchema& querySchema,
01065     const QValueList<QVariant>& params, 
01066     const SelectStatementOptions& options) const
01067 {
01068 //"SELECT FROM ..." is theoretically allowed "
01069 //if (querySchema.fieldCount()<1)
01070 //      return QString::null;
01071 // Each SQL identifier needs to be escaped in the generated query.
01072 
01073     if (!querySchema.statement().isEmpty())
01074         return querySchema.statement();
01075 
01078     Field *f;
01079     uint number = 0;
01080     bool singleTable = querySchema.tables()->count() <= 1;
01081     if (singleTable) {
01082         //make sure we will have single table:
01083         for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) {
01084             if (querySchema.isColumnVisible(number) && f->table() && f->table()->lookupFieldSchema( *f )) {
01085                 //uups, no, there's at least one left join
01086                 singleTable = false;
01087                 break;
01088             }
01089         }
01090     }
01091 
01092     QString sql; //final sql string
01093 //unused    QString s_from_additional; //additional tables list needed for lookup fields
01094     QString s_additional_joins; //additional joins needed for lookup fields
01095     QString s_additional_fields; //additional fields to append to the fields list
01096     sql.reserve(4096);
01097     number = 0;
01098     for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) {
01099         if (querySchema.isColumnVisible(number)) {
01100             if (!sql.isEmpty())
01101                 sql += QString::fromLatin1(", ");
01102 
01103             if (f->isQueryAsterisk()) {
01104                 if (!singleTable && static_cast<QueryAsterisk*>(f)->isSingleTableAsterisk()) //single-table *
01105                     sql += escapeIdentifier(f->table()->name(), options.identifierEscaping) +
01106                            QString::fromLatin1(".*");
01107                 else //all-tables * (or simplified table.* when there's only one table)
01108                     sql += QString::fromLatin1("*");
01109             }
01110             else {
01111                 if (f->isExpression()) {
01112                     sql += f->expression()->toString();
01113                 }
01114                 else {
01115                     if (!f->table()) //sanity check
01116                         return QString::null;
01117 
01118                     QString tableName;
01119                     int tablePosition = querySchema.tableBoundToColumn(number);
01120                     if (tablePosition>=0)
01121                         tableName = querySchema.tableAlias(tablePosition);
01122                     if (tableName.isEmpty())
01123                         tableName = f->table()->name();
01124 
01125                     if (!singleTable) {
01126                         sql += (escapeIdentifier(tableName, options.identifierEscaping) + ".");
01127                     }
01128                     sql += escapeIdentifier(f->name(), options.identifierEscaping);
01129                 }
01130                 QString aliasString = QString(querySchema.columnAlias(number));
01131                 if (!aliasString.isEmpty())
01132                     sql += (QString::fromLatin1(" AS ") + aliasString);
01134             }
01135             LookupFieldSchema *lookupFieldSchema = f->table() ? f->table()->lookupFieldSchema( *f ) : 0;
01136             if (lookupFieldSchema) {
01137                 // Lookup field schema found
01138                 // Now we also need to fetch "visible" value from the lookup table, not only the value of binding.
01139                 // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken)
01140                 // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField"
01141                 LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource();
01142                 if (rowSource.type()==LookupFieldSchema::RowSource::Table) {
01143                     TableSchema *lookupTable = querySchema.connection()->tableSchema( rowSource.name() );
01144                     Field *visibleField = 0;
01145                     Field *boundField = 0;
01146                     if (lookupTable && lookupFieldSchema->boundColumn()>=0 
01147                         && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
01148                         && (visibleField = lookupTable->field( lookupFieldSchema->visibleColumn()))
01149                         && (boundField = lookupTable->field( lookupFieldSchema->boundColumn() )))
01150                     {
01151                         //add LEFT OUTER JOIN
01152                         if (!s_additional_joins.isEmpty())
01153                             s_additional_joins += QString::fromLatin1(" ");
01154                         s_additional_joins += QString("LEFT OUTER JOIN %1 ON %2.%3=%4.%5")
01155                             .arg(escapeIdentifier(lookupTable->name(), options.identifierEscaping))
01156                             .arg(escapeIdentifier(f->table()->name(), options.identifierEscaping))
01157                             .arg(escapeIdentifier(f->name(), options.identifierEscaping))
01158                             .arg(escapeIdentifier(lookupTable->name(), options.identifierEscaping))
01159                             .arg(escapeIdentifier(boundField->name(), options.identifierEscaping));
01160 
01161                         //add visibleField to the list of SELECTed fields if it is not yes present there
01162                         if (!querySchema.findTableField( visibleField->table()->name()+"."+visibleField->name() )) {
01163                             if (!querySchema.table( visibleField->table()->name() )) {
01164 /* not true
01165                                 //table should be added after FROM
01166                                 if (!s_from_additional.isEmpty())
01167                                     s_from_additional += QString::fromLatin1(", ");
01168                                 s_from_additional += escapeIdentifier(visibleField->table()->name(), options.identifierEscaping);
01169                                 */
01170                             }
01171                             if (!s_additional_fields.isEmpty())
01172                                 s_additional_fields += QString::fromLatin1(", ");
01173                             s_additional_fields += (escapeIdentifier(visibleField->table()->name(), options.identifierEscaping) + "."
01174                                 + escapeIdentifier(visibleField->name(), options.identifierEscaping));
01175                         }
01176                     }
01177                 }
01178             }
01179         }
01180     }
01181 
01182     //add lookup fields
01183     if (!s_additional_fields.isEmpty())
01184         sql += (QString::fromLatin1(", ") + s_additional_fields);
01185 
01186     if (options.alsoRetrieveROWID) { //append rowid column
01187         QString s;
01188         if (!sql.isEmpty())
01189             s = QString::fromLatin1(", ");
01190         if (querySchema.masterTable())
01191             s += (escapeIdentifier(querySchema.masterTable()->name())+".");
01192         s += m_driver->beh->ROW_ID_FIELD_NAME;
01193         sql += s;
01194     }
01195 
01196     sql.prepend("SELECT ");
01197     TableSchema::List* tables = querySchema.tables();
01198     if (tables && !tables->isEmpty()) {
01199         sql += QString::fromLatin1(" FROM ");
01200         QString s_from;
01201         TableSchema *table;
01202         number = 0;
01203         for (TableSchema::ListIterator it(*tables); (table = it.current());
01204             ++it, number++)
01205         {
01206             if (!s_from.isEmpty())
01207                 s_from += QString::fromLatin1(", ");
01208             s_from += escapeIdentifier(table->name(), options.identifierEscaping);
01209             QString aliasString = QString(querySchema.tableAlias(number));
01210             if (!aliasString.isEmpty())
01211                 s_from += (QString::fromLatin1(" AS ") + aliasString);
01212         }
01213 /*unused    if (!s_from_additional.isEmpty()) {//additional tables list needed for lookup fields
01214             if (!s_from.isEmpty())
01215                 s_from += QString::fromLatin1(", ");
01216             s_from += s_from_additional;
01217         }*/
01218         sql += s_from;
01219     }
01220     QString s_where;
01221     s_where.reserve(4096);
01222 
01223     //JOINS
01224     if (!s_additional_joins.isEmpty()) {
01225         sql += QString::fromLatin1(" ") + s_additional_joins + QString::fromLatin1(" ");
01226     }
01227 
01228 //@todo: we're using WHERE for joins now; use INNER/LEFT/RIGHT JOIN later
01229 
01230     //WHERE
01231     Relationship *rel;
01232     bool wasWhere = false; //for later use
01233     for (Relationship::ListIterator it(*querySchema.relationships()); (rel = it.current()); ++it) {
01234         if (s_where.isEmpty()) {
01235             wasWhere = true;
01236         }
01237         else
01238             s_where += QString::fromLatin1(" AND ");
01239         Field::Pair *pair;
01240         QString s_where_sub;
01241         for (QPtrListIterator<Field::Pair> p_it(*rel->fieldPairs()); (pair = p_it.current()); ++p_it) {
01242             if (!s_where_sub.isEmpty())
01243                 s_where_sub += QString::fromLatin1(" AND ");
01244             s_where_sub += (
01245                 escapeIdentifier(pair->first->table()->name(), options.identifierEscaping) +
01246                 QString::fromLatin1(".") +
01247                 escapeIdentifier(pair->first->name(), options.identifierEscaping) +
01248                 QString::fromLatin1(" = ")  +
01249                 escapeIdentifier(pair->second->table()->name(), options.identifierEscaping) +
01250                 QString::fromLatin1(".") +
01251                 escapeIdentifier(pair->second->name(), options.identifierEscaping));
01252         }
01253         if (rel->fieldPairs()->count()>1) {
01254             s_where_sub.prepend("(");
01255             s_where_sub += QString::fromLatin1(")");
01256         }
01257         s_where += s_where_sub;
01258     }
01259     //EXPLICITLY SPECIFIED WHERE EXPRESSION
01260     if (querySchema.whereExpression()) {
01261         QuerySchemaParameterValueListIterator paramValuesIt(*m_driver, params);
01262         QuerySchemaParameterValueListIterator *paramValuesItPtr = params.isEmpty() ? 0 : &paramValuesIt;
01263         if (wasWhere) {
01264 //TODO: () are not always needed
01265             s_where = "(" + s_where + ") AND (" + querySchema.whereExpression()->toString(paramValuesItPtr) + ")";
01266         }
01267         else {
01268             s_where = querySchema.whereExpression()->toString(paramValuesItPtr);
01269         }
01270     }
01271     if (!s_where.isEmpty())
01272         sql += QString::fromLatin1(" WHERE ") + s_where;
01274     //(use wasWhere here)
01275     
01276     // ORDER BY
01277     QString orderByString( querySchema.orderByColumnList().toSQLString(!singleTable/*includeTableName*/) );
01278     if (!orderByString.isEmpty())
01279         sql += (" ORDER BY " + orderByString);
01280     
01281     //KexiDBDbg << sql << endl;
01282     return sql;
01283 }
01284 
01285 QString Connection::selectStatement( KexiDB::TableSchema& tableSchema,
01286     const SelectStatementOptions& options) const
01287 {
01288     return selectStatement( *tableSchema.query(), options );
01289 }
01290 
01291 Field* Connection::findSystemFieldName(KexiDB::FieldList* fieldlist)
01292 {
01293     Field *f = fieldlist->fields()->first();
01294     while (f) {
01295         if (m_driver->isSystemFieldName( f->name() ))
01296             return f;
01297         f = fieldlist->fields()->next();
01298     }
01299     return 0;
01300 }
01301 
01302 Q_ULLONG Connection::lastInsertedAutoIncValue(const QString& aiFieldName, const QString& tableName,
01303     Q_ULLONG* ROWID)
01304 {
01305     Q_ULLONG row_id = drv_lastInsertRowID();
01306     if (ROWID)
01307         *ROWID = row_id;
01308     if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
01309         return row_id;
01310     }
01311     RowData rdata;
01312     if (row_id<=0 || true!=querySingleRecord(
01313         QString::fromLatin1("SELECT ") + tableName + QString::fromLatin1(".") + aiFieldName + QString::fromLatin1(" FROM ") + tableName
01314         + QString::fromLatin1(" WHERE ") + m_driver->beh->ROW_ID_FIELD_NAME + QString::fromLatin1("=") + QString::number(row_id), rdata))
01315     {
01316 //      KexiDBDbg << "Connection::lastInsertedAutoIncValue(): row_id<=0 || true!=querySingleRecord()" << endl;
01317         return (Q_ULLONG)-1; //ULL;
01318     }
01319     return rdata[0].toULongLong();
01320 }
01321 
01322 Q_ULLONG Connection::lastInsertedAutoIncValue(const QString& aiFieldName,
01323     const KexiDB::TableSchema& table, Q_ULLONG* ROWID)
01324 {
01325     return lastInsertedAutoIncValue(aiFieldName,table.name(), ROWID);
01326 }
01327 
01329 static FieldList* createFieldListForKexi__Fields(TableSchema *kexi__fieldsSchema)
01330 {
01331     if (!kexi__fieldsSchema)
01332         return 0;
01333     return kexi__fieldsSchema->subList(
01334         "t_id",
01335         "f_type",
01336         "f_name",
01337         "f_length",
01338         "f_precision",
01339         "f_constraints",
01340         "f_options",
01341         "f_default",
01342         "f_order",
01343         "f_caption",
01344         "f_help"
01345     );
01346 }
01347 
01349 void buildValuesForKexi__Fields(QValueList<QVariant>& vals, Field* f)
01350 {
01351     vals.clear();
01352     vals
01353     << QVariant(f->table()->id())
01354     << QVariant(f->type())
01355     << QVariant(f->name())
01356     << QVariant(f->isFPNumericType() ? f->scale() : f->length())
01357     << QVariant(f->isFPNumericType() ? f->precision() : 0)
01358     << QVariant(f->constraints())
01359     << QVariant(f->options())
01360         // KexiDB::variantToString() is needed here because the value can be of any QVariant type, 
01361         // depending on f->type()
01362     << (f->defaultValue().isNull() 
01363             ? QVariant() : QVariant(KexiDB::variantToString( f->defaultValue() ))) 
01364     << QVariant(f->order())
01365     << QVariant(f->caption())
01366     << QVariant(f->description());
01367 }
01368 
01369 bool Connection::storeMainFieldSchema(Field *field)
01370 {
01371     if (!field || !field->table())
01372         return false;
01373     FieldList *fl = createFieldListForKexi__Fields(d->tables_byname["kexi__fields"]);
01374     if (!fl)
01375         return false;
01376 
01377     QValueList<QVariant> vals;
01378     buildValuesForKexi__Fields(vals, field);
01379     QValueList<QVariant>::ConstIterator valsIt = vals.constBegin();
01380     Field *f;
01381     bool first = true;
01382     QString sql = "UPDATE kexi__fields SET ";
01383     for (Field::ListIterator it( fl->fieldsIterator() ); (f = it.current()); ++it, ++valsIt) {
01384         sql.append( (first ? QString::null : QString(", ")) +
01385             f->name() + "=" + m_driver->valueToSQL( f, *valsIt ) );
01386         if (first)
01387             first = false;
01388     }
01389     delete fl;
01390     
01391     sql.append(QString(" WHERE t_id=") + QString::number( field->table()->id() )
01392         + " AND f_name=" + m_driver->valueToSQL( Field::Text, field->name() ) );
01393     return executeSQL( sql );
01394 }
01395 
01396 #define createTable_ERR \
01397     { KexiDBDbg << "Connection::createTable(): ERROR!" <<endl; \
01398       setError(this, i18n("Creating table failed.")); \
01399       rollbackAutoCommitTransaction(tg.transaction()); \
01400       return false; }
01401         //setError( errorNum(), i18n("Creating table failed.") + " " + errorMsg()); 
01402 
01404 
01411 bool Connection::createTable( KexiDB::TableSchema* tableSchema, bool replaceExisting )
01412 {
01413     if (!tableSchema || !checkIsDatabaseUsed())
01414         return false;
01415 
01416     //check if there are any fields
01417     if (tableSchema->fieldCount()<1) {
01418         clearError();
01419         setError(ERR_CANNOT_CREATE_EMPTY_OBJECT, i18n("Cannot create table without fields."));
01420         return false;
01421     }
01422     const bool internalTable = dynamic_cast<InternalTableSchema*>(tableSchema);
01423 
01424     const QString &tableName = tableSchema->name().lower();
01425 
01426     if (!internalTable) {
01427         if (m_driver->isSystemObjectName( tableName )) {
01428             clearError();
01429             setError(ERR_SYSTEM_NAME_RESERVED, i18n("System name \"%1\" cannot be used as table name.")
01430                 .arg(tableSchema->name()));
01431             return false;
01432         }
01433 
01434         Field *sys_field = findSystemFieldName(tableSchema);
01435         if (sys_field) {
01436             clearError();
01437             setError(ERR_SYSTEM_NAME_RESERVED,
01438                 i18n("System name \"%1\" cannot be used as one of fields in \"%2\" table.")
01439                 .arg(sys_field->name()).arg(tableName));
01440             return false;
01441         }
01442     }
01443 
01444     bool previousSchemaStillKept = false;
01445 
01446     KexiDB::TableSchema *existingTable = 0;
01447     if (replaceExisting) {
01448         //get previous table (do not retrieve, though)
01449         existingTable = d->tables_byname[tableName];
01450         if (existingTable) {
01451             if (existingTable == tableSchema) {
01452                 clearError();
01453                 setError(ERR_OBJECT_EXISTS, 
01454                     i18n("Could not create the same table \"%1\" twice.").arg(tableSchema->name()) );
01455                 return false;
01456             }
01457 //TODO(js): update any structure (e.g. queries) that depend on this table!
01458             if (existingTable->id()>0)
01459                 tableSchema->m_id = existingTable->id(); //copy id from existing table
01460             previousSchemaStillKept = true;
01461             if (!dropTable( existingTable, false /*alsoRemoveSchema*/ ))
01462                 return false;
01463         }
01464     }
01465     else {
01466         if (this->tableSchema( tableSchema->name() ) != 0) {
01467             clearError();
01468             setError(ERR_OBJECT_EXISTS, i18n("Table \"%1\" already exists.").arg(tableSchema->name()) );
01469             return false;
01470         }
01471     }
01472 
01473 /*  if (replaceExisting) {
01474     //get previous table (do not retrieve, though)
01475     KexiDB::TableSchema *existingTable = d->tables_byname.take(name);
01476     if (oldTable) {
01477     }*/
01478 
01479     TransactionGuard tg;
01480     if (!beginAutoCommitTransaction(tg))
01481         return false;
01482 
01483     if (!drv_createTable(*tableSchema))
01484         createTable_ERR;
01485 
01486     //add schema data to kexi__* tables
01487     if (!internalTable) {
01488         //update kexi__objects
01489         if (!storeObjectSchemaData( *tableSchema, true ))
01490             createTable_ERR;
01491 
01492         TableSchema *ts = d->tables_byname["kexi__fields"];
01493         if (!ts)
01494             return false;
01495         //for sanity: remove field info (if any) for this table id
01496         if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id()))
01497             return false;
01498 
01499         FieldList *fl = createFieldListForKexi__Fields(d->tables_byname["kexi__fields"]);
01500         if (!fl)
01501             return false;
01502 
01503 //      int order = 0;
01504         Field *f;
01505         for (Field::ListIterator it( *tableSchema->fields() ); (f = it.current()); ++it/*, order++*/) {
01506             QValueList<QVariant> vals;
01507             buildValuesForKexi__Fields(vals, f);
01508             if (!insertRecord(*fl, vals ))
01509                 createTable_ERR;
01510         }
01511         delete fl;
01512 
01513         if (!storeExtendedTableSchemaData(*tableSchema))
01514             createTable_ERR;
01515     }
01516 
01517     //finally:
01518 /*  if (replaceExisting) {
01519         if (existingTable) {
01520             d->tables.take(existingTable->id());
01521             delete existingTable;
01522         }
01523     }*/
01524 
01525     bool res = commitAutoCommitTransaction(tg.transaction());
01526 
01527     if (res) {
01528         if (internalTable) {
01529             //insert the internal table into structures
01530             insertInternalTableSchema(tableSchema);
01531         }
01532         else {
01533             if (previousSchemaStillKept) {
01534                 //remove previous table schema
01535                 removeTableSchemaInternal(tableSchema);
01536             }
01537             //store one schema object locally:
01538             d->tables.insert(tableSchema->id(), tableSchema);
01539             d->tables_byname.insert(tableSchema->name().lower(), tableSchema);
01540         }
01541         //ok, this table is not created by the connection
01542         tableSchema->m_conn = this;
01543     }
01544     return res;
01545 }
01546 
01547 void Connection::removeTableSchemaInternal(TableSchema *tableSchema)
01548 {
01549     d->tables_byname.remove(tableSchema->name());
01550     d->tables.remove(tableSchema->id());
01551 }
01552 
01553 bool Connection::removeObject( uint objId )
01554 {
01555     clearError();
01556     //remove table schema from kexi__* tables
01557     if (!KexiDB::deleteRow(*this, d->tables_byname["kexi__objects"], "o_id", objId) //schema entry
01558         || !KexiDB::deleteRow(*this, d->tables_byname["kexi__objectdata"], "o_id", objId)) {//data blocks
01559         setError(ERR_DELETE_SERVER_ERROR, i18n("Could not remove object's data."));
01560         return false;
01561     }
01562     return true;
01563 }
01564 
01565 bool Connection::drv_dropTable( const QString& name )
01566 {
01567     m_sql = "DROP TABLE " + escapeIdentifier(name);
01568     return executeSQL(m_sql);
01569 }
01570 
01572 
01578 tristate Connection::dropTable( KexiDB::TableSchema* tableSchema )
01579 {
01580     return dropTable( tableSchema, true );
01581 }
01582 
01583 tristate Connection::dropTable( KexiDB::TableSchema* tableSchema, bool alsoRemoveSchema)
01584 {
01585 // Each SQL identifier needs to be escaped in the generated query.
01586     clearError();
01587     if (!tableSchema)
01588         return false;
01589 
01590     QString errmsg(i18n("Table \"%1\" cannot be removed.\n"));
01591     //be sure that we handle the correct TableSchema object:
01592     if (tableSchema->id() < 0
01593         || this->tableSchema(tableSchema->name())!=tableSchema
01594         || this->tableSchema(tableSchema->id())!=tableSchema)
01595     {
01596         setError(ERR_OBJECT_NOT_FOUND, errmsg.arg(tableSchema->name())
01597             +i18n("Unexpected name or identifier."));
01598         return false;
01599     }
01600 
01601     tristate res = closeAllTableSchemaChangeListeners(*tableSchema);
01602     if (true!=res)
01603         return res;
01604 
01605     //sanity checks:
01606     if (m_driver->isSystemObjectName( tableSchema->name() )) {
01607         setError(ERR_SYSTEM_NAME_RESERVED, errmsg.arg(tableSchema->name()) + d->strItIsASystemObject());
01608         return false;
01609     }
01610 
01611     TransactionGuard tg;
01612     if (!beginAutoCommitTransaction(tg))
01613         return false;
01614 
01615     //for sanity we're checking if this table exists physically
01616     if (drv_containsTable(tableSchema->name())) {
01617         if (!drv_dropTable(tableSchema->name()))
01618             return false;
01619     }
01620 
01621     TableSchema *ts = d->tables_byname["kexi__fields"];
01622     if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id())) //field entries
01623         return false;
01624 
01625     //remove table schema from kexi__objects table
01626     if (!removeObject( tableSchema->id() )) {
01627         return false;
01628     }
01629 
01630     if (alsoRemoveSchema) {
01632         tristate res = removeDataBlock( tableSchema->id(), "extended_schema");
01633         if (!res)
01634             return false;
01635         removeTableSchemaInternal(tableSchema);
01636     }
01637     return commitAutoCommitTransaction(tg.transaction());
01638 }
01639 
01640 tristate Connection::dropTable( const QString& table )
01641 {
01642     clearError();
01643     TableSchema* ts = tableSchema( table );
01644     if (!ts) {
01645         setError(ERR_OBJECT_NOT_FOUND, i18n("Table \"%1\" does not exist.")
01646             .arg(table));
01647         return false;
01648     }
01649     return dropTable(ts);
01650 }
01651 
01652 tristate Connection::alterTable( TableSchema& tableSchema, TableSchema& newTableSchema )
01653 {
01654     clearError();
01655     tristate res = closeAllTableSchemaChangeListeners(tableSchema);
01656     if (true!=res)
01657         return res;
01658 
01659     if (&tableSchema == &newTableSchema) {
01660         setError(ERR_OBJECT_THE_SAME, i18n("Could not alter table \"%1\" using the same table.")
01661             .arg(tableSchema.name()));
01662         return false;
01663     }
01664 //TODO(js): implement real altering
01665 //TODO(js): update any structure (e.g. query) that depend on this table!
01666     bool ok, empty;
01667 #if 0//TODO ucomment:
01668     empty = isEmpty( tableSchema, ok ) && ok;
01669 #else
01670     empty = true;
01671 #endif
01672     if (empty) {
01673         ok = createTable(&newTableSchema, true/*replace*/);
01674     }
01675     return ok;
01676 }
01677 
01678 bool Connection::alterTableName(TableSchema& tableSchema, const QString& newName, bool replace)
01679 {
01680     clearError();
01681     if (&tableSchema!=d->tables[tableSchema.id()]) {
01682         setError(ERR_OBJECT_NOT_FOUND, i18n("Unknown table \"%1\"").arg(tableSchema.name()));
01683         return false;
01684     }
01685     if (newName.isEmpty() || !KexiUtils::isIdentifier(newName)) {
01686         setError(ERR_INVALID_IDENTIFIER, i18n("Invalid table name \"%1\"").arg(newName));
01687         return false;
01688     }
01689     const QString oldTableName = tableSchema.name();
01690     const QString newTableName = newName.lower().stripWhiteSpace();
01691     if (oldTableName.lower().stripWhiteSpace() == newTableName) {
01692         setError(ERR_OBJECT_THE_SAME, i18n("Could rename table \"%1\" using the same name.")
01693             .arg(newTableName));
01694         return false;
01695     }
01696 //TODO: alter table name for server DB backends!
01697 //TODO: what about objects (queries/forms) that use old name?
01698 //TODO
01699     TableSchema *tableToReplace = this->tableSchema( newName );
01700     const bool destTableExists = tableToReplace != 0;
01701     const int origID = destTableExists ? tableToReplace->id() : -1; //will be reused in the new table
01702     if (!replace && destTableExists) {
01703         setError(ERR_OBJECT_EXISTS,
01704             i18n("Could not rename table \"%1\" to \"%2\". Table \"%3\" already exists.")
01705             .arg(tableSchema.name()).arg(newName).arg(newName));
01706         return false;
01707     }
01708 
01709 //helper:
01710 #define alterTableName_ERR \
01711         tableSchema.setName(oldTableName) //restore old name
01712 
01713     TransactionGuard tg;
01714     if (!beginAutoCommitTransaction(tg))
01715         return false;
01716 
01717     // drop the table replaced (with schema)
01718     if (destTableExists) {
01719         if (!replace) {
01720             return false;
01721         }
01722         if (!dropTable( newName )) {
01723             return false;
01724         }
01725 
01726         // the new table owns the previous table's id:
01727         if (!executeSQL(QString::fromLatin1("UPDATE kexi__objects SET o_id=%1 WHERE o_id=%2 AND o_type=%3")
01728             .arg(origID).arg(tableSchema.id()).arg((int)TableObjectType)))
01729         {
01730             return false;
01731         }
01732         if (!executeSQL(QString::fromLatin1("UPDATE kexi__fields SET t_id=%1 WHERE t_id=%2")
01733             .arg(origID).arg(tableSchema.id())))
01734         {
01735             return false;
01736         }
01737         d->tables.take(tableSchema.id());
01738         d->tables.insert(origID, &tableSchema);
01739         //maintain table ID
01740         tableSchema.m_id = origID;
01741     }
01742 
01743     if (!drv_alterTableName(tableSchema, newTableName)) {
01744         alterTableName_ERR;
01745         return false;
01746     }
01747 
01748     // Update kexi__objects
01749     //TODO
01750     if (!executeSQL(QString::fromLatin1("UPDATE kexi__objects SET o_name=%1 WHERE o_id=%2")
01751         .arg(m_driver->escapeString(tableSchema.name())).arg(tableSchema.id())))
01752     {
01753         alterTableName_ERR;
01754         return false;
01755     }
01756 //TODO what about caption?
01757 
01758     //restore old name: it will be changed soon!
01759     tableSchema.setName(oldTableName);
01760 
01761     if (!commitAutoCommitTransaction(tg.transaction())) {
01762         alterTableName_ERR;
01763         return false;
01764     }
01765 
01766     //update tableSchema:
01767     d->tables_byname.take(tableSchema.name());
01768     tableSchema.setName(newTableName);
01769     d->tables_byname.insert(tableSchema.name(), &tableSchema);
01770     return true;
01771 }
01772 
01773 bool Connection::drv_alterTableName(TableSchema& tableSchema, const QString& newName)
01774 {
01775     const QString oldTableName = tableSchema.name();
01776     tableSchema.setName(newName);
01777 
01778     if (!executeSQL(QString::fromLatin1("ALTER TABLE %1 RENAME TO %2")
01779         .arg(escapeIdentifier(oldTableName)).arg(escapeIdentifier(newName))))
01780     {
01781         tableSchema.setName(oldTableName); //restore old name
01782         return false;
01783     }
01784     return true;
01785 }
01786 
01787 bool Connection::dropQuery( KexiDB::QuerySchema* querySchema )
01788 {
01789     clearError();
01790     if (!querySchema)
01791         return false;
01792 
01793     TransactionGuard tg;
01794     if (!beginAutoCommitTransaction(tg))
01795         return false;
01796 
01797 /*  TableSchema *ts = d->tables_byname["kexi__querydata"];
01798     if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
01799         return false;
01800 
01801     ts = d->tables_byname["kexi__queryfields"];
01802     if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
01803         return false;
01804 
01805     ts = d->tables_byname["kexi__querytables"];
01806     if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
01807         return false;*/
01808 
01809     //remove query schema from kexi__objects table
01810     if (!removeObject( querySchema->id() )) {
01811         return false;
01812     }
01813 
01814 //TODO(js): update any structure that depend on this table!
01815     d->queries_byname.remove(querySchema->name());
01816     d->queries.remove(querySchema->id());
01817 
01818     return commitAutoCommitTransaction(tg.transaction());
01819 }
01820 
01821 bool Connection::dropQuery( const QString& query )
01822 {
01823     clearError();
01824     QuerySchema* qs = querySchema( query );
01825     if (!qs) {
01826         setError(ERR_OBJECT_NOT_FOUND, i18n("Query \"%1\" does not exist.")
01827             .arg(query));
01828         return false;
01829     }
01830     return dropQuery(qs);
01831 }
01832 
01833 bool Connection::drv_createTable( const KexiDB::TableSchema& tableSchema )
01834 {
01835     m_sql = createTableStatement(tableSchema);
01836     KexiDBDbg<<"******** "<<m_sql<<endl;
01837     return executeSQL(m_sql);
01838 }
01839 
01840 bool Connection::drv_createTable( const QString& tableSchemaName )
01841 {
01842     TableSchema *ts = d->tables_byname[tableSchemaName];
01843     if (!ts)
01844         return false;
01845     return drv_createTable(*ts);
01846 }
01847 
01848 bool Connection::beginAutoCommitTransaction(TransactionGuard &tg)
01849 {
01850     if ((m_driver->d->features & Driver::IgnoreTransactions)
01851         || !d->autoCommit)
01852     {
01853         tg.setTransaction( Transaction() );
01854         return true;
01855     }
01856 
01857     // commit current transaction (if present) for drivers
01858     // that allow single transaction per connection
01859     if (m_driver->d->features & Driver::SingleTransactions) {
01860         if (d->default_trans_started_inside) //only commit internally started transaction
01861             if (!commitTransaction(d->default_trans, true)) {
01862                 tg.setTransaction( Transaction() );
01863                 return false; //we have a real error
01864             }
01865 
01866         d->default_trans_started_inside = d->default_trans.isNull();
01867         if (!d->default_trans_started_inside) {
01868             tg.setTransaction( d->default_trans );
01869             tg.doNothing();
01870             return true; //reuse externally started transaction
01871         }
01872     }
01873     else if (!(m_driver->d->features & Driver::MultipleTransactions)) {
01874         tg.setTransaction( Transaction() );
01875         return true; //no trans. supported at all - just return
01876     }
01877     tg.setTransaction( beginTransaction() );
01878     return !error();
01879 }
01880 
01881 bool Connection::commitAutoCommitTransaction(const Transaction& trans)
01882 {
01883     if (m_driver->d->features & Driver::IgnoreTransactions)
01884         return true;
01885     if (trans.isNull() || !m_driver->transactionsSupported())
01886         return true;
01887     if (m_driver->d->features & Driver::SingleTransactions) {
01888         if (!d->default_trans_started_inside) //only commit internally started transaction
01889             return true; //give up
01890     }
01891     return commitTransaction(trans, true);
01892 }
01893 
01894 bool Connection::rollbackAutoCommitTransaction(const Transaction& trans)
01895 {
01896     if (trans.isNull() || !m_driver->transactionsSupported())
01897         return true;
01898     return rollbackTransaction(trans);
01899 }
01900 
01901 #define SET_ERR_TRANS_NOT_SUPP \
01902     { setError(ERR_UNSUPPORTED_DRV_FEATURE, \
01903      i18n("Transactions are not supported for \"%1\" driver.").arg(m_driver->name() )); }
01904 
01905 #define SET_BEGIN_TR_ERROR \
01906      { if (!error()) \
01907         setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Begin transaction failed")); }
01908 
01909 Transaction Connection::beginTransaction()
01910 {
01911     if (!checkIsDatabaseUsed())
01912         return Transaction::null;
01913     Transaction trans;
01914     if (m_driver->d->features & Driver::IgnoreTransactions) {
01915         //we're creating dummy transaction data here,
01916         //so it will look like active
01917         trans.m_data = new TransactionData(this);
01918         d->transactions.append(trans);
01919         return trans;
01920     }
01921     if (m_driver->d->features & Driver::SingleTransactions) {
01922         if (d->default_trans.active()) {
01923             setError(ERR_TRANSACTION_ACTIVE, i18n("Transaction already started.") );
01924             return Transaction::null;
01925         }
01926         if (!(trans.m_data = drv_beginTransaction())) {
01927             SET_BEGIN_TR_ERROR;
01928             return Transaction::null;
01929         }
01930         d->default_trans = trans;
01931         d->transactions.append(trans);
01932         return d->default_trans;
01933     }
01934     if (m_driver->d->features & Driver::MultipleTransactions) {
01935         if (!(trans.m_data = drv_beginTransaction())) {
01936             SET_BEGIN_TR_ERROR;
01937             return Transaction::null;
01938         }
01939         d->transactions.append(trans);
01940         return trans;
01941     }
01942 
01943     SET_ERR_TRANS_NOT_SUPP;
01944     return Transaction::null;
01945 }
01946 
01947 bool Connection::commitTransaction(const Transaction trans, bool ignore_inactive)
01948 {
01949     if (!isDatabaseUsed())
01950         return false;
01951 //  if (!checkIsDatabaseUsed())
01952         //return false;
01953     if ( !m_driver->transactionsSupported()
01954         && !(m_driver->d->features & Driver::IgnoreTransactions))
01955     {
01956         SET_ERR_TRANS_NOT_SUPP;
01957         return false;
01958     }
01959     Transaction t = trans;
01960     if (!t.active()) { //try default tr.
01961         if (!d->default_trans.active()) {
01962             if (ignore_inactive)
01963                 return true;
01964             clearError();
01965             setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") );
01966             return false;
01967         }
01968         t = d->default_trans;
01969         d->default_trans = Transaction::null; //now: no default tr.
01970     }
01971     bool ret = true;
01972     if (! (m_driver->d->features & Driver::IgnoreTransactions) )
01973         ret = drv_commitTransaction(t.m_data);
01974     if (t.m_data)
01975         t.m_data->m_active = false; //now this transaction if inactive
01976     if (!d->dont_remove_transactions) //true=transaction obj will be later removed from list
01977         d->transactions.remove(t);
01978     if (!ret && !error())
01979         setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on commit transaction"));
01980     return ret;
01981 }
01982 
01983 bool Connection::rollbackTransaction(const Transaction trans, bool ignore_inactive)
01984 {
01985     if (!isDatabaseUsed())
01986         return false;
01987 //  if (!checkIsDatabaseUsed())
01988 //      return false;
01989     if ( !m_driver->transactionsSupported()
01990         && !(m_driver->d->features & Driver::IgnoreTransactions))
01991     {
01992         SET_ERR_TRANS_NOT_SUPP;
01993         return false;
01994     }
01995     Transaction t = trans;
01996     if (!t.active()) { //try default tr.
01997         if (!d->default_trans.active()) {
01998             if (ignore_inactive)
01999                 return true;
02000             clearError();
02001             setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") );
02002             return false;
02003         }
02004         t = d->default_trans;
02005         d->default_trans = Transaction::null; //now: no default tr.
02006     }
02007     bool ret = true;
02008     if (! (m_driver->d->features & Driver::IgnoreTransactions) )
02009         ret = drv_rollbackTransaction(t.m_data);
02010     if (t.m_data)
02011         t.m_data->m_active = false; //now this transaction if inactive
02012     if (!d->dont_remove_transactions) //true=transaction obj will be later removed from list
02013         d->transactions.remove(t);
02014     if (!ret && !error())
02015         setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on rollback transaction"));
02016     return ret;
02017 }
02018 
02019 #undef SET_ERR_TRANS_NOT_SUPP
02020 #undef SET_BEGIN_TR_ERROR
02021 
02022 /*bool Connection::duringTransaction()
02023 {
02024     return drv_duringTransaction();
02025 }*/
02026 
02027 Transaction& Connection::defaultTransaction() const
02028 {
02029     return d->default_trans;
02030 }
02031 
02032 void Connection::setDefaultTransaction(const Transaction& trans)
02033 {
02034     if (!isDatabaseUsed())
02035         return;
02036 //  if (!checkIsDatabaseUsed())
02037     //  return;
02038     if ( !(m_driver->d->features & Driver::IgnoreTransactions)
02039         && (!trans.active() || !m_driver->transactionsSupported()) )
02040     {
02041         return;
02042     }
02043     d->default_trans = trans;
02044 }
02045 
02046 const QValueList<Transaction>& Connection::transactions()
02047 {
02048     return d->transactions;
02049 }
02050 
02051 bool Connection::autoCommit() const
02052 {
02053     return d->autoCommit;
02054 }
02055 
02056 bool Connection::setAutoCommit(bool on)
02057 {
02058     if (d->autoCommit == on || m_driver->d->features & Driver::IgnoreTransactions)
02059         return true;
02060     if (!drv_setAutoCommit(on))
02061         return false;
02062     d->autoCommit = on;
02063     return true;
02064 }
02065 
02066 TransactionData* Connection::drv_beginTransaction()
02067 {
02068     QString old_sql = m_sql; //don't
02069     if (!executeSQL( "BEGIN" ))
02070         return 0;
02071     return new TransactionData(this);
02072 }
02073 
02074 bool Connection::drv_commitTransaction(TransactionData *)
02075 {
02076     return executeSQL( "COMMIT" );
02077 }
02078 
02079 bool Connection::drv_rollbackTransaction(TransactionData *)
02080 {
02081     return executeSQL( "ROLLBACK" );
02082 }
02083 
02084 bool Connection::drv_setAutoCommit(bool /*on*/)
02085 {
02086     return true;
02087 }
02088 
02089 Cursor* Connection::executeQuery( const QString& statement, uint cursor_options )
02090 {
02091     if (statement.isEmpty())
02092         return 0;
02093     Cursor *c = prepareQuery( statement, cursor_options );
02094     if (!c)
02095         return 0;
02096     if (!c->open()) {//err - kill that
02097         setError(c);
02098         delete c;
02099         return 0;
02100     }
02101     return c;
02102 }
02103 
02104 Cursor* Connection::executeQuery( QuerySchema& query, const QValueList<QVariant>& params, 
02105     uint cursor_options )
02106 {
02107     Cursor *c = prepareQuery( query, params, cursor_options );
02108     if (!c)
02109         return 0;
02110     if (!c->open()) {//err - kill that
02111         setError(c);
02112         delete c;
02113         return 0;
02114     }
02115     return c;
02116 }
02117 
02118 Cursor* Connection::executeQuery( QuerySchema& query, uint cursor_options )
02119 {
02120     return executeQuery(query, QValueList<QVariant>(), cursor_options);
02121 }
02122 
02123 Cursor* Connection::executeQuery( TableSchema& table, uint cursor_options )
02124 {
02125     return executeQuery( *table.query(), cursor_options );
02126 }
02127 
02128 Cursor* Connection::prepareQuery( TableSchema& table, uint cursor_options )
02129 {
02130     return prepareQuery( *table.query(), cursor_options );
02131 }
02132 
02133 Cursor* Connection::prepareQuery( QuerySchema& query, const QValueList<QVariant>& params, 
02134     uint cursor_options )
02135 {
02136     Cursor* cursor = prepareQuery(query, cursor_options);
02137     if (cursor)
02138         cursor->setQueryParameters(params);
02139     return cursor;
02140 }
02141 
02142 bool Connection::deleteCursor(Cursor *cursor)
02143 {
02144     if (!cursor)
02145         return false;
02146     if (cursor->connection()!=this) {//illegal call
02147         KexiDBWarn << "Connection::deleteCursor(): Cannot delete the cursor not owned by the same connection!" << endl;
02148         return false;
02149     }
02150     const bool ret = cursor->close();
02151     delete cursor;
02152     return ret;
02153 }
02154 
02155 bool Connection::setupObjectSchemaData( const RowData &data, SchemaData &sdata )
02156 {
02157     //not found: retrieve schema
02158 /*  KexiDB::Cursor *cursor;
02159     if (!(cursor = executeQuery( QString("select * from kexi__objects where o_id='%1'").arg(objId) )))
02160         return false;
02161     if (!cursor->moveFirst()) {
02162         deleteCursor(cursor);
02163         return false;
02164     }*/
02165     //if (!ok) {
02166         //deleteCursor(cursor);
02167         //return 0;
02168 //  }
02169     bool ok;
02170     sdata.m_id = data[0].toInt(&ok);
02171     if (!ok) {
02172         return false;
02173     }
02174     sdata.m_name = data[2].toString();
02175     if (!KexiUtils::isIdentifier( sdata.m_name )) {
02176         setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"").arg(sdata.m_name));
02177         return false;
02178     }
02179     sdata.m_caption = data[3].toString();
02180     sdata.m_desc = data[4].toString();
02181 
02182 //  KexiDBDbg<<"@@@ Connection::setupObjectSchemaData() == " << sdata.schemaDataDebugString() << endl;
02183     return true;
02184 }
02185 
02186 tristate Connection::loadObjectSchemaData( int objectID, SchemaData &sdata )
02187 {
02188     RowData data;
02189     if (true!=querySingleRecord(QString::fromLatin1(
02190         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects where o_id=%1")
02191         .arg(objectID), data))
02192         return cancelled;
02193     return setupObjectSchemaData( data, sdata );
02194 }
02195 
02196 tristate Connection::loadObjectSchemaData( int objectType, const QString& objectName, SchemaData &sdata )
02197 {
02198     RowData data;
02199     if (true!=querySingleRecord(QString::fromLatin1("SELECT o_id, o_type, o_name, o_caption, o_desc "
02200         "FROM kexi__objects WHERE o_type=%1 AND lower(o_name)=%2")
02201         .arg(objectType).arg(m_driver->valueToSQL(Field::Text, objectName.lower())), data))
02202         return cancelled;
02203     return setupObjectSchemaData( data, sdata );
02204 }
02205 
02206 bool Connection::storeObjectSchemaData( SchemaData &sdata, bool newObject )
02207 {
02208     TableSchema *ts = d->tables_byname["kexi__objects"];
02209     if (!ts)
02210         return false;
02211     if (newObject) {
02212         int existingID;
02213         if (true == querySingleNumber(QString::fromLatin1(
02214             "SELECT o_id FROM kexi__objects WHERE o_type=%1 AND lower(o_name)=%2")
02215             .arg(sdata.type()).arg(m_driver->valueToSQL(Field::Text, sdata.name().lower())), existingID))
02216         {
02217             //we already have stored a schema data with the same name and type:
02218             //just update it's properties as it would be existing object
02219             sdata.m_id = existingID;
02220             newObject = false;
02221         }
02222     }
02223     if (newObject) {
02224         FieldList *fl;
02225         bool ok;
02226         if (sdata.id()<=0) {//get new ID
02227             fl = ts->subList("o_type", "o_name", "o_caption", "o_desc");
02228             ok = fl!=0;
02229             if (ok && !insertRecord(*fl, QVariant(sdata.type()), QVariant(sdata.name()),
02230             QVariant(sdata.caption()), QVariant(sdata.description()) ))
02231                 ok = false;
02232             delete fl;
02233             if (!ok)
02234                 return false;
02235             //fetch newly assigned ID
02237             int obj_id = (int)lastInsertedAutoIncValue("o_id",*ts);
02238             KexiDBDbg << "######## NEW obj_id == " << obj_id << endl;
02239             if (obj_id<=0)
02240                 return false;
02241             sdata.m_id = obj_id;
02242             return true;
02243         } else {
02244             fl = ts->subList("o_id", "o_type", "o_name", "o_caption", "o_desc");
02245             ok = fl!=0;
02246             if (ok && !insertRecord(*fl, QVariant(sdata.id()), QVariant(sdata.type()), QVariant(sdata.name()),
02247                 QVariant(sdata.caption()), QVariant(sdata.description()) ))
02248                 ok = false;
02249             delete fl;
02250             return ok;
02251         }
02252     }
02253     //existing object:
02254     return executeSQL(QString("UPDATE kexi__objects SET o_type=%2, o_caption=%3, o_desc=%4 WHERE o_id=%1")
02255         .arg(sdata.id()).arg(sdata.type())
02256         .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.caption()))
02257         .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.description())) );
02258 }
02259 
02260 tristate Connection::querySingleRecordInternal(RowData &data, const QString* sql, QuerySchema* query, 
02261     bool addLimitTo1)
02262 {
02263     Q_ASSERT(sql || query);
02265     if (sql)
02266         m_sql = addLimitTo1 ? (*sql + " LIMIT 1") : *sql; // is this safe?
02267     KexiDB::Cursor *cursor;
02268     if (!(cursor = sql ? executeQuery( m_sql ) : executeQuery( *query ))) {
02269         KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
02270         return false;
02271     }
02272     if (!cursor->moveFirst() || cursor->eof()) {
02273         const tristate result = cursor->error() ? false : cancelled;
02274         KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() m_sql=" << m_sql << endl;
02275         setError(cursor);
02276         deleteCursor(cursor);
02277         return result;
02278     }
02279     cursor->storeCurrentRow(data);
02280     return deleteCursor(cursor);
02281 }
02282 
02283 tristate Connection::querySingleRecord(const QString& sql, RowData &data, bool addLimitTo1)
02284 {
02285     return querySingleRecordInternal(data, &sql, 0, addLimitTo1);
02286 }
02287 
02288 tristate Connection::querySingleRecord(QuerySchema& query, RowData &data, bool addLimitTo1)
02289 {
02290     return querySingleRecordInternal(data, 0, &query, addLimitTo1);
02291 }
02292 
02293 bool Connection::checkIfColumnExists(Cursor *cursor, uint column)
02294 {
02295     if (column >= cursor->fieldCount()) {
02296         setError(ERR_CURSOR_RECORD_FETCHING, i18n("Column %1 does not exist for the query.").arg(column));
02297         return false;
02298     }
02299     return true;
02300 }
02301 
02302 tristate Connection::querySingleString(const QString& sql, QString &value, uint column, bool addLimitTo1)
02303 {
02304     KexiDB::Cursor *cursor;
02305     m_sql = addLimitTo1 ? (sql + " LIMIT 1") : sql; // is this safe?;
02306     if (!(cursor = executeQuery( m_sql ))) {
02307         KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
02308         return false;
02309     }
02310     if (!cursor->moveFirst() || cursor->eof()) {
02311         const tristate result = cursor->error() ? false : cancelled;
02312         KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() " << m_sql << endl;
02313         deleteCursor(cursor);
02314         return result;
02315     }
02316     if (!checkIfColumnExists(cursor, column)) {
02317         deleteCursor(cursor);
02318         return false;
02319     }
02320     value = cursor->value(column).toString();
02321     return deleteCursor(cursor);
02322 }
02323 
02324 tristate Connection::querySingleNumber(const QString& sql, int &number, uint column, bool addLimitTo1)
02325 {
02326     static QString str;
02327     static bool ok;
02328     const tristate result = querySingleString(sql, str, column, addLimitTo1);
02329     if (result!=true)
02330         return result;
02331     number = str.toInt(&ok);
02332     return ok;
02333 }
02334 
02335 bool Connection::queryStringList(const QString& sql, QStringList& list, uint column)
02336 {
02337     KexiDB::Cursor *cursor;
02338     clearError();
02339     m_sql = sql;
02340     if (!(cursor = executeQuery( m_sql ))) {
02341         KexiDBWarn << "Connection::queryStringList(): !executeQuery() " << m_sql << endl;
02342         return false;
02343     }
02344     if (!checkIfColumnExists(cursor, column)) {
02345         deleteCursor(cursor);
02346         return false;
02347     }
02348     cursor->moveFirst();
02349     if (cursor->error()) {
02350         setError(cursor);
02351         deleteCursor(cursor);
02352         return false;
02353     }
02354     list.clear();
02355     while (!cursor->eof()) {
02356         list.append( cursor->value(column).toString() );
02357         if (!cursor->moveNext() && cursor->error()) {
02358             setError(cursor);
02359             deleteCursor(cursor);
02360             return false;
02361         }
02362     }
02363     return deleteCursor(cursor);
02364 }
02365 
02366 bool Connection::resultExists(const QString& sql, bool &success, bool addLimitTo1)
02367 {
02368     KexiDB::Cursor *cursor;
02369     //optimization
02370     if (m_driver->beh->SELECT_1_SUBQUERY_SUPPORTED) {
02371         //this is at least for sqlite
02372         if (addLimitTo1 && sql.left(6).upper() == "SELECT")
02373             m_sql = QString("SELECT 1 FROM (") + sql + ") LIMIT 1"; // is this safe?;
02374         else
02375             m_sql = sql;
02376     }
02377     else {
02378         if (addLimitTo1 && sql.left(6).upper() == "SELECT")
02379             m_sql = sql + " LIMIT 1"; //not always safe!
02380         else
02381             m_sql = sql;
02382     }
02383     if (!(cursor = executeQuery( m_sql ))) {
02384         KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
02385         success = false;
02386         return false;
02387     }
02388     if (!cursor->moveFirst() || cursor->eof()) {
02389         success = !cursor->error();
02390         KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() " << m_sql << endl;
02391         setError(cursor);
02392         deleteCursor(cursor);
02393         return false;
02394     }
02395     success = deleteCursor(cursor);
02396     return true;
02397 }
02398 
02399 bool Connection::isEmpty( TableSchema& table, bool &success )
02400 {
02401     return !resultExists( selectStatement( *table.query() ), success );
02402 }
02403 
02404 int Connection::resultCount(const QString& sql)
02405 {
02406     int count = -1; //will be changed only on success of querySingleNumber()
02407     m_sql = QString::fromLatin1("SELECT COUNT() FROM (") + sql + ")";
02408     querySingleNumber(m_sql, count);
02409     return count;
02410 }
02411 
02413 static void createExtendedTableSchemaMainElementIfNeeded(
02414     QDomDocument& doc, QDomElement& extendedTableSchemaMainEl,
02415     bool& extendedTableSchemaStringIsEmpty)
02416 {
02417     if (!extendedTableSchemaStringIsEmpty)
02418         return;
02419     //init document
02420     extendedTableSchemaMainEl = doc.createElement("EXTENDED_TABLE_SCHEMA");
02421     doc.appendChild( extendedTableSchemaMainEl );
02422     extendedTableSchemaMainEl.setAttribute("version", QString::number(KEXIDB_EXTENDED_TABLE_SCHEMA_VERSION));
02423     extendedTableSchemaStringIsEmpty = false;
02424 }
02425 
02427 static void createExtendedTableSchemaFieldElementIfNeeded(QDomDocument& doc, 
02428     QDomElement& extendedTableSchemaMainEl, const QString& fieldName, QDomElement& extendedTableSchemaFieldEl)
02429 {
02430     if (!extendedTableSchemaFieldEl.isNull())
02431         return;
02432     extendedTableSchemaFieldEl = doc.createElement("field");
02433     extendedTableSchemaMainEl.appendChild( extendedTableSchemaFieldEl );
02434     extendedTableSchemaFieldEl.setAttribute("name", fieldName);
02435 }
02436 
02443 static void addFieldPropertyToExtendedTableSchemaData( 
02444     Field *f, const char* propertyName, const QVariant& propertyValue, 
02445     QDomDocument& doc, QDomElement& extendedTableSchemaMainEl, 
02446     QDomElement& extendedTableSchemaFieldEl,
02447     bool& extendedTableSchemaStringIsEmpty,
02448     bool custom = false )
02449 {
02450     createExtendedTableSchemaMainElementIfNeeded(doc,
02451         extendedTableSchemaMainEl, extendedTableSchemaStringIsEmpty);
02452     createExtendedTableSchemaFieldElementIfNeeded(
02453         doc, extendedTableSchemaMainEl, f->name(), extendedTableSchemaFieldEl);
02454 
02455     //create <property>
02456     QDomElement extendedTableSchemaFieldPropertyEl = doc.createElement("property");
02457     extendedTableSchemaFieldEl.appendChild( extendedTableSchemaFieldPropertyEl );
02458     if (custom)
02459         extendedTableSchemaFieldPropertyEl.setAttribute("custom", "true");
02460     extendedTableSchemaFieldPropertyEl.setAttribute("name", propertyName);
02461     QDomElement extendedTableSchemaFieldPropertyValueEl;
02462     switch (propertyValue.type()) {
02463     case QVariant::String:
02464         extendedTableSchemaFieldPropertyValueEl = doc.createElement("string");
02465         break;
02466     case QVariant::CString:
02467         extendedTableSchemaFieldPropertyValueEl = doc.createElement("cstring");
02468         break;
02469     case QVariant::Int:
02470     case QVariant::Double:
02471     case QVariant::UInt:
02472     case QVariant::LongLong:
02473     case QVariant::ULongLong:
02474         extendedTableSchemaFieldPropertyValueEl = doc.createElement("number");
02475         break;
02476     case QVariant::Bool:
02477         extendedTableSchemaFieldPropertyValueEl = doc.createElement("bool");
02478         break;
02479     default:
02481         KexiDBFatal << "addFieldPropertyToExtendedTableSchemaData(): impl. error" << endl;
02482     }
02483     extendedTableSchemaFieldPropertyEl.appendChild( extendedTableSchemaFieldPropertyValueEl );
02484     extendedTableSchemaFieldPropertyValueEl.appendChild( 
02485         doc.createTextNode( propertyValue.toString() ) );
02486 }
02487 
02488 bool Connection::storeExtendedTableSchemaData(TableSchema& tableSchema)
02489 {
02491     QDomDocument doc("EXTENDED_TABLE_SCHEMA");
02492     QDomElement extendedTableSchemaMainEl;
02493     bool extendedTableSchemaStringIsEmpty = true;
02494 
02495     //for each field:
02496     Field *f;
02497     for (Field::ListIterator it( *tableSchema.fields() ); (f = it.current()); ++it) {
02498         QDomElement extendedTableSchemaFieldEl;
02499         if (f->visibleDecimalPlaces()>=0/*nondefault*/ && KexiDB::supportsVisibleDecimalPlacesProperty(f->type())) {
02500             addFieldPropertyToExtendedTableSchemaData( 
02501                 f, "visibleDecimalPlaces", f->visibleDecimalPlaces(), doc, 
02502                 extendedTableSchemaMainEl, extendedTableSchemaFieldEl, 
02503                 extendedTableSchemaStringIsEmpty );
02504         }
02505         // boolean field with "not null"
02506 
02507         // add custom properties
02508         const Field::CustomPropertiesMap customProperties(f->customProperties());
02509         foreach( Field::CustomPropertiesMap::ConstIterator, itCustom, customProperties ) {
02510             addFieldPropertyToExtendedTableSchemaData( 
02511                 f, itCustom.key(), itCustom.data(), doc, 
02512                 extendedTableSchemaMainEl, extendedTableSchemaFieldEl, extendedTableSchemaStringIsEmpty,
02513                 /*custom*/true );
02514         }
02515         // save lookup table specification, if present
02516         LookupFieldSchema *lookupFieldSchema = tableSchema.lookupFieldSchema( *f );
02517         if (lookupFieldSchema) {
02518             createExtendedTableSchemaMainElementIfNeeded(doc, extendedTableSchemaMainEl, 
02519                 extendedTableSchemaStringIsEmpty);
02520             createExtendedTableSchemaFieldElementIfNeeded(
02521                 doc, extendedTableSchemaMainEl, f->name(), extendedTableSchemaFieldEl);
02522             LookupFieldSchema::saveToDom(*lookupFieldSchema, doc, extendedTableSchemaFieldEl);
02523         }
02524     }
02525 
02526     // Store extended schema information (see ExtendedTableSchemaInformation in Kexi Wiki)
02527     if (extendedTableSchemaStringIsEmpty) {
02528 #ifdef KEXI_DEBUG_GUI
02529         KexiUtils::addAlterTableActionDebug(QString("** Extended table schema REMOVED."));
02530 #endif
02531         if (!removeDataBlock( tableSchema.id(), "extended_schema"))
02532             return false;
02533     }
02534     else {
02535 #ifdef KEXI_DEBUG_GUI
02536         KexiUtils::addAlterTableActionDebug(QString("** Extended table schema set to:\n")+doc.toString(4));
02537 #endif
02538         if (!storeDataBlock( tableSchema.id(), doc.toString(1), "extended_schema" ))
02539             return false;
02540     }
02541     return true;
02542 }
02543 
02544 bool Connection::loadExtendedTableSchemaData(TableSchema& tableSchema)
02545 {
02546 #define loadExtendedTableSchemaData_ERR \
02547     { setError(i18n("Error while loading extended table schema information.")); \
02548       return false; }
02549 #define loadExtendedTableSchemaData_ERR2(details) \
02550     { setError(i18n("Error while loading extended table schema information."), details); \
02551       return false; }
02552 #define loadExtendedTableSchemaData_ERR3(data) \
02553     { setError(i18n("Error while loading extended table schema information."), \
02554       i18n("Invalid XML data: ") + data.left(1024) ); \
02555       return false; }
02556 
02557     // Load extended schema information, if present (see ExtendedTableSchemaInformation in Kexi Wiki)
02558     QString extendedTableSchemaString;
02559     tristate res = loadDataBlock( tableSchema.id(), extendedTableSchemaString, "extended_schema" );
02560     if (!res)
02561         loadExtendedTableSchemaData_ERR;
02562     // extendedTableSchemaString will be just empty if there is no such data block
02563 
02564 #ifdef KEXIDB_LOOKUP_FIELD_TEST
02565 //<temp. for LookupFieldSchema tests>
02566     if (tableSchema.name()=="cars") {
02567         LookupFieldSchema *lookupFieldSchema = new LookupFieldSchema();
02568         lookupFieldSchema->rowSource().setType(LookupFieldSchema::RowSource::Table);
02569         lookupFieldSchema->rowSource().setName("persons");
02570         lookupFieldSchema->setBoundColumn(0); //id
02571         lookupFieldSchema->setVisibleColumn(3); //surname
02572         tableSchema.setLookupFieldSchema( "owner", lookupFieldSchema );
02573     }
02574 //</temp. for LookupFieldSchema tests>
02575 #endif
02576 
02577     if (extendedTableSchemaString.isEmpty())
02578         return true;
02579 
02580     QDomDocument doc;
02581     QString errorMsg;
02582     int errorLine, errorColumn;
02583     if (!doc.setContent( extendedTableSchemaString, &errorMsg, &errorLine, &errorColumn ))
02584         loadExtendedTableSchemaData_ERR2( i18n("Error in XML data: \"%1\" in line %2, column %3.\nXML data: ")
02585             .arg(errorMsg).arg(errorLine).arg(errorColumn) + extendedTableSchemaString.left(1024));
02586 
02588     
02589     if (doc.doctype().name()!="EXTENDED_TABLE_SCHEMA")
02590         loadExtendedTableSchemaData_ERR3( extendedTableSchemaString );
02591 
02592     QDomElement docEl = doc.documentElement();
02593     if (docEl.tagName()!="EXTENDED_TABLE_SCHEMA")
02594         loadExtendedTableSchemaData_ERR3( extendedTableSchemaString );
02595 
02596     for (QDomNode n = docEl.firstChild(); !n.isNull(); n = n.nextSibling()) {
02597         QDomElement fieldEl = n.toElement();
02598         if (fieldEl.tagName()=="field") {
02599             Field *f = tableSchema.field( fieldEl.attribute("name") );
02600             if (f) {
02601                 //set properties of the field:
02603                 for (QDomNode propNode = fieldEl.firstChild(); 
02604                     !propNode.isNull(); propNode = propNode.nextSibling())
02605                 {
02606                     QDomElement propEl = propNode.toElement();
02607                     bool ok;
02608                     int intValue;
02609                     if (propEl.tagName()=="property") {
02610                         QCString propertyName = propEl.attribute("name").latin1();
02611                         if (propEl.attribute("custom")=="true") {
02612                             //custom property
02613                             f->setCustomProperty(propertyName, 
02614                                 KexiDB::loadPropertyValueFromDom( propEl.firstChild() ));
02615                         }
02616                         else if (propertyName == "visibleDecimalPlaces"
02617                             && KexiDB::supportsVisibleDecimalPlacesProperty(f->type()))
02618                         {
02619                             intValue = KexiDB::loadIntPropertyValueFromDom( propEl.firstChild(), &ok );
02620                             if (ok)
02621                                 f->setVisibleDecimalPlaces(intValue);
02622                         }
02624                     }
02625                     else if (propEl.tagName()=="lookup-column") {
02626                         LookupFieldSchema *lookupFieldSchema = LookupFieldSchema::loadFromDom(propEl);
02627                         tableSchema.setLookupFieldSchema( f->name(), lookupFieldSchema );
02628                     }
02629                 }
02630             }
02631             else {
02632                 KexiDBWarn << "Connection::loadExtendedTableSchemaData(): no such field \"" 
02633                     << fieldEl.attribute("name") << "\" in table \"" << tableSchema.name() << "\"" << endl;
02634             }
02635         }
02636     }
02637 
02638     return true;
02639 }
02640 
02641 KexiDB::TableSchema* Connection::setupTableSchema( const RowData &data )
02642 {
02643     TableSchema *t = new TableSchema( this );
02644     if (!setupObjectSchemaData( data, *t )) {
02645         delete t;
02646         return 0;
02647     }
02648 /*  if (!deleteCursor(table_cur)) {
02649         delete t;
02650         return 0;
02651     }*/
02652 
02653     KexiDB::Cursor *cursor;
02654     if (!(cursor = executeQuery(
02655         QString::fromLatin1("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
02656             "f_options, f_default, f_order, f_caption, f_help"
02657             " FROM kexi__fields WHERE t_id=%1 ORDER BY f_order").arg(t->m_id) )))
02658     {
02659         delete t;
02660         return 0;
02661     }
02662     if (!cursor->moveFirst()) {
02663         if (!cursor->error() && cursor->eof()) {
02664             setError(i18n("Table has no fields defined."));
02665         }
02666         deleteCursor(cursor);
02667         delete t;
02668         return 0;
02669     }
02670 
02671     // For each field: load its schema
02672     bool ok;
02673     while (!cursor->eof()) {
02674 //      KexiDBDbg<<"@@@ f_name=="<<cursor->value(2).asCString()<<endl;
02675 
02676         int f_int_type = cursor->value(1).toInt(&ok);
02677         if (f_int_type<=Field::InvalidType || f_int_type>Field::LastType)
02678             ok = false;
02679         if (!ok)
02680             break;
02681         Field::Type f_type = (Field::Type)f_int_type;
02682         int f_len = QMAX( 0, cursor->value(3).toInt(&ok) );
02683         if (!ok)
02684             break;
02685         int f_prec = cursor->value(4).toInt(&ok);
02686         if (!ok)
02687             break;
02688         int f_constr = cursor->value(5).toInt(&ok);
02689         if (!ok)
02690             break;
02691         int f_opts = cursor->value(6).toInt(&ok);
02692         if (!ok)
02693             break;
02694 
02695         if (!KexiUtils::isIdentifier( cursor->value(2).asString() )) {
02696             setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"")
02697                 .arg( cursor->value(2).asString() ));
02698             ok=false;
02699             break;
02700         }
02701 
02702         Field *f = new Field(
02703             cursor->value(2).asString(), f_type, f_constr, f_opts, f_len, f_prec );
02704 
02705         f->setDefaultValue( KexiDB::stringToVariant(cursor->value(7).toString(), Field::variantType( f_type ), ok) );
02706         if (!ok) {
02707             KexiDBWarn << "Connection::setupTableSchema() problem with KexiDB::stringToVariant(" 
02708                 << cursor->value(7).toString() << ")" << endl;
02709         }
02710         ok = true; //problem with defaultValue is not critical
02711 
02712         f->m_caption = cursor->value(9).asString();
02713         f->m_desc = cursor->value(10).asString();
02714         t->addField(f);
02715         cursor->moveNext();
02716     }
02717 
02718     if (!ok) {//error:
02719         deleteCursor(cursor);
02720         delete t;
02721         return 0;
02722     }
02723 
02724     if (!deleteCursor(cursor)) {
02725         delete t;
02726         return 0;
02727     }
02728 
02729     if (!loadExtendedTableSchemaData(*t)) {
02730         delete t;
02731         return 0;
02732     }
02733     //store locally:
02734     d->tables.insert(t->m_id, t);
02735     d->tables_byname.insert(t->m_name.lower(), t);
02736     return t;
02737 }
02738 
02739 TableSchema* Connection::tableSchema( const QString& tableName )
02740 {
02741     QString m_tableName = tableName.lower();
02742     TableSchema *t = d->tables_byname[m_tableName];
02743     if (t)
02744         return t;
02745     //not found: retrieve schema
02746     RowData data;
02747     if (true!=querySingleRecord(QString::fromLatin1(
02748         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE lower(o_name)='%1' AND o_type=%2")
02749             .arg(m_tableName).arg(KexiDB::TableObjectType), data))
02750         return 0;
02751 
02752     return setupTableSchema(data);
02753 }
02754 
02755 TableSchema* Connection::tableSchema( int tableId )
02756 {
02757     TableSchema *t = d->tables[tableId];
02758     if (t)
02759         return t;
02760     //not found: retrieve schema
02761     RowData data;
02762     if (true!=querySingleRecord(QString::fromLatin1(
02763         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1")
02764         .arg(tableId), data))
02765         return 0;
02766 
02767     return setupTableSchema(data);
02768 }
02769 
02770 tristate Connection::loadDataBlock( int objectID, QString &dataString, const QString& dataID )
02771 {
02772     if (objectID<=0)
02773         return false;
02774     return querySingleString(
02775         QString("SELECT o_data FROM kexi__objectdata WHERE o_id=") + QString::number(objectID)
02776         + " AND " + KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID),
02777         dataString );
02778 }
02779 
02780 bool Connection::storeDataBlock( int objectID, const QString &dataString, const QString& dataID )
02781 {
02782     if (objectID<=0)
02783         return false;
02784     QString sql(QString::fromLatin1("SELECT kexi__objectdata.o_id FROM kexi__objectdata WHERE o_id=%1").arg(objectID));
02785     QString sql_sub( KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID) );
02786 
02787     bool ok, exists;
02788     exists = resultExists(sql + " and " + sql_sub, ok);
02789     if (!ok)
02790         return false;
02791     if (exists) {
02792         return executeSQL( "UPDATE kexi__objectdata SET o_data="
02793             + m_driver->valueToSQL( KexiDB::Field::LongText, dataString )
02794             + " WHERE o_id=" + QString::number(objectID) + " AND " + sql_sub );
02795     }
02796     return executeSQL(
02797         QString::fromLatin1("INSERT INTO kexi__objectdata (o_id, o_data, o_sub_id) VALUES (")
02798         + QString::number(objectID) +"," + m_driver->valueToSQL( KexiDB::Field::LongText, dataString )
02799         + "," + m_driver->valueToSQL( KexiDB::Field::Text, dataID ) + ")" );
02800 }
02801 
02802 bool Connection::removeDataBlock( int objectID, const QString& dataID)
02803 {
02804     if (objectID<=0)
02805         return false;
02806     if (dataID.isEmpty())
02807         return KexiDB::deleteRow(*this, "kexi__objectdata", "o_id", QString::number(objectID));
02808     else
02809         return KexiDB::deleteRow(*this, "kexi__objectdata",
02810         "o_id", KexiDB::Field::Integer, objectID, "o_sub_id", KexiDB::Field::Text, dataID);
02811 }
02812 
02813 KexiDB::QuerySchema* Connection::setupQuerySchema( const RowData &data )
02814 {
02815     bool ok = true;
02816     const int objID = data[0].toInt(&ok);
02817     if (!ok)
02818         return false;
02819     QString sqlText;
02820     if (!loadDataBlock( objID, sqlText, "sql" )) {
02821         setError(ERR_OBJECT_NOT_FOUND, 
02822             i18n("Could not find definition for query \"%1\". Removing this query is recommended.")
02823             .arg(data[2].toString()));
02824         return 0;
02825     }
02826     d->parser()->parse( sqlText );
02827     KexiDB::QuerySchema *query = d->parser()->query();
02828     //error?
02829     if (!query) {
02830         setError(ERR_SQL_PARSE_ERROR, 
02831             i18n("<p>Could not load definition for query \"%1\". "
02832             "SQL statement for this query is invalid:<br><tt>%2</tt></p>\n"
02833             "<p>You can open this query in Text View and correct it.</p>").arg(data[2].toString())
02834             .arg(d->parser()->statement()));
02835         return 0;
02836     }
02837     if (!setupObjectSchemaData( data, *query )) {
02838         delete query;
02839         return 0;
02840     }
02841     d->queries.insert(query->m_id, query);
02842     d->queries_byname.insert(query->m_name, query);
02843     return query;
02844 }
02845 
02846 QuerySchema* Connection::querySchema( const QString& queryName )
02847 {
02848     QString m_queryName = queryName.lower();
02849     QuerySchema *q = d->queries_byname[m_queryName];
02850     if (q)
02851         return q;
02852     //not found: retrieve schema
02853     RowData data;
02854     if (true!=querySingleRecord(QString::fromLatin1(
02855         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE lower(o_name)='%1' AND o_type=%2")
02856             .arg(m_queryName).arg(KexiDB::QueryObjectType), data))
02857         return 0;
02858 
02859     return setupQuerySchema(data);
02860 }
02861 
02862 QuerySchema* Connection::querySchema( int queryId )
02863 {
02864     QuerySchema *q = d->queries[queryId];
02865     if (q)
02866         return q;
02867     //not found: retrieve schema
02868     clearError();
02869     RowData data;
02870     if (true!=querySingleRecord(QString::fromLatin1(
02871         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1").arg(queryId), data))
02872         return 0;
02873 
02874     return setupQuerySchema(data);
02875 }
02876 
02877 bool Connection::setQuerySchemaObsolete( const QString& queryName )
02878 {
02879     QuerySchema* oldQuery = querySchema( queryName );
02880     if (!oldQuery)
02881         return false;
02882     d->obsoleteQueries.append(oldQuery);
02883     d->queries_byname.take(queryName);
02884     d->queries.take(oldQuery->id());
02885     return true;
02886 }
02887 
02888 TableSchema* Connection::newKexiDBSystemTableSchema(const QString& tsname)
02889 {
02890     TableSchema *ts = new TableSchema(tsname.lower());
02891     insertInternalTableSchema( ts );
02892     return ts;
02893 }
02894 
02895 bool Connection::isInternalTableSchema(const QString& tableName)
02896 {
02897     return (d->kexiDBSystemTables[ d->tables_byname[tableName] ]) 
02898         // these are here for compatiblility because we're no longer instantiate 
02899         // them but can exist in projects created with previous Kexi versions:
02900         || tableName=="kexi__final" || tableName=="kexi__useractions";
02901 }
02902 
02903 void Connection::insertInternalTableSchema(TableSchema *tableSchema)
02904 {
02905     tableSchema->setKexiDBSystem(true);
02906     d->kexiDBSystemTables.insert(tableSchema, tableSchema);
02907     d->tables_byname.insert(tableSchema->name(), tableSchema);
02908 }
02909 
02911 bool Connection::setupKexiDBSystemSchema()
02912 {
02913     if (!d->kexiDBSystemTables.isEmpty())
02914         return true; //already set up
02915 
02916     TableSchema *t_objects = newKexiDBSystemTableSchema("kexi__objects");
02917     t_objects->addField( new Field("o_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) )
02918     .addField( new Field("o_type", Field::Byte, 0, Field::Unsigned) )
02919     .addField( new Field("o_name", Field::Text) )
02920     .addField( new Field("o_caption", Field::Text ) )
02921     .addField( new Field("o_desc", Field::LongText ) );
02922 
02923     t_objects->debug();
02924 
02925     TableSchema *t_objectdata = newKexiDBSystemTableSchema("kexi__objectdata");
02926     t_objectdata->addField( new Field("o_id", Field::Integer, Field::NotNull, Field::Unsigned) )
02927     .addField( new Field("o_data", Field::LongText) )
02928     .addField( new Field("o_sub_id", Field::Text) );
02929 
02930     TableSchema *t_fields = newKexiDBSystemTableSchema("kexi__fields");
02931     t_fields->addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) )
02932     .addField( new Field("f_type", Field::Byte, 0, Field::Unsigned) )
02933     .addField( new Field("f_name", Field::Text ) )
02934     .addField( new Field("f_length", Field::Integer ) )
02935     .addField( new Field("f_precision", Field::Integer ) )
02936     .addField( new Field("f_constraints", Field::Integer ) )
02937     .addField( new Field("f_options", Field::Integer ) )
02938     .addField( new Field("f_default", Field::Text ) )
02939     //these are additional properties:
02940     .addField( new Field("f_order", Field::Integer ) )
02941     .addField( new Field("f_caption", Field::Text ) )
02942     .addField( new Field("f_help", Field::LongText ) );
02943 
02944 /*  TableSchema *t_querydata = newKexiDBSystemTableSchema("kexi__querydata");
02945     t_querydata->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
02946     .addField( new Field("q_sql", Field::LongText ) )
02947     .addField( new Field("q_valid", Field::Boolean ) );
02948 
02949     TableSchema *t_queryfields = newKexiDBSystemTableSchema("kexi__queryfields");
02950     t_queryfields->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
02951     .addField( new Field("f_order", Field::Integer ) )
02952     .addField( new Field("f_id", Field::Integer ) )
02953     .addField( new Field("f_tab_asterisk", Field::Integer, 0, Field::Unsigned) )
02954     .addField( new Field("f_alltab_asterisk", Field::Boolean) );
02955 
02956     TableSchema *t_querytables = newKexiDBSystemTableSchema("kexi__querytables");
02957     t_querytables->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
02958     .addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) )
02959     .addField( new Field("t_order", Field::Integer, 0, Field::Unsigned) );*/
02960 
02961     TableSchema *t_db = newKexiDBSystemTableSchema("kexi__db");
02962     t_db->addField( new Field("db_property", Field::Text, Field::NoConstraints, Field::NoOptions, 32 ) )
02963     .addField( new Field("db_value", Field::LongText ) );
02964 
02965 /* moved to KexiProject...
02966     TableSchema *t_parts = newKexiDBSystemTableSchema("kexi__parts");
02967     t_parts->addField( new Field("p_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) )
02968     .addField( new Field("p_name", Field::Text) )
02969     .addField( new Field("p_mime", Field::Text ) )
02970     .addField( new Field("p_url", Field::Text ) );
02971 */
02972 
02973 /*UNUSED
02974     TableSchema *t_final = newKexiDBSystemTableSchema("kexi__final");
02975     t_final->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) )
02976     .addField( new Field("property", Field::LongText ) )
02977     .addField( new Field("value", Field::BLOB) );
02978 
02979     TableSchema *t_useractions = newKexiDBSystemTableSchema("kexi__useractions");
02980     t_useractions->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) )
02981     .addField( new Field("scope", Field::Integer ) )
02982     .addField( new Field("name", Field::LongText ) )
02983     .addField( new Field("text", Field::LongText ) )
02984     .addField( new Field("icon", Field::LongText ) )
02985     .addField( new Field("method", Field::Integer ) )
02986     .addField( new Field("arguments", Field::LongText) );
02987 */
02988     return true;
02989 }
02990 
02991 void Connection::removeMe(TableSchema *ts)
02992 {
02993     if (ts && !m_destructor_started) {
02994         d->tables.take(ts->id());
02995         d->tables_byname.take(ts->name());
02996     }
02997 }
02998 
02999 QString Connection::anyAvailableDatabaseName()
03000 {
03001     if (!d->availableDatabaseName.isEmpty()) {
03002         return d->availableDatabaseName;
03003     }
03004     return m_driver->beh->ALWAYS_AVAILABLE_DATABASE_NAME;
03005 }
03006 
03007 void Connection::setAvailableDatabaseName(const QString& dbName)
03008 {
03009     d->availableDatabaseName = dbName;
03010 }
03011 
03013 inline void updateRowDataWithNewValues(QuerySchema &query, RowData& data, KexiDB::RowEditBuffer::DBMap& b,
03014     QMap<QueryColumnInfo*,int>& columnsOrderExpanded)
03015 {
03016     columnsOrderExpanded = query.columnsOrder(QuerySchema::ExpandedList);
03017     QMap<QueryColumnInfo*,int>::ConstIterator columnsOrderExpandedIt;
03018     for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
03019         columnsOrderExpandedIt = columnsOrderExpanded.find( it.key() );
03020         if (columnsOrderExpandedIt == columnsOrderExpanded.constEnd()) {
03021             KexiDBWarn << "(Connection) updateRowDataWithNewValues(): \"now also assign new value in memory\" step "
03022                 "- could not find item '" << it.key()->aliasOrName() << "'" << endl;
03023             continue;
03024         }
03025         data[ columnsOrderExpandedIt.data() ] = it.data();
03026     }
03027 }
03028 
03029 bool Connection::updateRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool useROWID)
03030 {
03031 // Each SQL identifier needs to be escaped in the generated query.
03032 //  query.debug();
03033 
03034     KexiDBDbg << "Connection::updateRow.." << endl;
03035     clearError();
03036     //--get PKEY
03037     if (buf.dbBuffer().isEmpty()) {
03038         KexiDBDbg << " -- NO CHANGES DATA!" << endl;
03039         return true;
03040     }
03041     TableSchema *mt = query.masterTable();
03042     if (!mt) {
03043         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03044         setError(ERR_UPDATE_NO_MASTER_TABLE,
03045             i18n("Could not update row because there is no master table defined."));
03046         return false;
03047     }
03048     IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
03049     if (!useROWID && !pkey) {
03050         KexiDBWarn << " -- NO MASTER TABLE's PKEY!" << endl;
03051         setError(ERR_UPDATE_NO_MASTER_TABLES_PKEY,
03052             i18n("Could not update row because master table has no primary key defined."));
03054         return false;
03055     }
03056     //update the record:
03057     m_sql = "UPDATE " + escapeIdentifier(mt->name()) + " SET ";
03058     QString sqlset, sqlwhere;
03059     sqlset.reserve(1024);
03060     sqlwhere.reserve(1024);
03061     KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer();
03062     for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
03063         if (it.key()->field->table()!=mt)
03064             continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
03065         if (!sqlset.isEmpty())
03066             sqlset+=",";
03067         sqlset += (escapeIdentifier(it.key()->field->name()) + "=" +
03068             m_driver->valueToSQL(it.key()->field,it.data()));
03069     }
03070     if (pkey) {
03071         QValueVector<int> pkeyFieldsOrder = query.pkeyFieldsOrder();
03072         KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
03073         if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
03074             KexiDBWarn << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
03075             setError(ERR_UPDATE_NO_ENTIRE_MASTER_TABLES_PKEY,
03076                 i18n("Could not update row because it does not contain entire master table's primary key."));
03077             return false;
03078         }
03079         if (!pkey->fields()->isEmpty()) {
03080             uint i=0;
03081             for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) {
03082                 if (!sqlwhere.isEmpty())
03083                     sqlwhere+=" AND ";
03084                 QVariant val = data[ pkeyFieldsOrder[i] ];
03085                 if (val.isNull() || !val.isValid()) {
03086                     setError(ERR_UPDATE_NULL_PKEY_FIELD,
03087                         i18n("Primary key's field \"%1\" cannot be empty.").arg(it.current()->name()));
03088     //js todo: pass the field's name somewhere!
03089                     return false;
03090                 }
03091                 sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" +
03092                     m_driver->valueToSQL( it.current(), val ) );
03093             }
03094         }
03095     }
03096     else {//use ROWID
03097         sqlwhere = ( escapeIdentifier(m_driver->beh->ROW_ID_FIELD_NAME) + "="
03098             + m_driver->valueToSQL(Field::BigInteger, data[data.size()-1]));
03099     }
03100     m_sql += (sqlset + " WHERE " + sqlwhere);
03101     KexiDBDbg << " -- SQL == " << ((m_sql.length() > 400) ? (m_sql.left(400)+"[.....]") : m_sql) << endl;
03102 
03103     if (!executeSQL(m_sql)) {
03104         setError(ERR_UPDATE_SERVER_ERROR, i18n("Row updating on the server failed."));
03105         return false;
03106     }
03107     //success: now also assign new values in memory:
03108     QMap<QueryColumnInfo*,int> columnsOrderExpanded;
03109     updateRowDataWithNewValues(query, data, b, columnsOrderExpanded);
03110     return true;
03111 }
03112 
03113 bool Connection::insertRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool getROWID)
03114 {
03115 // Each SQL identifier needs to be escaped in the generated query.
03116     KexiDBDbg << "Connection::updateRow.." << endl;
03117     clearError();
03118     //--get PKEY
03119     /*disabled: there may be empty rows (with autoinc)
03120     if (buf.dbBuffer().isEmpty()) {
03121         KexiDBDbg << " -- NO CHANGES DATA!" << endl;
03122         return true; }*/
03123     TableSchema *mt = query.masterTable();
03124     if (!mt) {
03125         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03126         setError(ERR_INSERT_NO_MASTER_TABLE,
03127             i18n("Could not insert row because there is no master table defined."));
03128         return false;
03129     }
03130     IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
03131     if (!getROWID && !pkey)
03132         KexiDBWarn << " -- WARNING: NO MASTER TABLE's PKEY" << endl;
03133 
03134     QString sqlcols, sqlvals;
03135     sqlcols.reserve(1024);
03136     sqlvals.reserve(1024);
03137 
03138     //insert the record:
03139     m_sql = "INSERT INTO " + escapeIdentifier(mt->name()) + " (";
03140     KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer();
03141 
03142     // add default values, if available (for any column without value explicity set)
03143     const QueryColumnInfo::Vector fieldsExpanded( query.fieldsExpanded( QuerySchema::Unique ) );
03144     for (uint i=0; i<fieldsExpanded.count(); i++) {
03145         QueryColumnInfo *ci = fieldsExpanded.at(i);
03146         if (ci->field && KexiDB::isDefaultValueAllowed(ci->field) 
03147             && !ci->field->defaultValue().isNull() 
03148             && !b.contains( ci ))
03149         {
03150             KexiDBDbg << "Connection::insertRow(): adding default value '" << ci->field->defaultValue().toString()
03151                 << "' for column '" << ci->field->name() << "'" << endl;
03152             b.insert( ci, ci->field->defaultValue() );
03153         }
03154     }
03155 
03156     if (b.isEmpty()) {
03157         // empty row inserting requested:
03158         if (!getROWID && !pkey) {
03159             KexiDBWarn << "MASTER TABLE's PKEY REQUIRED FOR INSERTING EMPTY ROWS: INSERT CANCELLED" << endl;
03160             setError(ERR_INSERT_NO_MASTER_TABLES_PKEY,
03161                 i18n("Could not insert row because master table has no primary key defined."));
03162             return false;
03163         }
03164         if (pkey) {
03165             QValueVector<int> pkeyFieldsOrder = query.pkeyFieldsOrder();
03166 //          KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
03167             if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
03168                 KexiDBWarn << "NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
03169                 setError(ERR_INSERT_NO_ENTIRE_MASTER_TABLES_PKEY,
03170                     i18n("Could not insert row because it does not contain entire master table's primary key.")
03171                     .arg(query.name()));
03172                 return false;
03173             }
03174         }
03175         //at least one value is needed for VALUES section: find it and set to NULL:
03176         Field *anyField = mt->anyNonPKField();
03177         if (!anyField) {
03178             if (!pkey) {
03179                 KexiDBWarn << "WARNING: NO FIELD AVAILABLE TO SET IT TO NULL" << endl;
03180                 return false;
03181             }
03182             else {
03183                 //try to set NULL in pkey field (could not work for every SQL engine!)
03184                 anyField = pkey->fields()->first();
03185             }
03186         }
03187         sqlcols += escapeIdentifier(anyField->name());
03188         sqlvals += m_driver->valueToSQL(anyField,QVariant()/*NULL*/);
03189     }
03190     else {
03191         // non-empty row inserting requested:
03192         for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
03193             if (it.key()->field->table()!=mt)
03194                 continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
03195             if (!sqlcols.isEmpty()) {
03196                 sqlcols+=",";
03197                 sqlvals+=",";
03198             }
03199             sqlcols += escapeIdentifier(it.key()->field->name());
03200             sqlvals += m_driver->valueToSQL(it.key()->field,it.data());
03201         }
03202     }
03203     m_sql += (sqlcols + ") VALUES (" + sqlvals + ")");
03204 //  KexiDBDbg << " -- SQL == " << m_sql << endl;
03205 
03206     bool res = executeSQL(m_sql);
03207 
03208     if (!res) {
03209         setError(ERR_INSERT_SERVER_ERROR, i18n("Row inserting on the server failed."));
03210         return false;
03211     }
03212     //success: now also assign a new value in memory:
03213     QMap<QueryColumnInfo*,int> columnsOrderExpanded;
03214     updateRowDataWithNewValues(query, data, b, columnsOrderExpanded);
03215 
03216     //fetch autoincremented values
03217     QueryColumnInfo::List *aif_list = query.autoIncrementFields();
03218     Q_ULLONG ROWID = 0;
03219     if (pkey && !aif_list->isEmpty()) {
03221         QueryColumnInfo *id_columnInfo = aif_list->first();
03223         Q_ULLONG last_id = lastInsertedAutoIncValue(
03224             id_columnInfo->field->name(), id_columnInfo->field->table()->name(), &ROWID);
03225         if (last_id==(Q_ULLONG)-1 || last_id<=0) {
03228             return false;
03229         }
03230         RowData aif_data;
03231         QString getAutoIncForInsertedValue = QString::fromLatin1("SELECT ")
03232             + query.autoIncrementSQLFieldsList(m_driver)
03233             + QString::fromLatin1(" FROM ")
03234             + escapeIdentifier(id_columnInfo->field->table()->name())
03235             + QString::fromLatin1(" WHERE ")
03236             + escapeIdentifier(id_columnInfo->field->name()) + "="
03237             + QString::number(last_id);
03238         if (true!=querySingleRecord(getAutoIncForInsertedValue, aif_data)) {
03240             return false;
03241         }
03242         QueryColumnInfo::ListIterator ci_it(*aif_list);
03243         QueryColumnInfo *ci;
03244         for (uint i=0; (ci = ci_it.current()); ++ci_it, i++) {
03245 //          KexiDBDbg << "Connection::insertRow(): AUTOINCREMENTED FIELD " << fi->field->name() << " == "
03246 //              << aif_data[i].toInt() << endl;
03247             ( data[ columnsOrderExpanded[ ci ] ] = aif_data[i] ).cast( ci->field->variantType() ); //cast to get proper type
03248         }
03249     }
03250     else {
03251         ROWID = drv_lastInsertRowID();
03252 //      KexiDBDbg << "Connection::insertRow(): new ROWID == " << (uint)ROWID << endl;
03253         if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
03254             KexiDBWarn << "Connection::insertRow(): m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE" << endl;
03255             return false;
03256         }
03257     }
03258     if (getROWID && /*sanity check*/data.size() > fieldsExpanded.size()) {
03259 //      KexiDBDbg << "Connection::insertRow(): new ROWID == " << (uint)ROWID << endl;
03260         data[data.size()-1] = ROWID;
03261     }
03262     return true;
03263 }
03264 
03265 bool Connection::deleteRow(QuerySchema &query, RowData& data, bool useROWID)
03266 {
03267 // Each SQL identifier needs to be escaped in the generated query.
03268     KexiDBWarn << "Connection::deleteRow.." << endl;
03269     clearError();
03270     TableSchema *mt = query.masterTable();
03271     if (!mt) {
03272         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03273         setError(ERR_DELETE_NO_MASTER_TABLE,
03274             i18n("Could not delete row because there is no master table defined.")
03275             .arg(query.name()));
03276         return false;
03277     }
03278     IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
03279 
03281     if (!useROWID && !pkey) {
03282         KexiDBWarn << " -- WARNING: NO MASTER TABLE's PKEY" << endl;
03283         setError(ERR_DELETE_NO_MASTER_TABLES_PKEY,
03284             i18n("Could not delete row because there is no primary key for master table defined."));
03285         return false;
03286     }
03287 
03288     //update the record:
03289     m_sql = "DELETE FROM " + escapeIdentifier(mt->name()) + " WHERE ";
03290     QString sqlwhere;
03291     sqlwhere.reserve(1024);
03292 
03293     if (pkey) {
03294         QValueVector<int> pkeyFieldsOrder = query.pkeyFieldsOrder();
03295         KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
03296         if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
03297             KexiDBWarn << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
03298             setError(ERR_DELETE_NO_ENTIRE_MASTER_TABLES_PKEY,
03299                 i18n("Could not delete row because it does not contain entire master table's primary key."));
03300             return false;
03301         }
03302         uint i=0;
03303         for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) {
03304             if (!sqlwhere.isEmpty())
03305                 sqlwhere+=" AND ";
03306             QVariant val = data[ pkeyFieldsOrder[i] ];
03307             if (val.isNull() || !val.isValid()) {
03308                 setError(ERR_DELETE_NULL_PKEY_FIELD, i18n("Primary key's field \"%1\" cannot be empty.")
03309                     .arg(it.current()->name()));
03310 //js todo: pass the field's name somewhere!
03311                 return false;
03312             }
03313             sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" +
03314                 m_driver->valueToSQL( it.current(), val ) );
03315         }
03316     }
03317     else {//use ROWID
03318         sqlwhere = ( escapeIdentifier(m_driver->beh->ROW_ID_FIELD_NAME) + "="
03319             + m_driver->valueToSQL(Field::BigInteger, data[data.size()-1]));
03320     }
03321     m_sql += sqlwhere;
03322     KexiDBDbg << " -- SQL == " << m_sql << endl;
03323 
03324     if (!executeSQL(m_sql)) {
03325         setError(ERR_DELETE_SERVER_ERROR, i18n("Row deletion on the server failed."));
03326         return false;
03327     }
03328     return true;
03329 }
03330 
03331 bool Connection::deleteAllRows(QuerySchema &query)
03332 {
03333     clearError();
03334     TableSchema *mt = query.masterTable();
03335     if (!mt) {
03336         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03337         return false;
03338     }
03339     IndexSchema *pkey = mt->primaryKey();
03340     if (!pkey || pkey->fields()->isEmpty())
03341         KexiDBWarn << "Connection::deleteAllRows -- WARNING: NO MASTER TABLE's PKEY" << endl;
03342 
03343     m_sql = "DELETE FROM " + escapeIdentifier(mt->name());
03344 
03345     KexiDBDbg << " -- SQL == " << m_sql << endl;
03346 
03347     if (!executeSQL(m_sql)) {
03348         setError(ERR_DELETE_SERVER_ERROR, i18n("Row deletion on the server failed."));
03349         return false;
03350     }
03351     return true;
03352 }
03353 
03354 void Connection::registerForTableSchemaChanges(TableSchemaChangeListenerInterface& listener,
03355     TableSchema &schema)
03356 {
03357     QPtrList<TableSchemaChangeListenerInterface>* listeners = d->tableSchemaChangeListeners[&schema];
03358     if (!listeners) {
03359         listeners = new QPtrList<TableSchemaChangeListenerInterface>();
03360         d->tableSchemaChangeListeners.insert(&schema, listeners);
03361     }
03362 //TODO: inefficient
03363     if (listeners->findRef( &listener )==-1)
03364         listeners->append( &listener );
03365 }
03366 
03367 void Connection::unregisterForTableSchemaChanges(TableSchemaChangeListenerInterface& listener,
03368     TableSchema &schema)
03369 {
03370     QPtrList<TableSchemaChangeListenerInterface>* listeners = d->tableSchemaChangeListeners[&schema];
03371     if (!listeners)
03372         return;
03373 //TODO: inefficient
03374     listeners->remove( &listener );
03375 }
03376 
03377 void Connection::unregisterForTablesSchemaChanges(TableSchemaChangeListenerInterface& listener)
03378 {
03379     for (QPtrDictIterator< QPtrList<TableSchemaChangeListenerInterface> > it(d->tableSchemaChangeListeners);
03380         it.current(); ++it)
03381     {
03382         if (-1!=it.current()->find(&listener))
03383             it.current()->take();
03384     }
03385 }
03386 
03387 QPtrList<Connection::TableSchemaChangeListenerInterface>*
03388 Connection::tableSchemaChangeListeners(TableSchema& tableSchema) const
03389 {
03390     KexiDBDbg << d->tableSchemaChangeListeners.count() << endl;
03391     return d->tableSchemaChangeListeners[&tableSchema];
03392 }
03393 
03394 tristate Connection::closeAllTableSchemaChangeListeners(TableSchema& tableSchema)
03395 {
03396     QPtrList<Connection::TableSchemaChangeListenerInterface> *listeners = d->tableSchemaChangeListeners[&tableSchema];
03397     if (!listeners)
03398         return true;
03399     QPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> tmpListeners(*listeners); //safer copy
03400     tristate res = true;
03401     //try to close every window
03402     for (QPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> it(tmpListeners);
03403         it.current() && res==true; ++it)
03404     {
03405         res = it.current()->closeListener();
03406     }
03407     return res;
03408 }
03409 
03410 /*PreparedStatement::Ptr Connection::prepareStatement(PreparedStatement::StatementType, 
03411         TableSchema&)
03412 {
03413     //safe?
03414     return 0;
03415 }*/
03416 
03417 void Connection::setReadOnly(bool set)
03418 {
03419     if (d->isConnected)
03420         return; //sanity
03421     d->readOnly = set;
03422 }
03423 
03424 bool Connection::isReadOnly() const
03425 {
03426     return d->readOnly;
03427 }
03428 
03429 #include "connection.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys