kexi

queryschema.cpp

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