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