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