diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/kexidb/queryschema.h | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
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
Diffstat (limited to 'kexi/kexidb/queryschema.h')
-rw-r--r-- | kexi/kexidb/queryschema.h | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/kexi/kexidb/queryschema.h b/kexi/kexidb/queryschema.h new file mode 100644 index 00000000..76dfa757 --- /dev/null +++ b/kexi/kexidb/queryschema.h @@ -0,0 +1,832 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2007 Jaroslaw Staniek <js@iidea.pl> + + This library 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 library 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 library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_QUERY_H +#define KEXIDB_QUERY_H + +#include <qvaluevector.h> +#include <qstring.h> +#include <qmap.h> +#include <qptrlist.h> + +#include "fieldlist.h" +#include "schemadata.h" +#include "tableschema.h" +#include "relationship.h" + +namespace KexiDB { + +class Connection; +class QueryAsterisk; +class QuerySchemaPrivate; +class QuerySchemaParameter; +typedef QValueList<QuerySchemaParameter> QuerySchemaParameterList; + +//! @short Helper class that assigns additional information for the column in a query +/*! The following information is assigned: + - alias + - visibility + QueryColumnInfo::Vector is created and returned by QuerySchema::fieldsExpanded(). + It is efficiently cached within the QuerySchema object. +*/ +class KEXI_DB_EXPORT QueryColumnInfo +{ + public: + typedef QPtrVector<QueryColumnInfo> Vector; + typedef QPtrList<QueryColumnInfo> List; + typedef QPtrListIterator<QueryColumnInfo> ListIterator; + + QueryColumnInfo(Field *f, const QCString& _alias, bool _visible, QueryColumnInfo *foreignColumn = 0); + ~QueryColumnInfo(); + + //! \return alias if it is not empty, field's name otherwise. + inline QCString aliasOrName() const { + return alias.isEmpty() ? field->name().latin1() : (const char*)alias; + } + + //! \return field's caption if it is not empty, field's alias otherwise. + //! If alias is also empty - returns field's name. + inline QString captionOrAliasOrName() const { + return field->caption().isEmpty() ? QString(aliasOrName()) : field->caption(); } + + Field *field; + QCString alias; + + /*! \return index of column with visible lookup value within the 'fields expanded' vector. + -1 means no visible lookup value is available because there is no lookup for the column defined. + Cached for efficiency as we use this information frequently. + @see LookupFieldSchema::visibleVolumn() */ + inline int indexForVisibleLookupValue() const { return m_indexForVisibleLookupValue; } + + /*! Sets index of column with visible lookup value within the 'fields expanded' vector. */ + inline void setIndexForVisibleLookupValue(int index) { m_indexForVisibleLookupValue = index; } + + //! \return non-0 if this column is a visible column for other column + QueryColumnInfo *foreignColumn() const { return m_foreignColumn; } + + /*! \return string for debugging purposes. */ + QString debugString() const; + + //! true if this column is visible to the user (and its data is fetched by the engine) + bool visible : 1; + + private: + /*! Index of column with visible lookup value within the 'fields expanded' vector. + @see indexForVisibleLookupValue() */ + int m_indexForVisibleLookupValue; + + //! Non-0 if this column is a visible column for \a m_foreignColumn + QueryColumnInfo *m_foreignColumn; +}; + +//! @short KexiDB::OrderByColumn provides information about a single query column used for sorting +/*! The column can be expression or table field. */ +class KEXI_DB_EXPORT OrderByColumn +{ + public: + typedef QValueListConstIterator<OrderByColumn> ListConstIterator; + OrderByColumn(); + OrderByColumn(QueryColumnInfo& column, bool ascending = true, int pos = -1); + + //! Like above but used when the field \a field is not present on the list of columns. + //! (e.g. SELECT a FROM t ORDER BY b; where T is a table with fields (a,b)). + OrderByColumn(Field& field, bool ascending = true); + + ~OrderByColumn(); + + //! A column to sort. + inline QueryColumnInfo* column() const { return m_column; } + + /*! A helper for column() that allows you to know that sorting column + was defined by providing its position. -1 by default. + Example query: SELECT a, b FROM T ORDER BY 2 */ + inline int position() const { return m_pos; } + + //! A field to sort, used only in case when the second constructor was used. + inline Field *field() const { return m_field; } + + //! \return true if ascending sorting should be performed (the default). + inline bool ascending() const { return m_ascending; } + + //! \return true if this column is thesame as \a col + bool operator== ( const OrderByColumn& col ) const + { return m_column==col.m_column && m_field==col.m_field + && m_ascending==col.m_ascending; } + + /*! \return string for debugging purposes. */ + QString debugString() const; + + /*! \return a string like "name ASC" usable for building a SQL statement. + If \a includeTableNames is true (the default) field is output in a form + of "tablename.fieldname" (but only if fieldname is not a name of alias). + \a drv and \a identifierEscaping are used for escaping the table and field identifiers. */ + QString toSQLString(bool includeTableName = true, + Driver *drv = 0, int identifierEscaping = Driver::EscapeDriver|Driver::EscapeAsNecessary) const; + + protected: + //! Column to sort + QueryColumnInfo* m_column; //!< 0 if m_field is non-0. + int m_pos; //!< A helper for m_column that allows to know that sorting column + //!< was defined by providing its position. -1 by default. + //!< e.g. SELECT a, b FROM T ORDER BY 2 + Field* m_field; //!< Used only in case when the second contructor is used. + + //! true if ascending sorting should be performed (the default). + bool m_ascending : 1; +}; + +//! A base for KexiDB::OrderByColumnList +typedef QValueList<OrderByColumn> OrderByColumnListBase; + +//! @short KexiDB::OrderByColumnList provides list of sorted columns for a query schema +class KEXI_DB_EXPORT OrderByColumnList : protected OrderByColumnListBase +{ + public: + /*! Constructs empty list of ordered columns. */ + OrderByColumnList(); + + ~OrderByColumnList(); + + /*! Appends multiple fields for sorting. \a querySchema + is used to find appropriate field or alias name. + \return false if there is at least one name for which a field or alias name does not exist + (all the newly appended fields are removed in this case) */ + bool appendFields(QuerySchema& querySchema, + const QString& field1, bool ascending1 = true, + const QString& field2 = QString::null, bool ascending2 = true, + const QString& field3 = QString::null, bool ascending3 = true, + const QString& field4 = QString::null, bool ascending4 = true, + const QString& field5 = QString::null, bool ascending5 = true); + + /*! Appends column \a columnInfo. Ascending sorting is set is \a ascending is true. */ + void appendColumn(QueryColumnInfo& columnInfo, bool ascending = true); + + /*! Appends a field \a field. Ascending sorting is set is \a ascending is true. + Read documentation of \ref OrderByColumn(const Field& field, bool ascending = true) + for more info. */ + void appendField(Field& field, bool ascending = true); + + /*! Appends field with a name \a field. Ascending sorting is set is \a ascending is true. + \return true on successful appending, and false if there is no such field or alias + name in the \a querySchema. */ + bool appendField(QuerySchema& querySchema, const QString& fieldName, + bool ascending = true); + + /*! Appends a column that is at position \a pos (counted from 0). + \return true on successful adding and false if there is no such position \a pos. */ + bool appendColumn(QuerySchema& querySchema, bool ascending = true, int pos = -1); + + /*! Appends \a column to the list. */ + void appendColumn(const OrderByColumn& column); + + /*! \return true if the list is empty. */ + bool isEmpty() const { return OrderByColumnListBase::isEmpty(); } + + /*! \return number of elements of the list. */ + uint count() const { return OrderByColumnListBase::count(); } + + /*! Removes all elements from the list. */ + void clear() { OrderByColumnListBase::clear(); } + + const_iterator constBegin () const { return OrderByColumnListBase::constBegin(); } + const_iterator constEnd () const { return OrderByColumnListBase::constEnd(); } + + /*! \return string for debugging purposes. */ + QString debugString() const; + + /*! \return a string like "name ASC, 2 DESC" usable for building a SQL statement. + If \a includeTableNames is true (the default) fields are output in a form + of "tablename.fieldname". + \a drv and \a identifierEscaping are used for escaping the table and field identifiers. */ + QString toSQLString(bool includeTableNames = true, + Driver *drv = 0, int identifierEscaping = Driver::EscapeDriver|Driver::EscapeAsNecessary) const; +}; + +//! @short KexiDB::QuerySchema provides information about database query +/*! The query that can be executed using KexiDB-compatible SQL database engine + or used as an introspection tool. KexiDB parser builds QuerySchema objects + by parsing SQL statements. */ +class KEXI_DB_EXPORT QuerySchema : public FieldList, public SchemaData +{ + public: + /*! Creates empty query object (without columns). */ + QuerySchema(); + + /*! Creates query schema object that is equivalent to "SELECT * FROM table" + sql command. Schema of \a table is used to contruct this query -- + it is defined by just adding all the fields to the query in natural order. + To avoid problems (e.g. with fields added outside of Kexi using ALTER TABLE) + we do not use "all-tables query asterisk" (see QueryAsterisk) item to achieve + this effect. + + Properties such as the name and caption of the query are inherited + from table schema. + + We consider that query schema based on \a table is not (a least yet) stored + in a system table, so query connection is set to NULL + (even if \a tableSchema's connection is not NULL). + Id of the created query is set to 0. */ + QuerySchema(TableSchema& tableSchema); + + /*! Copy constructor. Creates deep copy of \a querySchema. + QueryAsterisk objects are deeply copied while only pointers to Field objects are copied. */ + QuerySchema(const QuerySchema& querySchema); + + virtual ~QuerySchema(); + + /*! Inserts \a field to the columns list at \a position. + Inserted field will not be owned by this QuerySchema object, + but still by corresponding TableSchema. + + As \a field object you can also pass KexiDB::QueryAsterisk, + (see QueryAsterisk class description). + + Note: After inserting a field, corresponding table will be automatically + added to query's tables list if it is not present there (see tables()). + Field must have its table assigned. + + Added field will be visible. Use insertField(position, field, false) + to add invisible field. + */ + virtual FieldList& insertField(uint position, Field *field); + + /* Like above method, but you can also set column's visibility. + New column is not bound explicitly to any table. + */ + FieldList& insertField(uint position, Field *field, bool visible); + + /* Like above method, but you can also explicitly bound the new column + to specific position on tables list. + If \a visible is true (the default), the field will be visible. + If bindToTable==-1, no particular table should be bound. + @see tableBoundToColumn(uint columnPosition) */ + FieldList& insertField(uint position, Field *field, + int bindToTable, bool visible = true); + + /*! Adds \a field to the columns list. + If \a visible is true (the default), the field will be visible. + \sa insertField() */ + FieldList& addField(Field* field, bool visible = true); + + /*! Adds \a field to the columns list. Also binds to a table + at \a bindToTable position. Use bindToTable==-1 if no table should be bound. + If \a visible is true (the default), the field will be visible. + \sa insertField() + \sa tableBoundToColumn(uint columnPosition) + */ + FieldList& addField(Field* field, int bindToTable, + bool visible = true); + + /*! Removes field from the columns list. Use with care. */ + virtual void removeField(Field *field); + + /*! Adds a field built on top of \a expr expression. + This creates a new Field object and adds it to the query schema using addField(). */ + FieldList& addExpression(BaseExpr* expr, bool visible = true); + + /*! \return visibility flag for column at \a position. + By default column is visible. */ + bool isColumnVisible(uint position) const; + + //! Sets visibility flag for column at \a position to \a v. + void setColumnVisible(uint position, bool v); + + /*! Adds \a asterisk at the and of columns list. */ + FieldList& addAsterisk(QueryAsterisk *asterisk, bool visible = true); + + /*! Removes all columns and their aliases from the columns list, + removes all tables and their aliases from the tables list within this query. + Sets master table information to NULL. + Does not destroy any objects though. Clears name and all other properties. + \sa FieldList::clear() */ + virtual void clear(); + + /*! \return string for debugging purposes. */ + virtual QString debugString(); + + /*! If query was created using a connection, + returns this connection object, otherwise NULL. */ + Connection* connection() const; + + /*! \return table that is master to this query. + All potentially-editable columns within this query belong just to this table. + This method also can return NULL if there are no tables at all, + or if previously assigned master table schema has been removed + with removeTable(). + Every query that has at least one table defined, should have + assigned a master table. + If no master table is assigned explicitym but this method there is only + one table used for this query even if there are table aliases, + a single table is returned here. + (e.g. "T" table is returned for "SELECT T1.A, T2.B FROM T T1, T T2" statement). */ + TableSchema* masterTable() const; + + /*! Sets master table of this query to \a table. + This table should be also added to query's tables list + using addTable(). If \a table equals NULL, nothing is performed. + \sa masterTable() */ + void setMasterTable(TableSchema *table); + + /*! \return list of tables used in a query. + This also includes master table. + \sa masterTable() */ + TableSchema::List* tables() const; + + /*! Adds \a table schema as one of tables used in a query. + if \a alias is not empty, it will be assigned to this table + using setTableAlias(position, alias) + */ + void addTable(TableSchema *table, const QCString& alias = QCString()); + + /*! Removes \a table schema from this query. + This does not destroy \a table object but only takes it out of the list. + If this table was master for the query, master table information is also + invalidated. */ + void removeTable(TableSchema *table); + + /*! \return table with name \a tableName or 0 if this query has no such table. */ + TableSchema* table(const QString& tableName) const; + + /*! \return true if the query uses \a table. */ + bool contains(TableSchema *table) const; + + /*! Convenience function. + \return table field by searching through all tables in this query. + The field does not need to be included on the list of query columns. + Similarly, query aliases are not taken into account. + + \a tableOrTableAndFieldName string may contain table name and field name + with '.' character between them, e.g. "mytable.myfield". + This is recommended way to avoid ambiguity. + 0 is returned if the query has no such + table defined of the table has no such field defined. + If you do not provide a table name, the first field found is returned. + + QuerySchema::table("mytable")->field("myfield") could be + alternative for findTableField("mytable.myfield") but it can crash + if "mytable" is not defined in the query. + + @see KexiDB::splitToTableAndFieldParts() + */ + Field* findTableField(const QString &tableOrTableAndFieldName) const; + + /*! \return alias of a column at \a position or null string + If there is no alias for this column + or if there is no such column within the query defined. + If the column is an expression and has no alias defined, + a new unique alias will be generated automatically on this call. + */ + QCString columnAlias(uint position) const; + + /*! Provided for convenience. + \return true if a column at \a position has non empty alias defined + within the query. + If there is no alias for this column, + or if there is no such column in the query defined, false is returned. */ + bool hasColumnAlias(uint position) const; + + /*! Sets \a alias for a column at \a position, within the query. + Passing empty string to \a alias clears alias for a given column. */ + void setColumnAlias(uint position, const QCString& alias); + + /*! \return a table position (within FROM section), + that is bound to column at \a columnPosition (within SELECT section). + This information can be used to find if there is alias defined for + a table that is referenced by a given column. + + For example, for "SELECT t2.id FROM table1 t1, table2 t2" query statement, + columnBoundToTable(0) returns 1, what means that table at position 1 + (within FROM section) is bound to column at position 0, so we can + now call tableAlias(1) to see if we have used alias for this column (t2.d) + or just a table name (table2.d). + + These checkings are performed e.g. by Connection::queryStatement() + to construct a statement string maximally identical to originally + defined query statement. + + -1 is returned if: + - \a columnPosition is out of range (i.e. < 0 or >= fieldCount()) + - a column at \a columnPosition is not bound to any table (i.e. + no database field is used for this column, + e.g. "1" constant for "SELECT 1 from table" query statement) + */ + int tableBoundToColumn(uint columnPosition) const; + + /*! \return alias of a table at \a position (within FROM section) + or null string if there is no alias for this table + or if there is no such table within the query defined. */ + QCString tableAlias(uint position) const; + + /*! \return table position (within FROM section) that has attached + alias \a name. + If there is no such alias, -1 is returned. + Only first table's position attached for this alias is returned. + It is not especially bad, since aliases rarely can be duplicated, + what leads to ambiguity. + Duplicated aliases are only allowed for trivial queries that have + no database fields used within their columns, + e.g. "SELECT 1 from table1 t, table2 t" is ok + but "SELECT t.id from table1 t, table2 t" is not. + */ + int tablePositionForAlias(const QCString& name) const; + + /*! \return table position (within FROM section) for \a tableName. + -1 is returend if there's no such table declared in the FROM section. + \sa tablePositions() + */ + int tablePosition(const QString& tableName) const; + + /*! \return a list of all \a tableName table occurrences (within FROM section). + E.g. for "SELECT * FROM table t, table t2" [0, 1] list is returned. + Empty list is returned there's no such table declared + in the FROM section at all. + \sa tablePosition() + */ + QValueList<int> tablePositions(const QString& tableName) const; + + /*! Provided for convenience. + \return true if a table at \a position (within FROM section of the the query) + has non empty alias defined. + If there is no alias for this table, + or if there is no such table in the query defined, false is returned. */ + bool hasTableAlias(uint position) const; + + /*! \return column position that has defined alias \a name. + If there is no such alias, -1 is returned. */ + int columnPositionForAlias(const QCString& name) const; + + /*! Sets \a alias for a table at \a position (within FROM section + of the the query). + Passing empty sting to \a alias clears alias for a given table + (only for specified \a position). */ + void setTableAlias(uint position, const QCString& alias); + + /*! \return a list of relationships defined for this query */ + Relationship::List* relationships() const; + + /*! Adds a new relationship defined by \a field1 and \a field2. + Both fields should belong to two different tables of this query. + This is convenience function useful for a typical cases. + It automatically creates Relationship object for this query. + If one of the fields are primary keys, it will be detected + and appropriate master-detail relation will be established. + This functiuon does nothing if the arguments are invalid. */ + Relationship* addRelationship( Field *field1, Field *field2 ); + + /*! \return list of QueryAsterisk objects defined for this query */ + Field::List* asterisks() const; + + /*! \return field for \a identifier or 0 if no field for this name + was found within the query. fieldsExpanded() method is used + to lookup expanded list of the query fields, so queries with asterisks + are processed well. + If a field has alias defined, name is not taken into account, + but only its alias. If a field has no alias: + - field's name is checked + - field's table and field's name are checked in a form of "tablename.fieldname", + so you can provide \a identifier in this form to avoid ambiguity. + + If there are more than one fields with the same name equal to \a identifier, + first-found is returned (checking is performed from first to last query field). + Structures needed to compute result of this method are cached, + so only first usage costs o(n) - another usages cost o(1). + + Example: + Let query be defined by "SELECT T.B AS X, T.* FROM T" statement and let T + be table containing fields A, B, C. + Expanded list of columns for the query is: T.B AS X, T.A, T.B, T.C. + - Calling field("B") will return a pointer to third query column (not the first, + because it is covered by "X" alias). Additionally, calling field("X") + will return the same pointer. + - Calling field("T.A") will return the same pointer as field("A"). + */ + virtual Field* field(const QString& name, bool expanded = true); + + /*! \return field id or NULL if there is no such a field. */ + inline Field* field(uint id) { return FieldList::field(id); } + + /*! Like QuerySchema::field(const QString& name) but returns not only Field + object for \a identifier but entire QueryColumnInfo object. + \a identifier can be: + - a fieldname + - an aliasname + - a tablename.fieldname + - a tablename.aliasname + Note that if there are two occurrrences of the same name, + only the first is accessible using this method. For instance, + calling columnInfo("name") for "SELECT t1.name, t2.name FROM t1, t2" statement + will only return the column related to t1.name and not t2.name, so you'll need to + explicitly specify "t2.name" as the identifier to get the second column. */ + QueryColumnInfo* columnInfo(const QString& identifier, bool expanded = true); + + /*! Options used in fieldsExpanded(). */ + enum FieldsExpandedOptions { + Default, //!< All fields are returned even if duplicated + Unique, //!< Unique list of fields is returned + WithInternalFields, //!< Like Default but internal fields (for lookup) are appended + WithInternalFieldsAndRowID //!< Like WithInternalFields but RowID (big int type) field + //!< is appended after internal fields + }; + + /*! \return fully expanded list of fields. + QuerySchema::fields() returns vector of fields used for the query columns, + but in a case when there are asterisks defined for the query, + it does not expand QueryAsterisk objects to field lists but return every + asterisk as-is. + This could be inconvenient when you need just a fully expanded list of fields, + so this method does the work for you. + + If \a options is Unique, each field is returned in the vector only once + (first found field is selected). + Note however, that the same field can be returned more than once if it has attached + a different alias. + For example, let t be TABLE( a, b ) and let query be defined + by "SELECT *, a AS alfa FROM t" statement. Both fieldsExpanded(Default) + and fieldsExpanded(Unique) will return [ a, b, a (alfa) ] list. + On the other hand, for query defined by "SELECT *, a FROM t" statement, + fieldsExpanded(Default) will return [ a, b, a ] list while + fieldsExpanded(Unique) will return [ a, b ] list. + + If \a options is WithInternalFields or WithInternalFieldsAndRowID, + additional internal fields are also appended to the vector. + + If \a options is WithInternalFieldsAndRowID, + one fake BigInteger column is appended to make space for ROWID column used + by KexiDB::Cursor implementations. For example, let persons be TABLE( surname, city_id ), + let city_number reference cities.is in TABLE cities( id, name ) and let query q be defined + by "SELECT * FROM t" statement. If we want to display persons' city names instead of city_id's. + To do this, cities.name has to be retrieved as well, so the following statement should be used: + "SELECT * FROM persons, cities.name LEFT OUTER JOIN cities ON persons.city_id=cities.id". + Thus, calling fieldsExpanded(WithInternalFieldsAndRowID) will return 4 elements instead of 2: + persons.surname, persons.city_id, cities.name, {ROWID}. The {ROWID} item is the placeholder + used for fetching ROWID by KexiDB cursors. + + By default, all fields are returned in the vector even + if there are multiple occurrences of one or more (options == Default). + + Note: You should assign the resulted vector in your space - it will be shared + and implicity copied on any modification. + This method's result is cached by QuerySchema object. +@todo js: UPDATE CACHE! + */ + QueryColumnInfo::Vector fieldsExpanded(FieldsExpandedOptions options = Default); + + /*! \return list of fields internal fields used for lookup columns. */ + QueryColumnInfo::Vector internalFields(); + + /*! \return info for expanded of internal field at index \a index. + The returned field can be either logical or internal (for lookup), + the latter case is true if \a index >= fieldsExpanded().count(). + Equivalent of QuerySchema::fieldsExpanded(WithInternalFields).at(index). */ + QueryColumnInfo* expandedOrInternalField(uint index); + + /*! Options used in columnsOrder(). */ + enum ColumnsOrderOptions { + UnexpandedList, //!< A map for unexpanded list is created + UnexpandedListWithoutAsterisks, //!< A map for unexpanded list is created, with asterisks skipped + ExpandedList //!< A map for expanded list is created + }; + + /*! \return a map for fast lookup of query columns' order. + - If \a options is UnexpandedList, each QueryColumnInfo pointer is mapped to the index + within (unexpanded) list of fields, i.e. "*" or "table.*" asterisks are considered + to be single items. + - If \a options is UnexpandedListWithoutAsterisks, each QueryColumnInfo pointer + is mapped to the index within (unexpanded) list of columns that come from asterisks + like "*" or "table.*" are not included in the map at all. + - If \a options is ExpandedList (the default) this method provides is exactly opposite + information compared to vector returned by fieldsExpanded(). + + This method's result is cached by the QuerySchema object. + Note: indices of internal fields (see internalFields()) are also returned + here - in this case the index is counted as a sum of size(e) + i (where "e" is + the list of expanded fields and i is the column index within internal fields list). + This feature is used eg. at the end of Connection::updateRow() where need indices of + fields (including internal) to update all the values in memory. + + Example use: let t be table (int id, name text, surname text) and q be query + defined by a statement "select * from t". + + - columnsOrder(ExpandedList) will return the following map: QueryColumnInfo(id)->0, + QueryColumnInfo(name)->1, QueryColumnInfo(surname)->2. + - columnsOrder(UnexpandedList) will return the following map: QueryColumnInfo(id)->0, + QueryColumnInfo(name)->0, QueryColumnInfo(surname)->0 because the column + list is not expanded. This way you can use the returned index to get Field* + pointer using field(uint) method of FieldList superclass. + - columnsOrder(UnexpandedListWithoutAsterisks) will return the following map: + QueryColumnInfo(id)->0, + */ + QMap<QueryColumnInfo*,int> columnsOrder(ColumnsOrderOptions options = ExpandedList); + + /*! \return table describing order of primary key (PKEY) fields within the query. + Indexing is performed against vector returned by fieldsExpanded(). + It is usable for e.g. Conenction::updateRow(), when we need + to locate each primary key's field in a constant time. + + Returned vector is owned and cached by QuerySchema object. When you assign it, + it is implicity shared. Its size is equal to number of primary key + fields defined for master table (masterTable()->primaryKey()->fieldCount()). + + Each element of the returned vector: + - can belong to [0..fieldsExpanded().count()-1] if there is such + primary key's field in the fieldsExpanded() list. + - can be equal to -1 if there is no such primary key's field + in the fieldsExpanded() list. + + If there are more than one primary key's field included in the query, + only first-found column (oin the fieldsExpanded() list) for each pkey's field is included. + + Returns empty vector if there is no master table or no master table's pkey. + @see example for pkeyFieldsCount(). +@todo js: UPDATE CACHE! + */ + QValueVector<int> pkeyFieldsOrder(); + + /*! \return number of master table's primary key fields included in this query. + This method is useful to quickly check whether the vector returned by pkeyFieldsOrder() + if filled completely. + + User e.g. in Connection::updateRow() to check if entire primary + key information is specified. + + Examples: let table T has (ID1 INTEGER, ID2 INTEGER, A INTEGER) fields, + and let (ID1, ID2) is T's primary key. + -# The query defined by "SELECT * FROM T" statement contains all T's + primary key's fields as T is the master table, and thus pkeyFieldsCount() + will return 2 (both primary key's fields are in the fieldsExpanded() list), + and pkeyFieldsOrder() will return vector {0, 1}. + -# The query defined by "SELECT A, ID2 FROM T" statement, and thus pkeyFieldsCount() + will return 1 (only one primary key's field is in the fieldsExpanded() list), + and pkeyFieldsOrder() will return vector {-1, 1}, as second primary key's field + is at position #1 and first field is not specified at all within the query. + */ + uint pkeyFieldsCount(); + + /*! \return a list of field infos for all auto-incremented fields + from master table of this query. This result is cached for efficiency. + fieldsExpanded() is used for that. + */ + QueryColumnInfo::List* autoIncrementFields(); + + /*! \return a preset statement (if any). */ + QString statement() const; + + /*! Forces a query statement (i.e. no statement is composed from QuerySchema's content) */ + void setStatement(const QString &s); + + /*! \return a string that is a result of concatenating all column names + for \a infolist, with "," between each one. + This is usable e.g. as argument like "field1,field2" + for "INSERT INTO (xxx) ..". The result of this method is effectively cached, + and it is invalidated when set of fields changes (e.g. using clear() + or addField()). + + This method is similar to FieldList::sqlFieldsList() it just uses + QueryColumnInfo::List instead of Field::List. + */ + static QString sqlColumnsList(QueryColumnInfo::List* infolist, Driver *driver); + + /*! \return cached sql list created using sqlColumnsList() on a list returned + by autoIncrementFields(). */ + QString autoIncrementSQLFieldsList(Driver *driver); + + /*! Sets a WHERE expression \a exp. It will be owned by this query, + so you can forget about it. Previously set WHERE expression will be deleted. + You can pass 0 to remove expresssion. */ + void setWhereExpression(BaseExpr *expr); + + /*! \return WHERE expression or 0 if this query has no WHERE expression */ + BaseExpr *whereExpression() const; + + /*! Adds a part to WHERE expression. + Simplifies creating of WHERE expression, if used instead + of setWhereExpression(BaseExpr *expr). */ + void addToWhereExpression(KexiDB::Field *field, const QVariant& value, int relation = '='); + + /*! Sets a list of columns for ORDER BY section of the query. + Each name on the list must be a field or alias present within the query + and must not be covered by aliases. If one or more names cannot be found + within the query, the method will have no effect. + Any previous ORDER BY settings will be removed. + + Note that this information is cleared whenever you call methods that + modify list of columns (QueryColumnInfo), i.e. insertFiled(), + addField(), removeField(), addExpression(), etc. + (because OrderByColumn items can point to a QueryColumnInfo that's removed by these + methods), so you should use setOrderByColumnList() method after the query + is completely built. */ + void setOrderByColumnList(const OrderByColumnList& list); + + /*! \return a list of columns listed in ORDER BY section of the query. + Read notes for \ref setOrderByColumnList(). */ + OrderByColumnList& orderByColumnList() const; + + /*! \return query schema parameters. These are taked from the WHERE section + (a tree of expression items). */ + QuerySchemaParameterList parameters(); + + protected: + void init(); + + void computeFieldsExpanded(); + + QuerySchemaPrivate *d; + + friend class Connection; + friend class QuerySchemaPrivate; +}; + +//! @short KexiDB::QueryAsterisk class encapsulates information about single asterisk in query definition +/*! There are two types of query asterisks: + + 1. "Single-table" asterisk, that references all fields of given table used + in the query. + Example SQL statement: + \code + SELECT staff.*, cars.model from staff, cars WHERE staff.car = cars.number; + \endcode + The "staff.*" element is our "single-table" asterisk; + this tells us that we want to get all fields of table "staff". + + 2. "All-tables" asterisk, that references all fields of all tables used in the query. + Example SQL statement: + \code + SELECT * from staff, cars WHERE staff.car = cars.number; + \endcode + The "*" is our "all-tables" asterisk; + this tells us that we want to get all fields of all used tables (here: "staff" and "cars"). + + There can be many asterisks of 1st type defined for given single query. + There can be one asterisk of 2nd type defined for given single query. +*/ +class KEXI_DB_EXPORT QueryAsterisk : public Field +{ + public: + /*! Constructs query asterisk definition object. + Pass table schema to \a table if this asterisk should be + of type "single-table", otherwise (if you want to define + "all-tables" type asterisk), omit this parameter. + + QueryAsterisk objects are owned by QuerySchema object + (not by TableSchema object like for ordinary Field objects) + for that the QueryAsterisk object was added (using QuerySchema::addField()). + */ + QueryAsterisk( QuerySchema *query, TableSchema *table = 0 ); + + virtual ~QueryAsterisk(); + + /*! \return Query object for that this asterisk object is defined */ + QuerySchema *query() const { return static_cast<QuerySchema*>(m_parent); } + + /*! \return Table schema for this asterisk + if it has "single-table" type (1st type) + or NULL if it has "all-tables" type (2nd type) defined. */ + virtual TableSchema* table() const { return m_table; } + + /*! Sets table schema for this asterisk. + \a table may be NULL - then the asterisk becames "all-tables" type asterisk. */ + virtual void setTable(TableSchema *table); + + /*! Reimplemented. */ + virtual bool isQueryAsterisk() const { return true; } + + /*! This is convenience method that returns true + if the asterisk has "all-tables" type (2nd type).*/ + bool isSingleTableAsterisk() const { return m_table!=NULL; } + + /*! This is convenience method that returns true + if the asterisk has "single-tables" type (2nd type).*/ + bool isAllTableAsterisk() const { return m_table==NULL; } + + /*! \return String for debugging purposes. */ + virtual QString debugString() const; + + protected: + //! \return a deep copy of this object. Used in FieldList(const FieldList& fl). + virtual Field* copy() const; + + /*! Table schema for this asterisk */ + TableSchema* m_table; + + friend class QuerySchema; +}; + +} //namespace KexiDB + +#endif |