kexi

connection.cpp

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