Kexi API Documentation (2.0 alpha)

connection.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this program; see the file COPYING.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <kexidb/connection.h>
00021 
00022 #include "error.h"
00023 #include "connection_p.h"
00024 #include "connectiondata.h"
00025 #include "driver.h"
00026 #include "driver_p.h"
00027 #include "schemadata.h"
00028 #include "tableschema.h"
00029 #include "relationship.h"
00030 #include "transaction.h"
00031 #include "cursor.h"
00032 #include "global.h"
00033 #include "roweditbuffer.h"
00034 #include "utils.h"
00035 #include "dbproperties.h"
00036 #include "lookupfieldschema.h"
00037 #include "parser/parser.h"
00038 
00039 #include <kexiutils/utils.h>
00040 #include <kexiutils/identifier.h>
00041 
00042 #include <qdir.h>
00043 #include <qfileinfo.h>
00044 #include <qguardedptr.h>
00045 #include <qdom.h>
00046 
00047 #include <klocale.h>
00048 #include <kdebug.h>
00049 
00050 #define KEXIDB_EXTENDED_TABLE_SCHEMA_VERSION 1
00051 
00052 //#define KEXIDB_LOOKUP_FIELD_TEST
00053 
00054 namespace KexiDB {
00055 
00056 Connection::SelectStatementOptions::SelectStatementOptions()
00057  : identifierEscaping(Driver::EscapeDriver|Driver::EscapeAsNecessary)
00058  , alsoRetrieveROWID(false)
00059 {
00060 }
00061 
00062 Connection::SelectStatementOptions::~SelectStatementOptions()
00063 {
00064 }
00065 
00066 //================================================
00067 
00068 ConnectionInternal::ConnectionInternal(Connection *conn)
00069  : connection(conn)
00070 {
00071 }
00072 
00073 ConnectionInternal::~ConnectionInternal()
00074 {
00075 }
00076 
00077 //================================================
00079 class ConnectionPrivate
00080 {
00081     public:
00082         ConnectionPrivate(Connection* const conn, ConnectionData &conn_data)
00083          : conn(conn)
00084          , conn_data(&conn_data)
00085          , tableSchemaChangeListeners(101)
00086          , m_parser(0)
00087          , tables_byname(101, false)
00088          , queries_byname(101, false)
00089          , kexiDBSystemTables(101)
00090          , dont_remove_transactions(false)
00091          , skip_databaseExists_check_in_useDatabase(false)
00092          , default_trans_started_inside(false)
00093          , isConnected(false)
00094          , autoCommit(true)
00095         {
00096             tableSchemaChangeListeners.setAutoDelete(true);
00097             obsoleteQueries.setAutoDelete(true);
00098 
00099             tables.setAutoDelete(true);
00100             tables_byname.setAutoDelete(false);//tables is owner, not me
00101             kexiDBSystemTables.setAutoDelete(true);//only system tables
00102             queries.setAutoDelete(true);
00103             queries_byname.setAutoDelete(false);//queries is owner, not me
00104 
00105             //reasonable sizes: TODO
00106             tables.resize(101);
00107             queries.resize(101);
00108         }
00109         ~ConnectionPrivate()
00110         {
00111             delete m_parser;
00112         }
00113 
00114         void errorInvalidDBContents(const QString& details) {
00115             conn->setError( ERR_INVALID_DATABASE_CONTENTS, i18n("Invalid database contents. ")+details);
00116         }
00117 
00118         QString strItIsASystemObject() const {
00119             return i18n("It is a system object.");
00120         }
00121 
00122         inline Parser *parser() { return m_parser ? m_parser : (m_parser = new Parser(conn)); }
00123 
00124         Connection* const conn; 
00125         QGuardedPtr<ConnectionData> conn_data; 
00126 
00131         Transaction default_trans;
00132         QValueList<Transaction> transactions;
00133 
00134         QPtrDict< QPtrList<Connection::TableSchemaChangeListenerInterface> > tableSchemaChangeListeners;
00135 
00138         QPtrList<QuerySchema> obsoleteQueries;
00139 
00140 
00142         KexiDB::ServerVersionInfo serverVersion;
00143 
00145         KexiDB::DatabaseVersionInfo databaseVersion;
00146 
00147         Parser *m_parser;
00148 
00150         QIntDict<TableSchema> tables;
00151         QDict<TableSchema> tables_byname;
00152         QIntDict<QuerySchema> queries;
00153         QDict<QuerySchema> queries_byname;
00154 
00156         QPtrDict<TableSchema> kexiDBSystemTables;
00157 
00159         DatabaseProperties* dbProperties;
00160 
00161         QString availableDatabaseName; 
00162         QString usedDatabase; 
00163 
00166         bool dont_remove_transactions : 1;
00167 
00170         bool skip_databaseExists_check_in_useDatabase : 1;
00171 
00180         bool default_trans_started_inside : 1;
00181 
00182         bool isConnected : 1;
00183 
00184         bool autoCommit : 1;
00185 
00187         bool readOnly : 1;
00188 };
00189 
00190 }//namespace KexiDB
00191 
00192 //================================================
00193 using namespace KexiDB;
00194 
00196 QStringList KexiDB_kexiDBSystemTableNames;
00197 
00198 Connection::Connection( Driver *driver, ConnectionData &conn_data )
00199     : QObject()
00200     ,KexiDB::Object()
00201     ,d(new ConnectionPrivate(this, conn_data))
00202     ,m_driver(driver)
00203     ,m_destructor_started(false)
00204 {
00205     d->dbProperties = new DatabaseProperties(this);
00206     m_cursors.setAutoDelete(true);
00207 //  d->transactions.setAutoDelete(true);
00208     //reasonable sizes: TODO
00209     m_cursors.resize(101);
00210 //  d->transactions.resize(101);//woohoo! so many transactions?
00211     m_sql.reserve(0x4000);
00212 }
00213 
00214 void Connection::destroy()
00215 {
00216     disconnect();
00217     //do not allow the driver to touch me: I will kill myself.
00218     m_driver->d->connections.take( this );
00219 }
00220 
00221 Connection::~Connection()
00222 {
00223     m_destructor_started = true;
00224 //  KexiDBDbg << "Connection::~Connection()" << endl;
00225     delete d->dbProperties;
00226     delete d;
00227     d = 0;
00228 /*  if (m_driver) {
00229         if (m_is_connected) {
00230             //delete own table schemas
00231             d->tables.clear();
00232             //delete own cursors:
00233             m_cursors.clear();
00234         }
00235         //do not allow the driver to touch me: I will kill myself.
00236         m_driver->m_connections.take( this );
00237     }*/
00238 }
00239 
00240 ConnectionData* Connection::data() const
00241 {
00242     return d->conn_data;
00243 }
00244 
00245 bool Connection::connect()
00246 {
00247     clearError();
00248     if (d->isConnected) {
00249         setError(ERR_ALREADY_CONNECTED, i18n("Connection already established.") );
00250         return false;
00251     }
00252 
00253     d->serverVersion.clear();
00254     if (!(d->isConnected = drv_connect(d->serverVersion))) {
00255         setError(m_driver->isFileDriver() ?
00256             i18n("Could not open \"%1\" project file.").arg(QDir::convertSeparators(d->conn_data->fileName()))
00257             : i18n("Could not connect to \"%1\" database server.").arg(d->conn_data->serverInfoString()) );
00258     }
00259     return d->isConnected;
00260 }
00261 
00262 bool Connection::isDatabaseUsed() const
00263 {
00264     return !d->usedDatabase.isEmpty() && d->isConnected && drv_isDatabaseUsed();
00265 }
00266 
00267 void Connection::clearError()
00268 {
00269     Object::clearError();
00270     m_sql = QString::null;
00271 }
00272 
00273 bool Connection::disconnect()
00274 {
00275     clearError();
00276     if (!d->isConnected)
00277         return true;
00278 
00279     if (!closeDatabase())
00280         return false;
00281 
00282     bool ok = drv_disconnect();
00283     if (ok)
00284         d->isConnected = false;
00285     return ok;
00286 }
00287 
00288 bool Connection::isConnected() const
00289 {
00290     return d->isConnected;
00291 }
00292 
00293 bool Connection::checkConnected()
00294 {
00295     if (d->isConnected) {
00296         clearError();
00297         return true;
00298     }
00299     setError(ERR_NO_CONNECTION, i18n("Not connected to the database server.") );
00300     return false;
00301 }
00302 
00303 bool Connection::checkIsDatabaseUsed()
00304 {
00305     if (isDatabaseUsed()) {
00306         clearError();
00307         return true;
00308     }
00309     setError(ERR_NO_DB_USED, i18n("Currently no database is used.") );
00310     return false;
00311 }
00312 
00313 QStringList Connection::databaseNames(bool also_system_db)
00314 {
00315     KexiDBDbg << "Connection::databaseNames("<<also_system_db<<")"<< endl;
00316     if (!checkConnected())
00317         return QStringList();
00318 
00319     QString tmpdbName;
00320     //some engines need to have opened any database before executing "create database"
00321     if (!useTemporaryDatabaseIfNeeded(tmpdbName))
00322         return QStringList();
00323 
00324     QStringList list, non_system_list;
00325 
00326     bool ret = drv_getDatabasesList( list );
00327 
00328     if (!tmpdbName.isEmpty()) {
00329         //whatever result is - now we have to close temporary opened database:
00330         if (!closeDatabase())
00331             return QStringList();
00332     }
00333 
00334     if (!ret)
00335         return QStringList();
00336 
00337     if (also_system_db)
00338         return list;
00339     //filter system databases:
00340     for (QStringList::ConstIterator it = list.constBegin(); it!=list.constEnd(); ++it) {
00341         KexiDBDbg << "Connection::databaseNames(): " << *it << endl;
00342         if (!m_driver->isSystemDatabaseName(*it)) {
00343             KexiDBDbg << "add " << *it << endl;
00344             non_system_list << (*it);
00345         }
00346     }
00347     return non_system_list;
00348 }
00349 
00350 bool Connection::drv_getDatabasesList( QStringList &list )
00351 {
00352     list.clear();
00353     return true;
00354 }
00355 
00356 bool Connection::drv_databaseExists( const QString &dbName, bool ignoreErrors )
00357 {
00358     QStringList list = databaseNames(true);//also system
00359     if (error()) {
00360         return false;
00361     }
00362 
00363     if (list.find( dbName )==list.end()) {
00364         if (!ignoreErrors)
00365             setError(ERR_OBJECT_NOT_FOUND, i18n("The database \"%1\" does not exist.").arg(dbName));
00366         return false;
00367     }
00368 
00369     return true;
00370 }
00371 
00372 bool Connection::databaseExists( const QString &dbName, bool ignoreErrors )
00373 {
00374 //  KexiDBDbg << "Connection::databaseExists(" << dbName << "," << ignoreErrors << ")" << endl;
00375     if (!checkConnected())
00376         return false;
00377     clearError();
00378 
00379     if (m_driver->isFileDriver()) {
00380         //for file-based db: file must exists and be accessible
00381 //js: moved from useDatabase():
00382         QFileInfo file(d->conn_data->fileName());
00383         if (!file.exists() || ( !file.isFile() && !file.isSymLink()) ) {
00384             if (!ignoreErrors)
00385                 setError(ERR_OBJECT_NOT_FOUND, i18n("Database file \"%1\" does not exist.")
00386                 .arg(QDir::convertSeparators(d->conn_data->fileName())) );
00387             return false;
00388         }
00389         if (!file.isReadable()) {
00390             if (!ignoreErrors)
00391                 setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not readable.")
00392                 .arg(QDir::convertSeparators(d->conn_data->fileName())) );
00393             return false;
00394         }
00395         if (!file.isWritable()) {
00396             if (!ignoreErrors)
00397                 setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not writable.")
00398                 .arg(QDir::convertSeparators(d->conn_data->fileName())) );
00399             return false;
00400         }
00401         return true;
00402     }
00403 
00404     QString tmpdbName;
00405     //some engines need to have opened any database before executing "create database"
00406     const bool orig_skip_databaseExists_check_in_useDatabase = d->skip_databaseExists_check_in_useDatabase;
00407     d->skip_databaseExists_check_in_useDatabase = true;
00408     bool ret = useTemporaryDatabaseIfNeeded(tmpdbName);
00409     d->skip_databaseExists_check_in_useDatabase = orig_skip_databaseExists_check_in_useDatabase;
00410     if (!ret)
00411         return false;
00412 
00413     ret = drv_databaseExists(dbName, ignoreErrors);
00414 
00415     if (!tmpdbName.isEmpty()) {
00416         //whatever result is - now we have to close temporary opened database:
00417         if (!closeDatabase())
00418             return false;
00419     }
00420 
00421     return ret;
00422 }
00423 
00424 #define createDatabase_CLOSE \
00425     { if (!closeDatabase()) { \
00426         setError(i18n("Database \"%1\" created but could not be closed after creation.").arg(dbName) ); \
00427         return false; \
00428     } }
00429 
00430 #define createDatabase_ERROR \
00431     { createDatabase_CLOSE; return false; }
00432 
00433 
00434 bool Connection::createDatabase( const QString &dbName )
00435 {
00436     if (!checkConnected())
00437         return false;
00438 
00439     if (databaseExists( dbName )) {
00440         setError(ERR_OBJECT_EXISTS, i18n("Database \"%1\" already exists.").arg(dbName) );
00441         return false;
00442     }
00443     if (m_driver->isSystemDatabaseName( dbName )) {
00444         setError(ERR_SYSTEM_NAME_RESERVED, 
00445             i18n("Cannot create database \"%1\". This name is reserved for system database.").arg(dbName) );
00446         return false;
00447     }
00448     if (m_driver->isFileDriver()) {
00449         //update connection data if filename differs
00450         d->conn_data->setFileName( dbName );
00451     }
00452 
00453     QString tmpdbName;
00454     //some engines need to have opened any database before executing "create database"
00455     if (!useTemporaryDatabaseIfNeeded(tmpdbName))
00456         return false;
00457 
00458     //low-level create
00459     if (!drv_createDatabase( dbName )) {
00460         setError(i18n("Error creating database \"%1\" on the server.").arg(dbName) );
00461         closeDatabase();//sanity
00462         return false;
00463     }
00464 
00465     if (!tmpdbName.isEmpty()) {
00466         //whatever result is - now we have to close temporary opened database:
00467         if (!closeDatabase())
00468             return false;
00469     }
00470 
00471     if (!tmpdbName.isEmpty() || !m_driver->d->isDBOpenedAfterCreate) {
00472         //db need to be opened
00473         if (!useDatabase( dbName, false/*not yet kexi compatible!*/ )) {
00474             setError(i18n("Database \"%1\" created but could not be opened.").arg(dbName) );
00475             return false;
00476         }
00477     }
00478     else {
00479         //just for the rule
00480         d->usedDatabase = dbName;
00481     }
00482 
00483     Transaction trans;
00484     if (m_driver->transactionsSupported()) {
00485         trans = beginTransaction();
00486         if (!trans.active())
00487             return false;
00488     }
00489 //not needed since closeDatabase() rollbacks transaction: TransactionGuard trans_g(this);
00490 //  if (error())
00491 //      return false;
00492 
00493     //-create system tables schema objects
00494     if (!setupKexiDBSystemSchema())
00495         return false;
00496 
00497     //-physically create system tables
00498     for (QPtrDictIterator<TableSchema> it(d->kexiDBSystemTables); it.current(); ++it) {
00499         if (!drv_createTable( it.current()->name() ))
00500             createDatabase_ERROR;
00501     }
00502 
00503 /* moved to KexiProject...
00504 
00505     //-create default part info
00506     TableSchema *ts;
00507     if (!(ts = tableSchema("kexi__parts")))
00508         createDatabase_ERROR;
00509     FieldList *fl = ts->subList("p_id", "p_name", "p_mime", "p_url");
00510     if (!fl)
00511         createDatabase_ERROR;
00512     if (!insertRecord(*fl, QVariant(1), QVariant("Tables"), QVariant("kexi/table"), QVariant("http://koffice.org/kexi/")))
00513         createDatabase_ERROR;
00514     if (!insertRecord(*fl, QVariant(2), QVariant("Queries"), QVariant("kexi/query"), QVariant("http://koffice.org/kexi/")))
00515         createDatabase_ERROR;
00516 */
00517 
00518     //-insert KexiDB version info:
00519     TableSchema *t_db = tableSchema("kexi__db");
00520     if (!t_db)
00521         createDatabase_ERROR;
00522     if ( !insertRecord(*t_db, "kexidb_major_ver", KexiDB::version().major)
00523         || !insertRecord(*t_db, "kexidb_minor_ver", KexiDB::version().minor))
00524         createDatabase_ERROR;
00525 
00526     if (trans.active() && !commitTransaction(trans))
00527         createDatabase_ERROR;
00528 
00529     createDatabase_CLOSE;
00530     return true;
00531 }
00532 
00533 #undef createDatabase_CLOSE
00534 #undef createDatabase_ERROR
00535 
00536 bool Connection::useDatabase( const QString &dbName, bool kexiCompatible, bool *cancelled, MessageHandler* msgHandler )
00537 {
00538     if (cancelled)
00539         *cancelled = false;
00540     KexiDBDbg << "Connection::useDatabase(" << dbName << "," << kexiCompatible <<")" << endl;
00541     if (!checkConnected())
00542         return false;
00543 
00544     if (dbName.isEmpty())
00545         return false;
00546     QString my_dbName = dbName;
00547 //  if (my_dbName.isEmpty()) {
00548 //      const QStringList& db_lst = databaseNames();
00549 //      if (!db_lst.isEmpty())
00550 //          my_dbName = db_lst.first();
00551 //  }
00552     if (d->usedDatabase == my_dbName)
00553         return true; //already used
00554 
00555     if (!d->skip_databaseExists_check_in_useDatabase) {
00556         if (!databaseExists(my_dbName, false /*don't ignore errors*/))
00557             return false; //database must exist
00558     }
00559 
00560     if (!d->usedDatabase.isEmpty() && !closeDatabase()) //close db if already used
00561         return false;
00562 
00563     d->usedDatabase = "";
00564 
00565     if (!drv_useDatabase( my_dbName, cancelled, msgHandler )) {
00566         if (cancelled && *cancelled)
00567             return false;
00568         QString msg(i18n("Opening database \"%1\" failed.").arg( my_dbName ));
00569         if (error())
00570             setError( this, msg );
00571         else
00572             setError( msg );
00573         return false;
00574     }
00575 
00576     //-create system tables schema objects
00577     if (!setupKexiDBSystemSchema())
00578         return false;
00579 
00580     if (kexiCompatible && my_dbName.lower()!=anyAvailableDatabaseName().lower()) {
00581         //-get global database information
00582         int num;
00583         bool ok;
00584 //      static QString notfound_str = i18n("\"%1\" database property not found");
00585         num = d->dbProperties->value("kexidb_major_ver").toInt(&ok);
00586         if (!ok)
00587             return false;
00588         d->databaseVersion.major = num;
00589 /*      if (true!=querySingleNumber(
00590             "select db_value from kexi__db where db_property=" + m_driver->escapeString(QString("kexidb_major_ver")), num)) {
00591             d->errorInvalidDBContents(notfound_str.arg("kexidb_major_ver"));
00592             return false;
00593         }*/
00594         num = d->dbProperties->value("kexidb_minor_ver").toInt(&ok);
00595         if (!ok)
00596             return false;
00597         d->databaseVersion.minor = num;
00598 /*      if (true!=querySingleNumber(
00599             "select db_value from kexi__db where db_property=" + m_driver->escapeString(QString("kexidb_minor_ver")), num)) {
00600             d->errorInvalidDBContents(notfound_str.arg("kexidb_minor_ver"));
00601             return false;
00602         }*/
00603 
00604 #if 0 //this is already checked in DriverManagerInternal::lookupDrivers()
00605         //** error if major version does not match
00606         if (m_driver->versionMajor()!=KexiDB::versionMajor()) {
00607             setError(ERR_INCOMPAT_DATABASE_VERSION,
00608                 i18n("Database version (%1) does not match Kexi application's version (%2)")
00609                 .arg( QString("%1.%2").arg(versionMajor()).arg(versionMinor()) )
00610                 .arg( QString("%1.%2").arg(KexiDB::versionMajor()).arg(KexiDB::versionMinor()) ) );
00611             return false;
00612         }
00613         if (m_driver->versionMinor()!=KexiDB::versionMinor()) {
00614             //js TODO: COMPATIBILITY CODE HERE!
00615             //js TODO: CONVERSION CODE HERE (or signal that conversion is needed)
00616         }
00617 #endif
00618     }
00619     d->usedDatabase = my_dbName;
00620     return true;
00621 }
00622 
00623 bool Connection::closeDatabase()
00624 {
00625     if (d->usedDatabase.isEmpty())
00626         return true; //no db used
00627     if (!checkConnected())
00628         return true;
00629 
00630     bool ret = true;
00631 
00633     if (m_driver->transactionsSupported()) {
00634         //rollback all transactions
00635         QValueList<Transaction>::ConstIterator it;
00636         d->dont_remove_transactions=true; //lock!
00637         for (it=d->transactions.constBegin(); it!= d->transactions.constEnd(); ++it) {
00638             if (!rollbackTransaction(*it)) {//rollback as much as you can, don't stop on prev. errors
00639                 ret = false;
00640             }
00641             else {
00642                 KexiDBDbg << "Connection::closeDatabase(): transaction rolled back!" << endl;
00643                 KexiDBDbg << "Connection::closeDatabase(): trans.refcount==" <<
00644                  ((*it).m_data ? QString::number((*it).m_data->refcount) : "(null)") << endl;
00645             }
00646         }
00647         d->dont_remove_transactions=false; //unlock!
00648         d->transactions.clear(); //free trans. data
00649     }
00650 
00651     //delete own cursors:
00652     m_cursors.clear();
00653     //delete own schemas
00654     d->tables.clear();
00655     d->kexiDBSystemTables.clear();
00656     d->queries.clear();
00657 
00658     if (!drv_closeDatabase())
00659         return false;
00660 
00661     d->usedDatabase = "";
00662 //  KexiDBDbg << "Connection::closeDatabase(): " << ret << endl;
00663     return ret;
00664 }
00665 
00666 QString Connection::currentDatabase() const
00667 {
00668     return d->usedDatabase;
00669 }
00670 
00671 bool Connection::useTemporaryDatabaseIfNeeded(QString &tmpdbName)
00672 {
00673     if (!m_driver->isFileDriver() && m_driver->beh->USING_DATABASE_REQUIRED_TO_CONNECT
00674      && !isDatabaseUsed()) {
00675         //we have no db used, but it is required by engine to have used any!
00676         tmpdbName = anyAvailableDatabaseName();
00677         if (tmpdbName.isEmpty()) {
00678             setError(ERR_NO_DB_USED, i18n("Cannot find any database for temporary connection.") );
00679             return false;
00680         }
00681         const bool orig_skip_databaseExists_check_in_useDatabase = d->skip_databaseExists_check_in_useDatabase;
00682         d->skip_databaseExists_check_in_useDatabase = true;
00683         bool ret = useDatabase(tmpdbName, false);
00684         d->skip_databaseExists_check_in_useDatabase = orig_skip_databaseExists_check_in_useDatabase;
00685         if (!ret) {
00686             setError(errorNum(), 
00687                 i18n("Error during starting temporary connection using \"%1\" database name.")
00688                 .arg(tmpdbName) );
00689             return false;
00690         }
00691     }
00692     return true;
00693 }
00694 
00695 bool Connection::dropDatabase( const QString &dbName )
00696 {
00697     if (!checkConnected())
00698         return false;
00699 
00700     QString dbToDrop;
00701     if (dbName.isEmpty() && d->usedDatabase.isEmpty()) {
00702         if (!m_driver->isFileDriver()
00703          || (m_driver->isFileDriver() && d->conn_data->fileName().isEmpty()) ) {
00704             setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot drop database - name not specified.") );
00705             return false;
00706         }
00707         //this is a file driver so reuse previously passed filename
00708         dbToDrop = d->conn_data->fileName();
00709     }
00710     else {
00711         if (dbName.isEmpty()) {
00712             dbToDrop = d->usedDatabase;
00713         } else {
00714             if (m_driver->isFileDriver()) //lets get full path
00715                 dbToDrop = QFileInfo(dbName).absFilePath();
00716             else
00717                 dbToDrop = dbName;
00718         }
00719     }
00720 
00721     if (dbToDrop.isEmpty()) {
00722         setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot delete database - name not specified.") );
00723         return false;
00724     }
00725 
00726     if (m_driver->isSystemDatabaseName( dbToDrop )) {
00727         setError(ERR_SYSTEM_NAME_RESERVED, i18n("Cannot delete system database \"%1\".").arg(dbToDrop) );
00728         return false;
00729     }
00730 
00731     if (isDatabaseUsed() && d->usedDatabase == dbToDrop) {
00732         //we need to close database because cannot drop used this database
00733         if (!closeDatabase())
00734             return false;
00735     }
00736 
00737     QString tmpdbName;
00738     //some engines need to have opened any database before executing "drop database"
00739     if (!useTemporaryDatabaseIfNeeded(tmpdbName))
00740         return false;
00741 
00742     //ok, now we have access to dropping
00743     bool ret = drv_dropDatabase( dbToDrop );
00744 
00745     if (!tmpdbName.isEmpty()) {
00746         //whatever result is - now we have to close temporary opened database:
00747         if (!closeDatabase())
00748             return false;
00749     }
00750     return ret;
00751 }
00752 
00753 QStringList Connection::objectNames(int objType, bool* ok)
00754 {
00755     QStringList list;
00756 
00757     if (!checkIsDatabaseUsed()) {
00758         if(ok) 
00759             *ok = false;
00760         return list;
00761     }
00762 
00763     QString sql;
00764     if (objType==KexiDB::AnyObjectType)
00765         sql = "SELECT o_name FROM kexi__objects";
00766     else
00767         sql = QString::fromLatin1("SELECT o_name FROM kexi__objects WHERE o_type=%1").arg(objType);
00768 
00769     Cursor *c = executeQuery(sql);
00770     if (!c) {
00771         if(ok) 
00772             *ok = false;
00773         return list;
00774     }
00775 
00776     for (c->moveFirst(); !c->eof(); c->moveNext()) {
00777         QString name = c->value(0).toString();
00778         if (KexiUtils::isIdentifier( name )) {
00779             list.append(name);
00780         }
00781     }
00782 
00783     if (!deleteCursor(c)) {
00784         if(ok)
00785             *ok = false;
00786         return list;
00787     }
00788 
00789     if(ok)
00790         *ok = true;
00791     return list;
00792 }
00793 
00794 QStringList Connection::tableNames(bool also_system_tables)
00795 {
00796     bool ok = true;
00797     QStringList list = objectNames(TableObjectType, &ok);
00798     if (also_system_tables && ok) {
00799         list += Connection::kexiDBSystemTableNames();
00800     }
00801     return list;
00802 }
00803 
00805 const QStringList& Connection::kexiDBSystemTableNames()
00806 {
00807     if (KexiDB_kexiDBSystemTableNames.isEmpty()) {
00808         KexiDB_kexiDBSystemTableNames
00809         << "kexi__objects"
00810         << "kexi__objectdata"
00811         << "kexi__fields"
00812 //      << "kexi__querydata"
00813 //      << "kexi__queryfields"
00814 //      << "kexi__querytables"
00815         << "kexi__db"
00816         ;
00817     }
00818     return KexiDB_kexiDBSystemTableNames;
00819 }
00820 
00821 KexiDB::ServerVersionInfo* Connection::serverVersion() const
00822 {
00823     return isConnected() ? &d->serverVersion : 0;
00824 }
00825 
00826 KexiDB::DatabaseVersionInfo* Connection::databaseVersion() const
00827 {
00828     return isDatabaseUsed() ? &d->databaseVersion : 0;
00829 }
00830 
00831 DatabaseProperties& Connection::databaseProperties()
00832 {
00833     return *d->dbProperties;
00834 }
00835 
00836 QValueList<int> Connection::tableIds()
00837 {
00838     return objectIds(KexiDB::TableObjectType);
00839 }
00840 
00841 QValueList<int> Connection::queryIds()
00842 {
00843     return objectIds(KexiDB::QueryObjectType);
00844 }
00845 
00846 QValueList<int> Connection::objectIds(int objType)
00847 {
00848     QValueList<int> list;
00849 
00850     if (!checkIsDatabaseUsed())
00851         return list;
00852 
00853     Cursor *c = executeQuery(
00854         QString::fromLatin1("SELECT o_id, o_name FROM kexi__objects WHERE o_type=%1").arg(objType));
00855     if (!c)
00856         return list;
00857     for (c->moveFirst(); !c->eof(); c->moveNext())
00858     {
00859         QString tname = c->value(1).toString(); //kexi__objects.o_name
00860         if (KexiUtils::isIdentifier( tname )) {
00861             list.append(c->value(0).toInt()); //kexi__objects.o_id
00862         }
00863     }
00864 
00865     deleteCursor(c);
00866 
00867     return list;
00868 }
00869 
00870 QString Connection::createTableStatement( const KexiDB::TableSchema& tableSchema ) const
00871 {
00872 // Each SQL identifier needs to be escaped in the generated query.
00873     QString sql;
00874     sql.reserve(4096);
00875     sql = "CREATE TABLE " + escapeIdentifier(tableSchema.name()) + " (";
00876     bool first=true;
00877     Field::ListIterator it( tableSchema.m_fields );
00878     Field *field;
00879     for (;(field = it.current())!=0; ++it) {
00880         if (first)
00881             first = false;
00882         else
00883             sql += ", ";
00884         QString v = escapeIdentifier(field->name()) + " ";
00885         const bool autoinc = field->isAutoIncrement();
00886         const bool pk = field->isPrimaryKey() || (autoinc && m_driver->beh->AUTO_INCREMENT_REQUIRES_PK);
00887 //TODO: warning: ^^^^^ this allows only 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     number = 0;
01102     for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) {
01103         if (querySchema.isColumnVisible(number)) {
01104             if (!sql.isEmpty())
01105                 sql += QString::fromLatin1(", ");
01106 
01107             if (f->isQueryAsterisk()) {
01108                 if (!singleTable && static_cast<QueryAsterisk*>(f)->isSingleTableAsterisk()) //single-table *
01109                     sql += escapeIdentifier(f->table()->name(), options.identifierEscaping) +
01110                            QString::fromLatin1(".*");
01111                 else //all-tables * (or simplified table.* when there's only one table)
01112                     sql += QString::fromLatin1("*");
01113             }
01114             else {
01115                 if (f->isExpression()) {
01116                     sql += f->expression()->toString();
01117                 }
01118                 else {
01119                     if (!f->table()) //sanity check
01120                         return QString::null;
01121 
01122                     QString tableName;
01123                     int tablePosition = querySchema.tableBoundToColumn(number);
01124                     if (tablePosition>=0)
01125                         tableName = querySchema.tableAlias(tablePosition);
01126                     if (tableName.isEmpty())
01127                         tableName = f->table()->name();
01128 
01129                     if (!singleTable) {
01130                         sql += (escapeIdentifier(tableName, options.identifierEscaping) + ".");
01131                     }
01132                     sql += escapeIdentifier(f->name(), options.identifierEscaping);
01133                 }
01134                 QString aliasString = QString(querySchema.columnAlias(number));
01135                 if (!aliasString.isEmpty())
01136                     sql += (QString::fromLatin1(" AS ") + aliasString);
01138             }
01139             LookupFieldSchema *lookupFieldSchema = f->table() ? f->table()->lookupFieldSchema( *f ) : 0;
01140             if (lookupFieldSchema) {
01141                 // Lookup field schema found
01142                 // Now we also need to fetch "visible" value from the lookup table, not only the value of binding.
01143                 // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken)
01144                 // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField"
01145                 LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource();
01146                 if (rowSource.type()==LookupFieldSchema::RowSource::Table) {
01147                     TableSchema *lookupTable = querySchema.connection()->tableSchema( rowSource.name() );
01148                     Field *visibleField = 0;
01149                     Field *boundField = 0;
01150                     if (lookupTable && lookupFieldSchema->boundColumn()>=0 
01151                         && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
01152                         && (visibleField = lookupTable->field( lookupFieldSchema->visibleColumn()))
01153                         && (boundField = lookupTable->field( lookupFieldSchema->boundColumn() )))
01154                     {
01155                         //add LEFT OUTER JOIN
01156                         if (!s_additional_joins.isEmpty())
01157                             s_additional_joins += QString::fromLatin1(" ");
01158                         QString internalUniqueTableAlias( QString("__kexidb_") + lookupTable->name() + "_"
01159                             + QString::number(internalUniqueTableAliasNumber++) );
01160                         s_additional_joins += QString("LEFT OUTER JOIN %1 AS %2 ON %3.%4=%5.%6")
01161                             .arg(escapeIdentifier(lookupTable->name(), options.identifierEscaping))
01162                             .arg(internalUniqueTableAlias)
01163                             .arg(escapeIdentifier(f->table()->name(), options.identifierEscaping))
01164                             .arg(escapeIdentifier(f->name(), options.identifierEscaping))
01165                             .arg(internalUniqueTableAlias)
01166                             .arg(escapeIdentifier(boundField->name(), options.identifierEscaping));
01167 
01168                         //add visibleField to the list of SELECTed fields //if it is not yet present there
01169 //not needed                        if (!querySchema.findTableField( visibleField->table()->name()+"."+visibleField->name() )) {
01170                             if (!querySchema.table( visibleField->table()->name() )) {
01171 /* not true
01172                                 //table should be added after FROM
01173                                 if (!s_from_additional.isEmpty())
01174                                     s_from_additional += QString::fromLatin1(", ");
01175                                 s_from_additional += escapeIdentifier(visibleField->table()->name(), options.identifierEscaping);
01176                                 */
01177                             }
01178                             if (!s_additional_fields.isEmpty())
01179                                 s_additional_fields += QString::fromLatin1(", ");
01180                             s_additional_fields += (internalUniqueTableAlias + "." //escapeIdentifier(visibleField->table()->name(), options.identifierEscaping) + "."
01181                                 + escapeIdentifier(visibleField->name(), options.identifierEscaping));
01182 //not needed                        }
01183                     }
01184                 }
01185             }
01186         }
01187     }
01188 
01189     //add lookup fields
01190     if (!s_additional_fields.isEmpty())
01191         sql += (QString::fromLatin1(", ") + s_additional_fields);
01192 
01193     if (options.alsoRetrieveROWID) { //append rowid column
01194         QString s;
01195         if (!sql.isEmpty())
01196             s = QString::fromLatin1(", ");
01197         if (querySchema.masterTable())
01198             s += (escapeIdentifier(querySchema.masterTable()->name())+".");
01199         s += m_driver->beh->ROW_ID_FIELD_NAME;
01200         sql += s;
01201     }
01202 
01203     sql.prepend("SELECT ");
01204     TableSchema::List* tables = querySchema.tables();
01205     if (tables && !tables->isEmpty()) {
01206         sql += QString::fromLatin1(" FROM ");
01207         QString s_from;
01208         TableSchema *table;
01209         number = 0;
01210         for (TableSchema::ListIterator it(*tables); (table = it.current());
01211             ++it, number++)
01212         {
01213             if (!s_from.isEmpty())
01214                 s_from += QString::fromLatin1(", ");
01215             s_from += escapeIdentifier(table->name(), options.identifierEscaping);
01216             QString aliasString = QString(querySchema.tableAlias(number));
01217             if (!aliasString.isEmpty())
01218                 s_from += (QString::fromLatin1(" AS ") + aliasString);
01219         }
01220 /*unused    if (!s_from_additional.isEmpty()) {//additional tables list needed for lookup fields
01221             if (!s_from.isEmpty())
01222                 s_from += QString::fromLatin1(", ");
01223             s_from += s_from_additional;
01224         }*/
01225         sql += s_from;
01226     }
01227     QString s_where;
01228     s_where.reserve(4096);
01229 
01230     //JOINS
01231     if (!s_additional_joins.isEmpty()) {
01232         sql += QString::fromLatin1(" ") + s_additional_joins + QString::fromLatin1(" ");
01233     }
01234 
01235 //@todo: we're using WHERE for joins now; use INNER/LEFT/RIGHT JOIN later
01236 
01237     //WHERE
01238     Relationship *rel;
01239     bool wasWhere = false; //for later use
01240     for (Relationship::ListIterator it(*querySchema.relationships()); (rel = it.current()); ++it) {
01241         if (s_where.isEmpty()) {
01242             wasWhere = true;
01243         }
01244         else
01245             s_where += QString::fromLatin1(" AND ");
01246         Field::Pair *pair;
01247         QString s_where_sub;
01248         for (QPtrListIterator<Field::Pair> p_it(*rel->fieldPairs()); (pair = p_it.current()); ++p_it) {
01249             if (!s_where_sub.isEmpty())
01250                 s_where_sub += QString::fromLatin1(" AND ");
01251             s_where_sub += (
01252                 escapeIdentifier(pair->first->table()->name(), options.identifierEscaping) +
01253                 QString::fromLatin1(".") +
01254                 escapeIdentifier(pair->first->name(), options.identifierEscaping) +
01255                 QString::fromLatin1(" = ")  +
01256                 escapeIdentifier(pair->second->table()->name(), options.identifierEscaping) +
01257                 QString::fromLatin1(".") +
01258                 escapeIdentifier(pair->second->name(), options.identifierEscaping));
01259         }
01260         if (rel->fieldPairs()->count()>1) {
01261             s_where_sub.prepend("(");
01262             s_where_sub += QString::fromLatin1(")");
01263         }
01264         s_where += s_where_sub;
01265     }
01266     //EXPLICITLY SPECIFIED WHERE EXPRESSION
01267     if (querySchema.whereExpression()) {
01268         QuerySchemaParameterValueListIterator paramValuesIt(*m_driver, params);
01269         QuerySchemaParameterValueListIterator *paramValuesItPtr = params.isEmpty() ? 0 : &paramValuesIt;
01270         if (wasWhere) {
01271 //TODO: () are not always needed
01272             s_where = "(" + s_where + ") AND (" + querySchema.whereExpression()->toString(paramValuesItPtr) + ")";
01273         }
01274         else {
01275             s_where = querySchema.whereExpression()->toString(paramValuesItPtr);
01276         }
01277     }
01278     if (!s_where.isEmpty())
01279         sql += QString::fromLatin1(" WHERE ") + s_where;
01281     //(use wasWhere here)
01282     
01283     // ORDER BY
01284     QString orderByString( querySchema.orderByColumnList().toSQLString(!singleTable/*includeTableName*/) );
01285     if (!orderByString.isEmpty())
01286         sql += (" ORDER BY " + orderByString);
01287     
01288     //KexiDBDbg << sql << endl;
01289     return sql;
01290 }
01291 
01292 QString Connection::selectStatement( KexiDB::TableSchema& tableSchema,
01293     const SelectStatementOptions& options) const
01294 {
01295     return selectStatement( *tableSchema.query(), options );
01296 }
01297 
01298 Field* Connection::findSystemFieldName(KexiDB::FieldList* fieldlist)
01299 {
01300     Field *f = fieldlist->fields()->first();
01301     while (f) {
01302         if (m_driver->isSystemFieldName( f->name() ))
01303             return f;
01304         f = fieldlist->fields()->next();
01305     }
01306     return 0;
01307 }
01308 
01309 Q_ULLONG Connection::lastInsertedAutoIncValue(const QString& aiFieldName, const QString& tableName,
01310     Q_ULLONG* ROWID)
01311 {
01312     Q_ULLONG row_id = drv_lastInsertRowID();
01313     if (ROWID)
01314         *ROWID = row_id;
01315     if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) {
01316         return row_id;
01317     }
01318     RowData rdata;
01319     if (row_id<=0 || true!=querySingleRecord(
01320         QString::fromLatin1("SELECT ") + tableName + QString::fromLatin1(".") + aiFieldName + QString::fromLatin1(" FROM ") + tableName
01321         + QString::fromLatin1(" WHERE ") + m_driver->beh->ROW_ID_FIELD_NAME + QString::fromLatin1("=") + QString::number(row_id), rdata))
01322     {
01323 //      KexiDBDbg << "Connection::lastInsertedAutoIncValue(): row_id<=0 || true!=querySingleRecord()" << endl;
01324         return (Q_ULLONG)-1; //ULL;
01325     }
01326     return rdata[0].toULongLong();
01327 }
01328 
01329 Q_ULLONG Connection::lastInsertedAutoIncValue(const QString& aiFieldName,
01330     const KexiDB::TableSchema& table, Q_ULLONG* ROWID)
01331 {
01332     return lastInsertedAutoIncValue(aiFieldName,table.name(), ROWID);
01333 }
01334 
01336 static FieldList* createFieldListForKexi__Fields(TableSchema *kexi__fieldsSchema)
01337 {
01338     if (!kexi__fieldsSchema)
01339         return 0;
01340     return kexi__fieldsSchema->subList(
01341         "t_id",
01342         "f_type",
01343         "f_name",
01344         "f_length",
01345         "f_precision",
01346         "f_constraints",
01347         "f_options",
01348         "f_default",
01349         "f_order",
01350         "f_caption",
01351         "f_help"
01352     );
01353 }
01354 
01356 void buildValuesForKexi__Fields(QValueList<QVariant>& vals, Field* f)
01357 {
01358     vals.clear();
01359     vals
01360     << QVariant(f->table()->id())
01361     << QVariant(f->type())
01362     << QVariant(f->name())
01363     << QVariant(f->isFPNumericType() ? f->scale() : f->length())
01364     << QVariant(f->isFPNumericType() ? f->precision() : 0)
01365     << QVariant(f->constraints())
01366     << QVariant(f->options())
01367         // KexiDB::variantToString() is needed here because the value can be of any QVariant type, 
01368         // depending on f->type()
01369     << (f->defaultValue().isNull() 
01370             ? QVariant() : QVariant(KexiDB::variantToString( f->defaultValue() ))) 
01371     << QVariant(f->order())
01372     << QVariant(f->caption())
01373     << QVariant(f->description());
01374 }
01375 
01376 bool Connection::storeMainFieldSchema(Field *field)
01377 {
01378     if (!field || !field->table())
01379         return false;
01380     FieldList *fl = createFieldListForKexi__Fields(d->tables_byname["kexi__fields"]);
01381     if (!fl)
01382         return false;
01383 
01384     QValueList<QVariant> vals;
01385     buildValuesForKexi__Fields(vals, field);
01386     QValueList<QVariant>::ConstIterator valsIt = vals.constBegin();
01387     Field *f;
01388     bool first = true;
01389     QString sql = "UPDATE kexi__fields SET ";
01390     for (Field::ListIterator it( fl->fieldsIterator() ); (f = it.current()); ++it, ++valsIt) {
01391         sql.append( (first ? QString::null : QString(", ")) +
01392             f->name() + "=" + m_driver->valueToSQL( f, *valsIt ) );
01393         if (first)
01394             first = false;
01395     }
01396     delete fl;
01397     
01398     sql.append(QString(" WHERE t_id=") + QString::number( field->table()->id() )
01399         + " AND f_name=" + m_driver->valueToSQL( Field::Text, field->name() ) );
01400     return executeSQL( sql );
01401 }
01402 
01403 #define createTable_ERR \
01404     { KexiDBDbg << "Connection::createTable(): ERROR!" <<endl; \
01405       setError(this, i18n("Creating table failed.")); \
01406       rollbackAutoCommitTransaction(tg.transaction()); \
01407       return false; }
01408         //setError( errorNum(), i18n("Creating table failed.") + " " + errorMsg()); 
01409 
01411 
01418 bool Connection::createTable( KexiDB::TableSchema* tableSchema, bool replaceExisting )
01419 {
01420     if (!tableSchema || !checkIsDatabaseUsed())
01421         return false;
01422 
01423     //check if there are any fields
01424     if (tableSchema->fieldCount()<1) {
01425         clearError();
01426         setError(ERR_CANNOT_CREATE_EMPTY_OBJECT, i18n("Cannot create table without fields."));
01427         return false;
01428     }
01429     const bool internalTable = dynamic_cast<InternalTableSchema*>(tableSchema);
01430 
01431     const QString &tableName = tableSchema->name().lower();
01432 
01433     if (!internalTable) {
01434         if (m_driver->isSystemObjectName( tableName )) {
01435             clearError();
01436             setError(ERR_SYSTEM_NAME_RESERVED, i18n("System name \"%1\" cannot be used as table name.")
01437                 .arg(tableSchema->name()));
01438             return false;
01439         }
01440 
01441         Field *sys_field = findSystemFieldName(tableSchema);
01442         if (sys_field) {
01443             clearError();
01444             setError(ERR_SYSTEM_NAME_RESERVED,
01445                 i18n("System name \"%1\" cannot be used as one of fields in \"%2\" table.")
01446                 .arg(sys_field->name()).arg(tableName));
01447             return false;
01448         }
01449     }
01450 
01451     bool previousSchemaStillKept = false;
01452 
01453     KexiDB::TableSchema *existingTable = 0;
01454     if (replaceExisting) {
01455         //get previous table (do not retrieve, though)
01456         existingTable = d->tables_byname[tableName];
01457         if (existingTable) {
01458             if (existingTable == tableSchema) {
01459                 clearError();
01460                 setError(ERR_OBJECT_EXISTS, 
01461                     i18n("Could not create the same table \"%1\" twice.").arg(tableSchema->name()) );
01462                 return false;
01463             }
01464 //TODO(js): update any structure (e.g. queries) that depend on this table!
01465             if (existingTable->id()>0)
01466                 tableSchema->m_id = existingTable->id(); //copy id from existing table
01467             previousSchemaStillKept = true;
01468             if (!dropTable( existingTable, false /*alsoRemoveSchema*/ ))
01469                 return false