kexi

connection.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2007 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 one 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                 QString valToSQL( m_driver->valueToSQL( field, field->defaultValue() ) );
00926                 if (!valToSQL.isEmpty()) //for sanity
00927                     v += QString::fromLatin1(" DEFAULT ") + valToSQL;
00928             }
00929         }
00930         sql += v;
00931     }
00932     sql += ")";
00933     return sql;
00934 }
00935 
00936 //yeah, it is very efficient:
00937 #define C_A(a) , const QVariant& c ## a
00938 
00939 #define V_A0 m_driver->valueToSQL( tableSchema.field(0), c0 )
00940 #define V_A(a) +","+m_driver->valueToSQL( \
00941     tableSchema.field(a) ? tableSchema.field(a)->type() : Field::Text, c ## a )
00942 
00943 //      KexiDBDbg << "******** " << QString("INSERT INTO ") + 
00944 //          escapeIdentifier(tableSchema.name()) + 
00945 //          " VALUES (" + vals + ")" <<endl; 
00946 
00947 #define C_INS_REC(args, vals) \
00948     bool Connection::insertRecord(KexiDB::TableSchema &tableSchema args) {\
00949         return executeSQL( \
00950          QString("INSERT INTO ") + escapeIdentifier(tableSchema.name()) + " VALUES (" + vals + ")" \
00951         ); \
00952     }
00953 
00954 #define C_INS_REC_ALL \
00955 C_INS_REC( C_A(0), V_A0 ) \
00956 C_INS_REC( C_A(0) C_A(1), V_A0 V_A(1) ) \
00957 C_INS_REC( C_A(0) C_A(1) C_A(2), V_A0 V_A(1) V_A(2) ) \
00958 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) ) \
00959 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) ) \
00960 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) ) \
00961 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) ) \
00962 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) )
00963 
00964 C_INS_REC_ALL
00965 
00966 #undef V_A0
00967 #undef V_A
00968 #undef C_INS_REC
00969 
00970 #define V_A0 value += m_driver->valueToSQL( flist->first(), c0 );
00971 #define V_A( a ) value += ("," + m_driver->valueToSQL( flist->next(), c ## a ));
00972 //#define V_ALAST( a ) valueToSQL( flist->last(), c ## a )
00973 
00974 
00975 #define C_INS_REC(args, vals) \
00976     bool Connection::insertRecord(FieldList& fields args) \
00977     { \
00978         QString value; \
00979         Field::List *flist = fields.fields(); \
00980         vals \
00981         return executeSQL( \
00982             QString("INSERT INTO ") + \
00983         ((fields.fields()->first() && fields.fields()->first()->table()) ? \
00984             escapeIdentifier(fields.fields()->first()->table()->name()) : \
00985             "??") \
00986         + "(" + fields.sqlFieldsList(m_driver) + ") VALUES (" + value + ")" \
00987         ); \
00988     }
00989 
00990 C_INS_REC_ALL
00991 
00992 #undef C_A
00993 #undef V_A
00994 #undef V_ALAST
00995 #undef C_INS_REC
00996 #undef C_INS_REC_ALL
00997 
00998 bool Connection::insertRecord(TableSchema &tableSchema, QValueList<QVariant>& values)
00999 {
01000 // Each SQL identifier needs to be escaped in the generated query.
01001     Field::List *fields = tableSchema.fields();
01002     Field *f = fields->first();
01003 //  QString s_val;
01004 //  s_val.reserve(4096);
01005     m_sql = QString::null;
01006     QValueList<QVariant>::ConstIterator it = values.constBegin();
01007 //  int i=0;
01008     while (f && (it!=values.end())) {
01009         if (m_sql.isEmpty())
01010             m_sql = QString("INSERT INTO ") +
01011                 escapeIdentifier(tableSchema.name()) +
01012                 " VALUES (";
01013         else
01014             m_sql += ",";
01015         m_sql += m_driver->valueToSQL( f, *it );
01016 //      KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl;
01017         ++it;
01018         f=fields->next();
01019     }
01020     m_sql += ")";
01021 
01022 //  KexiDBDbg<<"******** "<< m_sql << endl;
01023     return executeSQL(m_sql);
01024 }
01025 
01026 bool Connection::insertRecord(FieldList& fields, QValueList<QVariant>& values)
01027 {
01028 // Each SQL identifier needs to be escaped in the generated query.
01029     Field::List *flist = fields.fields();
01030     Field *f = flist->first();
01031     if (!f)
01032         return false;
01033 //  QString s_val;
01034 //  s_val.reserve(4096);
01035     m_sql = QString::null;
01036     QValueList<QVariant>::ConstIterator it = values.constBegin();
01037 //  int i=0;
01038     while (f && (it!=values.constEnd())) {
01039         if (m_sql.isEmpty())
01040             m_sql = QString("INSERT INTO ") +
01041                 escapeIdentifier(flist->first()->table()->name()) + "(" +
01042                 fields.sqlFieldsList(m_driver) + ") VALUES (";
01043         else
01044             m_sql += ",";
01045         m_sql += m_driver->valueToSQL( f, *it );
01046 //      KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl;
01047         ++it;
01048         f=flist->next();
01049     }
01050     m_sql += ")";
01051 
01052     return executeSQL(m_sql);
01053 }
01054 
01055 bool Connection::executeSQL( const QString& statement )
01056 {
01057     m_sql = statement; //remember for error handling
01058     if (!drv_executeSQL( m_sql )) {
01059         m_errMsg = QString::null; //clear as this could be most probably jsut "Unknown error" string.
01060         m_errorSql = statement;
01061         setError(this, ERR_SQL_EXECUTION_ERROR, i18n("Error while executing SQL statement."));
01062         return false;
01063     }
01064     return true;
01065 }
01066 
01067 QString Connection::selectStatement( KexiDB::QuerySchema& querySchema,
01068     const QValueList<QVariant>& params, 
01069     const SelectStatementOptions& options) const
01070 {
01071 //"SELECT FROM ..." is theoretically allowed "
01072 //if (querySchema.fieldCount()<1)
01073 //      return QString::null;
01074 // Each SQL identifier needs to be escaped in the generated query.
01075 
01076     if (!querySchema.statement().isEmpty())
01077         return querySchema.statement();
01078 
01081     Field *f;
01082     uint number = 0;
01083     bool singleTable = querySchema.tables()->count() <= 1;
01084     if (singleTable) {
01085         //make sure we will have single table:
01086         for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) {
01087             if (querySchema.isColumnVisible(number) && f->table() && f->table()->lookupFieldSchema( *f )) {
01088                 //uups, no, there's at least one left join
01089                 singleTable = false;
01090                 break;
01091             }
01092         }
01093     }
01094 
01095     QString sql; //final sql string
01096     sql.reserve(4096);
01097 //unused    QString s_from_additional; //additional tables list needed for lookup fields
01098     QString s_additional_joins; //additional joins needed for lookup fields
01099     QString s_additional_fields; //additional fields to append to the fields list
01100     uint internalUniqueTableAliasNumber = 0; //used to build internalUniqueTableAliases
01101     uint internalUniqueQueryAliasNumber = 0; //used to build internalUniqueQueryAliases
01102     number = 0;
01103     QPtrList<QuerySchema> subqueries_for_lookup_data; // subqueries will be added to FROM section
01104     QString kexidb_subquery_prefix("__kexidb_subquery_");
01105     for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) {
01106         if (querySchema.isColumnVisible(number)) {
01107             if (!sql.isEmpty())
01108                 sql += QString::fromLatin1(", ");
01109 
01110             if (f->isQueryAsterisk()) {
01111                 if (!singleTable && static_cast<QueryAsterisk*>(f)->isSingleTableAsterisk()) //single-table *
01112                     sql += escapeIdentifier(f->table()->name(), options.identifierEscaping) +
01113                            QString::fromLatin1(".*");
01114                 else //all-tables * (or simplified table.* when there's only one table)
01115                     sql += QString::fromLatin1("*");
01116             }
01117             else {
01118                 if (f->isExpression()) {
01119                     sql += f->expression()->toString();
01120                 }
01121                 else {
01122                     if (!f->table()) //sanity check
01123                         return QString::null;
01124 
01125                     QString tableName;
01126                     int tablePosition = querySchema.tableBoundToColumn(number);
01127                     if (tablePosition>=0)
01128                         tableName = querySchema.tableAlias(tablePosition);
01129                     if (tableName.isEmpty())
01130                         tableName = f->table()->name();
01131 
01132                     if (!singleTable) {
01133                         sql += (escapeIdentifier(tableName, options.identifierEscaping) + ".");
01134                     }
01135                     sql += escapeIdentifier(f->name(), options.identifierEscaping);
01136                 }
01137                 QString aliasString = QString(querySchema.columnAlias(number));
01138                 if (!aliasString.isEmpty())
01139                     sql += (QString::fromLatin1(" AS ") + aliasString);
01141             }
01142             LookupFieldSchema *lookupFieldSchema = f->table() ? f->table()->lookupFieldSchema( *f ) : 0;
01143             if (lookupFieldSchema && lookupFieldSchema->boundColumn()>=0) {
01144                 // Lookup field schema found
01145                 // Now we also need to fetch "visible" value from the lookup table, not only the value of binding.
01146                 // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken)
01147                 // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField"
01148                 LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource();
01149                 if (rowSource.type()==LookupFieldSchema::RowSource::Table) {
01150                     TableSchema *lookupTable = querySchema.connection()->tableSchema( rowSource.name() );
01151                     FieldList* visibleColumns = 0;
01152                     Field *boundField = 0;
01153                     if (lookupTable
01154                         && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
01155                         && (visibleColumns = lookupTable->subList( lookupFieldSchema->visibleColumns() ))
01156                         && (boundField = lookupTable->field( lookupFieldSchema->boundColumn() )))
01157                     {
01158                         //add LEFT OUTER JOIN
01159                         if (!s_additional_joins.isEmpty())
01160                             s_additional_joins += QString::fromLatin1(" ");
01161                         QString internalUniqueTableAlias( QString("__kexidb_") + lookupTable->name() + "_"
01162                             + QString::number(internalUniqueTableAliasNumber++) );
01163                         s_additional_joins += QString("LEFT OUTER JOIN %1 AS %2 ON %3.%4=%5.%6")
01164                             .arg(escapeIdentifier(lookupTable->name(), options.identifierEscaping))
01165                             .arg(internalUniqueTableAlias)
01166                             .arg(escapeIdentifier(f->table()->name(), options.identifierEscaping))
01167                             .arg(escapeIdentifier(f->name(), options.identifierEscaping))
01168                             .arg(internalUniqueTableAlias)
01169                             .arg(escapeIdentifier(boundField->name(), options.identifierEscaping));
01170 
01171                         //add visibleField to the list of SELECTed fields //if it is not yet present there
01172 //not needed                        if (!querySchema.findTableField( visibleField->table()->name()+"."+visibleField->name() )) {
01173 #if 0
01174                         if (!querySchema.table( visibleField->table()->name() )) {
01175 /* not true
01176                             //table should be added after FROM
01177                             if (!s_from_additional.isEmpty())
01178                                 s_from_additional += QString::fromLatin1(", ");
01179                             s_from_additional += escapeIdentifier(visibleField->table()->name(), options.identifierEscaping);
01180                             */
01181                         }
01182 #endif
01183                         if (!s_additional_fields.isEmpty())
01184                             s_additional_fields += QString::fromLatin1(", ");
01185 //                          s_additional_fields += (internalUniqueTableAlias + "." //escapeIdentifier(visibleField->table()->name(), options.identifierEscaping) + "."
01186 //                                  escapeIdentifier(visibleField->name(), options.identifierEscaping));
01189                         s_additional_fields += visibleColumns->sqlFieldsList(
01190                             driver(), " || ' ' || ", internalUniqueTableAlias, options.identifierEscaping);
01191                     }
01192                     delete visibleColumns;
01193                 }
01194                 else if (rowSource.type()==LookupFieldSchema::RowSource::Query) {
01195                     QuerySchema *lookupQuery = querySchema.connection()->querySchema( rowSource.name() );
01196                     if (!lookupQuery) {
01197                         KexiDBWarn << "Connection::selectStatement(): !lookupQuery" << endl;
01198                         return QString::null;
01199                     }
01200                     const QueryColumnInfo::Vector fieldsExpanded( lookupQuery->fieldsExpanded() );
01201                     if ((uint)lookupFieldSchema->boundColumn() >= fieldsExpanded.count()) {
01202                         KexiDBWarn << "Connection::selectStatement(): (uint)lookupFieldSchema->boundColumn() >= fieldsExpanded.count()" << endl;
01203                         return QString::null;
01204                     }
01205                     QueryColumnInfo *boundColumnInfo = fieldsExpanded.at( lookupFieldSchema->boundColumn() );
01206                     if (!boundColumnInfo) {
01207                         KexiDBWarn << "Connection::selectStatement(): !boundColumnInfo" << endl;
01208                         return QString::null;
01209                     }
01210                     Field *boundField = boundColumnInfo->field;
01211                     if (!boundField) {
01212                         KexiDBWarn << "Connection::selectStatement(): !boundField" << endl;
01213                         return QString::null;
01214                     }
01215                     //add LEFT OUTER JOIN
01216                     if (!s_additional_joins.isEmpty())
01217                         s_additional_joins += QString::fromLatin1(" ");
01218                     QString internalUniqueQueryAlias( 
01219                         kexidb_subquery_prefix + lookupQuery->name() + "_"
01220                         + QString::number(internalUniqueQueryAliasNumber++) );
01221                     s_additional_joins += QString("LEFT OUTER JOIN (%1) AS %2 ON %3.%4=%5.%6")
01222                         .arg(selectStatement( *lookupQuery, params, options ))
01223                         .arg(internalUniqueQueryAlias)
01224                         .arg(escapeIdentifier(f->table()->name(), options.identifierEscaping))
01225                         .arg(escapeIdentifier(f->name(), options.identifierEscaping))
01226                         .arg(internalUniqueQueryAlias)
01227                         .arg(escapeIdentifier(boundColumnInfo->aliasOrName(), options.identifierEscaping));
01228 
01229                     if (!s_additional_fields.isEmpty())
01230                         s_additional_fields += QString::fromLatin1(", ");
01231                     const QValueList<uint> visibleColumns( lookupFieldSchema->visibleColumns() );
01232                     QString expression;
01233                     foreach (QValueList<uint>::ConstIterator, visibleColumnsIt, visibleColumns) {
01236                         if (fieldsExpanded.count() <= (*visibleColumnsIt)) {
01237                             KexiDBWarn << "Connection::selectStatement(): fieldsExpanded.count() <= (*visibleColumnsIt) : "
01238                                 << fieldsExpanded.count() << " <= " << *visibleColumnsIt << endl;
01239                             return QString::null;
01240                         }
01241                         if (!expression.isEmpty())
01242                             expression += " || ' ' || ";
01243                         expression += (internalUniqueQueryAlias + "." + 
01244                             escapeIdentifier(fieldsExpanded[*visibleColumnsIt]->aliasOrName(),
01245                                 options.identifierEscaping));
01246                     }
01247                     s_additional_fields += expression;
01248 //subqueries_for_lookup_data.append(lookupQuery);
01249                 }
01250                 else {
01251                     KexiDBWarn << "Connection::selectStatement(): unsupported row source type " 
01252                         << rowSource.typeName() << endl;
01253                     return false;
01254                 }
01255             }
01256         }
01257     }
01258 
01259     //add lookup fields
01260     if (!s_additional_fields.isEmpty())
01261         sql += (QString::fromLatin1(", ") + s_additional_fields);
01262 
01263     if (options.alsoRetrieveROWID) { //append rowid column
01264         QString s;
01265         if (!sql.isEmpty())
01266             s = QString::fromLatin1(", ");
01267         if (querySchema.masterTable())
01268             s += (escapeIdentifier(querySchema.masterTable()->name())+".");
01269         s += m_driver->beh->ROW_ID_FIELD_NAME;
01270         sql += s;
01271     }
01272 
01273     sql.prepend("SELECT ");
01274     TableSchema::List* tables = querySchema.tables();
01275     if ((tables && !tables->isEmpty()) || !subqueries_for_lookup_data.isEmpty()) {
01276         sql += QString::fromLatin1(" FROM ");
01277         QString s_from;
01278         if (tables) {
01279             TableSchema *table;
01280             number = 0;
01281             for (TableSchema::ListIterator it(*tables); (table = it.current());
01282                 ++it, number++)
01283             {
01284                 if (!s_from.isEmpty())
01285                     s_from += QString::fromLatin1(", ");
01286                 s_from += escapeIdentifier(table->name(), options.identifierEscaping);
01287                 QString aliasString = QString(querySchema.tableAlias(number));
01288                 if (!aliasString.isEmpty())
01289                     s_from += (QString::fromLatin1(" AS ") + aliasString);
01290             }
01291     /*unused    if (!s_from_additional.isEmpty()) {//additional tables list needed for lookup fields
01292                 if (!s_from.isEmpty())
01293                     s_from += QString::fromLatin1(", ");
01294                 s_from += s_from_additional;
01295             }*/
01296         }
01297         // add subqueries for lookup data
01298         uint subqueries_for_lookup_data_counter = 0;
01299         for (QPtrListIterator<QuerySchema> it(subqueries_for_lookup_data); 
01300             subqueries_for_lookup_data.current();   ++it, subqueries_for_lookup_data_counter++)
01301         {
01302             if (!s_from.isEmpty())
01303                 s_from += QString::fromLatin1(", ");
01304             s_from += QString::fromLatin1("(");
01305             s_from += selectStatement( *it.current(), params, options );
01306             s_from += QString::fromLatin1(") AS %1%2")
01307                 .arg(kexidb_subquery_prefix).arg(subqueries_for_lookup_data_counter);
01308         }
01309         sql += s_from;
01310     }
01311     QString s_where;
01312     s_where.reserve(4096);
01313 
01314     //JOINS
01315     if (!s_additional_joins.isEmpty()) {
01316         sql += QString::fromLatin1(" ") + s_additional_joins + QString::fromLatin1(" ");
01317     }
01318 
01319 //@todo: we're using WHERE for joins now; use INNER/LEFT/RIGHT JOIN later
01320 
01321     //WHERE
01322     Relationship *rel;
01323     bool wasWhere = false; //for later use
01324     for (Relationship::ListIterator it(*querySchema.relationships()); (rel = it.current()); ++it) {
01325         if (s_where.isEmpty()) {
01326             wasWhere = true;
01327         }
01328         else
01329             s_where += QString::fromLatin1(" AND ");
01330         Field::Pair *pair;
01331         QString s_where_sub;
01332         for (QPtrListIterator<Field::Pair> p_it(*rel->fieldPairs()); (pair = p_it.current()); ++p_it) {
01333             if (!s_where_sub.isEmpty())
01334                 s_where_sub += QString::fromLatin1(" AND ");
01335             s_where_sub += (
01336                 escapeIdentifier(pair->first->table()->name(), options.identifierEscaping) +
01337                 QString::fromLatin1(".") +
01338                 escapeIdentifier(pair->first->name(), options.identifierEscaping) +
01339                 QString::fromLatin1(" = ")  +
01340                 escapeIdentifier(pair->second->table()->name(), options.identifierEscaping) +
01341                 QString::fromLatin1(".") +
01342                 escapeIdentifier(pair->second->name(), options.identifierEscaping));
01343         }
01344         if (rel->fieldPairs()->count()>1) {
01345             s_where_sub.prepend("(");
01346             s_where_sub += QString::fromLatin1(")");
01347         }
01348         s_where += s_where_sub;
01349     }
01350     //EXPLICITLY SPECIFIED WHERE EXPRESSION
01351     if (querySchema.whereExpression()) {
01352         QuerySchemaParameterValueListIterator paramValuesIt(*m_driver, params);
01353         QuerySchemaParameterValueListIterator *paramValuesItPtr = params.isEmpty() ? 0 : &paramValuesIt;
01354         if (wasWhere) {
01355 //TODO: () are not always needed
01356             s_where = "(" + s_where + ") AND (" + querySchema.whereExpression()->toString(paramValuesItPtr) + ")";
01357         }
01358         else {
01359             s_where = querySchema.whereExpression()->toString(paramValuesItPtr);
01360         }
01361     }
01362     if (!s_where.isEmpty())
01363         sql += QString::fromLatin1(" WHERE ") + s_where;
01365     //(use wasWhere here)
01366     
01367     // ORDER BY
01368     QString orderByString( 
01369         querySchema.orderByColumnList().toSQLString(!singleTable/*includeTableName*/, 
01370             driver(), options.identifierEscaping) );
01371     const QValueVector<int> pkeyFieldsOrder( querySchema.pkeyFieldsOrder() ); 
01372     if (orderByString.isEmpty() && !pkeyFieldsOrder.isEmpty()) {
01373         //add automatic ORDER BY if there is no explicity defined (especially helps when there are complex JOINs)
01374         OrderByColumnList automaticPKOrderBy;
01375         const QueryColumnInfo::Vector fieldsExpanded( querySchema.fieldsExpanded() );
01376         foreach (QValueVector<int>::ConstIterator, it, pkeyFieldsOrder) {
01377             if ((*it) < 0) // no field mentioned in this query
01378                 continue;
01379             if ((*it) >= (int)fieldsExpanded.count()) {
01380                 KexiDBWarn << "Connection::selectStatement(): ORDER BY: (*it) >= fieldsExpanded.count() - " 
01381                     << (*it) << " >= " << fieldsExpanded.count() << endl;
01382                 continue;
01383             }
01384             QueryColumnInfo *ci = fieldsExpanded[ *it ];
01385             automaticPKOrderBy.appendColumn( *ci );
01386         }
01387         orderByString = automaticPKOrderBy.toSQLString(!singleTable/*includeTableName*/, 
01388             driver(), options.identifierEscaping);
01389     }
01390     if (!orderByString.isEmpty())
01391         sql += (" ORDER BY " + orderByString);
01392     
01393     //KexiDBDbg << sql << endl;
01394     return sql;
01395 }
01396 
01397 QString Connection::selectStatement( KexiDB::TableSchema& tableSchema,
01398     const SelectStatementOptions& options) const
01399 {
01400     return selectStatement( *tableSchema.query(), options );
01401 }
01402 
01403 Field* Connection::findSystemFieldName(KexiDB::FieldList* fieldlist)
01404 {
01405     Field *f = fieldlist->fields()->first();
01406     while (f) {
01407         if (m_driver->isSystemFieldName( f->name() ))
01408             return f;
01409         f = fieldlist->fields()->next();
01410     }
01411     return 0;
01412 }
01413 
01414 Q_ULLONG Connection::lastInsertedAutoIncValue(const QString& aiFieldName, const QString& tableName,
01415     Q_ULLONG* ROWID)
01416 {
01417     Q_ULLONG row_id = drv_lastInsertRowID();
01418     if (ROWID)
01419         *ROWID = row_id;
01420     if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
01421         return row_id;
01422     }
01423     RowData rdata;
01424     if (row_id<=0 || true!=querySingleRecord(
01425         QString::fromLatin1("SELECT ") + tableName + QString::fromLatin1(".") + aiFieldName + QString::fromLatin1(" FROM ") + tableName
01426         + QString::fromLatin1(" WHERE ") + m_driver->beh->ROW_ID_FIELD_NAME + QString::fromLatin1("=") + QString::number(row_id), rdata))
01427     {
01428 //      KexiDBDbg << "Connection::lastInsertedAutoIncValue(): row_id<=0 || true!=querySingleRecord()" << endl;
01429         return (Q_ULLONG)-1; //ULL;
01430     }
01431     return rdata[0].toULongLong();
01432 }
01433 
01434 Q_ULLONG Connection::lastInsertedAutoIncValue(const QString& aiFieldName,
01435     const KexiDB::TableSchema& table, Q_ULLONG* ROWID)
01436 {
01437     return lastInsertedAutoIncValue(aiFieldName,table.name(), ROWID);
01438 }
01439 
01441 static FieldList* createFieldListForKexi__Fields(TableSchema *kexi__fieldsSchema)
01442 {
01443     if (!kexi__fieldsSchema)
01444         return 0;
01445     return kexi__fieldsSchema->subList(
01446         "t_id",
01447         "f_type",
01448         "f_name",
01449         "f_length",
01450         "f_precision",
01451         "f_constraints",
01452         "f_options",
01453         "f_default",
01454         "f_order",
01455         "f_caption",
01456         "f_help"
01457     );
01458 }
01459 
01461 void buildValuesForKexi__Fields(QValueList<QVariant>& vals, Field* f)
01462 {
01463     vals.clear();
01464     vals
01465     << QVariant(f->table()->id())
01466     << QVariant(f->type())
01467     << QVariant(f->name())
01468     << QVariant(f->isFPNumericType() ? f->scale() : f->length())
01469     << QVariant(f->isFPNumericType() ? f->precision() : 0)
01470     << QVariant(f->constraints())
01471     << QVariant(f->options())
01472         // KexiDB::variantToString() is needed here because the value can be of any QVariant type, 
01473         // depending on f->type()
01474     << (f->defaultValue().isNull() 
01475             ? QVariant() : QVariant(KexiDB::variantToString( f->defaultValue() ))) 
01476     << QVariant(f->order())
01477     << QVariant(f->caption())
01478     << QVariant(f->description());
01479 }
01480 
01481 bool Connection::storeMainFieldSchema(Field *field)
01482 {
01483     if (!field || !field->table())
01484         return false;
01485     FieldList *fl = createFieldListForKexi__Fields(d->tables_byname["kexi__fields"]);
01486     if (!fl)
01487         return false;
01488 
01489     QValueList<QVariant> vals;
01490     buildValuesForKexi__Fields(vals, field);
01491     QValueList<QVariant>::ConstIterator valsIt = vals.constBegin();
01492     Field *f;
01493     bool first = true;
01494     QString sql = "UPDATE kexi__fields SET ";
01495     for (Field::ListIterator it( fl->fieldsIterator() ); (f = it.current()); ++it, ++valsIt) {
01496         sql.append( (first ? QString::null : QString(", ")) +
01497             f->name() + "=" + m_driver->valueToSQL( f, *valsIt ) );
01498         if (first)
01499             first = false;
01500     }
01501     delete fl;
01502     
01503     sql.append(QString(" WHERE t_id=") + QString::number( field->table()->id() )
01504         + " AND f_name=" + m_driver->valueToSQL( Field::Text, field->name() ) );
01505     return executeSQL( sql );
01506 }
01507 
01508 #define createTable_ERR \
01509     { KexiDBDbg << "Connection::createTable(): ERROR!" <<endl; \
01510       setError(this, i18n("Creating table failed.")); \
01511       rollbackAutoCommitTransaction(tg.transaction()); \
01512       return false; }
01513         //setError( errorNum(), i18n("Creating table failed.") + " " + errorMsg()); 
01514 
01516 
01523 bool Connection::createTable( KexiDB::TableSchema* tableSchema, bool replaceExisting )
01524 {
01525     if (!tableSchema || !checkIsDatabaseUsed())
01526         return false;
01527 
01528     //check if there are any fields
01529     if (tableSchema->fieldCount()<1) {
01530         clearError();
01531         setError(ERR_CANNOT_CREATE_EMPTY_OBJECT, i18n("Cannot create table without fields."));
01532         return false;
01533     }
01534     const bool internalTable = dynamic_cast<InternalTableSchema*>(tableSchema);
01535 
01536     const QString &tableName = tableSchema->name().lower();
01537 
01538     if (!internalTable) {
01539         if (m_driver->isSystemObjectName( tableName )) {
01540             clearError();
01541             setError(ERR_SYSTEM_NAME_RESERVED, i18n("System name \"%1\" cannot be used as table name.")
01542                 .arg(tableSchema->name()));
01543             return false;
01544         }
01545 
01546         Field *sys_field = findSystemFieldName(tableSchema);
01547         if (sys_field) {
01548             clearError();
01549             setError(ERR_SYSTEM_NAME_RESERVED,
01550                 i18n("System name \"%1\" cannot be used as one of fields in \"%2\" table.")
01551                 .arg(sys_field->name()).arg(tableName));
01552             return false;
01553         }
01554     }
01555 
01556     bool previousSchemaStillKept = false;
01557 
01558     KexiDB::TableSchema *existingTable = 0;
01559     if (replaceExisting) {
01560         //get previous table (do not retrieve, though)
01561         existingTable = d->tables_byname[tableName];
01562         if (existingTable) {
01563             if (existingTable == tableSchema) {
01564                 clearError();
01565                 setError(ERR_OBJECT_EXISTS, 
01566                     i18n("Could not create the same table \"%1\" twice.").arg(tableSchema->name()) );
01567                 return false;
01568             }
01569 //TODO(js): update any structure (e.g. queries) that depend on this table!
01570             if (existingTable->id()>0)
01571                 tableSchema->m_id = existingTable->id(); //copy id from existing table
01572             previousSchemaStillKept = true;
01573             if (!dropTable( existingTable, false /*alsoRemoveSchema*/ ))
01574                 return false;
01575         }
01576     }
01577     else {
01578         if (this->tableSchema( tableSchema->name() ) != 0) {
01579             clearError();
01580             setError(ERR_OBJECT_EXISTS, i18n("Table \"%1\" already exists.").arg(tableSchema->name()) );
01581             return false;
01582         }
01583     }
01584 
01585 /*  if (replaceExisting) {
01586     //get previous table (do not retrieve, though)
01587     KexiDB::TableSchema *existingTable = d->tables_byname.take(name);
01588     if (oldTable) {
01589     }*/
01590 
01591     TransactionGuard tg;
01592     if (!beginAutoCommitTransaction(tg))
01593         return false;
01594 
01595     if (!drv_createTable(*tableSchema))
01596         createTable_ERR;
01597 
01598     //add schema data to kexi__* tables
01599     if (!internalTable) {
01600         //update kexi__objects
01601         if (!storeObjectSchemaData( *tableSchema, true ))
01602             createTable_ERR;
01603 
01604         TableSchema *ts = d->tables_byname["kexi__fields"];
01605         if (!ts)
01606             return false;
01607         //for sanity: remove field info (if any) for this table id
01608         if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id()))
01609             return false;
01610 
01611         FieldList *fl = createFieldListForKexi__Fields(d->tables_byname["kexi__fields"]);
01612         if (!fl)
01613             return false;
01614 
01615 //      int order = 0;
01616         Field *f;
01617         for (Field::ListIterator it( *tableSchema->fields() ); (f = it.current()); ++it/*, order++*/) {
01618             QValueList<QVariant> vals;
01619             buildValuesForKexi__Fields(vals, f);
01620             if (!insertRecord(*fl, vals ))
01621                 createTable_ERR;
01622         }
01623         delete fl;
01624 
01625         if (!storeExtendedTableSchemaData(*tableSchema))
01626             createTable_ERR;
01627     }
01628 
01629     //finally:
01630 /*  if (replaceExisting) {
01631         if (existingTable) {
01632             d->tables.take(existingTable->id());
01633             delete existingTable;
01634         }
01635     }*/
01636 
01637     bool res = commitAutoCommitTransaction(tg.transaction());
01638 
01639     if (res) {
01640         if (internalTable) {
01641             //insert the internal table into structures
01642             insertInternalTableSchema(tableSchema);
01643         }
01644         else {
01645             if (previousSchemaStillKept) {
01646                 //remove previous table schema
01647                 removeTableSchemaInternal(tableSchema);
01648             }
01649             //store one schema object locally:
01650             d->tables.insert(tableSchema->id(), tableSchema);
01651             d->tables_byname.insert(tableSchema->name().lower(), tableSchema);
01652         }
01653         //ok, this table is not created by the connection
01654         tableSchema->m_conn = this;
01655     }
01656     return res;
01657 }
01658 
01659 void Connection::removeTableSchemaInternal(TableSchema *tableSchema)
01660 {
01661     d->tables_byname.remove(tableSchema->name());
01662     d->tables.remove(tableSchema->id());
01663 }
01664 
01665 bool Connection::removeObject( uint objId )
01666 {
01667     clearError();
01668     //remove table schema from kexi__* tables
01669     if (!KexiDB::deleteRow(*this, d->tables_byname["kexi__objects"], "o_id", objId) //schema entry
01670         || !KexiDB::deleteRow(*this, d->tables_byname["kexi__objectdata"], "o_id", objId)) {//data blocks
01671         setError(ERR_DELETE_SERVER_ERROR, i18n("Could not remove object's data."));
01672         return false;
01673     }
01674     return true;
01675 }
01676 
01677 bool Connection::drv_dropTable( const QString& name )
01678 {
01679     m_sql = "DROP TABLE " + escapeIdentifier(name);
01680     return executeSQL(m_sql);
01681 }
01682 
01684 
01690 tristate Connection::dropTable( KexiDB::TableSchema* tableSchema )
01691 {
01692     return dropTable( tableSchema, true );
01693 }
01694 
01695 tristate Connection::dropTable( KexiDB::TableSchema* tableSchema, bool alsoRemoveSchema)
01696 {
01697 // Each SQL identifier needs to be escaped in the generated query.
01698     clearError();
01699     if (!tableSchema)
01700         return false;
01701 
01702     QString errmsg(i18n("Table \"%1\" cannot be removed.\n"));
01703     //be sure that we handle the correct TableSchema object:
01704     if (tableSchema->id() < 0
01705         || this->tableSchema(tableSchema->name())!=tableSchema
01706         || this->tableSchema(tableSchema->id())!=tableSchema)
01707     {
01708         setError(ERR_OBJECT_NOT_FOUND, errmsg.arg(tableSchema->name())
01709             +i18n("Unexpected name or identifier."));
01710         return false;
01711     }
01712 
01713     tristate res = closeAllTableSchemaChangeListeners(*tableSchema);
01714     if (true!=res)
01715         return res;
01716 
01717     //sanity checks:
01718     if (m_driver->isSystemObjectName( tableSchema->name() )) {
01719         setError(ERR_SYSTEM_NAME_RESERVED, errmsg.arg(tableSchema->name()) + d->strItIsASystemObject());
01720         return false;
01721     }
01722 
01723     TransactionGuard tg;
01724     if (!beginAutoCommitTransaction(tg))
01725         return false;
01726 
01727     //for sanity we're checking if this table exists physically
01728     if (drv_containsTable(tableSchema->name())) {
01729         if (!drv_dropTable(tableSchema->name()))
01730             return false;
01731     }
01732 
01733     TableSchema *ts = d->tables_byname["kexi__fields"];
01734     if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id())) //field entries
01735         return false;
01736 
01737     //remove table schema from kexi__objects table
01738     if (!removeObject( tableSchema->id() )) {
01739         return false;
01740     }
01741 
01742     if (alsoRemoveSchema) {
01744         tristate res = removeDataBlock( tableSchema->id(), "extended_schema");
01745         if (!res)
01746             return false;
01747         removeTableSchemaInternal(tableSchema);
01748     }
01749     return commitAutoCommitTransaction(tg.transaction());
01750 }
01751 
01752 tristate Connection::dropTable( const QString& table )
01753 {
01754     clearError();
01755     TableSchema* ts = tableSchema( table );
01756     if (!ts) {
01757         setError(ERR_OBJECT_NOT_FOUND, i18n("Table \"%1\" does not exist.")
01758             .arg(table));
01759         return false;
01760     }
01761     return dropTable(ts);
01762 }
01763 
01764 tristate Connection::alterTable( TableSchema& tableSchema, TableSchema& newTableSchema )
01765 {
01766     clearError();
01767     tristate res = closeAllTableSchemaChangeListeners(tableSchema);
01768     if (true!=res)
01769         return res;
01770 
01771     if (&tableSchema == &newTableSchema) {
01772         setError(ERR_OBJECT_THE_SAME, i18n("Could not alter table \"%1\" using the same table.")
01773             .arg(tableSchema.name()));
01774         return false;
01775     }
01776 //TODO(js): implement real altering
01777 //TODO(js): update any structure (e.g. query) that depend on this table!
01778     bool ok, empty;
01779 #if 0//TODO ucomment:
01780     empty = isEmpty( tableSchema, ok ) && ok;
01781 #else
01782     empty = true;
01783 #endif
01784     if (empty) {
01785         ok = createTable(&newTableSchema, true/*replace*/);
01786     }
01787     return ok;
01788 }
01789 
01790 bool Connection::alterTableName(TableSchema& tableSchema, const QString& newName, bool replace)
01791 {
01792     clearError();
01793     if (&tableSchema!=d->tables[tableSchema.id()]) {
01794         setError(ERR_OBJECT_NOT_FOUND, i18n("Unknown table \"%1\"").arg(tableSchema.name()));
01795         return false;
01796     }
01797     if (newName.isEmpty() || !KexiUtils::isIdentifier(newName)) {
01798         setError(ERR_INVALID_IDENTIFIER, i18n("Invalid table name \"%1\"").arg(newName));
01799         return false;
01800     }
01801     const QString oldTableName = tableSchema.name();
01802     const QString newTableName = newName.lower().stripWhiteSpace();
01803     if (oldTableName.lower().stripWhiteSpace() == newTableName) {
01804         setError(ERR_OBJECT_THE_SAME, i18n("Could rename table \"%1\" using the same name.")
01805             .arg(newTableName));
01806         return false;
01807     }
01808 //TODO: alter table name for server DB backends!
01809 //TODO: what about objects (queries/forms) that use old name?
01810 //TODO
01811     TableSchema *tableToReplace = this->tableSchema( newName );
01812     const bool destTableExists = tableToReplace != 0;
01813     const int origID = destTableExists ? tableToReplace->id() : -1; //will be reused in the new table
01814     if (!replace && destTableExists) {
01815         setError(ERR_OBJECT_EXISTS,
01816             i18n("Could not rename table \"%1\" to \"%2\". Table \"%3\" already exists.")
01817             .arg(tableSchema.name()).arg(newName).arg(newName));
01818         return false;
01819     }
01820 
01821 //helper:
01822 #define alterTableName_ERR \
01823         tableSchema.setName(oldTableName) //restore old name
01824 
01825     TransactionGuard tg;
01826     if (!beginAutoCommitTransaction(tg))
01827         return false;
01828 
01829     // drop the table replaced (with schema)
01830     if (destTableExists) {
01831         if (!replace) {
01832             return false;
01833         }
01834         if (!dropTable( newName )) {
01835             return false;
01836         }
01837 
01838         // the new table owns the previous table's id:
01839         if (!executeSQL(QString::fromLatin1("UPDATE kexi__objects SET o_id=%1 WHERE o_id=%2 AND o_type=%3")
01840             .arg(origID).arg(tableSchema.id()).arg((int)TableObjectType)))
01841         {
01842             return false;
01843         }
01844         if (!executeSQL(QString::fromLatin1("UPDATE kexi__fields SET t_id=%1 WHERE t_id=%2")
01845             .arg(origID).arg(tableSchema.id())))
01846         {
01847             return false;
01848         }
01849         d->tables.take(tableSchema.id());
01850         d->tables.insert(origID, &tableSchema);
01851         //maintain table ID
01852         tableSchema.m_id = origID;
01853     }
01854 
01855     if (!drv_alterTableName(tableSchema, newTableName)) {
01856         alterTableName_ERR;
01857         return false;
01858     }
01859 
01860     // Update kexi__objects
01861     //TODO
01862     if (!executeSQL(QString::fromLatin1("UPDATE kexi__objects SET o_name=%1 WHERE o_id=%2")
01863         .arg(m_driver->escapeString(tableSchema.name())).arg(tableSchema.id())))
01864     {
01865         alterTableName_ERR;
01866         return false;
01867     }
01868 //TODO what about caption?
01869 
01870     //restore old name: it will be changed soon!
01871     tableSchema.setName(oldTableName);
01872 
01873     if (!commitAutoCommitTransaction(tg.transaction())) {
01874         alterTableName_ERR;
01875         return false;
01876     }
01877 
01878     //update tableSchema:
01879     d->tables_byname.take(tableSchema.name());
01880     tableSchema.setName(newTableName);
01881     d->tables_byname.insert(tableSchema.name(), &tableSchema);
01882     return true;
01883 }
01884 
01885 bool Connection::drv_alterTableName(TableSchema& tableSchema, const QString& newName)
01886 {
01887     const QString oldTableName = tableSchema.name();
01888     tableSchema.setName(newName);
01889 
01890     if (!executeSQL(QString::fromLatin1("ALTER TABLE %1 RENAME TO %2")
01891         .arg(escapeIdentifier(oldTableName)).arg(escapeIdentifier(newName))))
01892     {
01893         tableSchema.setName(oldTableName); //restore old name
01894         return false;
01895     }
01896     return true;
01897 }
01898 
01899 bool Connection::dropQuery( KexiDB::QuerySchema* querySchema )
01900 {
01901     clearError();
01902     if (!querySchema)
01903         return false;
01904 
01905     TransactionGuard tg;
01906     if (!beginAutoCommitTransaction(tg))
01907         return false;
01908 
01909 /*  TableSchema *ts = d->tables_byname["kexi__querydata"];
01910     if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
01911         return false;
01912 
01913     ts = d->tables_byname["kexi__queryfields"];
01914     if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
01915         return false;
01916 
01917     ts = d->tables_byname["kexi__querytables"];
01918     if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id()))
01919         return false;*/
01920 
01921     //remove query schema from kexi__objects table
01922     if (!removeObject( querySchema->id() )) {
01923         return false;
01924     }
01925 
01926 //TODO(js): update any structure that depend on this table!
01927     d->queries_byname.remove(querySchema->name());
01928     d->queries.remove(querySchema->id());
01929 
01930     return commitAutoCommitTransaction(tg.transaction());
01931 }
01932 
01933 bool Connection::dropQuery( const QString& query )
01934 {
01935     clearError();
01936     QuerySchema* qs = querySchema( query );
01937     if (!qs) {
01938         setError(ERR_OBJECT_NOT_FOUND, i18n("Query \"%1\" does not exist.")
01939             .arg(query));
01940         return false;
01941     }
01942     return dropQuery(qs);
01943 }
01944 
01945 bool Connection::drv_createTable( const KexiDB::TableSchema& tableSchema )
01946 {
01947     m_sql = createTableStatement(tableSchema);
01948     KexiDBDbg<<"******** "<<m_sql<<endl;
01949     return executeSQL(m_sql);
01950 }
01951 
01952 bool Connection::drv_createTable( const QString& tableSchemaName )
01953 {
01954     TableSchema *ts = d->tables_byname[tableSchemaName];
01955     if (!ts)
01956         return false;
01957     return drv_createTable(*ts);
01958 }
01959 
01960 bool Connection::beginAutoCommitTransaction(TransactionGuard &tg)
01961 {
01962     if ((m_driver->d->features & Driver::IgnoreTransactions)
01963         || !d->autoCommit)
01964     {
01965         tg.setTransaction( Transaction() );
01966         return true;
01967     }
01968 
01969     // commit current transaction (if present) for drivers
01970     // that allow single transaction per connection
01971     if (m_driver->d->features & Driver::SingleTransactions) {
01972         if (d->default_trans_started_inside) //only commit internally started transaction
01973             if (!commitTransaction(d->default_trans, true)) {
01974                 tg.setTransaction( Transaction() );
01975                 return false; //we have a real error
01976             }
01977 
01978         d->default_trans_started_inside = d->default_trans.isNull();
01979         if (!d->default_trans_started_inside) {
01980             tg.setTransaction( d->default_trans );
01981             tg.doNothing();
01982             return true; //reuse externally started transaction
01983         }
01984     }
01985     else if (!(m_driver->d->features & Driver::MultipleTransactions)) {
01986         tg.setTransaction( Transaction() );
01987         return true; //no trans. supported at all - just return
01988     }
01989     tg.setTransaction( beginTransaction() );
01990     return !error();
01991 }
01992 
01993 bool Connection::commitAutoCommitTransaction(const Transaction& trans)
01994 {
01995     if (m_driver->d->features & Driver::IgnoreTransactions)
01996         return true;
01997     if (trans.isNull() || !m_driver->transactionsSupported())
01998         return true;
01999     if (m_driver->d->features & Driver::SingleTransactions) {
02000         if (!d->default_trans_started_inside) //only commit internally started transaction
02001             return true; //give up
02002     }
02003     return commitTransaction(trans, true);
02004 }
02005 
02006 bool Connection::rollbackAutoCommitTransaction(const Transaction& trans)
02007 {
02008     if (trans.isNull() || !m_driver->transactionsSupported())
02009         return true;
02010     return rollbackTransaction(trans);
02011 }
02012 
02013 #define SET_ERR_TRANS_NOT_SUPP \
02014     { setError(ERR_UNSUPPORTED_DRV_FEATURE, \
02015      i18n("Transactions are not supported for \"%1\" driver.").arg(m_driver->name() )); }
02016 
02017 #define SET_BEGIN_TR_ERROR \
02018      { if (!error()) \
02019         setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Begin transaction failed")); }
02020 
02021 Transaction Connection::beginTransaction()
02022 {
02023     if (!checkIsDatabaseUsed())
02024         return Transaction::null;
02025     Transaction trans;
02026     if (m_driver->d->features & Driver::IgnoreTransactions) {
02027         //we're creating dummy transaction data here,
02028         //so it will look like active
02029         trans.m_data = new TransactionData(this);
02030         d->transactions.append(trans);
02031         return trans;
02032     }
02033     if (m_driver->d->features & Driver::SingleTransactions) {
02034         if (d->default_trans.active()) {
02035             setError(ERR_TRANSACTION_ACTIVE, i18n("Transaction already started.") );
02036             return Transaction::null;
02037         }
02038         if (!(trans.m_data = drv_beginTransaction())) {
02039             SET_BEGIN_TR_ERROR;
02040             return Transaction::null;
02041         }
02042         d->default_trans = trans;
02043         d->transactions.append(trans);
02044         return d->default_trans;
02045     }
02046     if (m_driver->d->features & Driver::MultipleTransactions) {
02047         if (!(trans.m_data = drv_beginTransaction())) {
02048             SET_BEGIN_TR_ERROR;
02049             return Transaction::null;
02050         }
02051         d->transactions.append(trans);
02052         return trans;
02053     }
02054 
02055     SET_ERR_TRANS_NOT_SUPP;
02056     return Transaction::null;
02057 }
02058 
02059 bool Connection::commitTransaction(const Transaction trans, bool ignore_inactive)
02060 {
02061     if (!isDatabaseUsed())
02062         return false;
02063 //  if (!checkIsDatabaseUsed())
02064         //return false;
02065     if ( !m_driver->transactionsSupported()
02066         && !(m_driver->d->features & Driver::IgnoreTransactions))
02067     {
02068         SET_ERR_TRANS_NOT_SUPP;
02069         return false;
02070     }
02071     Transaction t = trans;
02072     if (!t.active()) { //try default tr.
02073         if (!d->default_trans.active()) {
02074             if (ignore_inactive)
02075                 return true;
02076             clearError();
02077             setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") );
02078             return false;
02079         }
02080         t = d->default_trans;
02081         d->default_trans = Transaction::null; //now: no default tr.
02082     }
02083     bool ret = true;
02084     if (! (m_driver->d->features & Driver::IgnoreTransactions) )
02085         ret = drv_commitTransaction(t.m_data);
02086     if (t.m_data)
02087         t.m_data->m_active = false; //now this transaction if inactive
02088     if (!d->dont_remove_transactions) //true=transaction obj will be later removed from list
02089         d->transactions.remove(t);
02090     if (!ret && !error())
02091         setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on commit transaction"));
02092     return ret;
02093 }
02094 
02095 bool Connection::rollbackTransaction(const Transaction trans, bool ignore_inactive)
02096 {
02097     if (!isDatabaseUsed())
02098         return false;
02099 //  if (!checkIsDatabaseUsed())
02100 //      return false;
02101     if ( !m_driver->transactionsSupported()
02102         && !(m_driver->d->features & Driver::IgnoreTransactions))
02103     {
02104         SET_ERR_TRANS_NOT_SUPP;
02105         return false;
02106     }
02107     Transaction t = trans;
02108     if (!t.active()) { //try default tr.
02109         if (!d->default_trans.active()) {
02110             if (ignore_inactive)
02111                 return true;
02112             clearError();
02113             setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") );
02114             return false;
02115         }
02116         t = d->default_trans;
02117         d->default_trans = Transaction::null; //now: no default tr.
02118     }
02119     bool ret = true;
02120     if (! (m_driver->d->features & Driver::IgnoreTransactions) )
02121         ret = drv_rollbackTransaction(t.m_data);
02122     if (t.m_data)
02123         t.m_data->m_active = false; //now this transaction if inactive
02124     if (!d->dont_remove_transactions) //true=transaction obj will be later removed from list
02125         d->transactions.remove(t);
02126     if (!ret && !error())
02127         setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on rollback transaction"));
02128     return ret;
02129 }
02130 
02131 #undef SET_ERR_TRANS_NOT_SUPP
02132 #undef SET_BEGIN_TR_ERROR
02133 
02134 /*bool Connection::duringTransaction()
02135 {
02136     return drv_duringTransaction();
02137 }*/
02138 
02139 Transaction& Connection::defaultTransaction() const
02140 {
02141     return d->default_trans;
02142 }
02143 
02144 void Connection::setDefaultTransaction(const Transaction& trans)
02145 {
02146     if (!isDatabaseUsed())
02147         return;
02148 //  if (!checkIsDatabaseUsed())
02149     //  return;
02150     if ( !(m_driver->d->features & Driver::IgnoreTransactions)
02151         && (!trans.active() || !m_driver->transactionsSupported()) )
02152     {
02153         return;
02154     }
02155     d->default_trans = trans;
02156 }
02157 
02158 const QValueList<Transaction>& Connection::transactions()
02159 {
02160     return d->transactions;
02161 }
02162 
02163 bool Connection::autoCommit() const
02164 {
02165     return d->autoCommit;
02166 }
02167 
02168 bool Connection::setAutoCommit(bool on)
02169 {
02170     if (d->autoCommit == on || m_driver->d->features & Driver::IgnoreTransactions)
02171         return true;
02172     if (!drv_setAutoCommit(on))
02173         return false;
02174     d->autoCommit = on;
02175     return true;
02176 }
02177 
02178 TransactionData* Connection::drv_beginTransaction()
02179 {
02180     QString old_sql = m_sql; //don't
02181     if (!executeSQL( "BEGIN" ))
02182         return 0;
02183     return new TransactionData(this);
02184 }
02185 
02186 bool Connection::drv_commitTransaction(TransactionData *)
02187 {
02188     return executeSQL( "COMMIT" );
02189 }
02190 
02191 bool Connection::drv_rollbackTransaction(TransactionData *)
02192 {
02193     return executeSQL( "ROLLBACK" );
02194 }
02195 
02196 bool Connection::drv_setAutoCommit(bool /*on*/)
02197 {
02198     return true;
02199 }
02200 
02201 Cursor* Connection::executeQuery( const QString& statement, uint cursor_options )
02202 {
02203     if (statement.isEmpty())
02204         return 0;
02205     Cursor *c = prepareQuery( statement, cursor_options );
02206     if (!c)
02207         return 0;
02208     if (!c->open()) {//err - kill that
02209         setError(c);
02210         delete c;
02211         return 0;
02212     }
02213     return c;
02214 }
02215 
02216 Cursor* Connection::executeQuery( QuerySchema& query, const QValueList<QVariant>& params, 
02217     uint cursor_options )
02218 {
02219     Cursor *c = prepareQuery( query, params, cursor_options );
02220     if (!c)
02221         return 0;
02222     if (!c->open()) {//err - kill that
02223         setError(c);
02224         delete c;
02225         return 0;
02226     }
02227     return c;
02228 }
02229 
02230 Cursor* Connection::executeQuery( QuerySchema& query, uint cursor_options )
02231 {
02232     return executeQuery(query, QValueList<QVariant>(), cursor_options);
02233 }
02234 
02235 Cursor* Connection::executeQuery( TableSchema& table, uint cursor_options )
02236 {
02237     return executeQuery( *table.query(), cursor_options );
02238 }
02239 
02240 Cursor* Connection::prepareQuery( TableSchema& table, uint cursor_options )
02241 {
02242     return prepareQuery( *table.query(), cursor_options );
02243 }
02244 
02245 Cursor* Connection::prepareQuery( QuerySchema& query, const QValueList<QVariant>& params, 
02246     uint cursor_options )
02247 {
02248     Cursor* cursor = prepareQuery(query, cursor_options);
02249     if (cursor)
02250         cursor->setQueryParameters(params);
02251     return cursor;
02252 }
02253 
02254 bool Connection::deleteCursor(Cursor *cursor)
02255 {
02256     if (!cursor)
02257         return false;
02258     if (cursor->connection()!=this) {//illegal call
02259         KexiDBWarn << "Connection::deleteCursor(): Cannot delete the cursor not owned by the same connection!" << endl;
02260         return false;
02261     }
02262     const bool ret = cursor->close();
02263     delete cursor;
02264     return ret;
02265 }
02266 
02267 bool Connection::setupObjectSchemaData( const RowData &data, SchemaData &sdata )
02268 {
02269     //not found: retrieve schema
02270 /*  KexiDB::Cursor *cursor;
02271     if (!(cursor = executeQuery( QString("select * from kexi__objects where o_id='%1'").arg(objId) )))
02272         return false;
02273     if (!cursor->moveFirst()) {
02274         deleteCursor(cursor);
02275         return false;
02276     }*/
02277     //if (!ok) {
02278         //deleteCursor(cursor);
02279         //return 0;
02280 //  }
02281     bool ok;
02282     sdata.m_id = data[0].toInt(&ok);
02283     if (!ok) {
02284         return false;
02285     }
02286     sdata.m_name = data[2].toString();
02287     if (!KexiUtils::isIdentifier( sdata.m_name )) {
02288         setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"").arg(sdata.m_name));
02289         return false;
02290     }
02291     sdata.m_caption = data[3].toString();
02292     sdata.m_desc = data[4].toString();
02293 
02294 //  KexiDBDbg<<"@@@ Connection::setupObjectSchemaData() == " << sdata.schemaDataDebugString() << endl;
02295     return true;
02296 }
02297 
02298 tristate Connection::loadObjectSchemaData( int objectID, SchemaData &sdata )
02299 {
02300     RowData data;
02301     if (true!=querySingleRecord(QString::fromLatin1(
02302         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1")
02303         .arg(objectID), data))
02304         return cancelled;
02305     return setupObjectSchemaData( data, sdata );
02306 }
02307 
02308 tristate Connection::loadObjectSchemaData( int objectType, const QString& objectName, SchemaData &sdata )
02309 {
02310     RowData data;
02311     if (true!=querySingleRecord(QString::fromLatin1("SELECT o_id, o_type, o_name, o_caption, o_desc "
02312         "FROM kexi__objects WHERE o_type=%1 AND lower(o_name)=%2")
02313         .arg(objectType).arg(m_driver->valueToSQL(Field::Text, objectName.lower())), data))
02314         return cancelled;
02315     return setupObjectSchemaData( data, sdata );
02316 }
02317 
02318 bool Connection::storeObjectSchemaData( SchemaData &sdata, bool newObject )
02319 {
02320     TableSchema *ts = d->tables_byname["kexi__objects"];
02321     if (!ts)
02322         return false;
02323     if (newObject) {
02324         int existingID;
02325         if (true == querySingleNumber(QString::fromLatin1(
02326             "SELECT o_id FROM kexi__objects WHERE o_type=%1 AND lower(o_name)=%2")
02327             .arg(sdata.type()).arg(m_driver->valueToSQL(Field::Text, sdata.name().lower())), existingID))
02328         {
02329             //we already have stored a schema data with the same name and type:
02330             //just update it's properties as it would be existing object
02331             sdata.m_id = existingID;
02332             newObject = false;
02333         }
02334     }
02335     if (newObject) {
02336         FieldList *fl;
02337         bool ok;
02338         if (sdata.id()<=0) {//get new ID
02339             fl = ts->subList("o_type", "o_name", "o_caption", "o_desc");
02340             ok = fl!=0;
02341             if (ok && !insertRecord(*fl, QVariant(sdata.type()), QVariant(sdata.name()),
02342             QVariant(sdata.caption()), QVariant(sdata.description()) ))
02343                 ok = false;
02344             delete fl;
02345             if (!ok)
02346                 return false;
02347             //fetch newly assigned ID
02349             int obj_id = (int)lastInsertedAutoIncValue("o_id",*ts);
02350             KexiDBDbg << "######## NEW obj_id == " << obj_id << endl;
02351             if (obj_id<=0)
02352                 return false;
02353             sdata.m_id = obj_id;
02354             return true;
02355         } else {
02356             fl = ts->subList("o_id", "o_type", "o_name", "o_caption", "o_desc");
02357             ok = fl!=0;
02358             if (ok && !insertRecord(*fl, QVariant(sdata.id()), QVariant(sdata.type()), QVariant(sdata.name()),
02359                 QVariant(sdata.caption()), QVariant(sdata.description()) ))
02360                 ok = false;
02361             delete fl;
02362             return ok;
02363         }
02364     }
02365     //existing object:
02366     return executeSQL(QString("UPDATE kexi__objects SET o_type=%2, o_caption=%3, o_desc=%4 WHERE o_id=%1")
02367         .arg(sdata.id()).arg(sdata.type())
02368         .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.caption()))
02369         .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.description())) );
02370 }
02371 
02372 tristate Connection::querySingleRecordInternal(RowData &data, const QString* sql, QuerySchema* query, 
02373     bool addLimitTo1)
02374 {
02375     Q_ASSERT(sql || query);
02377     if (sql)
02378         m_sql = addLimitTo1 ? (*sql + " LIMIT 1") : *sql; // is this safe?
02379     KexiDB::Cursor *cursor;
02380     if (!(cursor = sql ? executeQuery( m_sql ) : executeQuery( *query ))) {
02381         KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
02382         return false;
02383     }
02384     if (!cursor->moveFirst() || cursor->eof()) {
02385         const tristate result = cursor->error() ? false : cancelled;
02386         KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() m_sql=" << m_sql << endl;
02387         setError(cursor);
02388         deleteCursor(cursor);
02389         return result;
02390     }
02391     cursor->storeCurrentRow(data);
02392     return deleteCursor(cursor);
02393 }
02394 
02395 tristate Connection::querySingleRecord(const QString& sql, RowData &data, bool addLimitTo1)
02396 {
02397     return querySingleRecordInternal(data, &sql, 0, addLimitTo1);
02398 }
02399 
02400 tristate Connection::querySingleRecord(QuerySchema& query, RowData &data, bool addLimitTo1)
02401 {
02402     return querySingleRecordInternal(data, 0, &query, addLimitTo1);
02403 }
02404 
02405 bool Connection::checkIfColumnExists(Cursor *cursor, uint column)
02406 {
02407     if (column >= cursor->fieldCount()) {
02408         setError(ERR_CURSOR_RECORD_FETCHING, i18n("Column %1 does not exist for the query.").arg(column));
02409         return false;
02410     }
02411     return true;
02412 }
02413 
02414 tristate Connection::querySingleString(const QString& sql, QString &value, uint column, bool addLimitTo1)
02415 {
02416     KexiDB::Cursor *cursor;
02417     m_sql = addLimitTo1 ? (sql + " LIMIT 1") : sql; // is this safe?;
02418     if (!(cursor = executeQuery( m_sql ))) {
02419         KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
02420         return false;
02421     }
02422     if (!cursor->moveFirst() || cursor->eof()) {
02423         const tristate result = cursor->error() ? false : cancelled;
02424         KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() " << m_sql << endl;
02425         deleteCursor(cursor);
02426         return result;
02427     }
02428     if (!checkIfColumnExists(cursor, column)) {
02429         deleteCursor(cursor);
02430         return false;
02431     }
02432     value = cursor->value(column).toString();
02433     return deleteCursor(cursor);
02434 }
02435 
02436 tristate Connection::querySingleNumber(const QString& sql, int &number, uint column, bool addLimitTo1)
02437 {
02438     static QString str;
02439     static bool ok;
02440     const tristate result = querySingleString(sql, str, column, addLimitTo1);
02441     if (result!=true)
02442         return result;
02443     number = str.toInt(&ok);
02444     return ok;
02445 }
02446 
02447 bool Connection::queryStringList(const QString& sql, QStringList& list, uint column)
02448 {
02449     KexiDB::Cursor *cursor;
02450     clearError();
02451     m_sql = sql;
02452     if (!(cursor = executeQuery( m_sql ))) {
02453         KexiDBWarn << "Connection::queryStringList(): !executeQuery() " << m_sql << endl;
02454         return false;
02455     }
02456     cursor->moveFirst();
02457     if (cursor->error()) {
02458         setError(cursor);
02459         deleteCursor(cursor);
02460         return false;
02461     }
02462     if (!cursor->eof() && !checkIfColumnExists(cursor, column)) {
02463         deleteCursor(cursor);
02464         return false;
02465     }
02466     list.clear();
02467     while (!cursor->eof()) {
02468         list.append( cursor->value(column).toString() );
02469         if (!cursor->moveNext() && cursor->error()) {
02470             setError(cursor);
02471             deleteCursor(cursor);
02472             return false;
02473         }
02474     }
02475     return deleteCursor(cursor);
02476 }
02477 
02478 bool Connection::resultExists(const QString& sql, bool &success, bool addLimitTo1)
02479 {
02480     KexiDB::Cursor *cursor;
02481     //optimization
02482     if (m_driver->beh->SELECT_1_SUBQUERY_SUPPORTED) {
02483         //this is at least for sqlite
02484         if (addLimitTo1 && sql.left(6).upper() == "SELECT")
02485             m_sql = QString("SELECT 1 FROM (") + sql + ") LIMIT 1"; // is this safe?;
02486         else
02487             m_sql = sql;
02488     }
02489     else {
02490         if (addLimitTo1 && sql.left(6).upper() == "SELECT")
02491             m_sql = sql + " LIMIT 1"; //not always safe!
02492         else
02493             m_sql = sql;
02494     }
02495     if (!(cursor = executeQuery( m_sql ))) {
02496         KexiDBWarn << "Connection::querySingleRecord(): !executeQuery() " << m_sql << endl;
02497         success = false;
02498         return false;
02499     }
02500     if (!cursor->moveFirst() || cursor->eof()) {
02501         success = !cursor->error();
02502         KexiDBWarn << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof() " << m_sql << endl;
02503         setError(cursor);
02504         deleteCursor(cursor);
02505         return false;
02506     }
02507     success = deleteCursor(cursor);
02508     return true;
02509 }
02510 
02511 bool Connection::isEmpty( TableSchema& table, bool &success )
02512 {
02513     return !resultExists( selectStatement( *table.query() ), success );
02514 }
02515 
02517 static void createExtendedTableSchemaMainElementIfNeeded(
02518     QDomDocument& doc, QDomElement& extendedTableSchemaMainEl,
02519     bool& extendedTableSchemaStringIsEmpty)
02520 {
02521     if (!extendedTableSchemaStringIsEmpty)
02522         return;
02523     //init document
02524     extendedTableSchemaMainEl = doc.createElement("EXTENDED_TABLE_SCHEMA");
02525     doc.appendChild( extendedTableSchemaMainEl );
02526     extendedTableSchemaMainEl.setAttribute("version", QString::number(KEXIDB_EXTENDED_TABLE_SCHEMA_VERSION));
02527     extendedTableSchemaStringIsEmpty = false;
02528 }
02529 
02531 static void createExtendedTableSchemaFieldElementIfNeeded(QDomDocument& doc, 
02532     QDomElement& extendedTableSchemaMainEl, const QString& fieldName, QDomElement& extendedTableSchemaFieldEl,
02533     bool append = true)
02534 {
02535     if (!extendedTableSchemaFieldEl.isNull())
02536         return;
02537     extendedTableSchemaFieldEl = doc.createElement("field");
02538     if (append)
02539         extendedTableSchemaMainEl.appendChild( extendedTableSchemaFieldEl );
02540     extendedTableSchemaFieldEl.setAttribute("name", fieldName);
02541 }
02542 
02549 static void addFieldPropertyToExtendedTableSchemaData( 
02550     Field *f, const char* propertyName, const QVariant& propertyValue, 
02551     QDomDocument& doc, QDomElement& extendedTableSchemaMainEl, 
02552     QDomElement& extendedTableSchemaFieldEl,
02553     bool& extendedTableSchemaStringIsEmpty,
02554     bool custom = false )
02555 {
02556     createExtendedTableSchemaMainElementIfNeeded(doc,
02557         extendedTableSchemaMainEl, extendedTableSchemaStringIsEmpty);
02558     createExtendedTableSchemaFieldElementIfNeeded(
02559         doc, extendedTableSchemaMainEl, f->name(), extendedTableSchemaFieldEl);
02560 
02561     //create <property>
02562     QDomElement extendedTableSchemaFieldPropertyEl = doc.createElement("property");
02563     extendedTableSchemaFieldEl.appendChild( extendedTableSchemaFieldPropertyEl );
02564     if (custom)
02565         extendedTableSchemaFieldPropertyEl.setAttribute("custom", "true");
02566     extendedTableSchemaFieldPropertyEl.setAttribute("name", propertyName);
02567     QDomElement extendedTableSchemaFieldPropertyValueEl;
02568     switch (propertyValue.type()) {
02569     case QVariant::String:
02570         extendedTableSchemaFieldPropertyValueEl = doc.createElement("string");
02571         break;
02572     case QVariant::CString:
02573         extendedTableSchemaFieldPropertyValueEl = doc.createElement("cstring");
02574         break;
02575     case QVariant::Int:
02576     case QVariant::Double:
02577     case QVariant::UInt:
02578     case QVariant::LongLong:
02579     case QVariant::ULongLong:
02580         extendedTableSchemaFieldPropertyValueEl = doc.createElement("number");
02581         break;
02582     case QVariant::Bool:
02583         extendedTableSchemaFieldPropertyValueEl = doc.createElement("bool");
02584         break;
02585     default:
02587         KexiDBFatal << "addFieldPropertyToExtendedTableSchemaData(): impl. error" << endl;
02588     }
02589     extendedTableSchemaFieldPropertyEl.appendChild( extendedTableSchemaFieldPropertyValueEl );
02590     extendedTableSchemaFieldPropertyValueEl.appendChild( 
02591         doc.createTextNode( propertyValue.toString() ) );
02592 }
02593 
02594 bool Connection::storeExtendedTableSchemaData(TableSchema& tableSchema)
02595 {
02597     QDomDocument doc("EXTENDED_TABLE_SCHEMA");
02598     QDomElement extendedTableSchemaMainEl;
02599     bool extendedTableSchemaStringIsEmpty = true;
02600 
02601     //for each field:
02602     Field *f;
02603     for (Field::ListIterator it( *tableSchema.fields() ); (f = it.current()); ++it) {
02604         QDomElement extendedTableSchemaFieldEl;
02605         if (f->visibleDecimalPlaces()>=0/*nondefault*/ && KexiDB::supportsVisibleDecimalPlacesProperty(f->type())) {
02606             addFieldPropertyToExtendedTableSchemaData( 
02607                 f, "visibleDecimalPlaces", f->visibleDecimalPlaces(), doc, 
02608                 extendedTableSchemaMainEl, extendedTableSchemaFieldEl, 
02609                 extendedTableSchemaStringIsEmpty );
02610         }
02611         // boolean field with "not null"
02612 
02613         // add custom properties
02614         const Field::CustomPropertiesMap customProperties(f->customProperties());
02615         foreach( Field::CustomPropertiesMap::ConstIterator, itCustom, customProperties ) {
02616             addFieldPropertyToExtendedTableSchemaData( 
02617                 f, itCustom.key(), itCustom.data(), doc, 
02618                 extendedTableSchemaMainEl, extendedTableSchemaFieldEl, extendedTableSchemaStringIsEmpty,
02619                 /*custom*/true );
02620         }
02621         // save lookup table specification, if present
02622         LookupFieldSchema *lookupFieldSchema = tableSchema.lookupFieldSchema( *f );
02623         if (lookupFieldSchema) {
02624             createExtendedTableSchemaFieldElementIfNeeded(
02625                 doc, extendedTableSchemaMainEl, f->name(), extendedTableSchemaFieldEl, false/* !append */);
02626             LookupFieldSchema::saveToDom(*lookupFieldSchema, doc, extendedTableSchemaFieldEl);
02627 
02628             if (extendedTableSchemaFieldEl.hasChildNodes()) {
02629                 // this element provides the definition, so let's append it now
02630                 createExtendedTableSchemaMainElementIfNeeded(doc, extendedTableSchemaMainEl, 
02631                     extendedTableSchemaStringIsEmpty);
02632                 extendedTableSchemaMainEl.appendChild( extendedTableSchemaFieldEl );
02633             }
02634         }
02635     }
02636 
02637     // Store extended schema information (see ExtendedTableSchemaInformation in Kexi Wiki)
02638     if (extendedTableSchemaStringIsEmpty) {
02639 #ifdef KEXI_DEBUG_GUI
02640         KexiUtils::addAlterTableActionDebug(QString("** Extended table schema REMOVED."));
02641 #endif
02642         if (!removeDataBlock( tableSchema.id(), "extended_schema"))
02643             return false;
02644     }
02645     else {
02646 #ifdef KEXI_DEBUG_GUI
02647         KexiUtils::addAlterTableActionDebug(QString("** Extended table schema set to:\n")+doc.toString(4));
02648 #endif
02649         if (!storeDataBlock( tableSchema.id(), doc.toString(1), "extended_schema" ))
02650             return false;
02651     }
02652     return true;
02653 }
02654 
02655 bool Connection::loadExtendedTableSchemaData(TableSchema& tableSchema)
02656 {
02657 #define loadExtendedTableSchemaData_ERR \
02658     { setError(i18n("Error while loading extended table schema information.")); \
02659       return false; }
02660 #define loadExtendedTableSchemaData_ERR2(details) \
02661     { setError(i18n("Error while loading extended table schema information."), details); \
02662       return false; }
02663 #define loadExtendedTableSchemaData_ERR3(data) \
02664     { setError(i18n("Error while loading extended table schema information."), \
02665       i18n("Invalid XML data: ") + data.left(1024) ); \
02666       return false; }
02667 
02668     // Load extended schema information, if present (see ExtendedTableSchemaInformation in Kexi Wiki)
02669     QString extendedTableSchemaString;
02670     tristate res = loadDataBlock( tableSchema.id(), extendedTableSchemaString, "extended_schema" );
02671     if (!res)
02672         loadExtendedTableSchemaData_ERR;
02673     // extendedTableSchemaString will be just empty if there is no such data block
02674 
02675 #ifdef KEXIDB_LOOKUP_FIELD_TEST
02676 //<temp. for LookupFieldSchema tests>
02677     if (tableSchema.name()=="cars") {
02678         LookupFieldSchema *lookupFieldSchema = new LookupFieldSchema();
02679         lookupFieldSchema->rowSource().setType(LookupFieldSchema::RowSource::Table);
02680         lookupFieldSchema->rowSource().setName("persons");
02681         lookupFieldSchema->setBoundColumn(0); //id
02682         lookupFieldSchema->setVisibleColumn(3); //surname
02683         tableSchema.setLookupFieldSchema( "owner", lookupFieldSchema );
02684     }
02685 //</temp. for LookupFieldSchema tests>
02686 #endif
02687 
02688     if (extendedTableSchemaString.isEmpty())
02689         return true;
02690 
02691     QDomDocument doc;
02692     QString errorMsg;
02693     int errorLine, errorColumn;
02694     if (!doc.setContent( extendedTableSchemaString, &errorMsg, &errorLine, &errorColumn ))
02695         loadExtendedTableSchemaData_ERR2( i18n("Error in XML data: \"%1\" in line %2, column %3.\nXML data: ")
02696             .arg(errorMsg).arg(errorLine).arg(errorColumn) + extendedTableSchemaString.left(1024));
02697 
02699     
02700     if (doc.doctype().name()!="EXTENDED_TABLE_SCHEMA")
02701         loadExtendedTableSchemaData_ERR3( extendedTableSchemaString );
02702 
02703     QDomElement docEl = doc.documentElement();
02704     if (docEl.tagName()!="EXTENDED_TABLE_SCHEMA")
02705         loadExtendedTableSchemaData_ERR3( extendedTableSchemaString );
02706 
02707     for (QDomNode n = docEl.firstChild(); !n.isNull(); n = n.nextSibling()) {
02708         QDomElement fieldEl = n.toElement();
02709         if (fieldEl.tagName()=="field") {
02710             Field *f = tableSchema.field( fieldEl.attribute("name") );
02711             if (f) {
02712                 //set properties of the field:
02714                 for (QDomNode propNode = fieldEl.firstChild(); 
02715                     !propNode.isNull(); propNode = propNode.nextSibling())
02716                 {
02717                     QDomElement propEl = propNode.toElement();
02718                     bool ok;
02719                     int intValue;
02720                     if (propEl.tagName()=="property") {
02721                         QCString propertyName = propEl.attribute("name").latin1();
02722                         if (propEl.attribute("custom")=="true") {
02723                             //custom property
02724                             f->setCustomProperty(propertyName, 
02725                                 KexiDB::loadPropertyValueFromDom( propEl.firstChild() ));
02726                         }
02727                         else if (propertyName == "visibleDecimalPlaces"
02728                             && KexiDB::supportsVisibleDecimalPlacesProperty(f->type()))
02729                         {
02730                             intValue = KexiDB::loadIntPropertyValueFromDom( propEl.firstChild(), &ok );
02731                             if (ok)
02732                                 f->setVisibleDecimalPlaces(intValue);
02733                         }
02735                     }
02736                     else if (propEl.tagName()=="lookup-column") {
02737                         LookupFieldSchema *lookupFieldSchema = LookupFieldSchema::loadFromDom(propEl);
02738                         if (lookupFieldSchema)
02739                             lookupFieldSchema->debug();
02740                         tableSchema.setLookupFieldSchema( f->name(), lookupFieldSchema );
02741                     }
02742                 }
02743             }
02744             else {
02745                 KexiDBWarn << "Connection::loadExtendedTableSchemaData(): no such field \"" 
02746                     << fieldEl.attribute("name") << "\" in table \"" << tableSchema.name() << "\"" << endl;
02747             }
02748         }
02749     }
02750 
02751     return true;
02752 }
02753 
02754 KexiDB::Field* Connection::setupField( const RowData &data )
02755 {
02756     bool ok = true;
02757     int f_int_type = data.at(1).toInt(&ok);
02758     if (f_int_type<=Field::InvalidType || f_int_type>Field::LastType)
02759         ok = false;
02760     if (!ok)
02761         return 0;
02762     Field::Type f_type = (Field::Type)f_int_type;
02763     int f_len = QMAX( 0, data.at(3).toInt(&ok) );
02764     if (!ok)
02765         return 0;
02766     int f_prec = data.at(4).toInt(&ok);
02767     if (!ok)
02768         return 0;
02769     int f_constr = data.at(5).toInt(&ok);
02770     if (!ok)
02771         return 0;
02772     int f_opts = data.at(6).toInt(&ok);
02773     if (!ok)
02774         return 0;
02775 
02776     if (!KexiUtils::isIdentifier( data.at(2).toString() )) {
02777         setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"")
02778             .arg( data.at(2).toString() ));
02779         ok = false;
02780         return 0;
02781     }
02782 
02783     Field *f = new Field(
02784         data.at(2).toString(), f_type, f_constr, f_opts, f_len, f_prec );
02785 
02786     f->setDefaultValue( KexiDB::stringToVariant(data.at(7).toString(), Field::variantType( f_type ), ok) );
02787     if (!ok) {
02788         KexiDBWarn << "Connection::setupTableSchema() problem with KexiDB::stringToVariant(" 
02789             << data.at(7).toString() << ")" << endl;
02790     }
02791     ok = true; //problem with defaultValue is not critical
02792 
02793     f->m_caption = data.at(9).toString();
02794     f->m_desc = data.at(10).toString();
02795     return f;
02796 }
02797 
02798 KexiDB::TableSchema* Connection::setupTableSchema( const RowData &data )
02799 {
02800     TableSchema *t = new TableSchema( this );
02801     if (!setupObjectSchemaData( data, *t )) {
02802         delete t;
02803         return 0;
02804     }
02805 
02806     KexiDB::Cursor *cursor;
02807     if (!(cursor = executeQuery(
02808         QString::fromLatin1("SELECT t_id, f_type, f_name, f_length, f_precision, f_constraints, "
02809             "f_options, f_default, f_order, f_caption, f_help"
02810             " FROM kexi__fields WHERE t_id=%1 ORDER BY f_order").arg(t->m_id) )))
02811     {
02812         delete t;
02813         return 0;
02814     }
02815     if (!cursor->moveFirst()) {
02816         if (!cursor->error() && cursor->eof()) {
02817             setError(i18n("Table has no fields defined."));
02818         }
02819         deleteCursor(cursor);
02820         delete t;
02821         return 0;
02822     }
02823 
02824     // For each field: load its schema
02825     RowData fieldData;
02826     bool ok = true;
02827     while (!cursor->eof()) {
02828 //      KexiDBDbg<<"@@@ f_name=="<<cursor->value(2).asCString()<<endl;
02829         cursor->storeCurrentRow(fieldData); 
02830         Field *f = setupField(fieldData);
02831         if (!f) {
02832             ok = false;
02833             break;
02834         }
02835         t->addField(f);
02836         cursor->moveNext();
02837     }
02838 
02839     if (!ok) {//error:
02840         deleteCursor(cursor);
02841         delete t;
02842         return 0;
02843     }
02844 
02845     if (!deleteCursor(cursor)) {
02846         delete t;
02847         return 0;
02848     }
02849 
02850     if (!loadExtendedTableSchemaData(*t)) {
02851         delete t;
02852         return 0;
02853     }
02854     //store locally:
02855     d->tables.insert(t->m_id, t);
02856     d->tables_byname.insert(t->m_name.lower(), t);
02857     return t;
02858 }
02859 
02860 TableSchema* Connection::tableSchema( const QString& tableName )
02861 {
02862     QString m_tableName = tableName.lower();
02863     TableSchema *t = d->tables_byname[m_tableName];
02864     if (t)
02865         return t;
02866     //not found: retrieve schema
02867     RowData data;
02868     if (true!=querySingleRecord(QString::fromLatin1(
02869         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE lower(o_name)='%1' AND o_type=%2")
02870             .arg(m_tableName).arg(KexiDB::TableObjectType), data))
02871         return 0;
02872 
02873     return setupTableSchema(data);
02874 }
02875 
02876 TableSchema* Connection::tableSchema( int tableId )
02877 {
02878     TableSchema *t = d->tables[tableId];
02879     if (t)
02880         return t;
02881     //not found: retrieve schema
02882     RowData data;
02883     if (true!=querySingleRecord(QString::fromLatin1(
02884         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1")
02885         .arg(tableId), data))
02886         return 0;
02887 
02888     return setupTableSchema(data);
02889 }
02890 
02891 tristate Connection::loadDataBlock( int objectID, QString &dataString, const QString& dataID )
02892 {
02893     if (objectID<=0)
02894         return false;
02895     return querySingleString(
02896         QString("SELECT o_data FROM kexi__objectdata WHERE o_id=") + QString::number(objectID)
02897         + " AND " + KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID),
02898         dataString );
02899 }
02900 
02901 bool Connection::storeDataBlock( int objectID, const QString &dataString, const QString& dataID )
02902 {
02903     if (objectID<=0)
02904         return false;
02905     QString sql(QString::fromLatin1("SELECT kexi__objectdata.o_id FROM kexi__objectdata WHERE o_id=%1").arg(objectID));
02906     QString sql_sub( KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID) );
02907 
02908     bool ok, exists;
02909     exists = resultExists(sql + " and " + sql_sub, ok);
02910     if (!ok)
02911         return false;
02912     if (exists) {
02913         return executeSQL( "UPDATE kexi__objectdata SET o_data="
02914             + m_driver->valueToSQL( KexiDB::Field::LongText, dataString )
02915             + " WHERE o_id=" + QString::number(objectID) + " AND " + sql_sub );
02916     }
02917     return executeSQL(
02918         QString::fromLatin1("INSERT INTO kexi__objectdata (o_id, o_data, o_sub_id) VALUES (")
02919         + QString::number(objectID) +"," + m_driver->valueToSQL( KexiDB::Field::LongText, dataString )
02920         + "," + m_driver->valueToSQL( KexiDB::Field::Text, dataID ) + ")" );
02921 }
02922 
02923 bool Connection::removeDataBlock( int objectID, const QString& dataID)
02924 {
02925     if (objectID<=0)
02926         return false;
02927     if (dataID.isEmpty())
02928         return KexiDB::deleteRow(*this, "kexi__objectdata", "o_id", QString::number(objectID));
02929     else
02930         return KexiDB::deleteRow(*this, "kexi__objectdata",
02931         "o_id", KexiDB::Field::Integer, objectID, "o_sub_id", KexiDB::Field::Text, dataID);
02932 }
02933 
02934 KexiDB::QuerySchema* Connection::setupQuerySchema( const RowData &data )
02935 {
02936     bool ok = true;
02937     const int objID = data[0].toInt(&ok);
02938     if (!ok)
02939         return false;
02940     QString sqlText;
02941     if (!loadDataBlock( objID, sqlText, "sql" )) {
02942         setError(ERR_OBJECT_NOT_FOUND, 
02943             i18n("Could not find definition for query \"%1\". Removing this query is recommended.")
02944             .arg(data[2].toString()));
02945         return 0;
02946     }
02947     d->parser()->parse( sqlText );
02948     KexiDB::QuerySchema *query = d->parser()->query();
02949     //error?
02950     if (!query) {
02951         setError(ERR_SQL_PARSE_ERROR, 
02952             i18n("<p>Could not load definition for query \"%1\". "
02953             "SQL statement for this query is invalid:<br><tt>%2</tt></p>\n"
02954             "<p>You can open this query in Text View and correct it.</p>").arg(data[2].toString())
02955             .arg(d->parser()->statement()));
02956         return 0;
02957     }
02958     if (!setupObjectSchemaData( data, *query )) {
02959         delete query;
02960         return 0;
02961     }
02962     d->queries.insert(query->m_id, query);
02963     d->queries_byname.insert(query->m_name, query);
02964     return query;
02965 }
02966 
02967 QuerySchema* Connection::querySchema( const QString& queryName )
02968 {
02969     QString m_queryName = queryName.lower();
02970     QuerySchema *q = d->queries_byname[m_queryName];
02971     if (q)
02972         return q;
02973     //not found: retrieve schema
02974     RowData data;
02975     if (true!=querySingleRecord(QString::fromLatin1(
02976         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE lower(o_name)='%1' AND o_type=%2")
02977             .arg(m_queryName).arg(KexiDB::QueryObjectType), data))
02978         return 0;
02979 
02980     return setupQuerySchema(data);
02981 }
02982 
02983 QuerySchema* Connection::querySchema( int queryId )
02984 {
02985     QuerySchema *q = d->queries[queryId];
02986     if (q)
02987         return q;
02988     //not found: retrieve schema
02989     clearError();
02990     RowData data;
02991     if (true!=querySingleRecord(QString::fromLatin1(
02992         "SELECT o_id, o_type, o_name, o_caption, o_desc FROM kexi__objects WHERE o_id=%1").arg(queryId), data))
02993         return 0;
02994 
02995     return setupQuerySchema(data);
02996 }
02997 
02998 bool Connection::setQuerySchemaObsolete( const QString& queryName )
02999 {
03000     QuerySchema* oldQuery = querySchema( queryName );
03001     if (!oldQuery)
03002         return false;
03003     d->obsoleteQueries.append(oldQuery);
03004     d->queries_byname.take(queryName);
03005     d->queries.take(oldQuery->id());
03006     return true;
03007 }
03008 
03009 TableSchema* Connection::newKexiDBSystemTableSchema(const QString& tsname)
03010 {
03011     TableSchema *ts = new TableSchema(tsname.lower());
03012     insertInternalTableSchema( ts );
03013     return ts;
03014 }
03015 
03016 bool Connection::isInternalTableSchema(const QString& tableName)
03017 {
03018     return (d->kexiDBSystemTables[ d->tables_byname[tableName] ]) 
03019         // these are here for compatiblility because we're no longer instantiate 
03020         // them but can exist in projects created with previous Kexi versions:
03021         || tableName=="kexi__final" || tableName=="kexi__useractions";
03022 }
03023 
03024 void Connection::insertInternalTableSchema(TableSchema *tableSchema)
03025 {
03026     tableSchema->setKexiDBSystem(true);
03027     d->kexiDBSystemTables.insert(tableSchema, tableSchema);
03028     d->tables_byname.insert(tableSchema->name(), tableSchema);
03029 }
03030 
03032 bool Connection::setupKexiDBSystemSchema()
03033 {
03034     if (!d->kexiDBSystemTables.isEmpty())
03035         return true; //already set up
03036 
03037     TableSchema *t_objects = newKexiDBSystemTableSchema("kexi__objects");
03038     t_objects->addField( new Field("o_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) )
03039     .addField( new Field("o_type", Field::Byte, 0, Field::Unsigned) )
03040     .addField( new Field("o_name", Field::Text) )
03041     .addField( new Field("o_caption", Field::Text ) )
03042     .addField( new Field("o_desc", Field::LongText ) );
03043 
03044     t_objects->debug();
03045 
03046     TableSchema *t_objectdata = newKexiDBSystemTableSchema("kexi__objectdata");
03047     t_objectdata->addField( new Field("o_id", Field::Integer, Field::NotNull, Field::Unsigned) )
03048     .addField( new Field("o_data", Field::LongText) )
03049     .addField( new Field("o_sub_id", Field::Text) );
03050 
03051     TableSchema *t_fields = newKexiDBSystemTableSchema("kexi__fields");
03052     t_fields->addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) )
03053     .addField( new Field("f_type", Field::Byte, 0, Field::Unsigned) )
03054     .addField( new Field("f_name", Field::Text ) )
03055     .addField( new Field("f_length", Field::Integer ) )
03056     .addField( new Field("f_precision", Field::Integer ) )
03057     .addField( new Field("f_constraints", Field::Integer ) )
03058     .addField( new Field("f_options", Field::Integer ) )
03059     .addField( new Field("f_default", Field::Text ) )
03060     //these are additional properties:
03061     .addField( new Field("f_order", Field::Integer ) )
03062     .addField( new Field("f_caption", Field::Text ) )
03063     .addField( new Field("f_help", Field::LongText ) );
03064 
03065 /*  TableSchema *t_querydata = newKexiDBSystemTableSchema("kexi__querydata");
03066     t_querydata->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
03067     .addField( new Field("q_sql", Field::LongText ) )
03068     .addField( new Field("q_valid", Field::Boolean ) );
03069 
03070     TableSchema *t_queryfields = newKexiDBSystemTableSchema("kexi__queryfields");
03071     t_queryfields->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
03072     .addField( new Field("f_order", Field::Integer ) )
03073     .addField( new Field("f_id", Field::Integer ) )
03074     .addField( new Field("f_tab_asterisk", Field::Integer, 0, Field::Unsigned) )
03075     .addField( new Field("f_alltab_asterisk", Field::Boolean) );
03076 
03077     TableSchema *t_querytables = newKexiDBSystemTableSchema("kexi__querytables");
03078     t_querytables->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) )
03079     .addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) )
03080     .addField( new Field("t_order", Field::Integer, 0, Field::Unsigned) );*/
03081 
03082     TableSchema *t_db = newKexiDBSystemTableSchema("kexi__db");
03083     t_db->addField( new Field("db_property", Field::Text, Field::NoConstraints, Field::NoOptions, 32 ) )
03084     .addField( new Field("db_value", Field::LongText ) );
03085 
03086 /* moved to KexiProject...
03087     TableSchema *t_parts = newKexiDBSystemTableSchema("kexi__parts");
03088     t_parts->addField( new Field("p_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) )
03089     .addField( new Field("p_name", Field::Text) )
03090     .addField( new Field("p_mime", Field::Text ) )
03091     .addField( new Field("p_url", Field::Text ) );
03092 */
03093 
03094 /*UNUSED
03095     TableSchema *t_final = newKexiDBSystemTableSchema("kexi__final");
03096     t_final->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) )
03097     .addField( new Field("property", Field::LongText ) )
03098     .addField( new Field("value", Field::BLOB) );
03099 
03100     TableSchema *t_useractions = newKexiDBSystemTableSchema("kexi__useractions");
03101     t_useractions->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) )
03102     .addField( new Field("scope", Field::Integer ) )
03103     .addField( new Field("name", Field::LongText ) )
03104     .addField( new Field("text", Field::LongText ) )
03105     .addField( new Field("icon", Field::LongText ) )
03106     .addField( new Field("method", Field::Integer ) )
03107     .addField( new Field("arguments", Field::LongText) );
03108 */
03109     return true;
03110 }
03111 
03112 void Connection::removeMe(TableSchema *ts)
03113 {
03114     if (ts && !m_destructor_started) {
03115         d->tables.take(ts->id());
03116         d->tables_byname.take(ts->name());
03117     }
03118 }
03119 
03120 QString Connection::anyAvailableDatabaseName()
03121 {
03122     if (!d->availableDatabaseName.isEmpty()) {
03123         return d->availableDatabaseName;
03124     }
03125     return m_driver->beh->ALWAYS_AVAILABLE_DATABASE_NAME;
03126 }
03127 
03128 void Connection::setAvailableDatabaseName(const QString& dbName)
03129 {
03130     d->availableDatabaseName = dbName;
03131 }
03132 
03134 inline void updateRowDataWithNewValues(QuerySchema &query, RowData& data, KexiDB::RowEditBuffer::DBMap& b,
03135     QMap<QueryColumnInfo*,int>& columnsOrderExpanded)
03136 {
03137     columnsOrderExpanded = query.columnsOrder(QuerySchema::ExpandedList);
03138     QMap<QueryColumnInfo*,int>::ConstIterator columnsOrderExpandedIt;
03139     for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
03140         columnsOrderExpandedIt = columnsOrderExpanded.find( it.key() );
03141         if (columnsOrderExpandedIt == columnsOrderExpanded.constEnd()) {
03142             KexiDBWarn << "(Connection) updateRowDataWithNewValues(): \"now also assign new value in memory\" step "
03143                 "- could not find item '" << it.key()->aliasOrName() << "'" << endl;
03144             continue;
03145         }
03146         data[ columnsOrderExpandedIt.data() ] = it.data();
03147     }
03148 }
03149 
03150 bool Connection::updateRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool useROWID)
03151 {
03152 // Each SQL identifier needs to be escaped in the generated query.
03153 //  query.debug();
03154 
03155     KexiDBDbg << "Connection::updateRow.." << endl;
03156     clearError();
03157     //--get PKEY
03158     if (buf.dbBuffer().isEmpty()) {
03159         KexiDBDbg << " -- NO CHANGES DATA!" << endl;
03160         return true;
03161     }
03162     TableSchema *mt = query.masterTable();
03163     if (!mt) {
03164         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03165         setError(ERR_UPDATE_NO_MASTER_TABLE,
03166             i18n("Could not update row because there is no master table defined."));
03167         return false;
03168     }
03169     IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
03170     if (!useROWID && !pkey) {
03171         KexiDBWarn << " -- NO MASTER TABLE's PKEY!" << endl;
03172         setError(ERR_UPDATE_NO_MASTER_TABLES_PKEY,
03173             i18n("Could not update row because master table has no primary key defined."));
03175         return false;
03176     }
03177     //update the record:
03178     m_sql = "UPDATE " + escapeIdentifier(mt->name()) + " SET ";
03179     QString sqlset, sqlwhere;
03180     sqlset.reserve(1024);
03181     sqlwhere.reserve(1024);
03182     KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer();
03183     for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
03184         if (it.key()->field->table()!=mt)
03185             continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
03186         if (!sqlset.isEmpty())
03187             sqlset+=",";
03188         sqlset += (escapeIdentifier(it.key()->field->name()) + "=" +
03189             m_driver->valueToSQL(it.key()->field,it.data()));
03190     }
03191     if (pkey) {
03192         const QValueVector<int> pkeyFieldsOrder( query.pkeyFieldsOrder() );
03193         KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
03194         if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
03195             KexiDBWarn << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
03196             setError(ERR_UPDATE_NO_ENTIRE_MASTER_TABLES_PKEY,
03197                 i18n("Could not update row because it does not contain entire master table's primary key."));
03198             return false;
03199         }
03200         if (!pkey->fields()->isEmpty()) {
03201             uint i=0;
03202             for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) {
03203                 if (!sqlwhere.isEmpty())
03204                     sqlwhere+=" AND ";
03205                 QVariant val = data[ pkeyFieldsOrder[i] ];
03206                 if (val.isNull() || !val.isValid()) {
03207                     setError(ERR_UPDATE_NULL_PKEY_FIELD,
03208                         i18n("Primary key's field \"%1\" cannot be empty.").arg(it.current()->name()));
03209     //js todo: pass the field's name somewhere!
03210                     return false;
03211                 }
03212                 sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" +
03213                     m_driver->valueToSQL( it.current(), val ) );
03214             }
03215         }
03216     }
03217     else {//use ROWID
03218         sqlwhere = ( escapeIdentifier(m_driver->beh->ROW_ID_FIELD_NAME) + "="
03219             + m_driver->valueToSQL(Field::BigInteger, data[data.size()-1]));
03220     }
03221     m_sql += (sqlset + " WHERE " + sqlwhere);
03222     KexiDBDbg << " -- SQL == " << ((m_sql.length() > 400) ? (m_sql.left(400)+"[.....]") : m_sql) << endl;
03223 
03224     if (!executeSQL(m_sql)) {
03225         setError(ERR_UPDATE_SERVER_ERROR, i18n("Row updating on the server failed."));
03226         return false;
03227     }
03228     //success: now also assign new values in memory:
03229     QMap<QueryColumnInfo*,int> columnsOrderExpanded;
03230     updateRowDataWithNewValues(query, data, b, columnsOrderExpanded);
03231     return true;
03232 }
03233 
03234 bool Connection::insertRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool getROWID)
03235 {
03236 // Each SQL identifier needs to be escaped in the generated query.
03237     KexiDBDbg << "Connection::updateRow.." << endl;
03238     clearError();
03239     //--get PKEY
03240     /*disabled: there may be empty rows (with autoinc)
03241     if (buf.dbBuffer().isEmpty()) {
03242         KexiDBDbg << " -- NO CHANGES DATA!" << endl;
03243         return true; }*/
03244     TableSchema *mt = query.masterTable();
03245     if (!mt) {
03246         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03247         setError(ERR_INSERT_NO_MASTER_TABLE,
03248             i18n("Could not insert row because there is no master table defined."));
03249         return false;
03250     }
03251     IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
03252     if (!getROWID && !pkey)
03253         KexiDBWarn << " -- WARNING: NO MASTER TABLE's PKEY" << endl;
03254 
03255     QString sqlcols, sqlvals;
03256     sqlcols.reserve(1024);
03257     sqlvals.reserve(1024);
03258 
03259     //insert the record:
03260     m_sql = "INSERT INTO " + escapeIdentifier(mt->name()) + " (";
03261     KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer();
03262 
03263     // add default values, if available (for any column without value explicitly set)
03264     const QueryColumnInfo::Vector fieldsExpanded( query.fieldsExpanded( QuerySchema::Unique ) );
03265     for (uint i=0; i<fieldsExpanded.count(); i++) {
03266         QueryColumnInfo *ci = fieldsExpanded.at(i);
03267         if (ci->field && KexiDB::isDefaultValueAllowed(ci->field) 
03268             && !ci->field->defaultValue().isNull() 
03269             && !b.contains( ci ))
03270         {
03271             KexiDBDbg << "Connection::insertRow(): adding default value '" << ci->field->defaultValue().toString()
03272                 << "' for column '" << ci->field->name() << "'" << endl;
03273             b.insert( ci, ci->field->defaultValue() );
03274         }
03275     }
03276 
03277     if (b.isEmpty()) {
03278         // empty row inserting requested:
03279         if (!getROWID && !pkey) {
03280             KexiDBWarn << "MASTER TABLE's PKEY REQUIRED FOR INSERTING EMPTY ROWS: INSERT CANCELLED" << endl;
03281             setError(ERR_INSERT_NO_MASTER_TABLES_PKEY,
03282                 i18n("Could not insert row because master table has no primary key defined."));
03283             return false;
03284         }
03285         if (pkey) {
03286             const QValueVector<int> pkeyFieldsOrder( query.pkeyFieldsOrder() );
03287 //          KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
03288             if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
03289                 KexiDBWarn << "NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
03290                 setError(ERR_INSERT_NO_ENTIRE_MASTER_TABLES_PKEY,
03291                     i18n("Could not insert row because it does not contain entire master table's primary key.")
03292                     .arg(query.name()));
03293                 return false;
03294             }
03295         }
03296         //at least one value is needed for VALUES section: find it and set to NULL:
03297         Field *anyField = mt->anyNonPKField();
03298         if (!anyField) {
03299             if (!pkey) {
03300                 KexiDBWarn << "WARNING: NO FIELD AVAILABLE TO SET IT TO NULL" << endl;
03301                 return false;
03302             }
03303             else {
03304                 //try to set NULL in pkey field (could not work for every SQL engine!)
03305                 anyField = pkey->fields()->first();
03306             }
03307         }
03308         sqlcols += escapeIdentifier(anyField->name());
03309         sqlvals += m_driver->valueToSQL(anyField,QVariant()/*NULL*/);
03310     }
03311     else {
03312         // non-empty row inserting requested:
03313         for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) {
03314             if (it.key()->field->table()!=mt)
03315                 continue; // skip values for fields outside of the master table (e.g. a "visible value" of the lookup field)
03316             if (!sqlcols.isEmpty()) {
03317                 sqlcols+=",";
03318                 sqlvals+=",";
03319             }
03320             sqlcols += escapeIdentifier(it.key()->field->name());
03321             sqlvals += m_driver->valueToSQL(it.key()->field,it.data());
03322         }
03323     }
03324     m_sql += (sqlcols + ") VALUES (" + sqlvals + ")");
03325 //  KexiDBDbg << " -- SQL == " << m_sql << endl;
03326 
03327     bool res = executeSQL(m_sql);
03328 
03329     if (!res) {
03330         setError(ERR_INSERT_SERVER_ERROR, i18n("Row inserting on the server failed."));
03331         return false;
03332     }
03333     //success: now also assign a new value in memory:
03334     QMap<QueryColumnInfo*,int> columnsOrderExpanded;
03335     updateRowDataWithNewValues(query, data, b, columnsOrderExpanded);
03336 
03337     //fetch autoincremented values
03338     QueryColumnInfo::List *aif_list = query.autoIncrementFields();
03339     Q_ULLONG ROWID = 0;
03340     if (pkey && !aif_list->isEmpty()) {
03342         QueryColumnInfo *id_columnInfo = aif_list->first();
03344         Q_ULLONG last_id = lastInsertedAutoIncValue(
03345             id_columnInfo->field->name(), id_columnInfo->field->table()->name(), &ROWID);
03346         if (last_id==(Q_ULLONG)-1 || last_id<=0) {
03349             return false;
03350         }
03351         RowData aif_data;
03352         QString getAutoIncForInsertedValue = QString::fromLatin1("SELECT ")
03353             + query.autoIncrementSQLFieldsList(m_driver)
03354             + QString::fromLatin1(" FROM ")
03355             + escapeIdentifier(id_columnInfo->field->table()->name())
03356             + QString::fromLatin1(" WHERE ")
03357             + escapeIdentifier(id_columnInfo->field->name()) + "="
03358             + QString::number(last_id);
03359         if (true!=querySingleRecord(getAutoIncForInsertedValue, aif_data)) {
03361             return false;
03362         }
03363         QueryColumnInfo::ListIterator ci_it(*aif_list);
03364         QueryColumnInfo *ci;
03365         for (uint i=0; (ci = ci_it.current()); ++ci_it, i++) {
03366 //          KexiDBDbg << "Connection::insertRow(): AUTOINCREMENTED FIELD " << fi->field->name() << " == "
03367 //              << aif_data[i].toInt() << endl;
03368             ( data[ columnsOrderExpanded[ ci ] ] = aif_data[i] ).cast( ci->field->variantType() ); //cast to get proper type
03369         }
03370     }
03371     else {
03372         ROWID = drv_lastInsertRowID();
03373 //      KexiDBDbg << "Connection::insertRow(): new ROWID == " << (uint)ROWID << endl;
03374         if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
03375             KexiDBWarn << "Connection::insertRow(): m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE" << endl;
03376             return false;
03377         }
03378     }
03379     if (getROWID && /*sanity check*/data.size() > fieldsExpanded.size()) {
03380 //      KexiDBDbg << "Connection::insertRow(): new ROWID == " << (uint)ROWID << endl;
03381         data[data.size()-1] = ROWID;
03382     }
03383     return true;
03384 }
03385 
03386 bool Connection::deleteRow(QuerySchema &query, RowData& data, bool useROWID)
03387 {
03388 // Each SQL identifier needs to be escaped in the generated query.
03389     KexiDBWarn << "Connection::deleteRow.." << endl;
03390     clearError();
03391     TableSchema *mt = query.masterTable();
03392     if (!mt) {
03393         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03394         setError(ERR_DELETE_NO_MASTER_TABLE,
03395             i18n("Could not delete row because there is no master table defined.")
03396             .arg(query.name()));
03397         return false;
03398     }
03399     IndexSchema *pkey = (mt->primaryKey() && !mt->primaryKey()->fields()->isEmpty()) ? mt->primaryKey() : 0;
03400 
03402     if (!useROWID && !pkey) {
03403         KexiDBWarn << " -- WARNING: NO MASTER TABLE's PKEY" << endl;
03404         setError(ERR_DELETE_NO_MASTER_TABLES_PKEY,
03405             i18n("Could not delete row because there is no primary key for master table defined."));
03406         return false;
03407     }
03408 
03409     //update the record:
03410     m_sql = "DELETE FROM " + escapeIdentifier(mt->name()) + " WHERE ";
03411     QString sqlwhere;
03412     sqlwhere.reserve(1024);
03413 
03414     if (pkey) {
03415         const QValueVector<int> pkeyFieldsOrder( query.pkeyFieldsOrder() );
03416         KexiDBDbg << pkey->fieldCount() << " ? " << query.pkeyFieldsCount() << endl;
03417         if (pkey->fieldCount() != query.pkeyFieldsCount()) { //sanity check
03418             KexiDBWarn << " -- NO ENTIRE MASTER TABLE's PKEY SPECIFIED!" << endl;
03419             setError(ERR_DELETE_NO_ENTIRE_MASTER_TABLES_PKEY,
03420                 i18n("Could not delete row because it does not contain entire master table's primary key."));
03421             return false;
03422         }
03423         uint i=0;
03424         for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) {
03425             if (!sqlwhere.isEmpty())
03426                 sqlwhere+=" AND ";
03427             QVariant val = data[ pkeyFieldsOrder[i] ];
03428             if (val.isNull() || !val.isValid()) {
03429                 setError(ERR_DELETE_NULL_PKEY_FIELD, i18n("Primary key's field \"%1\" cannot be empty.")
03430                     .arg(it.current()->name()));
03431 //js todo: pass the field's name somewhere!
03432                 return false;
03433             }
03434             sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" +
03435                 m_driver->valueToSQL( it.current(), val ) );
03436         }
03437     }
03438     else {//use ROWID
03439         sqlwhere = ( escapeIdentifier(m_driver->beh->ROW_ID_FIELD_NAME) + "="
03440             + m_driver->valueToSQL(Field::BigInteger, data[data.size()-1]));
03441     }
03442     m_sql += sqlwhere;
03443     KexiDBDbg << " -- SQL == " << m_sql << endl;
03444 
03445     if (!executeSQL(m_sql)) {
03446         setError(ERR_DELETE_SERVER_ERROR, i18n("Row deletion on the server failed."));
03447         return false;
03448     }
03449     return true;
03450 }
03451 
03452 bool Connection::deleteAllRows(QuerySchema &query)
03453 {
03454     clearError();
03455     TableSchema *mt = query.masterTable();
03456     if (!mt) {
03457         KexiDBWarn << " -- NO MASTER TABLE!" << endl;
03458         return false;
03459     }
03460     IndexSchema *pkey = mt->primaryKey();
03461     if (!pkey || pkey->fields()->isEmpty())
03462         KexiDBWarn << "Connection::deleteAllRows -- WARNING: NO MASTER TABLE's PKEY" << endl;
03463 
03464     m_sql = "DELETE FROM " + escapeIdentifier(mt->name());
03465 
03466     KexiDBDbg << " -- SQL == " << m_sql << endl;
03467 
03468     if (!executeSQL(m_sql)) {
03469         setError(ERR_DELETE_SERVER_ERROR, i18n("Row deletion on the server failed."));
03470         return false;
03471     }
03472     return true;
03473 }
03474 
03475 void Connection::registerForTableSchemaChanges(TableSchemaChangeListenerInterface& listener,
03476     TableSchema &schema)
03477 {
03478     QPtrList<TableSchemaChangeListenerInterface>* listeners = d->tableSchemaChangeListeners[&schema];
03479     if (!listeners) {
03480         listeners = new QPtrList<TableSchemaChangeListenerInterface>();
03481         d->tableSchemaChangeListeners.insert(&schema, listeners);
03482     }
03483 //TODO: inefficient
03484     if (listeners->findRef( &listener )==-1)
03485         listeners->append( &listener );
03486 }
03487 
03488 void Connection::unregisterForTableSchemaChanges(TableSchemaChangeListenerInterface& listener,
03489     TableSchema &schema)
03490 {
03491     QPtrList<TableSchemaChangeListenerInterface>* listeners = d->tableSchemaChangeListeners[&schema];
03492     if (!listeners)
03493         return;
03494 //TODO: inefficient
03495     listeners->remove( &listener );
03496 }
03497 
03498 void Connection::unregisterForTablesSchemaChanges(TableSchemaChangeListenerInterface& listener)
03499 {
03500     for (QPtrDictIterator< QPtrList<TableSchemaChangeListenerInterface> > it(d->tableSchemaChangeListeners);
03501         it.current(); ++it)
03502     {
03503         if (-1!=it.current()->find(&listener))
03504             it.current()->take();
03505     }
03506 }
03507 
03508 QPtrList<Connection::TableSchemaChangeListenerInterface>*
03509 Connection::tableSchemaChangeListeners(TableSchema& tableSchema) const
03510 {
03511     KexiDBDbg << d->tableSchemaChangeListeners.count() << endl;
03512     return d->tableSchemaChangeListeners[&tableSchema];
03513 }
03514 
03515 tristate Connection::closeAllTableSchemaChangeListeners(TableSchema& tableSchema)
03516 {
03517     QPtrList<Connection::TableSchemaChangeListenerInterface> *listeners = d->tableSchemaChangeListeners[&tableSchema];
03518     if (!listeners)
03519         return true;
03520     QPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> tmpListeners(*listeners); //safer copy
03521     tristate res = true;
03522     //try to close every window
03523     for (QPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> it(tmpListeners);
03524         it.current() && res==true; ++it)
03525     {
03526         res = it.current()->closeListener();
03527     }
03528     return res;
03529 }
03530 
03531 /*PreparedStatement::Ptr Connection::prepareStatement(PreparedStatement::StatementType, 
03532         TableSchema&)
03533 {
03534     //safe?
03535     return 0;
03536 }*/
03537 
03538 void Connection::setReadOnly(bool set)
03539 {
03540     if (d->isConnected)
03541         return; //sanity
03542     d->readOnly = set;
03543 }
03544 
03545 bool Connection::isReadOnly() const
03546 {
03547     return d->readOnly;
03548 }
03549 
03550 #include "connection.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys