Kexi API Documentation (2.0 alpha)

queryschema.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This library 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 library 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 library; see the file COPYING.LIB.  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/queryschema.h"
00021 #include "kexidb/driver.h"
00022 #include "kexidb/connection.h"
00023 #include "kexidb/expression.h"
00024 #include "kexidb/parser/sqlparser.h"
00025 #include "utils.h"
00026 #include "lookupfieldschema.h"
00027 
00028 #include <assert.h>
00029 
00030 #include <qvaluelist.h>
00031 #include <qasciidict.h>
00032 #include <qptrdict.h>
00033 #include <qintdict.h>
00034 #include <qbitarray.h>
00035 
00036 #include <kdebug.h>
00037 #include <klocale.h>
00038 
00039 using namespace KexiDB;
00040 
00041 QueryColumnInfo::QueryColumnInfo(Field *f, const QCString& _alias, bool _visible, 
00042     QueryColumnInfo *foreignColumn)
00043  : field(f), alias(_alias), visible(_visible), m_indexForVisibleLookupValue(-1)
00044  , m_foreignColumn(foreignColumn)
00045 {
00046 }
00047 
00048 QueryColumnInfo::~QueryColumnInfo()
00049 {
00050 }
00051 
00052 QString QueryColumnInfo::debugString() const
00053 {
00054     return field->name() + 
00055         ( alias.isEmpty() ? QString::null 
00056             : (QString::fromLatin1(" AS ") + QString(alias)) );
00057 }
00058 
00059 //=======================================
00060 namespace KexiDB {
00062 class QuerySchemaPrivate
00063 {
00064     public:
00065         QuerySchemaPrivate(QuerySchema* q)
00066          : query(q)
00067          , masterTable(0)
00068          , fakeRowIDField(0)
00069          , fakeRowIDCol(0)
00070          , maxIndexWithAlias(-1)
00071          , visibility(64)
00072          , fieldsExpanded(0)
00073          , internalFields(0)
00074          , fieldsExpandedWithInternalAndRowID(0)
00075          , fieldsExpandedWithInternal(0)
00076          , autoincFields(0)
00077          , columnsOrder(0)
00078          , columnsOrderWithoutAsterisks(0)
00079          , columnsOrderExpanded(0)
00080          , pkeyFieldsOrder(0)
00081          , pkeyFieldsCount(0)
00082          , tablesBoundToColumns(64, -1)
00083          , tablePositionsForAliases(67, false)
00084          , columnPositionsForAliases(67, false)
00085          , whereExpr(0)
00086          , regenerateExprAliases(false)
00087         {
00088             columnAliases.setAutoDelete(true);
00089             tableAliases.setAutoDelete(true);
00090             asterisks.setAutoDelete(true);
00091             relations.setAutoDelete(true);
00092             tablePositionsForAliases.setAutoDelete(true);
00093             columnPositionsForAliases.setAutoDelete(true);
00094             visibility.fill(false);
00095         }
00096         ~QuerySchemaPrivate()
00097         {
00098             delete fieldsExpanded;
00099             delete internalFields;
00100             delete fieldsExpandedWithInternalAndRowID;
00101             delete fieldsExpandedWithInternal;
00102             delete autoincFields;
00103             delete columnsOrder;
00104             delete columnsOrderWithoutAsterisks;
00105             delete columnsOrderExpanded;
00106             delete pkeyFieldsOrder;
00107             delete whereExpr;
00108             delete fakeRowIDCol;
00109             delete fakeRowIDField;
00110         }
00111 
00112         void clear()
00113         {
00114             columnAliases.clear();
00115             tableAliases.clear();
00116             asterisks.clear();
00117             relations.clear();
00118             masterTable = 0;
00119             tables.clear();
00120             clearCachedData();
00121             delete pkeyFieldsOrder;
00122             pkeyFieldsOrder=0;
00123             visibility.fill(false);
00124             tablesBoundToColumns = QValueVector<int>(64,-1);
00125             tablePositionsForAliases.clear();
00126             columnPositionsForAliases.clear();
00127         }
00128 
00129         void clearCachedData()
00130         {
00131             orderByColumnList.clear();
00132             if (fieldsExpanded) {
00133                 delete fieldsExpanded;
00134                 fieldsExpanded = 0;
00135                 delete internalFields;
00136                 internalFields = 0;
00137                 delete columnsOrder;
00138                 columnsOrder = 0;
00139                 delete columnsOrderWithoutAsterisks;
00140                 columnsOrderWithoutAsterisks = 0;
00141                 delete columnsOrderExpanded;
00142                 columnsOrderExpanded = 0;
00143                 delete autoincFields;
00144                 autoincFields = 0;
00145                 autoIncrementSQLFieldsList = QString::null;
00146                 columnInfosByNameExpanded.clear();
00147                 columnInfosByName.clear();
00148             }
00149         }
00150 
00151         void setColumnAliasInternal(uint position, const QCString& alias)
00152         {
00153             columnAliases.replace(position, new QCString(alias));
00154             columnPositionsForAliases.replace(alias, new int(position));
00155             maxIndexWithAlias = QMAX( maxIndexWithAlias, (int)position );
00156         }
00157 
00158         void setColumnAlias(uint position, const QCString& alias)
00159         {
00160             QCString *oldAlias = columnAliases.take(position);
00161             if (oldAlias) {
00162                 tablePositionsForAliases.remove(*oldAlias);
00163                 delete oldAlias;
00164             }
00165             if (alias.isEmpty()) {
00166                 maxIndexWithAlias = -1;
00167             }
00168             else {
00169                 setColumnAliasInternal(position, alias);
00170             }
00171         }
00172 
00173         bool hasColumnAliases()
00174         {
00175             tryRegenerateExprAliases();
00176             return !columnAliases.isEmpty();
00177         }
00178 
00179         QCString* columnAlias(uint position)
00180         {
00181             tryRegenerateExprAliases();
00182             return columnAliases[position];
00183         }
00184 
00185         QuerySchema *query;
00186 
00190         TableSchema *masterTable;
00191         
00193         TableSchema::List tables;
00194 
00195         Field *fakeRowIDField; 
00196         QueryColumnInfo *fakeRowIDCol; 
00197 
00198     protected:
00199         void tryRegenerateExprAliases()
00200         {
00201             if (!regenerateExprAliases)
00202                 return;
00203             //regenerate missing aliases for experessions
00204             Field *f;
00205             uint p=0;
00206             uint colNum=0; //used to generate a name
00207             QCString columnAlias;
00208             for (Field::ListIterator it(query->fieldsIterator()); (f = it.current()); ++it, p++) {
00209                 if (f->isExpression() && !columnAliases[p]) {
00210                     //missing
00211                     for (;;) { //find 1st unused
00212                         colNum++;
00213                         columnAlias = (i18n("short for 'expression' word (only latin letters, please)", "expr") 
00214                             + QString::number(colNum)).latin1();
00215                         if (!tablePositionsForAliases[columnAlias])
00216                             break;
00217                     }
00218                     setColumnAliasInternal(p, columnAlias);
00219                 }
00220             }
00221             regenerateExprAliases = false;
00222         }
00223 
00225         QIntDict<QCString> columnAliases;
00226 
00227     public:
00229         QIntDict<QCString> tableAliases;
00230         
00232         int maxIndexWithAlias;
00233 
00235         int maxIndexWithTableAlias;
00236         
00238         QBitArray visibility;
00239 
00241         Field::List asterisks;
00242 
00244 //      Field::Vector *fieldsExpanded;
00245         QueryColumnInfo::Vector *fieldsExpanded;
00246 
00248         QueryColumnInfo::Vector *internalFields;
00249 
00252         QueryColumnInfo::Vector *fieldsExpandedWithInternalAndRowID;
00253 
00256         QueryColumnInfo::Vector *fieldsExpandedWithInternal;
00257 
00259         OrderByColumnList orderByColumnList;
00260 
00262         QueryColumnInfo::List *autoincFields;
00263         
00265         QString autoIncrementSQLFieldsList;
00266         QGuardedPtr<Driver> lastUsedDriverForAutoIncrementSQLFieldsList;
00267 
00269         QMap<QueryColumnInfo*,int> *columnsOrder;
00270 
00272         QMap<QueryColumnInfo*,int> *columnsOrderWithoutAsterisks;
00273 
00277         QMap<QueryColumnInfo*,int> *columnsOrderExpanded;
00278 
00279 //      QValueList<bool> detailedVisibility;
00280 
00282         QValueVector<int> *pkeyFieldsOrder;
00283 
00285         uint pkeyFieldsCount;
00286 
00288         QString statement;
00289 
00291         Relationship::List relations;
00292 
00308         QValueVector<int> tablesBoundToColumns;
00309         
00311         QAsciiDict<int> tablePositionsForAliases;
00312 
00314         QAsciiDict<int> columnPositionsForAliases;
00315 
00317         BaseExpr *whereExpr;
00318 
00319         QDict<QueryColumnInfo> columnInfosByNameExpanded;
00320 
00321         QDict<QueryColumnInfo> columnInfosByName; 
00322 
00325         bool regenerateExprAliases : 1;
00326 };
00327 }
00328 
00329 //=======================================
00330 
00331 OrderByColumn::OrderByColumn()
00332  : m_column(0)
00333  , m_pos(-1)
00334  , m_field(0)
00335  , m_ascending(true)
00336 {
00337 }
00338 
00339 OrderByColumn::OrderByColumn(QueryColumnInfo& column, bool ascending, int pos)
00340  : m_column(&column)
00341  , m_pos(pos)
00342  , m_field(0)
00343  , m_ascending(ascending)
00344 {
00345 }
00346 
00347 OrderByColumn::OrderByColumn(Field& field, bool ascending)
00348  : m_column(0)
00349  , m_pos(-1)
00350  , m_field(&field)
00351  , m_ascending(ascending)
00352 {
00353 }
00354 
00355 OrderByColumn::~OrderByColumn()
00356 {
00357 }
00358 
00359 QString OrderByColumn::debugString() const
00360 {
00361     QString orderString( m_ascending ? "ascending" : "descending" );
00362     if (m_column) {
00363         if (m_pos>-1)
00364             return QString("COLUMN_AT_POSITION_%1(%2, %3)")
00365                 .arg(m_pos+1).arg(m_column->debugString()).arg(orderString);
00366         else
00367             return QString("COLUMN(%1, %2)").arg(m_column->debugString()).arg(orderString);
00368     }
00369     return m_field ? QString("FIELD(%1, %2)").arg(m_field->debugString()).arg(orderString)
00370      : QString("NONE");
00371 }
00372 
00373 QString OrderByColumn::toSQLString(bool includeTableName) const
00374 {
00375     QString orderString( m_ascending ? "" : " DESC" );
00376     if (m_column) {
00377         if (m_pos>-1)
00378             return QString::number(m_pos+1) + orderString;
00379         else
00380             return ((includeTableName && m_column->alias.isEmpty()) 
00381                 ? m_column->field->table()->name()+"." : QString::null)
00382                 + QString(m_column->aliasOrName()) + orderString;
00383     }
00384     else {
00385         return 
00386             (includeTableName ? m_field->table()->name()+"." : QString::null)
00387             + (m_field ? m_field->name() : "??"/*error*/)  + orderString;
00388     }
00389 }
00390 
00391 //=======================================
00392 
00393 OrderByColumnList::OrderByColumnList()
00394  : OrderByColumnListBase()
00395 {
00396 }
00397 
00398 bool OrderByColumnList::appendFields(QuerySchema& querySchema,
00399     const QString& field1, bool ascending1, 
00400     const QString& field2, bool ascending2, 
00401     const QString& field3, bool ascending3, 
00402     const QString& field4, bool ascending4, 
00403     const QString& field5, bool ascending5)
00404 {
00405     uint numAdded = 0;
00406 #define ADD_COL(fieldName, ascending) \
00407     if (ok && !fieldName.isEmpty()) { \
00408         if (!appendField( querySchema, fieldName, ascending )) \
00409             ok = false; \
00410         else \
00411             numAdded++; \
00412     }
00413     bool ok = true;
00414     ADD_COL(field1, ascending1);
00415     ADD_COL(field2, ascending2);
00416     ADD_COL(field3, ascending3);
00417     ADD_COL(field4, ascending4);
00418     ADD_COL(field5, ascending5);
00419 #undef ADD_COL
00420     if (ok)
00421         return true;
00422     for (uint i=0; i<numAdded; i++)
00423         pop_back();
00424     return false;
00425 }
00426 
00427 OrderByColumnList::~OrderByColumnList()
00428 {
00429 }
00430 
00431 void OrderByColumnList::appendColumn(QueryColumnInfo& columnInfo, bool ascending)
00432 {
00433     appendColumn( OrderByColumn(columnInfo, ascending) );
00434 }
00435 
00436 bool OrderByColumnList::appendColumn(QuerySchema& querySchema, bool ascending, int pos)
00437 {
00438     QueryColumnInfo::Vector fieldsExpanded( querySchema.fieldsExpanded() );
00439     QueryColumnInfo* ci = (pos >= (int)fieldsExpanded.size()) ? 0 : fieldsExpanded[pos];
00440     if (!ci)
00441         return false;
00442     appendColumn( OrderByColumn(*ci, ascending, pos) );
00443     return true;
00444 }
00445 
00446 void OrderByColumnList::appendField(Field& field, bool ascending)
00447 {
00448     appendColumn( OrderByColumn(field, ascending) );
00449 }
00450 
00451 bool OrderByColumnList::appendField(QuerySchema& querySchema, 
00452     const QString& fieldName, bool ascending)
00453 {
00454     QueryColumnInfo *columnInfo = querySchema.columnInfo( fieldName );
00455     if (columnInfo) {
00456         appendColumn( OrderByColumn(*columnInfo, ascending) );
00457         return true;
00458     }
00459     Field *field = querySchema.findTableField(fieldName);
00460     if (field) {
00461         appendColumn( OrderByColumn(*field, ascending) );
00462         return true;
00463     }
00464     KexiDBWarn << "OrderByColumnList::addColumn(QuerySchema& querySchema, "
00465         "const QString& column, bool ascending): no such field \"" << fieldName << "\"" << endl;
00466     return false;
00467 }
00468         
00469 void OrderByColumnList::appendColumn(const OrderByColumn& column)
00470 {
00471     append( column );
00472 }
00473 
00474 QString OrderByColumnList::debugString() const
00475 {
00476     if (isEmpty())
00477         return "NONE";
00478     QString dbg;
00479     for (OrderByColumn::ListConstIterator it=constBegin(); it!=constEnd(); ++it) {
00480         if (!dbg.isEmpty())
00481             dbg += "\n";
00482         dbg += (*it).debugString();
00483     }
00484     return dbg;
00485 }
00486 
00487 QString OrderByColumnList::toSQLString(bool includeTableNames) const
00488 {
00489     QString string;
00490     for (OrderByColumn::ListConstIterator it=constBegin(); it!=constEnd(); ++it) {
00491         if (!string.isEmpty())
00492             string += ", ";
00493         string += (*it).toSQLString(includeTableNames);
00494     }
00495     return string;
00496 }
00497 
00498 //=======================================
00499 
00500 QuerySchema::QuerySchema()
00501     : FieldList(false)//fields are not owned by QuerySchema object
00502     , SchemaData(KexiDB::QueryObjectType)
00503     , d( new QuerySchemaPrivate(this) )
00504 {
00505     init();
00506 }
00507 
00508 QuerySchema::QuerySchema(TableSchema* tableSchema)
00509     : FieldList(false)
00510     , SchemaData(KexiDB::QueryObjectType)
00511     , d( new QuerySchemaPrivate(this) )
00512 {
00513     d->masterTable = tableSchema;
00514 //  assert(d->masterTable);
00515     init();
00516     if (!d->masterTable) {
00517         KexiDBWarn << "QuerySchema(TableSchema*): !d->masterTable" << endl;
00518         m_name = QString::null;
00519         return;
00520     }
00521     addTable(d->masterTable);
00522     //defaults:
00523     //inherit name from a table
00524     m_name = d->masterTable->name();
00525     //inherit caption from a table
00526     m_caption = d->masterTable->caption();
00527     
00528 //replaced by explicit field list: //add all fields of the table as asterisk:
00529 //replaced by explicit field list:  addField( new QueryAsterisk(this) );
00530 
00531     // add explicit field list to avoid problems (e.g. with fields added outside of Kexi):
00532     for (Field::ListIterator it( d->masterTable->fieldsIterator() ); it.current(); ++it) {
00533         addField( it.current() );
00534     }
00535 }
00536 
00537 QuerySchema::~QuerySchema()
00538 {
00539     delete d;
00540 }
00541 
00542 void QuerySchema::init()
00543 {
00544     m_type = KexiDB::QueryObjectType;
00545 //m_fields_by_name.setAutoDelete( true ); //because we're using QueryColumnInfoEntry objects
00546 }
00547 
00548 void QuerySchema::clear()
00549 {
00550     FieldList::clear();
00551     SchemaData::clear();
00552     d->clear();
00553 }
00554 
00555 FieldList& QuerySchema::insertField(uint position, Field *field, bool visible)
00556 {
00557     return insertField(position, field, -1/*don't bind*/, visible);
00558 }
00559 
00560 /*virtual*/
00561 FieldList& QuerySchema::insertField(uint position, Field *field)
00562 {
00563     return insertField( position, field, -1/*don't bind*/, true );
00564 }
00565 
00566 FieldList& QuerySchema::insertField(uint position, Field *field, 
00567     int bindToTable, bool visible)
00568 {
00569     if (!field) {
00570         KexiDBWarn << "QuerySchema::insertField(): !field" << endl;
00571         return *this;
00572     }
00573 
00574     if (position>m_fields.count()) {
00575         KexiDBWarn << "QuerySchema::insertField(): position (" << position << ") out of range" << endl;
00576         return *this;
00577     }
00578     if (!field->isQueryAsterisk() && !field->isExpression() && !field->table()) {
00579         KexiDBWarn << "QuerySchema::insertField(): WARNING: field '"<<field->name()
00580             <<"' must contain table information!" <<endl;
00581         return *this;
00582     }
00583     if (fieldCount()>=d->visibility.size()) {
00584         d->visibility.resize(d->visibility.size()*2);
00585         d->tablesBoundToColumns.resize(d->tablesBoundToColumns.size()*2);
00586     }
00587     d->clearCachedData();
00588     FieldList::insertField(position, field);
00589     if (field->isQueryAsterisk()) {
00590         d->asterisks.append(field);
00591         //if this is single-table asterisk,
00592         //add a table to list if doesn't exist there:
00593         if (field->table() && (d->tables.findRef(field->table())==-1))
00594             d->tables.append(field->table());
00595     }
00596     else if (field->table()) {
00597         //add a table to list if doesn't exist there:
00598         if (d->tables.findRef(field->table())==-1)
00599             d->tables.append(field->table());
00600     }
00601 //  //visible by default
00602 //  setFieldVisible(field, true);
00603 //  d->visibility.setBit(fieldCount()-1, visible);
00604     //update visibility
00605     //--move bits to make a place for a new one
00606     for (uint i=fieldCount()-1; i>position; i--)
00607         d->visibility.setBit(i, d->visibility.testBit(i-1));
00608     d->visibility.setBit(position, visible);
00609 
00610     //bind to table
00611     if (bindToTable < -1 && bindToTable>(int)d->tables.count()) {
00612         KexiDBWarn << "QuerySchema::insertField(): bindToTable (" << bindToTable 
00613             << ") out of range" << endl;
00614         bindToTable = -1;
00615     }
00616     //--move items to make a place for a new one
00617     for (uint i=fieldCount()-1; i>position; i--)
00618         d->tablesBoundToColumns[i] = d->tablesBoundToColumns[i-1];
00619     d->tablesBoundToColumns[ position ] = bindToTable;
00620     
00621     KexiDBDbg << "QuerySchema::insertField(): bound to table (" << bindToTable << "): " <<endl;
00622     if (bindToTable==-1)
00623         KexiDBDbg << " <NOT SPECIFIED>" << endl; 
00624     else
00625         KexiDBDbg << " name=" << d->tables.at(bindToTable)->name() 
00626             << " alias=" << tableAlias(bindToTable) <<  endl;
00627     QString s;
00628     for (uint i=0; i<fieldCount();i++)
00629         s+= (QString::number(d->tablesBoundToColumns[i]) + " ");
00630     KexiDBDbg << "tablesBoundToColumns == [" << s << "]" <<endl;
00631 
00632     if (field->isExpression())
00633         d->regenerateExprAliases = true;
00634 
00635     return *this;
00636 }
00637 
00638 int QuerySchema::tableBoundToColumn(uint columnPosition) const
00639 {
00640     if (columnPosition > d->tablesBoundToColumns.count()) {
00641         KexiDBWarn << "QuerySchema::tableBoundToColumn(): columnPosition (" << columnPosition
00642             << ") out of range" << endl;
00643         return -1;
00644     }
00645     return d->tablesBoundToColumns[columnPosition];
00646 }
00647 
00648 KexiDB::FieldList& QuerySchema::addField(KexiDB::Field* field, bool visible)
00649 {
00650     return insertField(m_fields.count(), field, visible);
00651 }
00652 
00653 KexiDB::FieldList& QuerySchema::addField(KexiDB::Field* field, int bindToTable, 
00654     bool visible)
00655 {
00656     return insertField(m_fields.count(), field, bindToTable, visible);
00657 }
00658 
00659 void QuerySchema::removeField(KexiDB::Field *field)
00660 {
00661     if (!field)
00662         return;
00663     d->clearCachedData();
00664     if (field->isQueryAsterisk()) {
00665         d->asterisks.remove(field); //this will destroy this asterisk
00666     }
00667 //TODO: should we also remove table for this field or asterisk?
00668     FieldList::removeField(field);
00669 }
00670 
00671 FieldList& QuerySchema::addExpression(BaseExpr* expr, bool visible)
00672 {
00673     return addField( new Field(this, expr), visible );
00674 }
00675 
00676 bool QuerySchema::isColumnVisible(uint position) const
00677 {
00678     return (position < fieldCount()) ? d->visibility.testBit(position) : false;
00679 }
00680 
00681 void QuerySchema::setColumnVisible(uint position, bool v)
00682 {
00683     if (position < fieldCount())
00684         d->visibility.setBit(position, v);
00685 }
00686 
00687 FieldList& QuerySchema::addAsterisk(QueryAsterisk *asterisk, bool visible)
00688 {
00689     if (!asterisk)
00690         return *this;
00691     //make unique name
00692     asterisk->m_name = (asterisk->table() ? asterisk->table()->name() + ".*" : "*") 
00693         + QString::number(asterisks()->count());
00694     return addField(asterisk, visible);
00695 }
00696 
00697 Connection* QuerySchema::connection() const
00698 {
00699     TableSchema *mt = masterTable();
00700     return mt ? mt->connection() : 0;
00701 }
00702 
00703 QString QuerySchema::debugString()
00704 {
00705     QString dbg;
00706     dbg.reserve(1024);
00707     //fields
00708     TableSchema *mt = masterTable();
00709     dbg = QString("QUERY ") + schemaDataDebugString() + "\n"
00710         + "-masterTable=" + (mt ? mt->name() :"<NULL>")
00711         + "\n-COLUMNS:\n"
00712         + ((fieldCount()>0) ? FieldList::debugString() : "<NONE>") + "\n"
00713         + "-FIELDS EXPANDED ";
00714 
00715     QString dbg1;
00716     uint fieldsExpandedCount = 0;
00717     if (fieldCount()>0) {
00718         QueryColumnInfo::Vector fe( fieldsExpanded() );
00719         fieldsExpandedCount = fe.size();
00720         for ( uint i=0; i < fieldsExpandedCount; i++ ) {
00721             QueryColumnInfo *ci = fe[i];
00722             if (!dbg1.isEmpty())
00723                 dbg1 += ",\n";
00724             dbg1 += ci->debugString();
00725         }
00726         dbg1 += "\n";
00727     }
00728     else {
00729         dbg1 = "<NONE>\n";
00730     }
00731     dbg1.prepend( QString("(%1):\n").arg(fieldsExpandedCount) );
00732     dbg += dbg1;
00733 
00734     //it's safer to delete fieldsExpanded for now 
00735     // (debugString() could be called before all fields are added)
00736 //causes a crash    d->clearCachedData();
00737 
00738     //bindings
00739     QString dbg2;
00740     dbg2.reserve(512);
00741     for (uint i = 0; i<fieldCount(); i++) {
00742         int tablePos = tableBoundToColumn(i);
00743         if (tablePos>=0) {
00744             QCString tAlias = tableAlias(tablePos);
00745             if (!tAlias.isEmpty()) {
00746                 dbg2 += (QString::fromLatin1(" field \"") + FieldList::field(i)->name() 
00747                     + "\" uses alias \"" + QString(tAlias) + "\" of table \""
00748                     + d->tables.at(tablePos)->name() + "\"\n");
00749             }
00750         }
00751     }
00752     if (!dbg2.isEmpty()) {
00753         dbg += "\n-BINDINGS:\n";
00754         dbg += dbg2;
00755     }
00756     
00757     //tables    
00758     TableSchema *table;
00759     QString table_names;
00760     table_names.reserve(512);
00761     for ( table = d->tables.first(); table; table = d->tables.next() ) {
00762         if (!table_names.isEmpty())
00763             table_names += ", ";
00764         table_names += (QString("'") + table->name() + "'");
00765     }
00766     if (d->tables.isEmpty())
00767         table_names = "<NONE>";
00768     dbg += (QString("-TABLES:\n") + table_names);
00769     QString aliases;
00770     if (!d->hasColumnAliases())
00771         aliases = "<NONE>\n";
00772     else {
00773         Field::ListIterator it( m_fields );
00774         for (int i=0; it.current(); ++it, i++) {
00775             QCString *alias = d->columnAlias(i);
00776             if (alias)
00777                 aliases += (QString("field #%1: ").arg(i) 
00778                     + (it.current()->name().isEmpty() ? "<noname>" : it.current()->name())
00779                     + " -> " + (const char*)*alias + "\n");
00780         }
00781     }
00782     //aliases
00783     dbg += QString("\n-COLUMN ALIASES:\n" + aliases);
00784     if (d->tableAliases.isEmpty())
00785         aliases = "<NONE>";
00786     else {
00787         aliases = "";
00788         TableSchema::ListIterator t_it(d->tables);
00789         for (int i=0; t_it.current(); ++t_it, i++) {
00790             QCString *alias = d->tableAliases[i];
00791             if (alias)
00792                 aliases += (QString("table #%1: ").arg(i) 
00793                     + (t_it.current()->name().isEmpty() ? "<noname>" : t_it.current()->name())
00794                     + " -> " + (const char*)*alias + "\n");
00795         }
00796     }
00797     dbg += QString("-TABLE ALIASES:\n" + aliases);
00798     QString where = d->whereExpr ? d->whereExpr->debugString() : QString::null;
00799     if (!where.isEmpty())
00800         dbg += (QString("\n-WHERE EXPRESSION:\n") + where);
00801     if (!orderByColumnList().isEmpty())
00802         dbg += (QString("\n-ORDER BY (%1):\n").arg(orderByColumnList().count()) 
00803             + orderByColumnList().debugString());
00804     return dbg;
00805 }
00806 
00807 TableSchema* QuerySchema::masterTable() const
00808 {
00809     if (d->masterTable)
00810         return d->masterTable;
00811     if (d->tables.isEmpty())
00812         return 0;
00813 
00814     //try to find master table if there's only one table (with possible aliasses)
00815     int num = 0;
00816     QString tableNameLower;
00817     for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00818         if (!tableNameLower.isEmpty() && it.current()->name().lower()!=tableNameLower) {
00819             //two or more different tables
00820             return 0;
00821         }
00822         tableNameLower = tableAlias(num);
00823     }
00824     return d->tables.first();
00825 }
00826 
00827 void QuerySchema::setMasterTable(TableSchema *table)
00828 { 
00829     if (table)
00830         d->masterTable=table; 
00831 }
00832 
00833 TableSchema::List* QuerySchema::tables() const
00834 {
00835     return &d->tables;
00836 }
00837 
00838 void QuerySchema::addTable(TableSchema *table, const QCString& alias)
00839 {
00840     KexiDBDbg << "QuerySchema::addTable() " << (void *)table 
00841         << " alias=" << alias << endl;
00842     if (!table)
00843         return;
00844     
00845     //only append table if:
00846     //-it has alias
00847     //-it has no alias but there is no such table on the list
00848     if (alias.isEmpty() && d->tables.findRef(table)!=-1) {
00849         const QString& tableNameLower = table->name().lower();
00850         const QString& aliasLower = QString(alias.lower());
00851         int num = 0;
00852         for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00853             if (it.current()->name().lower()==tableNameLower) {
00854                 const QString& tAlias = tableAlias(num);
00855                 if (tAlias == aliasLower) {
00856                     KexiDBWarn << "QuerySchema::addTable(): table with \"" 
00857                         << tAlias << "\" alias already added!" << endl;
00858                     return;
00859                 }
00860             }
00861         }
00862     }
00863     
00864     d->tables.append(table);
00865     
00866     if (!alias.isEmpty())
00867         setTableAlias(d->tables.count()-1, alias);
00868 }
00869 
00870 void QuerySchema::removeTable(TableSchema *table)
00871 {
00872     if (!table)
00873         return;
00874     if (d->masterTable == table)
00875         d->masterTable = 0;
00876     d->tables.remove(table);
00877     //todo: remove fields!
00878 }
00879 
00880 TableSchema* QuerySchema::table(const QString& tableName) const
00881 {
00882 //TODO: maybe use tables_byname?
00883     for (TableSchema::ListIterator it(d->tables); it.current(); ++it) {
00884         if (it.current()->name().lower()==tableName.lower())
00885             return it.current();
00886     }
00887     return 0;
00888 }
00889 
00890 bool QuerySchema::contains(TableSchema *table) const
00891 {
00892     return d->tables.findRef(table)!=-1;
00893 }
00894 
00895 Field* QuerySchema::findTableField(const QString &tableOrTableAndFieldName) const
00896 {
00897     QString tableName, fieldName;
00898     if (!KexiDB::splitToTableAndFieldParts(tableOrTableAndFieldName, 
00899         tableName, fieldName, KexiDB::SetFieldNameIfNoTableName)) {
00900         return 0;
00901     }
00902     if (tableName.isEmpty()) {
00903         for (TableSchema::ListIterator it(d->tables); it.current(); ++it) {
00904             if (it.current()->field(fieldName))
00905                 return it.current()->field(fieldName);
00906         }
00907         return 0;
00908     }
00909     TableSchema *tableSchema = table(tableName);
00910     if (!tableSchema)
00911         return 0;
00912     return tableSchema->field(fieldName);
00913 }
00914 
00915 QCString QuerySchema::columnAlias(uint position) const
00916 {
00917     QCString *a = d->columnAlias(position);
00918     return a ? *a : QCString();
00919 }
00920 
00921 bool QuerySchema::hasColumnAlias(uint position) const
00922 {
00923     return d->columnAlias(position)!=0;
00924 }
00925 
00926 void QuerySchema::setColumnAlias(uint position, const QCString& alias)
00927 {
00928     if (position >= m_fields.count()) {
00929         KexiDBWarn << "QuerySchema::setColumnAlias(): position ("  << position 
00930             << ") out of range!" << endl;
00931         return;
00932     }
00933     QCString fixedAlias = alias.stripWhiteSpace();
00934     Field *f = FieldList::field(position);
00935     if (f->captionOrName().isEmpty() && fixedAlias.isEmpty()) {
00936         KexiDBWarn << "QuerySchema::setColumnAlias(): position ("  << position 
00937             << ") could not remove alias when no name is specified for expression column!" << endl;
00938         return;
00939     }
00940     d->setColumnAlias(position, fixedAlias);
00941 }
00942 
00943 QCString QuerySchema::tableAlias(uint position) const
00944 {
00945     QCString *a = d->tableAliases[position];
00946     return a ? *a : QCString();
00947 }
00948 
00949 int QuerySchema::tablePositionForAlias(const QCString& name) const
00950 {
00951     int *num = d->tablePositionsForAliases[name];
00952     if (!num)
00953         return -1;
00954     return *num;
00955 }
00956 
00957 int QuerySchema::tablePosition(const QString& tableName) const
00958 {
00959     int num = 0;
00960     for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00961         if (it.current()->name().lower()==tableName.lower())
00962             return num;
00963     }
00964     return -1;
00965 }
00966 
00967 QValueList<int> QuerySchema::tablePositions(const QString& tableName) const
00968 {
00969     int num = 0;
00970     QValueList<int> result;
00971     const QString& tableNameLower = tableName.lower();
00972     for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00973         if (it.current()->name().lower()==tableNameLower) {
00974             result += num;
00975         }
00976     }
00977     return result;
00978 }
00979 
00980 bool QuerySchema::hasTableAlias(uint position) const
00981 {
00982     return d->tableAliases[position]!=0;
00983 }
00984 
00985 int QuerySchema::columnPositionForAlias(const QCString& name) const
00986 {
00987     int *num = d->columnPositionsForAliases[name];
00988     if (!num)
00989         return -1;
00990     return *num;
00991 }
00992 
00993 void QuerySchema::setTableAlias(uint position, const QCString& alias)
00994 {
00995     if (position >= d->tables.count()) {
00996         KexiDBWarn << "QuerySchema::setTableAlias(): position ("  << position 
00997             << ") out of range!" << endl;
00998         return;
00999     }
01000     QCString fixedAlias = alias.stripWhiteSpace();
01001     if (fixedAlias.isEmpty()) {
01002         QCString *oldAlias = d->tableAliases.take(position);
01003         if (oldAlias) {
01004             d->tablePositionsForAliases.remove(*oldAlias);
01005             delete oldAlias;
01006         }
01007 //          d->maxIndexWithTableAlias = -1;
01008     }
01009     else {
01010         d->tableAliases.replace(position, new QCString(fixedAlias));
01011         d->tablePositionsForAliases.replace(fixedAlias, new int(position));
01012 //      d->maxIndexWithTableAlias = QMAX( d->maxIndexWithTableAlias, (int)index );
01013     }
01014 }
01015 
01016 Relationship::List* QuerySchema::relationships() const
01017 {
01018     return &d->relations;
01019 }
01020 
01021 Field::List* QuerySchema::asterisks() const
01022 {
01023     return &d->asterisks;
01024 }
01025         
01026 QString QuerySchema::statement() const
01027 {
01028     return d->statement;
01029 }
01030 
01031 void QuerySchema::setStatement(const QString &s)
01032 {
01033     d->statement = s;
01034 }
01035 
01036 Field* QuerySchema::field(const QString& identifier, bool expanded)
01037 {
01038     QueryColumnInfo *ci = columnInfo(identifier, expanded);
01039     return ci ? ci->field : 0;
01040 }
01041 
01042 QueryColumnInfo* QuerySchema::columnInfo(const QString& identifier, bool expanded)
01043 {
01044     computeFieldsExpanded();
01045     return expanded ? d->columnInfosByNameExpanded[identifier] : d->columnInfosByName[identifier];
01046 }
01047 
01048 QueryColumnInfo::Vector QuerySchema::fieldsExpanded(FieldsExpandedOptions options)
01049 {
01050     computeFieldsExpanded();
01051     if (options == WithInternalFields || options == WithInternalFieldsAndRowID) {
01052         //a ref to a proper pointer (as we cache the vector for two cases)
01053         QueryColumnInfo::Vector*& tmpFieldsExpandedWithInternal = 
01054             (options == WithInternalFields) ? d->fieldsExpandedWithInternal : d->fieldsExpandedWithInternalAndRowID;
01055         //special case
01056         if (!tmpFieldsExpandedWithInternal) {
01057             //glue expanded and internal fields and cache it
01058             const uint size = d->fieldsExpanded->count()
01059                 + (d->internalFields ? d->internalFields->count() : 0)
01060                 + ((options == WithInternalFieldsAndRowID) ? 1 : 0) /*ROWID*/;
01061             tmpFieldsExpandedWithInternal = new QueryColumnInfo::Vector( size );
01062             const uint fieldsExpandedVectorSize = d->fieldsExpanded->size();
01063             for (uint i=0; i<fieldsExpandedVectorSize; i++)
01064                 tmpFieldsExpandedWithInternal->insert(i, d->fieldsExpanded->at(i));
01065             const uint internalFieldsCount = d->internalFields ? d->internalFields->size() : 0;
01066             if (internalFieldsCount > 0) {
01067                 for (uint i=0; i < internalFieldsCount; i++)
01068                     tmpFieldsExpandedWithInternal->insert(
01069                         fieldsExpandedVectorSize + i, d->internalFields->at(i));
01070             }
01071             if (options == WithInternalFieldsAndRowID) {
01072                 if (!d->fakeRowIDField) {
01073                     d->fakeRowIDField = new Field("rowID", Field::BigInteger);
01074                     d->fakeRowIDCol = new QueryColumnInfo(d->fakeRowIDField, QCString(), true);
01075                 }
01076                 tmpFieldsExpandedWithInternal->insert( 
01077                     fieldsExpandedVectorSize + internalFieldsCount, d->fakeRowIDCol );
01078             }
01079         }
01080         return *tmpFieldsExpandedWithInternal;
01081     }
01082 
01083     if (options == Default)
01084         return *d->fieldsExpanded;
01085 
01086     //options == Unique:
01087     QDict<char> columnsAlreadyFound;
01088     QueryColumnInfo::Vector result( d->fieldsExpanded->count() ); //initial size is set
01089 //  QMapConstIterator<QueryColumnInfo*, bool> columnsAlreadyFoundIt;
01090     //compute unique list
01091     uint uniqueListCount = 0;
01092     for (uint i=0; i<d->fieldsExpanded->count(); i++) {
01093         QueryColumnInfo *ci = (*d->fieldsExpanded)[i];
01094 //      columnsAlreadyFoundIt = columnsAlreadyFound.find(ci);
01095 //      uint foundColumnIndex = -1;
01096         if (!columnsAlreadyFound[ci->aliasOrName()]) {// columnsAlreadyFoundIt==columnsAlreadyFound.constEnd())
01097             columnsAlreadyFound.insert(ci->aliasOrName(), (char*)1);
01098             result.insert(uniqueListCount++, ci);
01099         }
01100     }
01101     result.resize(uniqueListCount); //update result size
01102     return result;
01103 }
01104 
01105 QueryColumnInfo::Vector QuerySchema::internalFields()
01106 {
01107     computeFieldsExpanded();
01108     return d->internalFields ? *d->internalFields : QueryColumnInfo::Vector();
01109 }
01110 
01111 QueryColumnInfo* QuerySchema::expandedOrInternalField(uint index)
01112 {
01113     QueryColumnInfo::Vector vector = fieldsExpanded(WithInternalFields);
01114     return (index < vector.size()) ? vector[index] : 0;
01115 }
01116 
01117 void QuerySchema::computeFieldsExpanded()
01118 {
01119     if (d->fieldsExpanded)
01120         return;
01121 
01122     if (!d->columnsOrder) {
01123         d->columnsOrder = new QMap<QueryColumnInfo*,int>();
01124         d->columnsOrderWithoutAsterisks = new QMap<QueryColumnInfo*,int>();
01125     }
01126     else {
01127         d->columnsOrder->clear();
01128         d->columnsOrderWithoutAsterisks->clear();
01129     }
01130 
01131     //collect all fields in a list (not a vector yet, because we do not know its size)
01132     QueryColumnInfo::List list; //temporary
01133     QueryColumnInfo::List lookup_list; //temporary, for collecting additional fields related to lookup fields
01134     QMap<QueryColumnInfo*, bool> columnInfosOutsideAsterisks; //helper for filling d->columnInfosByName
01135     uint i = 0;
01136     uint fieldPosition = 0;
01137     Field *f;
01138     for (Field::ListIterator it = fieldsIterator(); (f = it.current()); ++it, fieldPosition++) {
01139         if (f->isQueryAsterisk()) {
01140             if (static_cast<QueryAsterisk*>(f)->isSingleTableAsterisk()) {
01141                 Field::List *ast_fields = static_cast<QueryAsterisk*>(f)->table()->fields();
01142                 for (Field *ast_f = ast_fields->first(); ast_f; ast_f=ast_fields->next()) {
01143 //                  d->detailedVisibility += isFieldVisible(fieldPosition);
01144                     QueryColumnInfo *ci = new QueryColumnInfo(ast_f, QCString()/*no field for asterisk!*/,
01145                         isColumnVisible(fieldPosition));
01146                     list.append( ci );
01147                     KexiDBDbg << "QuerySchema::computeFieldsExpanded(): caching (unexpanded) columns order: "
01148                         << ci->debugString() << " at position " << fieldPosition << endl;
01149                     d->columnsOrder->insert(ci, fieldPosition);
01150 //                  list.append(ast_f);
01151                 }
01152             }
01153             else {//all-tables asterisk: iterate through table list
01154                 for (TableSchema *table = d->tables.first(); table; table = d->tables.next()) {
01155                     //add all fields from this table
01156                     Field::List *tab_fields = table->fields();
01157                     for (Field *tab_f = tab_fields->first(); tab_f; tab_f = tab_fields->next()) {
01159 //                      d->detailedVisibility += isFieldVisible(fieldPosition);
01160 //                      list.append(tab_f);
01161                         QueryColumnInfo *ci = new QueryColumnInfo(tab_f, QCString()/*no field for asterisk!*/,
01162                             isColumnVisible(fieldPosition));
01163                         list.append( ci );
01164                         KexiDBDbg << "QuerySchema::computeFieldsExpanded(): caching (unexpanded) columns order: "
01165                             << ci->debugString() << " at position " << fieldPosition << endl;
01166                         d->columnsOrder->insert(ci, fieldPosition);
01167                     }
01168                 }
01169             }
01170         }
01171         else {
01172             //a single field
01173 //          d->detailedVisibility += isFieldVisible(fieldPosition);
01174             QueryColumnInfo *ci = new QueryColumnInfo(f, columnAlias(fieldPosition), isColumnVisible(fieldPosition));
01175             list.append( ci );
01176             columnInfosOutsideAsterisks.insert( ci, true );
01177             KexiDBDbg << "QuerySchema::computeFieldsExpanded(): caching (unexpanded) column's order: "
01178                 << ci->debugString() << " at position " << fieldPosition << endl;
01179             d->columnsOrder->insert(ci, fieldPosition);
01180             d->columnsOrderWithoutAsterisks->insert(ci, fieldPosition);
01181 
01182             //handle lookup field schema
01183             LookupFieldSchema *lookupFieldSchema = f->table() ? f->table()->lookupFieldSchema( *f ) : 0;
01184             if (lookupFieldSchema) {
01185                 // Lookup field schema found:
01186                 // Now we also need to fetch "visible" value from the lookup table, not only the value of binding.
01187                 // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken)
01188                 // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField"
01189                 LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource();
01190                 if (rowSource.type()==LookupFieldSchema::RowSource::Table) {
01191                     TableSchema *lookupTable = connection()->tableSchema( rowSource.name() );
01192                     Field *visibleField = 0;
01193                     Field *boundField = 0;
01194                     if (lookupTable && lookupFieldSchema->boundColumn()>=0 
01195                         && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
01196                         && (visibleField = lookupTable->field( lookupFieldSchema->visibleColumn()))
01197                         && (boundField = lookupTable->field( lookupFieldSchema->boundColumn() )))
01198                     {
01199                         lookup_list.append( new QueryColumnInfo(visibleField, QCString(), true/*visible*/, ci/*foreign*/) );
01200 /*
01201                         //add visibleField to the list of SELECTed fields if it is not yes present there
01202                         if (!findTableField( visibleField->table()->name()+"."+visibleField->name() )) {
01203                             if (!table( visibleField->table()->name() )) {
01204                             }
01205                             if (!sql.isEmpty())
01206                                 sql += QString::fromLatin1(", ");
01207                             sql += (escapeIdentifier(visibleField->table()->name(), drvEscaping) + "."
01208                                 + escapeIdentifier(visibleField->name(), drvEscaping));
01209                         }*/
01210                     }
01211                 }
01212             }
01213         }
01214     }
01215     //prepare clean vector for expanded list, and a map for order information
01216     if (!d->fieldsExpanded) {
01217         d->fieldsExpanded = new QueryColumnInfo::Vector( list.count() );// Field::Vector( list.count() );
01218         d->fieldsExpanded->setAutoDelete(true);
01219         d->columnsOrderExpanded = new QMap<QueryColumnInfo*,int>();
01220     }
01221     else {//for future:
01222         d->fieldsExpanded->clear();
01223         d->fieldsExpanded->resize( list.count() );
01224         d->columnsOrderExpanded->clear();
01225     }
01226 
01227     /*fill (based on prepared 'list' and 'lookup_list'):
01228      -the vector
01229      -the map
01230      -"fields by name" dictionary
01231     */
01232     d->columnInfosByName.clear();
01233     d->columnInfosByNameExpanded.clear();
01234     i=0;
01235     QueryColumnInfo *ci;
01236     for (QueryColumnInfo::ListIterator it(list); (ci = it.current()); ++it, i++) 
01237     {
01238         d->fieldsExpanded->insert(i, ci);
01239         d->columnsOrderExpanded->insert(ci, i);
01240         //remember field by name/alias/table.name if there's no such string yet in d->columnInfosByNameExpanded
01241         if (!ci->alias.isEmpty()) {
01242             //store alias and table.alias
01243             if (!d->columnInfosByNameExpanded[ ci->alias ])
01244                 d->columnInfosByNameExpanded.insert( ci->alias, ci );
01245             QString tableAndAlias( ci->alias );
01246             if (ci->field->table())
01247                 tableAndAlias.prepend(ci->field->table()->name() + ".");
01248             if (!d->columnInfosByNameExpanded[ tableAndAlias ])
01249                 d->columnInfosByNameExpanded.insert( tableAndAlias, ci );
01250             //the same for "unexpanded" list
01251             if (columnInfosOutsideAsterisks.contains(ci)) {
01252                 if (!d->columnInfosByName[ ci->alias ])
01253                     d->columnInfosByName.insert( ci->alias, ci );
01254                 if (!d->columnInfosByName[ tableAndAlias ])
01255                     d->columnInfosByName.insert( tableAndAlias, ci );
01256             }
01257         }
01258         else {
01259             //no alias: store name and table.name
01260             if (!d->columnInfosByNameExpanded[ ci->field->name() ])
01261                 d->columnInfosByNameExpanded.insert( ci->field->name(), ci );
01262             QString tableAndName( ci->field->name() );
01263             if (ci->field->table())
01264                 tableAndName.prepend(ci->field->table()->name() + ".");
01265             if (!d->columnInfosByNameExpanded[ tableAndName ])
01266                 d->columnInfosByNameExpanded.insert( tableAndName, ci );
01267             //the same for "unexpanded" list
01268             if (columnInfosOutsideAsterisks.contains(ci)) {
01269                 if (!d->columnInfosByName[ ci->field->name() ])
01270                     d->columnInfosByName.insert( ci->field->name(), ci );
01271                 if (!d->columnInfosByName[ tableAndName ])
01272                     d->columnInfosByName.insert( tableAndName, ci );
01273             }
01274         }
01275     }
01276 
01277     //remove duplicates for lookup fields
01278     QDict<uint> lookup_dict; //used to fight duplicates and to update QueryColumnInfo::indexForVisibleLookupValue()
01279                              // (a mapping from table.name string to uint* lookupFieldIndex
01280     lookup_dict.setAutoDelete(true);
01281 #define LOOKUP_COLUMN_KEY(foreignField, field) ( field->table()->name() + "." + field->name() \
01282             + "_" + foreignField->table()->name() + "." + foreignField->name() )
01283     i=0;
01284     for (QueryColumnInfo::ListIterator it(lookup_list); (ci = it.current());)
01285     {
01286         QString key( LOOKUP_COLUMN_KEY(ci->foreignColumn()->field, ci->field) );
01287         if ( /* not needed   columnInfo( tableAndFieldName ) || */
01288             lookup_dict[ key ]) {
01289             // this table.field is already fetched by this query
01290             ++it;
01291             lookup_list.removeRef( ci );
01292         }
01293         else {
01294             lookup_dict.replace( key, new uint( i ) );
01295             ++it;
01296             i++;
01297         }
01298     }
01299 
01300     //create internal expanded list with lookup fields
01301     if (d->internalFields) {
01302         d->internalFields->clear();
01303         d->internalFields->resize( lookup_list.count() );
01304     }
01305     delete d->fieldsExpandedWithInternal; //clear cache
01306     delete d->fieldsExpandedWithInternalAndRowID; //clear cache
01307     d->fieldsExpandedWithInternal = 0;
01308     d->fieldsExpandedWithInternalAndRowID = 0;
01309     i=0;
01310     if (!lookup_list.isEmpty() && !d->internalFields) {//create on demand
01311         d->internalFields = new QueryColumnInfo::Vector( lookup_list.count() );
01312         d->internalFields->setAutoDelete(true);
01313     }
01314     for (QueryColumnInfo::ListIterator it(lookup_list); it.current();i++, ++it)
01315     {
01316         //add it to the internal list
01317         d->internalFields->insert(i, it.current());
01318         d->columnsOrderExpanded->insert(it.current(), list.count()+i);
01319     }
01320 
01321     //update QueryColumnInfo::indexForVisibleLookupValue() cache for columns
01322     for (i=0; i < d->fieldsExpanded->size(); i++) {
01323         QueryColumnInfo* ci = d->fieldsExpanded->at(i);
01325         LookupFieldSchema *lookupFieldSchema 
01326             = ci->field->table() ? ci->field->table()->lookupFieldSchema( *ci->field ) : 0;
01327         if (lookupFieldSchema) {
01328             LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource();
01329             TableSchema *lookupTable = connection()->tableSchema( rowSource.name() );
01330             Field *visibleField = 0;
01331             if (lookupTable && lookupFieldSchema->boundColumn()>=0 
01332                 && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount()
01333                 && (visibleField = lookupTable->field( lookupFieldSchema->visibleColumn())))
01334             {
01335                 QString key( LOOKUP_COLUMN_KEY(ci->field, visibleField) );//visibleTableAndFieldName( visibleField->table()->name()+"."+visibleField->name() );
01336                 uint *index = lookup_dict[ key ];
01337                 if (index)
01338                     ci->setIndexForVisibleLookupValue( d->fieldsExpanded->size() + *index );
01339             }
01340         }
01341     }
01342 }
01343 
01344 QMap<QueryColumnInfo*,int> QuerySchema::columnsOrder(ColumnsOrderOptions options)
01345 {
01346     if (!d->columnsOrder)
01347         computeFieldsExpanded();
01348     if (options == UnexpandedList)
01349         return *d->columnsOrder;
01350     else if (options == UnexpandedListWithoutAsterisks)
01351         return *d->columnsOrderWithoutAsterisks;
01352     return *d->columnsOrderExpanded;
01353 }
01354 
01355 QValueVector<int> QuerySchema::pkeyFieldsOrder()
01356 {
01357     if (d->pkeyFieldsOrder)
01358         return *d->pkeyFieldsOrder;
01359 
01360     TableSchema *tbl = masterTable();
01361     if (!tbl || !tbl->primaryKey())
01362         return QValueVector<int>();
01363 
01364     //get order of PKEY fields (e.g. for rows updating or inserting )
01365     IndexSchema *pkey = tbl->primaryKey();
01366     d->pkeyFieldsOrder = new QValueVector<int>( pkey->fieldCount(), -1 );
01367 
01368     const uint fCount = fieldsExpanded().count();
01369     d->pkeyFieldsCount = 0;
01370     for (uint i = 0; i<fCount; i++) {
01371         QueryColumnInfo *fi = d->fieldsExpanded->at(i);
01372         const int fieldIndex = fi->field->table()==tbl ? pkey->indexOf(fi->field) : -1;
01373         if (fieldIndex!=-1/* field found in PK */ 
01374             && d->pkeyFieldsOrder->at(fieldIndex)==-1 /* first time */)
01375         {
01376             KexiDBDbg << "QuerySchema::pkeyFieldsOrder(): FIELD " << fi->field->name() 
01377                 << " IS IN PKEY AT POSITION #" << fieldIndex << endl;
01378 //          (*d->pkeyFieldsOrder)[j]=i;
01379             (*d->pkeyFieldsOrder)[fieldIndex]=i;
01380             d->pkeyFieldsCount++;
01381 //          j++;
01382         }
01383     }
01384     KexiDBDbg << "QuerySchema::pkeyFieldsOrder(): " << d->pkeyFieldsCount
01385         << " OUT OF " << pkey->fieldCount() << " PKEY'S FIELDS FOUND IN QUERY " << name() << endl;
01386     return *d->pkeyFieldsOrder;
01387 }
01388 
01389 uint QuerySchema::pkeyFieldsCount()
01390 {
01391     (void)pkeyFieldsOrder(); /* rebuild information */
01392     return d->pkeyFieldsCount;
01393 }
01394 
01395 Relationship* QuerySchema::addRelationship( Field *field1, Field *field2 )
01396 {
01397 //@todo: find existing global db relationships
01398     Relationship *r = new Relationship(this, field1, field2);
01399     if (r->isEmpty()) {
01400         delete r;
01401         return 0;
01402     }
01403 
01404     d->relations.append( r );
01405     return r;
01406 }
01407 
01408 QueryColumnInfo::List* QuerySchema::autoIncrementFields()
01409 {
01410     if (!d->autoincFields) {
01411         d->autoincFields = new QueryColumnInfo::List();
01412     }
01413     TableSchema *mt = masterTable();
01414     if (!mt) {
01415         KexiDBWarn << "QuerySchema::autoIncrementFields(): no master table!" << endl;
01416         return d->autoincFields;
01417     }
01418     if (d->autoincFields->isEmpty()) {//no cache
01419         QueryColumnInfo::Vector fexp =