kexi

queryschema.cpp

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