kexi

queryschema.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2005 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 <kexidb/utils.h>
00026 
00027 #include <assert.h>
00028 
00029 #include <qvaluelist.h>
00030 #include <qasciidict.h>
00031 #include <qptrdict.h>
00032 #include <qintdict.h>
00033 #include <qbitarray.h>
00034 
00035 #include <kdebug.h>
00036 #include <klocale.h>
00037 
00038 using namespace KexiDB;
00039 
00040 //=======================================
00041 namespace KexiDB {
00043 class QuerySchemaPrivate
00044 {
00045     public:
00046         QuerySchemaPrivate(QuerySchema* q)
00047          : query(q)
00048          , masterTable(0)
00049          , maxIndexWithAlias(-1)
00050          , visibility(64)
00051          , fieldsExpanded(0)
00052          , orderByColumnList(0)
00053          , autoincFields(0)
00054          , fieldsOrder(0)
00055          , pkeyFieldsOrder(0)
00056          , pkeyFieldsCount(0)
00057          , tablesBoundToColumns(64, -1)
00058          , tablePositionsForAliases(67, false)
00059          , columnPositionsForAliases(67, false)
00060          , whereExpr(0)
00061          , regenerateExprAliases(false)
00062         {
00063             columnAliases.setAutoDelete(true);
00064             tableAliases.setAutoDelete(true);
00065             asterisks.setAutoDelete(true);
00066             relations.setAutoDelete(true);
00067             tablePositionsForAliases.setAutoDelete(true);
00068             columnPositionsForAliases.setAutoDelete(true);
00069             visibility.fill(false);
00070         }
00071         ~QuerySchemaPrivate()
00072         {
00073             delete fieldsExpanded;
00074             delete orderByColumnList;
00075             delete autoincFields;
00076             delete fieldsOrder;
00077             delete pkeyFieldsOrder;
00078             delete whereExpr;
00079         }
00080 
00081         void clear()
00082         {
00083             columnAliases.clear();
00084             tableAliases.clear();
00085             asterisks.clear();
00086             relations.clear();
00087             masterTable = 0;
00088             tables.clear();
00089             clearCachedData();
00090             delete pkeyFieldsOrder;
00091             pkeyFieldsOrder=0;
00092             visibility.fill(false);
00093             tablesBoundToColumns = QValueVector<int>(64,-1);
00094             tablePositionsForAliases.clear();
00095             columnPositionsForAliases.clear();
00096         }
00097 
00098         void clearCachedData()
00099         {
00100             if (fieldsExpanded) {
00101                 delete fieldsExpanded;
00102                 fieldsExpanded = 0;
00103                 delete fieldsOrder;
00104                 fieldsOrder = 0;
00105                 delete autoincFields;
00106                 autoincFields = 0;
00107                 autoIncrementSQLFieldsList = QString::null;
00108             }
00109         }
00110 
00111         void setColumnAliasInternal(uint position, const QCString& alias)
00112         {
00113             columnAliases.replace(position, new QCString(alias));
00114             columnPositionsForAliases.replace(alias, new int(position));
00115             maxIndexWithAlias = QMAX( maxIndexWithAlias, (int)position );
00116         }
00117 
00118         void setColumnAlias(uint position, const QCString& alias)
00119         {
00120             QCString *oldAlias = columnAliases.take(position);
00121             if (oldAlias) {
00122                 tablePositionsForAliases.remove(*oldAlias);
00123                 delete oldAlias;
00124             }
00125             if (alias.isEmpty()) {
00126                 maxIndexWithAlias = -1;
00127             }
00128             else {
00129                 setColumnAliasInternal(position, alias);
00130             }
00131         }
00132 
00133         bool hasColumnAliases()
00134         {
00135             tryRegenerateExprAliases();
00136             return !columnAliases.isEmpty();
00137         }
00138 
00139         QCString* columnAlias(uint position)
00140         {
00141             tryRegenerateExprAliases();
00142             return columnAliases[position];
00143         }
00144 
00145         QuerySchema *query;
00146 
00150         TableSchema *masterTable;
00151         
00153         TableSchema::List tables;
00154 
00155     protected:
00156         void tryRegenerateExprAliases()
00157         {
00158             if (!regenerateExprAliases)
00159                 return;
00160             //regenerate missing aliases for experessions
00161             Field *f;
00162             uint p=0;
00163             uint colNum=0; //used to generate a name
00164             QCString columnAlias;
00165             for (Field::ListIterator it(query->fieldsIterator()); (f = it.current()); ++it, p++) {
00166                 if (f->isExpression() && !columnAliases[p]) {
00167                     //missing
00168                     for (;;) { //find 1st unused
00169                         colNum++;
00170                         columnAlias = (i18n("short for 'expression' word (only latin letters, please)", "expr") 
00171                             + QString::number(colNum)).latin1();
00172                         if (!tablePositionsForAliases[columnAlias])
00173                             break;
00174                     }
00175                     setColumnAliasInternal(p, columnAlias);
00176                 }
00177             }
00178             regenerateExprAliases = false;
00179         }
00180 
00182         QIntDict<QCString> columnAliases;
00183 
00184     public:
00186         QIntDict<QCString> tableAliases;
00187         
00189         int maxIndexWithAlias;
00190 
00192         int maxIndexWithTableAlias;
00193         
00195         QBitArray visibility;
00196 
00198         Field::List asterisks;
00199 
00201 //      Field::Vector *fieldsExpanded;
00202         QueryColumnInfo::Vector *fieldsExpanded;
00203 
00205         QueryColumnInfo::Vector *orderByColumnList;
00206 
00208         QueryColumnInfo::List *autoincFields;
00209         
00211         QString autoIncrementSQLFieldsList;
00212         QGuardedPtr<Driver> lastUsedDriverForAutoIncrementSQLFieldsList;
00213 
00217         QMap<QueryColumnInfo*,int> *fieldsOrder;
00218 
00219 //      QValueList<bool> detailedVisibility;
00220 
00222         QValueVector<int> *pkeyFieldsOrder;
00223 
00225         uint pkeyFieldsCount;
00226 
00228         QString statement;
00229 
00231         Relationship::List relations;
00232 
00248         QValueVector<int> tablesBoundToColumns;
00249         
00251         QAsciiDict<int> tablePositionsForAliases;
00252 
00254         QAsciiDict<int> columnPositionsForAliases;
00255 
00257         BaseExpr *whereExpr;
00258 
00259         QDict<QueryColumnInfo> columnInfosByName;
00260 
00263         bool regenerateExprAliases : 1;
00264 };
00265 }
00266 
00267 //=======================================
00268 
00269 QuerySchema::QuerySchema()
00270     : FieldList(false)//fields are not owned by QuerySchema object
00271     , SchemaData(KexiDB::QueryObjectType)
00272     , d( new QuerySchemaPrivate(this) )
00273 {
00274     init();
00275 }
00276 
00277 QuerySchema::QuerySchema(TableSchema* tableSchema)
00278     : FieldList(false)
00279     , SchemaData(KexiDB::QueryObjectType)
00280     , d( new QuerySchemaPrivate(this) )
00281 {
00282     d->masterTable = tableSchema;
00283 //  assert(d->masterTable);
00284     init();
00285     if (!d->masterTable) {
00286         KexiDBWarn << "QuerySchema(TableSchema*): !d->masterTable" << endl;
00287         m_name = QString::null;
00288         return;
00289     }
00290     addTable(d->masterTable);
00291     //defaults:
00292     //inherit name from a table
00293     m_name = d->masterTable->name();
00294     //inherit caption from a table
00295     m_caption = d->masterTable->caption();
00296     
00297 //replaced by explicit field list: //add all fields of the table as asterisk:
00298 //replaced by explicit field list:  addField( new QueryAsterisk(this) );
00299 
00300     // add explicit field list to avoid problems (e.g. with fields added outside of Kexi):
00301     for (Field::ListIterator it( d->masterTable->fieldsIterator() ); it.current(); ++it) {
00302         addField( it.current() );
00303     }
00304 }
00305 
00306 QuerySchema::~QuerySchema()
00307 {
00308     delete d;
00309 }
00310 
00311 void QuerySchema::init()
00312 {
00313     m_type = KexiDB::QueryObjectType;
00314 //m_fields_by_name.setAutoDelete( true ); //because we're using QueryColumnInfoEntry objects
00315 }
00316 
00317 void QuerySchema::clear()
00318 {
00319     FieldList::clear();
00320     SchemaData::clear();
00321     d->clear();
00322 }
00323 
00324 FieldList& QuerySchema::insertField(uint position, Field *field, bool visible)
00325 {
00326     return insertField(position, field, -1/*don't bind*/, visible);
00327 }
00328 
00329 /*virtual*/
00330 FieldList& QuerySchema::insertField(uint position, Field *field)
00331 {
00332     return insertField( position, field, -1/*don't bind*/, true );
00333 }
00334 
00335 FieldList& QuerySchema::insertField(uint position, Field *field, 
00336     int bindToTable, bool visible)
00337 {
00338     if (!field) {
00339         KexiDBWarn << "QuerySchema::insertField(): !field" << endl;
00340         return *this;
00341     }
00342 
00343     if (position>m_fields.count()) {
00344         KexiDBWarn << "QuerySchema::insertField(): position (" << position << ") out of range" << endl;
00345         return *this;
00346     }
00347     if (!field->isQueryAsterisk() && !field->isExpression() && !field->table()) {
00348         KexiDBWarn << "QuerySchema::addField(): WARNING: field '"<<field->name()
00349             <<"' must contain table information!" <<endl;
00350         return *this;
00351     }
00352     if (fieldCount()>=d->visibility.size()) {
00353         d->visibility.resize(d->visibility.size()*2);
00354         d->tablesBoundToColumns.resize(d->tablesBoundToColumns.size()*2);
00355     }
00356     d->clearCachedData();
00357     FieldList::insertField(position, field);
00358     if (field->isQueryAsterisk()) {
00359         d->asterisks.append(field);
00360         //if this is single-table asterisk,
00361         //add a table to list if doesn't exist there:
00362         if (field->table() && (d->tables.findRef(field->table())==-1))
00363             d->tables.append(field->table());
00364     }
00365     else if (field->table()) {
00366         //add a table to list if doesn't exist there:
00367         if (d->tables.findRef(field->table())==-1)
00368             d->tables.append(field->table());
00369     }
00370 //  //visible by default
00371 //  setFieldVisible(field, true);
00372 //  d->visibility.setBit(fieldCount()-1, visible);
00373     //update visibility
00374     //--move bits to make a place for a new one
00375     for (uint i=fieldCount()-1; i>position; i--)
00376         d->visibility.setBit(i, d->visibility.testBit(i-1));
00377     d->visibility.setBit(position, visible);
00378 
00379     //bind to table
00380     if (bindToTable < -1 && bindToTable>(int)d->tables.count()) {
00381         KexiDBWarn << "QuerySchema::insertField(): bindToTable (" << bindToTable 
00382             << ") out of range" << endl;
00383         bindToTable = -1;
00384     }
00385     //--move items to make a place for a new one
00386     for (uint i=fieldCount()-1; i>position; i--)
00387         d->tablesBoundToColumns[i] = d->tablesBoundToColumns[i-1];
00388     d->tablesBoundToColumns[ position ] = bindToTable;
00389     
00390     KexiDBDbg << "QuerySchema::insertField(): bound to table (" << bindToTable << "): " <<endl;
00391     if (bindToTable==-1)
00392         KexiDBDbg << " <NOT SPECIFIED>" << endl; 
00393     else
00394         KexiDBDbg << " name=" << d->tables.at(bindToTable)->name() 
00395             << " alias=" << tableAlias(bindToTable) <<  endl;
00396     QString s;
00397     for (uint i=0; i<fieldCount();i++)
00398         s+= (QString::number(d->tablesBoundToColumns[i]) + " ");
00399     KexiDBDbg << "tablesBoundToColumns == [" << s << "]" <<endl;
00400 
00401     if (field->isExpression())
00402         d->regenerateExprAliases = true;
00403 
00404     return *this;
00405 }
00406 
00407 int QuerySchema::tableBoundToColumn(uint columnPosition) const
00408 {
00409     if (columnPosition > d->tablesBoundToColumns.count()) {
00410         KexiDBWarn << "QuerySchema::tableBoundToColumn(): columnPosition (" << columnPosition
00411             << ") out of range" << endl;
00412         return -1;
00413     }
00414     return d->tablesBoundToColumns[columnPosition];
00415 }
00416 
00417 KexiDB::FieldList& QuerySchema::addField(KexiDB::Field* field, bool visible)
00418 {
00419     return insertField(m_fields.count(), field, visible);
00420 }
00421 
00422 KexiDB::FieldList& QuerySchema::addField(KexiDB::Field* field, int bindToTable, 
00423     bool visible)
00424 {
00425     return insertField(m_fields.count(), field, bindToTable, visible);
00426 }
00427 
00428 void QuerySchema::removeField(KexiDB::Field *field)
00429 {
00430     if (!field)
00431         return;
00432     d->clearCachedData();
00433     if (field->isQueryAsterisk()) {
00434         d->asterisks.remove(field); //this will destroy this asterisk
00435     }
00436 //TODO: should we also remove table for this field or asterisk?
00437     FieldList::removeField(field);
00438 }
00439 
00440 bool QuerySchema::isColumnVisible(uint position) const
00441 {
00442     return (position < fieldCount()) ? d->visibility.testBit(position) : false;
00443 }
00444 
00445 void QuerySchema::setColumnVisible(uint position, bool v)
00446 {
00447     if (position < fieldCount())
00448         d->visibility.setBit(position, v);
00449 }
00450 
00451 FieldList& QuerySchema::addAsterisk(QueryAsterisk *asterisk, bool visible)
00452 {
00453     if (!asterisk)
00454         return *this;
00455     //make unique name
00456     asterisk->m_name = (asterisk->table() ? asterisk->table()->name() + ".*" : "*") 
00457         + QString::number(asterisks()->count());
00458     return addField(asterisk, visible);
00459 }
00460 
00461 Connection* QuerySchema::connection() const
00462 {
00463     TableSchema *mt = masterTable();
00464     return mt ? mt->connection() : 0;
00465 }
00466 
00467 QString QuerySchema::debugString()
00468 {
00469     QString dbg;
00470     dbg.reserve(1024);
00471     //fields
00472     TableSchema *mt = masterTable();
00473     dbg = QString("QUERY ") + schemaDataDebugString() + "\n"
00474         + "-masterTable=" + (mt ? mt->name() :"<NULL>")
00475         + "\n-COLUMNS:\n"
00476         + ((fieldCount()>0) ? FieldList::debugString() : "<NONE>") + "\n"
00477         + "-FIELDS EXPANDED ";
00478 
00479     QString dbg1;
00480     uint fieldsExpandedCount = 0;
00481     if (fieldCount()>0) {
00482         QueryColumnInfo::Vector fe( fieldsExpanded() );
00483         fieldsExpandedCount = fe.size();
00484         for ( uint i=0; i < fieldsExpandedCount; i++ ) {
00485             QueryColumnInfo *ci = fe[i];
00486             if (!dbg1.isEmpty())
00487                 dbg1 += ",\n";
00488             dbg1 += (ci->field->name() + 
00489                 (ci->alias.isEmpty() ? QString::null : (QString::fromLatin1(" AS ") + QString(ci->alias))));
00490         }
00491         dbg1 += "\n";
00492     }
00493     else {
00494         dbg1 = "<NONE>\n";
00495     }
00496     dbg1.prepend( QString("(%1):\n").arg(fieldsExpandedCount) );
00497     dbg += dbg1;
00498 
00499     //it's safer to delete fieldsExpanded for now 
00500     // (debugString() could be called before all fields are added)
00501 //causes a crash    d->clearCachedData();
00502 
00503     //bindings
00504     QString dbg2;
00505     dbg2.reserve(512);
00506     for (uint i = 0; i<fieldCount(); i++) {
00507         int tablePos = tableBoundToColumn(i);
00508         if (tablePos>=0) {
00509             QCString tAlias = tableAlias(tablePos);
00510             if (!tAlias.isEmpty()) {
00511                 dbg2 += (QString::fromLatin1(" field \"") + FieldList::field(i)->name() 
00512                     + "\" uses alias \"" + QString(tAlias) + "\" of table \""
00513                     + d->tables.at(tablePos)->name() + "\"\n");
00514             }
00515         }
00516     }
00517     if (!dbg2.isEmpty()) {
00518         dbg += "\n-BINDINGS:\n";
00519         dbg += dbg2;
00520     }
00521     
00522     //tables    
00523     TableSchema *table;
00524     QString table_names;
00525     table_names.reserve(512);
00526     for ( table = d->tables.first(); table; table = d->tables.next() ) {
00527         if (!table_names.isEmpty())
00528             table_names += ", ";
00529         table_names += (QString("'") + table->name() + "'");
00530     }
00531     if (d->tables.isEmpty())
00532         table_names = "<NONE>";
00533     dbg += (QString("-TABLES:\n") + table_names);
00534     QString aliases;
00535     if (!d->hasColumnAliases())
00536         aliases = "<NONE>\n";
00537     else {
00538         Field::ListIterator it( m_fields );
00539         for (int i=0; it.current(); ++it, i++) {
00540             QCString *alias = d->columnAlias(i);
00541             if (alias)
00542                 aliases += (QString("field #%1: ").arg(i) 
00543                     + (it.current()->name().isEmpty() ? "<noname>" : it.current()->name())
00544                     + " -> " + (const char*)*alias + "\n");
00545         }
00546     }
00547     //aliases
00548     dbg += QString("\n-COLUMN ALIASES:\n" + aliases);
00549     if (d->tableAliases.isEmpty())
00550         aliases = "<NONE>";
00551     else {
00552         aliases = "";
00553         TableSchema::ListIterator t_it(d->tables);
00554         for (int i=0; t_it.current(); ++t_it, i++) {
00555             QCString *alias = d->tableAliases[i];
00556             if (alias)
00557                 aliases += (QString("table #%1: ").arg(i) 
00558                     + (t_it.current()->name().isEmpty() ? "<noname>" : t_it.current()->name())
00559                     + " -> " + (const char*)*alias + "\n");
00560         }
00561     }
00562     dbg += QString("-TABLE ALIASES:\n" + aliases);
00563     QString where = d->whereExpr ? d->whereExpr->debugString() : QString::null;
00564     if (!where.isEmpty())
00565         dbg += QString("\n-WHERE EXPRESSION:\n" + where);
00566     return dbg;
00567 }
00568 
00569 TableSchema* QuerySchema::masterTable() const
00570 {
00571     if (d->masterTable)
00572         return d->masterTable;
00573     if (d->tables.isEmpty())
00574         return 0;
00575 
00576     //try to find master table if there's only one table (with possible aliasses)
00577     int num = 0;
00578     QString tableNameLower;
00579     for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00580         if (!tableNameLower.isEmpty() && it.current()->name().lower()!=tableNameLower) {
00581             //two or more different tables
00582             return 0;
00583         }
00584         tableNameLower = tableAlias(num);
00585     }
00586     return d->tables.first();
00587 }
00588 
00589 void QuerySchema::setMasterTable(TableSchema *table)
00590 { 
00591     if (table)
00592         d->masterTable=table; 
00593 }
00594 
00595 TableSchema::List* QuerySchema::tables() const
00596 {
00597     return &d->tables;
00598 }
00599 
00600 void QuerySchema::addTable(TableSchema *table, const QCString& alias)
00601 {
00602     KexiDBDbg << "QuerySchema::addTable() " << (void *)table 
00603         << " alias=" << alias << endl;
00604     if (!table)
00605         return;
00606     
00607     //only append table if:
00608     //-it has alias
00609     //-it has no alias but there is no such table on the list
00610     if (alias.isEmpty() && d->tables.findRef(table)!=-1) {
00611         const QString& tableNameLower = table->name().lower();
00612         const QString& aliasLower = QString(alias.lower());
00613         int num = 0;
00614         for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00615             if (it.current()->name().lower()==tableNameLower) {
00616                 const QString& tAlias = tableAlias(num);
00617                 if (tAlias == aliasLower) {
00618                     KexiDBWarn << "QuerySchema::addTable(): table with \"" 
00619                         << tAlias << "\" alias already added!" << endl;
00620                     return;
00621                 }
00622             }
00623         }
00624     }
00625     
00626     d->tables.append(table);
00627     
00628     if (!alias.isEmpty())
00629         setTableAlias(d->tables.count()-1, alias);
00630 }
00631 
00632 void QuerySchema::removeTable(TableSchema *table)
00633 {
00634     if (!table)
00635         return;
00636     if (d->masterTable == table)
00637         d->masterTable = 0;
00638     d->tables.remove(table);
00639     //todo: remove fields!
00640 }
00641 
00642 TableSchema* QuerySchema::table(const QString& tableName) const
00643 {
00644 //TODO: maybe use tables_byname?
00645     for (TableSchema::ListIterator it(d->tables); it.current(); ++it) {
00646         if (it.current()->name().lower()==tableName.lower())
00647             return it.current();
00648     }
00649     return 0;
00650 }
00651 
00652 bool QuerySchema::contains(TableSchema *table) const
00653 {
00654     return d->tables.findRef(table)!=-1;
00655 }
00656 
00657 Field* QuerySchema::findTableField(const QString &tableDotFieldName) const
00658 {
00659     QString tableName, fieldName;
00660     if (!KexiDB::splitToTableAndFieldParts(tableDotFieldName, tableName, fieldName))
00661         return 0;
00662     TableSchema *tableSchema = table(tableName);
00663     if (!tableSchema)
00664         return 0;
00665     return tableSchema->field(fieldName);
00666 }
00667 
00668 QCString QuerySchema::columnAlias(uint position) const
00669 {
00670     QCString *a = d->columnAlias(position);
00671     return a ? *a : QCString();
00672 }
00673 
00674 bool QuerySchema::hasColumnAlias(uint position) const
00675 {
00676     return d->columnAlias(position)!=0;
00677 }
00678 
00679 void QuerySchema::setColumnAlias(uint position, const QCString& alias)
00680 {
00681     if (position >= m_fields.count()) {
00682         KexiDBWarn << "QuerySchema::setColumnAlias(): position ("  << position 
00683             << ") out of range!" << endl;
00684         return;
00685     }
00686     QCString fixedAlias = alias.stripWhiteSpace();
00687     Field *f = FieldList::field(position);
00688     if (f->captionOrName().isEmpty() && fixedAlias.isEmpty()) {
00689         KexiDBWarn << "QuerySchema::setColumnAlias(): position ("  << position 
00690             << ") could not remove alias when no name is specified for expression column!" << endl;
00691         return;
00692     }
00693     d->setColumnAlias(position, fixedAlias);
00694 }
00695 
00696 QCString QuerySchema::tableAlias(uint position) const
00697 {
00698     QCString *a = d->tableAliases[position];
00699     return a ? *a : QCString();
00700 }
00701 
00702 int QuerySchema::tablePositionForAlias(const QCString& name) const
00703 {
00704     int *num = d->tablePositionsForAliases[name];
00705     if (!num)
00706         return -1;
00707     return *num;
00708 }
00709 
00710 int QuerySchema::tablePosition(const QString& tableName) const
00711 {
00712     int num = 0;
00713     for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00714         if (it.current()->name().lower()==tableName.lower())
00715             return num;
00716     }
00717     return -1;
00718 }
00719 
00720 QValueList<int> QuerySchema::tablePositions(const QString& tableName) const
00721 {
00722     int num = 0;
00723     QValueList<int> result;
00724     const QString& tableNameLower = tableName.lower();
00725     for (TableSchema::ListIterator it(d->tables); it.current(); ++it, num++) {
00726         if (it.current()->name().lower()==tableNameLower) {
00727             result += num;
00728         }
00729     }
00730     return result;
00731 }
00732 
00733 bool QuerySchema::hasTableAlias(uint position) const
00734 {
00735     return d->tableAliases[position]!=0;
00736 }
00737 
00738 int QuerySchema::columnPositionForAlias(const QCString& name) const
00739 {
00740     int *num = d->columnPositionsForAliases[name];
00741     if (!num)
00742         return -1;
00743     return *num;
00744 }
00745 
00746 void QuerySchema::setTableAlias(uint position, const QCString& alias)
00747 {
00748     if (position >= d->tables.count()) {
00749         KexiDBWarn << "QuerySchema::setTableAlias(): position ("  << position 
00750             << ") out of range!" << endl;
00751         return;
00752     }
00753     QCString fixedAlias = alias.stripWhiteSpace();
00754     if (fixedAlias.isEmpty()) {
00755         QCString *oldAlias = d->tableAliases.take(position);
00756         if (oldAlias) {
00757             d->tablePositionsForAliases.remove(*oldAlias);
00758             delete oldAlias;
00759         }
00760 //          d->maxIndexWithTableAlias = -1;
00761     }
00762     else {
00763         d->tableAliases.replace(position, new QCString(fixedAlias));
00764         d->tablePositionsForAliases.replace(fixedAlias, new int(position));
00765 //      d->maxIndexWithTableAlias = QMAX( d->maxIndexWithTableAlias, (int)index );
00766     }
00767 }
00768 
00769 Relationship::List* QuerySchema::relationships() const
00770 {
00771     return &d->relations;
00772 }
00773 
00774 Field::List* QuerySchema::asterisks() const
00775 {
00776     return &d->asterisks;
00777 }
00778         
00779 QString QuerySchema::statement() const
00780 {
00781     return d->statement;
00782 }
00783 
00784 void QuerySchema::setStatement(const QString &s)
00785 {
00786     d->statement = s;
00787 }
00788 
00789 Field* QuerySchema::field(const QString& name)
00790 {
00791     computeFieldsExpanded();
00792     QueryColumnInfo *ci = d->columnInfosByName[name];
00793     return ci ? ci->field : 0;
00794 }
00795 
00796 QueryColumnInfo* QuerySchema::columnInfo(const QString& name)
00797 {
00798     computeFieldsExpanded();
00799     return d->columnInfosByName[name];
00800 }
00801 
00802 QueryColumnInfo::Vector QuerySchema::fieldsExpanded(bool unique)
00803 {
00804     computeFieldsExpanded();
00805     if (!unique)
00806         return *d->fieldsExpanded;
00807 
00808     QDict<char> columnsAlreadyFound;
00809     QueryColumnInfo::Vector result( d->fieldsExpanded->count() ); //initial size is set
00810 //  QMapConstIterator<QueryColumnInfo*, bool> columnsAlreadyFoundIt;
00811     //compute unique list
00812     uint uniqueListCount = 0;
00813     for (uint i=0; i<d->fieldsExpanded->count(); i++) {
00814         QueryColumnInfo *ci = (*d->fieldsExpanded)[i];
00815 //      columnsAlreadyFoundIt = columnsAlreadyFound.find(ci);
00816 //      uint foundColumnIndex = -1;
00817         if (!columnsAlreadyFound[ci->aliasOrName()]) {// columnsAlreadyFoundIt==columnsAlreadyFound.constEnd())
00818             columnsAlreadyFound.insert(ci->aliasOrName(), (char*)1);
00819             result.insert(uniqueListCount++, ci);
00820         }
00821     }
00822     result.resize(uniqueListCount); //update result size
00823     return result;
00824 }
00825 
00826 void QuerySchema::computeFieldsExpanded()
00827 {
00828     if (d->fieldsExpanded) {
00829 //      if (detailedVisibility)
00830 //          *detailedVisibility = d->detailedVisibility;
00831         return;
00832     }
00833 
00834 //  if (detailedVisibility)
00835 //      detailedVisibility->clear();
00836 
00837 //  d->detailedVisibility.clear();
00838 //  m_fields_by_name.clear();
00839 
00840     //collect all fields in a list (not a vector yet, because we do not know its size)
00841     QueryColumnInfo::List list;
00842     int i = 0;
00843     int fieldPosition = 0;
00844     for (Field *f = m_fields.first(); f; f = m_fields.next(), fieldPosition++) {
00845         if (f->isQueryAsterisk()) {
00846             if (static_cast<QueryAsterisk*>(f)->isSingleTableAsterisk()) {
00847                 Field::List *ast_fields = static_cast<QueryAsterisk*>(f)->table()->fields();
00848                 for (Field *ast_f = ast_fields->first(); ast_f; ast_f=ast_fields->next()) {
00849 //                  d->detailedVisibility += isFieldVisible(fieldPosition);
00850                     list.append( new QueryColumnInfo(ast_f, QCString()/*no field for asterisk!*/,
00851                         isColumnVisible(fieldPosition)) 
00852                     );
00853 //                  list.append(ast_f);
00854                 }
00855             }
00856             else {//all-tables asterisk: itereate through table list
00857                 for (TableSchema *table = d->tables.first(); table; table = d->tables.next()) {
00858                     //add all fields from this table
00859                     Field::List *tab_fields = table->fields();
00860                     for (Field *tab_f = tab_fields->first(); tab_f; tab_f = tab_fields->next()) {
00862 //                      d->detailedVisibility += isFieldVisible(fieldPosition);
00863 //                      list.append(tab_f);
00864                         list.append( new QueryColumnInfo(tab_f, QCString()/*no field for asterisk!*/,
00865                             isColumnVisible(fieldPosition)) 
00866                         );
00867                     }
00868                 }
00869             }
00870         }
00871         else {
00872             //a single field
00873 //          d->detailedVisibility += isFieldVisible(fieldPosition);
00874             list.append(
00875                 new QueryColumnInfo(f, columnAlias(fieldPosition), isColumnVisible(fieldPosition)) );
00876 //          list.append(f);
00877         }
00878     }
00879     //prepare clean vector for expanded list, and a map for order information
00880     if (!d->fieldsExpanded) {
00881         d->fieldsExpanded = new QueryColumnInfo::Vector( list.count() );// Field::Vector( list.count() );
00882         d->fieldsExpanded->setAutoDelete(true);
00883         d->fieldsOrder = new QMap<QueryColumnInfo*,int>();
00884     }
00885     else {//for future:
00886         d->fieldsExpanded->clear();
00887         d->fieldsExpanded->resize( list.count() );
00888         d->fieldsOrder->clear();
00889     }
00890 
00891     /*fill:
00892      -the vector
00893      -the map
00894      -"fields by name" dictionary
00895     */
00896     d->columnInfosByName.clear();
00897     QueryColumnInfo::ListIterator it(list);
00898     for (i=0; it.current(); ++it, i++)
00899     {
00900         d->fieldsExpanded->insert(i,it.current());
00901         d->fieldsOrder->insert(it.current(),i);
00902         //remember field by name/alias/table.name if there's no such string yet in d->columnInfosByName
00903         if (!it.current()->alias.isEmpty()) {
00904             //alias
00905             if (!d->columnInfosByName[ it.current()->alias ])
00906                 d->columnInfosByName.insert( it.current()->alias, it.current() );
00907         }
00908         else {
00909             //no alias: store name and table.name
00910             if (!d->columnInfosByName[ it.current()->field->name() ])
00911                 d->columnInfosByName.insert( it.current()->field->name(), it.current() );
00912             QString tableAndName( it.current()->field->table()->name() + "." + it.current()->field->name() );
00913             if (!d->columnInfosByName[ tableAndName ])
00914                 d->columnInfosByName.insert( tableAndName, it.current() );
00915         }
00916     }
00917 //  if (detailedVisibility)
00918 //      *detailedVisibility = d->detailedVisibility;
00919 }
00920 
00921 QMap<QueryColumnInfo*,int> QuerySchema::fieldsOrder()
00922 {
00923     if (!d->fieldsOrder)
00924         computeFieldsExpanded();
00925     return *d->fieldsOrder;
00926 }
00927 
00928 QValueVector<int> QuerySchema::pkeyFieldsOrder()
00929 {
00930     if (d->pkeyFieldsOrder)
00931         return *d->pkeyFieldsOrder;
00932 
00933     TableSchema *tbl = masterTable();
00934     if (!tbl || !tbl->primaryKey())
00935         return QValueVector<int>();
00936 
00937     //get order of PKEY fields (e.g. for rows updating or inserting )
00938     IndexSchema *pkey = tbl->primaryKey();
00939     d->pkeyFieldsOrder = new QValueVector<int>( pkey->fieldCount(), -1 );
00940 
00941     const uint fCount = fieldsExpanded().count();
00942     d->pkeyFieldsCount = 0;
00943     for (uint i = 0; i<fCount; i++) {
00944         QueryColumnInfo *fi = d->fieldsExpanded->at(i);
00945         const int fieldIndex = fi->field->table()==tbl ? pkey->indexOf(fi->field) : -1;
00946         if (fieldIndex!=-1/* field found in PK */ 
00947             && d->pkeyFieldsOrder->at(fieldIndex)==-1 /* first time */)
00948         {
00949             KexiDBDbg << "QuerySchema::pkeyFieldsOrder(): FIELD " << fi->field->name() 
00950                 << " IS IN PKEY AT POSITION #" << fieldIndex << endl;
00951 //          (*d->pkeyFieldsOrder)[j]=i;
00952             (*d->pkeyFieldsOrder)[fieldIndex]=i;
00953             d->pkeyFieldsCount++;
00954 //          j++;
00955         }
00956     }
00957     KexiDBDbg << "QuerySchema::pkeyFieldsOrder(): " << d->pkeyFieldsCount
00958         << " OUT OF " << pkey->fieldCount() << " PKEY'S FIELDS FOUND IN QUERY " << name() << endl;
00959     return *d->pkeyFieldsOrder;
00960 }
00961 
00962 uint QuerySchema::pkeyFieldsCount()
00963 {
00964     (void)pkeyFieldsOrder(); /* rebuild information */
00965     return d->pkeyFieldsCount;
00966 }
00967 
00968 Relationship* QuerySchema::addRelationship( Field *field1, Field *field2 )
00969 {
00970 //@todo: find existing global db relationships
00971     Relationship *r = new Relationship(this, field1, field2);
00972     if (r->isEmpty()) {
00973         delete r;
00974         return 0;
00975     }
00976 
00977     d->relations.append( r );
00978     return r;
00979 }
00980 
00981 QueryColumnInfo::List* QuerySchema::autoIncrementFields()
00982 {
00983     if (!d->autoincFields) {
00984         d->autoincFields = new QueryColumnInfo::List();
00985     }
00986     TableSchema *mt = masterTable();
00987     if (!mt) {
00988         KexiDBWarn << "QuerySchema::autoIncrementFields(): no master table!" << endl;
00989         return d->autoincFields;
00990     }
00991     if (d->autoincFields->isEmpty()) {//no cache
00992         QueryColumnInfo::Vector fexp = fieldsExpanded();
00993         for (int i=0; i<(int)fexp.count(); i++) {
00994             QueryColumnInfo *fi = fexp[i];
00995             if (fi->field->table() == mt && fi->field->isAutoIncrement()) {
00996                 d->autoincFields->append( fi );
00997             }
00998         }
00999     }
01000     return d->autoincFields;
01001 }
01002 
01003 QString QuerySchema::sqlColumnsList(QueryColumnInfo::List* infolist, Driver *driver)
01004 {
01005     if (!infolist)
01006         return QString::null;
01007     QString result;
01008     result.reserve(256);
01009     QueryColumnInfo::ListIterator it( *infolist );
01010     bool start = true;
01011     for (; it.current(); ++it) {
01012         if (!start)
01013             result += ",";
01014         else
01015             start = false;
01016         result += driver->escapeIdentifier( it.current()->field->name() );
01017     }
01018     return result;
01019 }
01020 
01021 QString QuerySchema::autoIncrementSQLFieldsList(Driver *driver)
01022 {
01023     if ((Driver *)d->lastUsedDriverForAutoIncrementSQLFieldsList != driver
01024         || d->autoIncrementSQLFieldsList.isEmpty())
01025     {
01026         d->autoIncrementSQLFieldsList = QuerySchema::sqlColumnsList( autoIncrementFields(), driver );
01027         d->lastUsedDriverForAutoIncrementSQLFieldsList = driver;
01028     }
01029     return d->autoIncrementSQLFieldsList;
01030 }
01031 
01032 void QuerySchema::setWhereExpression(BaseExpr *expr)
01033 {
01034     delete d->whereExpr;
01035     d->whereExpr = expr;
01036 }
01037 
01038 void QuerySchema::addToWhereExpression(KexiDB::Field *field, const QVariant& value, int relation)
01039 {
01040     int token;
01041     if (value.isNull())
01042         token = SQL_NULL;
01043     else if (field->isIntegerType()) {
01044         token = INTEGER_CONST;
01045     }
01046     else if (field->isFPNumericType()) {
01047         token = REAL_CONST;
01048     }
01049     else {
01050         token = CHARACTER_STRING_LITERAL;
01052     }
01053     
01054     BinaryExpr * newExpr = new BinaryExpr(
01055         KexiDBExpr_Relational, 
01056         new ConstExpr( token, value ),
01057         relation,
01058         new VariableExpr((field->table() ? (field->table()->name()+".") : QString::null)+field->name())
01059     );
01060     if (d->whereExpr) {
01061         d->whereExpr = new BinaryExpr(
01062             KexiDBExpr_Logical, 
01063             d->whereExpr,
01064             AND,
01065             newExpr
01066         );
01067     }
01068     else {
01069         d->whereExpr = newExpr;
01070     }
01071 }
01072 
01073 /*
01074 void QuerySchema::addToWhereExpression(KexiDB::Field *field, const QVariant& value)
01075         switch (value.type()) {
01076         case Int: case UInt: case Bool: case LongLong: case ULongLong:
01077             token = INTEGER_CONST;
01078             break;
01079         case Double:
01080             token = REAL_CONST;
01081             break;
01082         default:
01083             token = CHARACTER_STRING_LITERAL;
01084         }
01086                 
01087 */
01088 
01089 BaseExpr *QuerySchema::whereExpression() const
01090 {
01091     return d->whereExpr;
01092 }
01093 
01094 void QuerySchema::setOrderByColumnList(const QStringList& columnNames)
01095 {
01096     Q_UNUSED(columnNames);
01098 // all field names should be fooun, exit otherwise ..........
01099 
01100     // OK
01101 //TODO  if (!d->orderByColumnList)
01102 //TODO
01103 }
01104 
01106 void QuerySchema::setOrderByColumnList(const QString& column1, const QString& column2, 
01107     const QString& column3, const QString& column4, const QString& column5)
01108 {
01109     Q_UNUSED(column1);
01110     Q_UNUSED(column2);
01111     Q_UNUSED(column3);
01112     Q_UNUSED(column4);
01113     Q_UNUSED(column5);
01116 }
01117 
01118 QueryColumnInfo::Vector QuerySchema::orderByColumnList() const
01119 {
01120     return d->orderByColumnList ? *d->orderByColumnList: QueryColumnInfo::Vector();
01121 }
01122 
01123 
01124 /*
01125     new field1, Field *field2
01126     if (!field1 || !field2) {
01127         kdWarning() << "QuerySchema::addRelationship(): !masterField || !detailsField" << endl;
01128         return;
01129     }
01130     if (field1->isQueryAsterisk() || field2->isQueryAsterisk()) {
01131         kdWarning() << "QuerySchema::addRelationship(): relationship's fields cannot be asterisks" << endl;
01132         return;
01133     }
01134     if (!hasField(field1) && !hasField(field2)) {
01135         kdWarning() << "QuerySchema::addRelationship(): fields do not belong to this query" << endl;
01136         return;
01137     }
01138     if (field1->table() == field2->table()) {
01139         kdWarning() << "QuerySchema::addRelationship(): fields cannot belong to the same table" << endl;
01140         return;
01141     }
01142 //@todo: check more things: -types
01143 //@todo: find existing global db relationships
01144 
01145     Field *masterField = 0, *detailsField = 0;
01146     IndexSchema *masterIndex = 0, *detailsIndex = 0;
01147     if (field1->isPrimaryKey() && field2->isPrimaryKey()) {
01148         //2 primary keys
01149         masterField = field1;
01150         masterIndex = masterField->table()->primaryKey();
01151         detailsField = field2;
01152         detailsIndex = masterField->table()->primaryKey();
01153     }
01154     else if (field1->isPrimaryKey()) {
01155         masterField = field1;
01156         masterIndex = masterField->table()->primaryKey();
01157         detailsField = field2;
01158 //@todo: check if it already exists
01159         detailsIndex = new IndexSchema(detailsField->table());
01160         detailsIndex->addField(detailsField);
01161         detailsIndex->setForeigKey(true);
01162     //      detailsField->setForeignKey(true);
01163     }
01164     else if (field2->isPrimaryKey()) {
01165         detailsField = field1;
01166         masterField = field2;
01167         masterIndex = masterField->table()->primaryKey();
01168 //@todo
01169     }
01170 
01171     if (!masterIndex || !detailsIndex)
01172         return; //failed
01173 
01174     Relationship *rel = new Relationship(masterIndex, detailsIndex);
01175 
01176     d->relations.append( rel );
01177 }*/
01178 
01179 //---------------------------------------------------
01180 
01181 QueryAsterisk::QueryAsterisk( QuerySchema *query, TableSchema *table )
01182     :Field()
01183     ,m_table(table)
01184 {
01185     assert(query);
01186     m_parent = query;
01187     setType(Field::Asterisk);
01188 }
01189 
01190 QueryAsterisk::~QueryAsterisk()
01191 {
01192 }
01193 
01194 void QueryAsterisk::setTable(TableSchema *table)
01195 {
01196     KexiDBDbg << "QueryAsterisk::setTable()" << endl;
01197     m_table=table;
01198 }
01199 
01200 QString QueryAsterisk::debugString()
01201 {
01202     QString dbg;
01203     if (isAllTableAsterisk()) {
01204         dbg += "ALL-TABLES ASTERISK (*) ON TABLES(";
01205         TableSchema *table;
01206         QString table_names;
01207         TableSchema::List *tables = query()->tables();
01208         for ( table = tables->first(); table; table = tables->next() ) {
01209             if (!table_names.isEmpty())
01210                 table_names += ", ";
01211             table_names += table->name();
01212         }
01213         dbg += (table_names + ")");
01214     }
01215     else {
01216         dbg += ("SINGLE-TABLE ASTERISK (" + table()->name() + ".*)");
01217     }
01218     return dbg;
01219 }
01220 
KDE Home | KDE Accessibility Home | Description of Access Keys