From 8362bf63dea22bbf6736609b0f49c152f975eb63 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 20 Jan 2010 01:29:50 +0000 Subject: Added old abandoned KDE3 version of koffice git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kexi/kexidb/connection.h | 1198 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1198 insertions(+) create mode 100644 kexi/kexidb/connection.h (limited to 'kexi/kexidb/connection.h') diff --git a/kexi/kexidb/connection.h b/kexi/kexidb/connection.h new file mode 100644 index 00000000..b72e01d4 --- /dev/null +++ b/kexi/kexidb/connection.h @@ -0,0 +1,1198 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2007 Jaroslaw Staniek + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_CONNECTION_H +#define KEXIDB_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace KexiDB { + +//! structure for storing single record with type information +typedef QValueVector RowData; + +class Cursor; +class ConnectionPrivate; +class RowEditBuffer; +class DatabaseProperties; +class AlterTableHandler; + +/*! @short Provides database connection, allowing queries and data modification. + + This class represents a database connection established within a data source. + It supports data queries and modification by creating client-side database cursors. + Database transactions are supported. +*/ +class KEXI_DB_EXPORT Connection : public QObject, public KexiDB::Object +{ + Q_OBJECT + + public: + + /*! Opened connection is automatically disconnected and removed + from driver's connections list. + Note for driver developers: you should call destroy() + from you Connection's subclass destructor. */ + virtual ~Connection(); + + /*! \return parameters that were used to create this connection. */ + ConnectionData* data() const; + + /*! \return the driver used for this connection. */ + inline Driver* driver() const { return m_driver; } + + /*! + \brief Connects to driver with given parameters. + \return true if successful. */ + bool connect(); + + /*! \return true, if connection is properly established. */ + bool isConnected() const; + + /*! \return true, both if connection is properly established + and any database within this connection is properly used + with useDatabase(). */ + bool isDatabaseUsed() const; + + /*! \return true for read only connection. Used especially for file-based drivers. + Can be reimplemented in a driver to provide real read-only flag of the connection + (SQlite3 dirver does this). */ + virtual bool isReadOnly() const; + + /*! Reimplemented from Object: also clears sql string. + @sa recentSQLString() */ + virtual void clearError(); + + /*! \brief Disconnects from driver with given parameters. + + The database (if used) is closed, and any active transactions + (if supported) are rolled back, so commit these before disconnecting, + if you'd like to save your changes. */ + bool disconnect(); + + /*! \return list of database names for opened connection. + If \a also_system_db is true, the system database names are also returned. */ + QStringList databaseNames(bool also_system_db = false); + + /*! \return true if database \a dbName exists. + If \a ignoreErrors is true, error flag of connection + won't be modified for any errors (it will quietly return), + else (ignoreErrors == false) we can check why the database does + not exist using error(), errorNum() and/or errorMsg(). */ + bool databaseExists( const QString &dbName, bool ignoreErrors = true ); + + /*! \brief Creates new database with name \a dbName, using this connection. + + If database with \a dbName already exists, or other error occurred, + false is returned. + For file-based drivers, \a dbName should be equal to the database + filename (the same as specified for ConnectionData). + + See doc/dev/kexidb_issues.txt document, chapter "Table schema, query schema, etc. storage" + for database schema documentation (detailed description of kexi__* 'system' tables). + + \sa useDatabase() */ + bool createDatabase( const QString &dbName ); + + /*! + \brief Opens an existing database specified by \a dbName. + + If \a kexiCompatible is true (the default) initial checks will be performed + to recognize database Kexi-specific format. Set \a kexiCompatible to false + if you're using native database (one that have no Kexi System tables). + For file-based drivers, \a dbName should be equal to filename + (the same as specified for ConnectionData). + \return true on success, false on failure. + If user has cancelled this action and \a cancelled is not 0, *cancelled is set to true. */ + bool useDatabase( const QString &dbName, bool kexiCompatible = true, bool *cancelled = 0, + MessageHandler* msgHandler = 0 ); + + /*! + \brief Closes currently used database for this connection. + + Any active transactions (if supported) are rolled back, + so commit these before closing, if you'd like to save your changes. */ + bool closeDatabase(); + + /*! \brief Get the name of the current database + + \return name of currently used database for this connection or empty string + if there is no used database */ + QString currentDatabase() const; + + /*! \brief Drops database with name \a dbName. + + if dbName is not specified, currently used database name is used + (it is closed before dropping). + */ + bool dropDatabase( const QString &dbName = QString::null ); + + /*! \return names of all the \a objecttype (see \a ObjectTypes in global.h) + schemas stored in currently used database. KexiDB::AnyObjectType can be passed + as \a objType to get names of objects of any type. + If \a ok is not null then variable pointed by it will be set to the result. + On error, the functions can return incomplete list. */ + QStringList objectNames(int objType = KexiDB::AnyObjectType, bool* ok = 0); + + /*! \return names of all table schemas stored in currently + used database. If \a also_system_tables is true, + internal KexiDB system table names (kexi__*) are also returned. + \sa kexiDBSystemTableNames() */ + QStringList tableNames(bool also_system_tables = false); + + /*! \return list of internal KexiDB system table names + (kexi__*). This does not mean that these tables can be found + in currently opened database. Just static list of table + names is returned. + + The list contents may depend on KexiDB library version; + opened database can contain fewer 'system' tables than in current + KexiDB implementation, if the current one is newer than the one used + to build the database. */ + static const QStringList& kexiDBSystemTableNames(); + + /*! \return server version information for this connection. + If database is not connected (i.e. isConnected() is false) 0 is returned. */ + KexiDB::ServerVersionInfo* serverVersion() const; + + /*! \return version information for this connection. + If database is not used (i.e. isDatabaseUsed() is false) 0 is returned. + It can be compared to drivers' and KexiDB library version to maintain + backward/upward compatiblility. */ + KexiDB::DatabaseVersionInfo* databaseVersion() const; + + /*! \return DatabaseProperties object allowing to read and write global database properties + for this connection. */ + DatabaseProperties& databaseProperties(); + + /*! \return ids of all table schema names stored in currently + used database. These ids can be later used as argument for tableSchema(). + This is a shortcut for objectIds(TableObjectType). + If \a also_system_tables is true, + Internal KexiDB system tables (kexi__*) are not available here + because these have no identifiers assigned (more formally: id=-1). */ + QValueList tableIds(); + + /*! \return ids of all database query schemas stored in currently + used database. These ids can be later used as argument for querySchema(). + This is a shortcut for objectIds(TableObjectType). */ + QValueList queryIds(); + + /*! \return names of all schemas of object with \a objType type + that are stored in currently used database. */ + QValueList objectIds(int objType); + + /*! \brief Creates new transaction handle and starts a new transaction. + \return KexiDB::Transaction object if transaction has been started + successfully, otherwise null transaction. + For drivers that allow single transaction per connection + (Driver::features() && SingleTransactions) this method can be called one time, + and then this single transaction will be default ( setDefaultTransaction() will + be called). + For drivers that allow multiple transactions per connection, no default transaction is + set automatically in beginTransaction() method, you could do this by hand. + \sa setDefaultTransaction(), defaultTransaction(). + */ + Transaction beginTransaction(); + +/*! \todo for nested transactions: + Tansaction* beginTransaction(transaction *parent_transaction); +*/ + /*! Commits transaction \a trans. + If there is not \a trans argument passed, and there is default transaction + (obtained from defaultTransaction()) defined, this one will be committed. + If default is not present, false is returned (when ignore_inactive is + false, the default), or true is returned (when ignore_inactive is true). + + On successful commit, \a trans object will be destroyed. + If this was default transaction, there is no default transaction for now. + */ + bool commitTransaction( Transaction trans = Transaction::null, + bool ignore_inactive = false ); + + /*! Rollbacks transaction \a trans. + If there is not \a trans argument passed, and there is default transaction + (obtained from defaultTransaction()) defined, this one will be rolled back. + If default is not present, false is returned (when ignore_inactive is + false, the default), or true is returned (when ignore_inactive is true). + + or any error occurred, false is returned. + + On successful rollback, \a trans object will be destroyed. + If this was default transaction, there is no default transaction for now. + */ + bool rollbackTransaction( Transaction trans = Transaction::null, + bool ignore_inactive = false ); + + /*! \return handle for default transaction for this connection + or null transaction if there is no such a transaction defined. + If transactions are supported: Any operation on database (e.g. inserts) + that is started without specifying transaction context, will be performed + in the context of this transaction. + + Returned null transaction doesn't mean that there is no transactions + started at all. + Default transaction can be defined automatically for some drivers -- + see beginTransaction(). + \sa KexiDB::Driver::transactionsSupported() + */ + Transaction& defaultTransaction() const; + + /*! Sets default transaction that will be used as context for operations + on data in opened database for this connection. */ + void setDefaultTransaction(const Transaction& trans); + + /*! \return set of handles of currently active transactions. + Note that in multithreading environment some of these + transactions can be already inactive after calling this method. + Use Transaction::active() to check that. Inactive transaction + handle is useless and can be safely dropped. + */ + const QValueList& transactions(); + + /*! \return true if "auto commit" option is on. + + When auto commit is on (the default on for any new Connection object), + every sql functional statement (statement that changes + data in the database implicitly starts a new transaction. + This transaction is automatically committed + after successful statement execution or rolled back on error. + + For drivers that do not support transactions (see Driver::features()) + this method shouldn't be called because it does nothing ans always returns false. + + No internal KexiDB object should changes this option, although auto commit's + behaviour depends on database engine's specifics. Engines that support only single + transaction per connection (see Driver::SingleTransactions), + use this single connection for autocommiting, so if there is already transaction + started by the KexiDB user program (with beginTransaction()), this transaction + is committed before any sql functional statement execution. In this situation + default transaction is also affected (see defaultTransaction()). + + Only for drivers that support nested transactions (Driver::NestedTransactions), + autocommiting works independently from previously started transaction, + + For other drivers set this option off if you need use transaction + for grouping more statements together. + + NOTE: nested transactions are not yet implemented in KexiDB API. + */ + bool autoCommit() const; + + /*! Changes auto commit option. This does not affect currently started transactions. + This option can be changed even when connection is not established. + \sa autoCommit() */ + bool setAutoCommit(bool on); + + /*! driver-specific string escaping */ +//js: MOVED TO Driver virtual QString escapeString(const QString& str) const = 0; +// virtual QCString escapeString(const QCString& str) const = 0; + + /*! Prepares SELECT query described by raw \a statement. + \return opened cursor created for results of this query + or NULL if there was any error. Cursor can have optionally applied \a cursor_options + (one of more selected from KexiDB::Cursor::Options). + Preparation means that returned cursor is created but not opened. + Open this when you would like to do it with Cursor::open(). + + Note for driver developers: you should initialize cursor engine-specific + resources and return Cursor subclass' object + (passing \a statement and \a cursor_options to it's constructor). + */ + virtual Cursor* prepareQuery( const QString& statement, uint cursor_options = 0) = 0; + + /*! \overload prepareQuery( const QString& statement = QString::null, uint cursor_options = 0) + Prepares query described by \a query schema. \a params are values of parameters that + will be inserted into places marked with [] before execution of the query. + + Note for driver developers: you should initialize cursor engine-specific + resources and return Cursor subclass' object + (passing \a query and \a cursor_options to it's constructor). + Kexi SQL and driver-specific escaping is performed on table names. + */ + Cursor* prepareQuery( QuerySchema& query, const QValueList& params, + uint cursor_options = 0 ); + + /*! \overload prepareQuery( QuerySchema& query, const QValueList& params, + uint cursor_options = 0 ) + Prepares query described by \a query schema without parameters. + */ + virtual Cursor* prepareQuery( QuerySchema& query, uint cursor_options = 0 ) = 0; + + /*! \overload prepareQuery( const QString& statement = QString::null, uint cursor_options = 0) + Statement is build from data provided by \a table schema, + it is like "select * from table_name".*/ + Cursor* prepareQuery( TableSchema& table, uint cursor_options = 0); + + /*! Executes SELECT query described by \a statement. + \return opened cursor created for results of this query + or NULL if there was any error on the cursor creation or opening. + Cursor can have optionally applied \a cursor_options + (one of more selected from KexiDB::Cursor::Options). + Identifiers in \a statement that are the same as keywords in Kexi + SQL or the backend's SQL need to have been escaped. + */ + Cursor* executeQuery( const QString& statement, uint cursor_options = 0 ); + + /*! \overload executeQuery( const QString& statement, uint cursor_options = 0 ) + \a params are values of parameters that + will be inserted into places marked with [] before execution of the query. + + Statement is build from data provided by \a query schema. + Kexi SQL and driver-specific escaping is performed on table names. */ + Cursor* executeQuery( QuerySchema& query, const QValueList& params, + uint cursor_options = 0 ); + + /*! \overload executeQuery( QuerySchema& query, const QValueList& params, + uint cursor_options = 0 ) */ + Cursor* executeQuery( QuerySchema& query, uint cursor_options = 0 ); + + /*! \overload executeQuery( const QString& statement, uint cursor_options = 0 ) + Executes query described by \a query schema without parameters. + Statement is build from data provided by \a table schema, + it is like "select * from table_name".*/ + Cursor* executeQuery( TableSchema& table, uint cursor_options = 0 ); + + /*! Deletes cursor \a cursor previously created by functions like executeQuery() + for this connection. + There is an attempt to close the cursor with Cursor::close() if it was opened. + Anyway, at last cursor is deleted. + \return true if cursor is properly closed before deletion. */ + bool deleteCursor(Cursor *cursor); + + /*! \return schema of a table pointed by \a tableId, retrieved from currently + used database. The schema is cached inside connection, + so retrieval is performed only once, on demand. */ + TableSchema* tableSchema( int tableId ); + + /*! \return schema of a table pointed by \a tableName, retrieved from currently + used database. KexiDB system table schema can be also retrieved. + \sa tableSchema( int tableId ) */ + TableSchema* tableSchema( const QString& tableName ); + + /*! \return schema of a query pointed by \a queryId, retrieved from currently + used database. The schema is cached inside connection, + so retrieval is performed only once, on demand. */ + QuerySchema* querySchema( int queryId ); + + /*! \return schema of a query pointed by \a queryName, retrieved from currently + used database. \sa querySchema( int queryId ) */ + QuerySchema* querySchema( const QString& queryName ); + + /*! Sets \a queryName query obsolete by moving it out of the query sets, so it will not be + accessible by querySchema( const QString& queryName ). The existing query object is not + destroyed, to avoid problems when it's referenced. In this case, + a new query schema will be retrieved directly from the backend. + + For now it's used in KexiQueryDesignerGuiEditor::storeLayout(). + This solves the problem when user has changed a query schema but already form still uses + previously instantiated query schema. + \return true if there is such query. Otherwise the method does nothing. */ + bool setQuerySchemaObsolete( const QString& queryName ); + +//js: MOVED TO Driver QString valueToSQL( const Field::Type ftype, const QVariant& v ) const; +// QString valueToSQL( const Field *field, const QVariant& v ) const; + + /*! Executes \a sql query and stores first record's data inside \a data. + This is convenient method when we need only first record from query result, + or when we know that query result has only one record. + If \a addLimitTo1 is true (the default), adds a LIMIT clause to the query, + so \a sql should not include one already. + \return true if query was successfully executed and first record has been found, + false on data retrieving failure, and cancelled if there's no single record available. */ + tristate querySingleRecord(const QString& sql, RowData &data, bool addLimitTo1 = true); + + /*! Like tristate querySingleRecord(const QString& sql, RowData &data) + but uses QuerySchema object. + If \a addLimitTo1 is true (the default), adds a LIMIT clause to the query. */ + tristate querySingleRecord(QuerySchema& query, RowData &data, bool addLimitTo1 = true); + + /*! Executes \a sql query and stores first record's field's (number \a column) string value + inside \a value. For efficiency it's recommended that a query defined by \a sql + should have just one field (SELECT one_field FROM ....). + If \a addLimitTo1 is true (the default), adds a LIMIT clause to the query, + so \a sql should not include one already. + \return true if query was successfully executed and first record has been found, + false on data retrieving failure, and cancelled if there's no single record available. + \sa queryStringList() */ + tristate querySingleString(const QString& sql, QString &value, uint column = 0, + bool addLimitTo1 = true); + + /*! Convenience function: executes \a sql query and stores first + record's field's (number \a column) value inside \a number. \sa querySingleString(). + Note: "LIMIT 1" is appended to \a sql statement if \a addLimitTo1 is true (the default). + \return true if query was successfully executed and first record has been found, + false on data retrieving failure, and cancelled if there's no single record available. */ + tristate querySingleNumber(const QString& sql, int &number, uint column = 0, + bool addLimitTo1 = true); + + /*! Executes \a sql query and stores Nth field's string value of every record + inside \a list, where N is equal to \a column. The list is initially cleared. + For efficiency it's recommended that a query defined by \a sql + should have just one field (SELECT one_field FROM ....). + \return true if all values were fetched successfuly, + false on data retrieving failure. Returning empty list can be still a valid result. + On errors, the list is not cleared, it may contain a few retrieved values. */ + bool queryStringList(const QString& sql, QStringList& list, uint column = 0); + + /*! \return true if there is at least one record returned in \a sql query. + Does not fetch any records. \a success will be set to false + on query execution errors (true otherwise), so you can see a difference between + "no results" and "query execution error" states. + Note: real executed query is: "SELECT 1 FROM (\a sql) LIMIT 1" + if \a addLimitTo1 is true (the default). */ + bool resultExists(const QString& sql, bool &success, bool addLimitTo1 = true); + + /*! \return true if there is at least one record in \a table. */ + bool isEmpty( TableSchema& table, bool &success ); + +//! @todo perhaps use Q_ULLONG here? + /*! \return number of records in \a sql query. + Does not fetch any records. -1 is returned on query execution errors (>0 otherwise). + Note: real executed query is: "SELECT COUNT() FROM (\a sql) LIMIT 1" + (using querySingleNumber()) */ + int resultCount(const QString& sql); + + //PROTOTYPE: + #define A , const QVariant& + #define H_INS_REC(args) bool insertRecord(TableSchema &tableSchema args) + #define H_INS_REC_ALL \ + H_INS_REC(A); \ + H_INS_REC(A A); \ + H_INS_REC(A A A); \ + H_INS_REC(A A A A); \ + H_INS_REC(A A A A A); \ + H_INS_REC(A A A A A A); \ + H_INS_REC(A A A A A A A); \ + H_INS_REC(A A A A A A A A) + H_INS_REC_ALL; + + #undef H_INS_REC + #define H_INS_REC(args) bool insertRecord(FieldList& fields args) + + H_INS_REC_ALL; + #undef H_INS_REC_ALL + #undef H_INS_REC + #undef A + + bool insertRecord(TableSchema &tableSchema, QValueList& values); + + bool insertRecord(FieldList& fields, QValueList& values); + + /*! Creates table defined by \a tableSchema. + Schema information is also added into kexi system tables, for later reuse. + \return true on success - \a tableSchema object is then + inserted to Connection structures - it is owned by Connection object now, + so you shouldn't destroy the tableSchema object by hand + (or declare it as local-scope variable). + + If \a replaceExisting is false (the default) and table with the same name + (as tableSchema->name()) exists, false is returned. + If \a replaceExisting is true, a table schema with the same name (if exists) + is overwritten, then a new table schema gets the same identifier + as existing table schema's identifier. + + Note that on error: + - \a tableSchema is not inserted into Connection's structures, + so you are still owner of this object + - existing table schema object is not destroyed (i.e. it is still available + e.g. using Connection::tableSchema(const QString& ), even if the table + was physically dropped. + */ + bool createTable( TableSchema* tableSchema, bool replaceExisting = false ); + + /*! Drops a table defined by \a tableSchema (both table object as well as physically). + If true is returned, schema information \a tableSchema is destoyed + (because it's owned), so don't keep this anymore! + No error is raised if the table does not exist physically + - its schema is removed even in this case. + */ +//! @todo (js): update any structure (e.g. query) that depend on this table! + tristate dropTable( TableSchema* tableSchema ); + + /*! It is a convenience function, does exactly the same as + bool dropTable( KexiDB::TableSchema* tableSchema ) */ + tristate dropTable( const QString& table ); + + /*! Alters \a tableSchema using \a newTableSchema in memory and on the db backend. + \return true on success, cancelled if altering was cancelled. */ +//! @todo (js): implement real altering +//! @todo (js): update any structure (e.g. query) that depend on this table! + tristate alterTable( TableSchema& tableSchema, TableSchema& newTableSchema); + + /*! Alters name of table described by \a tableSchema to \a newName. + If \a replace is true, destination table is completely dropped and replaced + by \a tableSchema, if present. In this case, identifier of + \a tableSchema becomes equal to the dropped table's id, what can be useful + if \a tableSchema was created with a temporary name and ID (used in AlterTableHandler). + + If \a replace is false (the default) and destination table is present + -- false is returned and ERR_OBJECT_EXISTS error is set. + The schema of \a tableSchema is updated on success. + \return true on success. */ + bool alterTableName(TableSchema& tableSchema, const QString& newName, bool replace = false); + + /*! Drops a query defined by \a querySchema. + If true is returned, schema information \a querySchema is destoyed + (because it's owned), so don't keep this anymore! + */ + bool dropQuery( QuerySchema* querySchema ); + + /*! It is a convenience function, does exactly the same as + bool dropQuery( KexiDB::QuerySchema* querySchema ) */ + bool dropQuery( const QString& query ); + + /*! Removes information about object with \a objId + from internal "kexi__object" and "kexi__objectdata" tables. + \return true on success. */ + bool removeObject( uint objId ); + + /*! \return first field from \a fieldlist that has system name, + null if there are no such field. + For checking Driver::isSystemFieldName() is used, so this check can + be driver-dependent. */ + Field* findSystemFieldName(FieldList *fieldlist); + + /*! \return name of any (e.g. first found) database for this connection. + This method does not close or open this connection. The method can be used + (it is also internally used, e.g. for database dropping) when we need + a database name before we can connect and execute any SQL statement + (e.g. DROP DATABASE). + + The method can return nul lstring, but in this situation no automatic (implicit) + connections could be made, what is useful by e.g. dropDatabase(). + + Note for driver developers: return here a name of database which you are sure + is existing. + Default implementation returns: + - value that previously had been set using setAvailableDatabaseName() for + this connection, if it is not empty + - else (2nd priority): value of DriverBehaviour::ALWAYS_AVAILABLE_DATABASE_NAME + if it is not empty. + + See decription of DriverBehaviour::ALWAYS_AVAILABLE_DATABASE_NAME member. + You may want to reimplement this method only when you need to depend on + this connection specifics + (e.g. you need to check something remotely). + */ + virtual QString anyAvailableDatabaseName(); + + /*! Sets \a dbName as name of a database that can be accessible. + This is option that e.g. application that make use of KexiDB library can set + to tune connection's behaviour when it needs to temporary connect to any database + in the server to do some work. + You can pass empty dbName - then anyAvailableDatabaseName() will try return + DriverBehaviour::ALWAYS_AVAILABLE_DATABASE_NAME (the default) value + instead of the one previously set with setAvailableDatabaseName(). + + \sa anyAvailableDatabaseName() + */ + void setAvailableDatabaseName(const QString& dbName); + + /*! Because some engines need to have opened any database before + executing administrative sql statements like "create database" or "drop database", + this method is used to use appropriate, existing database for this connection. + For file-based db drivers this always return true and does not set tmpdbName + to any value. For other db drivers: this sets tmpdbName to db name computed + using anyAvailableDatabaseName(), and if the name computed is empty, false + is returned; if it is not empty, useDatabase() is called. + False is returned also when useDatabase() fails. + You can call this method from your application's level if you really want to perform + tasks that require any used database. In such a case don't forget + to closeDatabase() if returned tmpdbName is not empty. + + Note: This method has nothing to do with creating or using temporary databases + in such meaning that these database are not persistent + */ + bool useTemporaryDatabaseIfNeeded(QString &tmpdbName); + + /*! \return autoincrement field's \a aiFieldName value + of last inserted record. This refers \a tableName table. + + Simply, method internally fetches last inserted record and returns selected + field's value. Requirements: field must be of integer type, there must be a + record inserted in current database session (whatever this means). + On error (Q_ULLONG)-1 is returned. + Last inserted record is identified by magical row identifier, usually called + ROWID (PostgreSQL has it as well as SQLite; + see DriverBehaviour::ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE). + ROWID's value will be assigned back to \a ROWID if this pointer is not null. + */ + Q_ULLONG lastInsertedAutoIncValue(const QString& aiFieldName, const QString& tableName, + Q_ULLONG* ROWID = 0); + + /*! \overload int lastInsertedAutoIncValue(const QString&, const QString&, Q_ULLONG*) + */ + Q_ULLONG lastInsertedAutoIncValue(const QString& aiFieldName, + const TableSchema& table, Q_ULLONG* ROWID = 0); + + /*! Executes query \a statement, but without returning resulting + rows (used mostly for functional queries). + Only use this method if you really need. */ + bool executeSQL( const QString& statement ); + + //! @short options used in selectStatement() + class KEXI_DB_EXPORT SelectStatementOptions + { + public: + SelectStatementOptions(); + ~SelectStatementOptions(); + + //! A mode for escaping identifier, Driver::EscapeDriver|Driver::EscapeAsNecessary by default + int identifierEscaping; + + //! True if ROWID should be also retrieved. False by default. + bool alsoRetrieveROWID : 1; + + /*! True if relations (LEFT OUTER JOIN) for visible lookup columns should be added. + True by default. This is set to false when user-visible statement is generated + e.g. for the Query Designer. */ + bool addVisibleLookupColumns : 1; + }; + + /*! \return "SELECT ..." statement's string needed for executing query + defined by \a querySchema and \a params. */ + QString selectStatement( QuerySchema& querySchema, + const QValueList& params, + const SelectStatementOptions& options = SelectStatementOptions() ) const; + + /*! \overload QString selectStatement( QuerySchema& querySchema, + QValueList params = QValueList(), + const SelectStatementOptions& options = SelectStatementOptions() ) const; + \return "SELECT ..." statement's string needed for executing query + defined by \a querySchema. */ + inline QString selectStatement( QuerySchema& querySchema, + const SelectStatementOptions& options = SelectStatementOptions() ) const + { + return selectStatement(querySchema, QValueList(), options); + } + + /*! Stores object's schema data (id, name, caption, help text) + described by \a sdata on the backend. + If \a newObject is true, new entry is created, + and (when sdata.id() was <=0), new, unique object identifier + is obtained and assigned to \a sdata (see SchemaData::id()). + + If \a newObject is false, it's expected that entry on the + backend already exists, so it's updated (changes to identifier are not allowed). + \return true on success. */ + bool storeObjectSchemaData( SchemaData &sdata, bool newObject ); + + /*! Added for convenience. + \sa setupObjectSchemaData( const KexiDB::RowData &data, SchemaData &sdata ). + \return true on success, false on failure and cancelled when such object couldn't */ + tristate loadObjectSchemaData( int objectID, SchemaData &sdata ); + + /*! Finds object schema data for object of type \a objectType and name \a objectName. + If the object is found, resulted schema is stored in \a sdata and true is returned, + otherwise false is returned. */ + tristate loadObjectSchemaData( int objectType, const QString& objectName, SchemaData &sdata ); + + /*! Loads (potentially large) data block (e.g. xml form's representation), referenced by objectID + and puts it to \a dataString. The can be block indexed with optional \a dataID. + \return true on success, false on failure and cancelled when there is no such data block + \sa storeDataBlock(). */ + tristate loadDataBlock( int objectID, QString &dataString, const QString& dataID ); + + /*! Stores (potentially large) data block \a dataString (e.g. xml form's representation), + referenced by objectID. Block will be stored in "kexi__objectdata" table and + an optional \a dataID identifier. + If there is already such record in the table, it's simply overwritten. + \return true on success + \sa loadDataBlock(). */ + bool storeDataBlock( int objectID, const QString &dataString, const QString& dataID = QString::null ); + + /*! Removes (potentially large) string data (e.g. xml form's representation), + referenced by objectID, and pointed by optional \a dataID. + \return true on success. Does not fail if the block does not exist. + Note that if \a dataID is not specified, all data blocks for this dialog will be removed. + \sa loadDataBlock() storeDataBlock(). */ + bool removeDataBlock( int objectID, const QString& dataID = QString::null); + + class KEXI_DB_EXPORT TableSchemaChangeListenerInterface + { + public: + TableSchemaChangeListenerInterface() {} + virtual ~TableSchemaChangeListenerInterface() {} + /*! Closes listening object so it will be deleted and thus no longer use + a conflicting table schema. */ + virtual tristate closeListener() = 0; + + /*! i18n'd string that can be displayed for user to inform about + e.g. conflicting listeners. */ + QString listenerInfoString; + }; +//TMP// TODO: will be more generic + /** Register \a listener for receiving (listening) information about changes + in TableSchema object. Changes could be: altering and removing. */ + void registerForTableSchemaChanges(TableSchemaChangeListenerInterface& listener, + TableSchema& schema); + + void unregisterForTableSchemaChanges(TableSchemaChangeListenerInterface& listener, + TableSchema &schema); + + void unregisterForTablesSchemaChanges(TableSchemaChangeListenerInterface& listener); + + QPtrList* + tableSchemaChangeListeners(TableSchema& tableSchema) const; + + tristate closeAllTableSchemaChangeListeners(TableSchema& tableSchema); + + /*! @internal Removes \a tableSchema from internal structures and + destroys it. Does not make any change at the backend. */ + void removeTableSchemaInternal(KexiDB::TableSchema *tableSchema); + + /*! @internal. Inserts internal table to Connection's structures, so it can be found by name. + This method is used for example in KexiProject to insert information about "kexi__blobs" + table schema. Use createTable() to physically create table. After createTable() + calling insertInternalTableSchema() is not required. + Also used internally by newKexiDBSystemTableSchema(const QString& tsname) */ + void insertInternalTableSchema(TableSchema *tableSchema); + +//! @todo move this somewhere to low level class (MIGRATION?) + /*! LOW LEVEL METHOD. For reimplemenation: returns true if table + with name \a tableName exists in the database. + \return false if it does not exist or error occurred. + The lookup is case insensitive. */ + virtual bool drv_containsTable( const QString &tableName ) = 0; + + /*! Creates table using \a tableSchema information. + \return true on success. Default implementation + builds a statement using createTableStatement() and calls drv_executeSQL() + Note for driver developers: reimplement this only if you want do to + this in other way. + + Moved to public for KexiMigrate. + @todo fix this after refactoring + */ + virtual bool drv_createTable( const TableSchema& tableSchema ); + + /*! Alters table's described \a tableSchema name to \a newName. + This is the default implementation, using "ALTER TABLE RENAME TO ", + what's supported by SQLite >= 3.2, PostgreSQL, MySQL. + Backends lacking ALTER TABLE, for example SQLite2, reimplement this with by an inefficient + data copying to a new table. In any case, renaming is performed at the backend. + It's good idea to keep the operation within a transaction. + \return true on success. + + Moved to public for KexiProject. + @todo fix this after refactoring + */ + virtual bool drv_alterTableName(TableSchema& tableSchema, const QString& newName); + + /*! Physically drops table named with \a name. + Default impelmentation executes "DROP TABLE.." command, + so you rarely want to change this. + + Moved to public for KexiMigrate + @todo fix this after refatoring + */ + virtual bool drv_dropTable( const QString& name ); + + /*! Prepare a SQL statement and return a \a PreparedStatement instance. */ + virtual PreparedStatement::Ptr prepareStatement(PreparedStatement::StatementType type, + FieldList& fields) = 0; + + bool isInternalTableSchema(const QString& tableName); + + /*! Setups schema data for object that owns sdata (e.g. table, query) + using \a cursor opened on 'kexi__objects' table, pointing to a record + corresponding to given object. + + Moved to public for KexiMigrate + @todo fix this after refatoring + */ + bool setupObjectSchemaData( const RowData &data, SchemaData &sdata ); + + /*! \return a new field table schema for a table retrieved from \a data. + Used internally by tableSchema(). + + Moved to public for KexiMigrate + @todo fix this after refatoring + */ + KexiDB::Field* setupField( const RowData &data ); + + protected: + /*! Used by Driver */ + Connection( Driver *driver, ConnectionData &conn_data ); + + /*! Method to be called form Connection's subclass destructor. + \sa ~Connection() */ + void destroy(); + + /*! @internal drops table \a tableSchema physically, but destroys + \a tableSchema object only if \a alsoRemoveSchema is true. + Used (alsoRemoveSchema==false) on table altering: + if recreating table can failed we're giving up and keeping + the original table schema (even if it is no longer points to any real data). */ + tristate dropTable( KexiDB::TableSchema* tableSchema, bool alsoRemoveSchema); + + /*! For reimplemenation: connects to database. \a version should be set to real + server's version. + \return true on success. */ + virtual bool drv_connect(KexiDB::ServerVersionInfo& version) = 0; + + /*! For reimplemenation: disconnects database + \return true on success. */ + virtual bool drv_disconnect() = 0; + + /*! Executes query \a statement, but without returning resulting + rows (used mostly for functional queries). + Only use this method if you really need. */ + virtual bool drv_executeSQL( const QString& statement ) = 0; + + /*! For reimplemenation: loads list of databases' names available for this connection + and adds these names to \a list. If your server is not able to offer such a list, + consider reimplementing drv_databaseExists() instead. + The method should return true only if there was no error on getting database names + list from the server. + Default implementation puts empty list into \a list and returns true. */ + virtual bool drv_getDatabasesList( QStringList &list ); + +//! @todo move this somewhere to low level class (MIGRATION?) + /*! LOW LEVEL METHOD. For reimplemenation: loads low-level list of table names + available for this connection. The names are in lower case. + The method should return true only if there was no error on getting database names + list from the server. */ + virtual bool drv_getTablesList( QStringList &list ) = 0; + + /*! For optional reimplemenation: asks server if database \a dbName exists. + This method is used internally in databaseExists(). The default implementation + calls databaseNames and checks if that list contains \a dbName. If you need to + ask the server specifically if a database exists, eg. if you can't retrieve a list + of all available database names, please reimplement this method and do all + needed checks. + + See databaseExists() description for details about ignoreErrors argument. + You should use this appropriately in your implementation. + + Note: This method should also work if there is already database used (with useDatabase()); + in this situation no changes should be made in current database selection. */ + virtual bool drv_databaseExists( const QString &dbName, bool ignoreErrors = true ); + + /*! For reimplemenation: creates new database using connection */ + virtual bool drv_createDatabase( const QString &dbName = QString::null ) = 0; + + /*! For reimplemenation: opens existing database using connection + \return true on success, false on failure and cancelled if user has cancelled this action. */ + virtual bool drv_useDatabase( const QString &dbName = QString::null, bool *cancelled = 0, + MessageHandler* msgHandler = 0 ) = 0; + + /*! For reimplemenation: closes previously opened database + using connection. */ + virtual bool drv_closeDatabase() = 0; + + /*! \return true if internal driver's structure is still in opened/connected + state and database is used. + Note for driver developers: Put here every test that you can do using your + internal engine's database API, + eg (a bit schematic): my_connection_struct->isConnected()==true. + Do not check things like Connection::isDatabaseUsed() here or other things + that "KexiDB already knows" at its level. + If you cannot test anything, just leave default implementation (that returns true). + + Result of this method is used as an addtional chance to check for isDatabaseUsed(). + Do not call this method from your driver's code, it should be used at KexiDB + level only. + */ + virtual bool drv_isDatabaseUsed() const { return true; } + + /*! For reimplemenation: drops database from the server + using connection. After drop, database shouldn't be accessible + anymore. */ + virtual bool drv_dropDatabase( const QString &dbName = QString::null ) = 0; + + /*! \return "CREATE TABLE ..." statement string needed for \a tableSchema + creation in the database. + + Note: The statement string can be specific for this connection's driver database, + and thus not reusable in general. + */ + QString createTableStatement( const TableSchema& tableSchema ) const; + + + /*! \return "SELECT ..." statement's string needed for executing query + defined by "select * from table_name" where table_name is \a tableSchema's name. + This method's variant can be useful when there is no appropriate QuerySchema defined. + + Note: The statement string can be specific for this connection's driver database, + and thus not reusable in general. + */ + QString selectStatement( TableSchema& tableSchema, + const SelectStatementOptions& options = SelectStatementOptions() ) const; + + /*! + Creates table named by \a tableSchemaName. Schema object must be on + schema tables' list before calling this method (otherwise false if returned). + Just uses drv_createTable( const KexiDB::TableSchema& tableSchema ). + Used internally, e.g. in createDatabase(). + \return true on success + */ + virtual bool drv_createTable( const QString& tableSchemaName ); + +// /*! Executes query \a statement and returns resulting rows +// (used mostly for SELECT query). */ +// virtual bool drv_executeQuery( const QString& statement ) = 0; + + /*! \return unique identifier of last inserted row. + Typically this is just primary key value. + This identifier could be reused when we want to reference + just inserted row. + Note for driver developers: contact js (at) iidea.pl + if your engine do not offers this information. */ + virtual Q_ULLONG drv_lastInsertRowID() = 0; + + /*! Note for driver developers: begins new transaction + and returns handle to it. Default implementation just + executes "BEGIN" sql statement and returns just empty data (TransactionData object). + + Drivers that do not support transactions (see Driver::features()) + do never call this method. + Reimplement this method if you need to do something more + (e.g. if you driver will support multiple transactions per connection). + Make subclass of TransactionData (declared in transaction.h) + and return object of this subclass. + You should return NULL if any error occurred. + Do not check anything in connection (isConnected(), etc.) - all is already done. + */ + virtual TransactionData* drv_beginTransaction(); + + /*! Note for driver developers: begins new transaction + and returns handle to it. Default implementation just + executes "COMMIT" sql statement and returns true on success. + + \sa drv_beginTransaction() + */ + virtual bool drv_commitTransaction(TransactionData* trans); + + /*! Note for driver developers: begins new transaction + and returns handle to it. Default implementation just + executes "ROLLBACK" sql statement and returns true on success. + + \sa drv_beginTransaction() + */ + virtual bool drv_rollbackTransaction(TransactionData* trans); + + /*! Changes autocommiting option for established connection. + \return true on success. + + Note for driver developers: reimplement this only if your engine + allows to set special auto commit option (like "SET AUTOCOMMIT=.." in MySQL). + If not, auto commit behaviour will be simulated if at least single + transactions per connection are supported by the engine. + Do not set any internal flags for autocommiting -- it is already done inside + setAutoCommit(). + + Default implementation does nothing with connection, just returns true. + + \sa drv_beginTransaction(), autoCommit(), setAutoCommit() + */ + virtual bool drv_setAutoCommit(bool on); + + /*! Internal, for handling autocommited transactions: + begins transaction if one is supported. + \return true if new transaction started + successfully or no transactions are supported at all by the driver + or if autocommit option is turned off. + A handle to a newly created transaction (or null on error) is passed + to \a tg parameter. + + Special case when used database driver has only single transaction support + (Driver::SingleTransactions): + and there is already transaction started, it is committed before + starting a new one, but only if this transaction has been started inside Connection object. + (i.e. by beginAutoCommitTransaction()). Otherwise, a new transaction will not be started, + but true will be returned immediately. + */ + bool beginAutoCommitTransaction(TransactionGuard& tg); + + /*! Internal, for handling autocommited transactions: + Commits transaction prevoiusly started with beginAutoCommitTransaction(). + \return true on success or when no transactions are supported + at all by the driver. + + Special case when used database driver has only single transaction support + (Driver::SingleTransactions): if \a trans has been started outside Connection object + (i.e. not by beginAutoCommitTransaction()), the transaction will not be committed. + */ + bool commitAutoCommitTransaction(const Transaction& trans); + + /*! Internal, for handling autocommited transactions: + Rollbacks transaction prevoiusly started with beginAutoCommitTransaction(). + \return true on success or when no transactions are supported + at all by the driver. + + Special case when used database driver has only single transaction support + (Driver::SingleTransactions): \a trans will not be rolled back + if it has been started outside this Connection object. + */ + bool rollbackAutoCommitTransaction(const Transaction& trans); + + /*! Creates cursor data and initializes cursor + using \a statement for later data retrieval. */ +// virtual CursorData* drv_createCursor( const QString& statement ) = 0; + /*! Closes and deletes cursor data. */ +// virtual bool drv_deleteCursor( CursorData *data ) = 0; + + /*! Helper: checks if connection is established; + if not: error message is set up and false returned */ + bool checkConnected(); + + /*! Helper: checks both if connection is established and database any is used; + if not: error message is set up and false returned */ + bool checkIsDatabaseUsed(); + + /*! \return a full table schema for a table retrieved using 'kexi__*' system tables. + Used internally by tableSchema() methods. */ + TableSchema* setupTableSchema( const RowData &data ); + + /*! \return a full query schema for a query using 'kexi__*' system tables. + Used internally by querySchema() methods. */ + QuerySchema* setupQuerySchema( const RowData &data ); + + /*! Update a row. */ + bool updateRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool useROWID = false); + /*! Insert a new row. */ + bool insertRow(QuerySchema &query, RowData& data, RowEditBuffer& buf, bool getROWID = false); + /*! Delete an existing row. */ + bool deleteRow(QuerySchema &query, RowData& data, bool useROWID = false); + /*! Delete all existing rows. */ + bool deleteAllRows(QuerySchema &query); + + /*! Allocates all needed table KexiDB system objects for kexi__* KexiDB liblary's + system tables schema. + These objects are used internally in this connection + and are added to list of tables (by name, + not by id because these have no ids). + */ + bool setupKexiDBSystemSchema(); + + /*! used internally by setupKexiDBSystemSchema(): + Allocates single table KexiDB system object named \a tsname + and adds this to list of such objects (for later removal on closeDatabase()). + */ + TableSchema* newKexiDBSystemTableSchema(const QString& tsname); + + //! Identifier escaping function in the associated Driver. + /*! Calls the identifier escaping function in the associated Driver to + escape table and column names. This should be used when explicitly + constructing SQL strings (e.g. "FROM " + escapeIdentifier(tablename)). + It should not be used for other functions (e.g. don't do + useDatabase(escapeIdentifier(database))), because the identifier will + be escaped when the called function generates, for example, "USE " + + escapeIdentifier(database). + + For efficiency, kexi__* system tables and columns therein are not escaped + - we assume these are valid identifiers for all drivers. + */ + inline QString escapeIdentifier(const QString& id, + int escaping = Driver::EscapeDriver|Driver::EscapeAsNecessary ) const { + return m_driver->escapeIdentifier(id, escaping); + } + + /*! Called by TableSchema -- signals destruction to Connection object + To avoid having deleted table object on its list. */ + void removeMe(TableSchema *ts); + + /*! @internal + \return true if the cursor \a cursor contains column \a column, + else, sets appropriate error with a message and returns false. */ + bool checkIfColumnExists(Cursor *cursor, uint column); + + /*! @internal used by querySingleRecord() methods. + Note: "LIMIT 1" is appended to \a sql statement if \a addLimitTo1 is true (the default). */ + tristate querySingleRecordInternal(RowData &data, const QString* sql, + QuerySchema* query, bool addLimitTo1 = true); + + /*! @internal used by Driver::createConnection(). + Only works if connection is not yet established. */ + void setReadOnly(bool set); + + /*! Loads extended schema information for table \a tableSchema, + if present (see ExtendedTableSchemaInformation in Kexi Wiki). + \return true on success */ + bool loadExtendedTableSchemaData(TableSchema& tableSchema); + + /*! Stores extended schema information for table \a tableSchema, + (see ExtendedTableSchemaInformation in Kexi Wiki). + The action is performed within the current transaction, + so it's up to you to commit. + Used, e.g. by createTable(), within its transaction. + \return true on success */ + bool storeExtendedTableSchemaData(TableSchema& tableSchema); + + /*! @internal + Stores main field's schema information for field \a field. + Used in table altering code when information in kexi__fields has to be updated. + \return true on success and false on failure. */ + bool storeMainFieldSchema(Field *field); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + /*! This is a part of alter table interface implementing lower-level operations + used to perform table schema altering. Used by AlterTableHandler. + + Changes value of field property. + \return true on success, false on failure, cancelled if the action has been cancelled. + + Note for driver developers: implement this if the driver has to support the altering. */ + virtual tristate drv_changeFieldProperty(TableSchema &table, Field& field, + const QString& propertyName, const QVariant& value) { + Q_UNUSED(table); Q_UNUSED(field); Q_UNUSED(propertyName); Q_UNUSED(value); + return cancelled; } + + //! cursors created for this connection + QPtrDict m_cursors; + + private: + ConnectionPrivate* d; //!< @internal d-pointer class. + Driver* const m_driver; //!< The driver this \a Connection instance uses. + bool m_destructor_started : 1; //!< helper: true if destructor is started. + + friend class KexiDB::Driver; + friend class KexiDB::Cursor; + friend class KexiDB::TableSchema; //!< for removeMe() + friend class KexiDB::DatabaseProperties; //!< for setError() + friend class ConnectionPrivate; + friend class KexiDB::AlterTableHandler; +}; + +} //namespace KexiDB + +#endif + -- cgit v1.2.1