kexi

utils.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004-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 "utils.h"
00021 #include "cursor.h"
00022 #include "drivermanager.h"
00023 #include "lookupfieldschema.h"
00024 
00025 #include <qmap.h>
00026 #include <qthread.h>
00027 #include <qdom.h>
00028 #include <qintdict.h>
00029 #include <qbuffer.h>
00030 
00031 #include <kdebug.h>
00032 #include <klocale.h>
00033 #include <kstaticdeleter.h>
00034 #include <kmessagebox.h>
00035 #include <klocale.h>
00036 #include <kiconloader.h>
00037 
00038 #include "utils_p.h"
00039 
00040 using namespace KexiDB;
00041 
00043 struct TypeCache
00044 {
00045     QMap< uint, TypeGroupList > tlist;
00046     QMap< uint, QStringList > nlist;
00047     QMap< uint, QStringList > slist;
00048     QMap< uint, Field::Type > def_tlist;
00049 };
00050 
00051 static KStaticDeleter<TypeCache> KexiDB_typeCacheDeleter;
00052 TypeCache *KexiDB_typeCache = 0;
00053 
00054 static void initList()
00055 {
00056     KexiDB_typeCacheDeleter.setObject( KexiDB_typeCache, new TypeCache() );
00057 
00058     for (uint t=0; t<=KexiDB::Field::LastType; t++) {
00059         const uint tg = KexiDB::Field::typeGroup( t );
00060         TypeGroupList list;
00061         QStringList name_list, str_list;
00062         if (KexiDB_typeCache->tlist.find( tg )!=KexiDB_typeCache->tlist.end()) {
00063             list = KexiDB_typeCache->tlist[ tg ];
00064             name_list = KexiDB_typeCache->nlist[ tg ];
00065             str_list = KexiDB_typeCache->slist[ tg ];
00066         }
00067         list+= t;
00068         name_list += KexiDB::Field::typeName( t );
00069         str_list += KexiDB::Field::typeString( t );
00070         KexiDB_typeCache->tlist[ tg ] = list;
00071         KexiDB_typeCache->nlist[ tg ] = name_list;
00072         KexiDB_typeCache->slist[ tg ] = str_list;
00073     }
00074 
00075     KexiDB_typeCache->def_tlist[ Field::InvalidGroup ] = Field::InvalidType;
00076     KexiDB_typeCache->def_tlist[ Field::TextGroup ] = Field::Text;
00077     KexiDB_typeCache->def_tlist[ Field::IntegerGroup ] = Field::Integer;
00078     KexiDB_typeCache->def_tlist[ Field::FloatGroup ] = Field::Double;
00079     KexiDB_typeCache->def_tlist[ Field::BooleanGroup ] = Field::Boolean;
00080     KexiDB_typeCache->def_tlist[ Field::DateTimeGroup ] = Field::Date;
00081     KexiDB_typeCache->def_tlist[ Field::BLOBGroup ] = Field::BLOB;
00082 }
00083 
00084 const TypeGroupList KexiDB::typesForGroup(KexiDB::Field::TypeGroup typeGroup)
00085 {
00086     if (!KexiDB_typeCache)
00087         initList();
00088     return KexiDB_typeCache->tlist[ typeGroup ];
00089 }
00090 
00091 QStringList KexiDB::typeNamesForGroup(KexiDB::Field::TypeGroup typeGroup)
00092 {
00093     if (!KexiDB_typeCache)
00094         initList();
00095     return KexiDB_typeCache->nlist[ typeGroup ];
00096 }
00097 
00098 QStringList KexiDB::typeStringsForGroup(KexiDB::Field::TypeGroup typeGroup)
00099 {
00100     if (!KexiDB_typeCache)
00101         initList();
00102     return KexiDB_typeCache->slist[ typeGroup ];
00103 }
00104 
00105 KexiDB::Field::Type KexiDB::defaultTypeForGroup(KexiDB::Field::TypeGroup typeGroup)
00106 {
00107     if (!KexiDB_typeCache)
00108         initList();
00109     return (typeGroup <= Field::LastTypeGroup) ? KexiDB_typeCache->def_tlist[ typeGroup ] : Field::InvalidType;
00110 }
00111 
00112 void KexiDB::getHTMLErrorMesage(Object* obj, QString& msg, QString &details)
00113 {
00114     Connection *conn = 0;
00115     if (!obj || !obj->error()) {
00116         if (dynamic_cast<Cursor*>(obj)) {
00117             conn = dynamic_cast<Cursor*>(obj)->connection();
00118             obj = conn;
00119         }
00120         else {
00121             return;
00122         }
00123     }
00124 //  if (dynamic_cast<Connection*>(obj)) {
00125     //  conn = dynamic_cast<Connection*>(obj);
00126     //}
00127     if (!obj || !obj->error())
00128         return;
00129     //lower level message is added to the details, if there is alread message specified
00130     if (!obj->msgTitle().isEmpty())
00131         msg += "<p>" + obj->msgTitle();
00132     
00133     if (msg.isEmpty())
00134         msg = "<p>" + obj->errorMsg();
00135     else
00136         details += "<p>" + obj->errorMsg();
00137 
00138     if (!obj->serverErrorMsg().isEmpty())
00139         details += "<p><b><nobr>" +i18n("Message from server:") + "</nobr></b><br>" + obj->serverErrorMsg();
00140     if (!obj->recentSQLString().isEmpty())
00141         details += "<p><b><nobr>" +i18n("SQL statement:") + QString("</nobr></b><br><tt>%1</tt>").arg(obj->recentSQLString());
00142     int serverResult;
00143     QString serverResultName;
00144     if (obj->serverResult()!=0) {
00145         serverResult = obj->serverResult();
00146         serverResultName = obj->serverResultName();
00147     }
00148     else {
00149         serverResult = obj->previousServerResult();
00150         serverResultName = obj->previousServerResultName();
00151     }
00152     if (!serverResultName.isEmpty())
00153         details += (QString("<p><b><nobr>")+i18n("Server result name:")+"</nobr></b><br>"+serverResultName);
00154     if (!details.isEmpty() 
00155         && (!obj->serverErrorMsg().isEmpty() || !obj->recentSQLString().isEmpty() || !serverResultName.isEmpty() || serverResult!=0) )
00156     {
00157         details += (QString("<p><b><nobr>")+i18n("Server result number:")+"</nobr></b><br>"+QString::number(serverResult));
00158     }
00159 
00160     if (!details.isEmpty() && !details.startsWith("<qt>")) {
00161         if (details.startsWith("<p>"))
00162             details = QString::fromLatin1("<qt>")+details;
00163         else
00164             details = QString::fromLatin1("<qt><p>")+details;
00165     }
00166 }
00167 
00168 void KexiDB::getHTMLErrorMesage(Object* obj, QString& msg)
00169 {
00170     getHTMLErrorMesage(obj, msg, msg);
00171 }
00172 
00173 void KexiDB::getHTMLErrorMesage(Object* obj, ResultInfo *result)
00174 {
00175     getHTMLErrorMesage(obj, result->msg, result->desc);
00176 }
00177 
00178 int KexiDB::idForObjectName( Connection &conn, const QString& objName, int objType )
00179 {
00180     RowData data;
00181     if (true!=conn.querySingleRecord(QString("select o_id from kexi__objects where lower(o_name)='%1' and o_type=%2")
00182         .arg(objName.lower()).arg(objType), data))
00183         return 0;
00184     bool ok;
00185     int id = data[0].toInt(&ok);
00186     return ok ? id : 0;
00187 }
00188 
00189 //-----------------------------------------
00190 
00191 TableOrQuerySchema::TableOrQuerySchema(Connection *conn, const QCString& name)
00192  : m_name(name)
00193 {
00194     m_table = conn->tableSchema(QString(name));
00195     m_query = m_table ? 0 : conn->querySchema(QString(name));
00196     if (!m_table && !m_query)
00197         KexiDBWarn << "TableOrQuery(FieldList &tableOrQuery) : "
00198             " tableOrQuery is neither table nor query!" << endl;
00199 }
00200 
00201 TableOrQuerySchema::TableOrQuerySchema(Connection *conn, const QCString& name, bool table)
00202  : m_name(name)
00203  , m_table(table ? conn->tableSchema(QString(name)) : 0)
00204  , m_query(table ? 0 : conn->querySchema(QString(name)))
00205 {
00206     if (table && !m_table)
00207         KexiDBWarn << "TableOrQuery(Connection *conn, const QCString& name, bool table) : "
00208             "no table specified!" << endl;
00209     if (!table && !m_query)
00210         KexiDBWarn << "TableOrQuery(Connection *conn, const QCString& name, bool table) : "
00211             "no query specified!" << endl;
00212 }
00213 
00214 TableOrQuerySchema::TableOrQuerySchema(FieldList &tableOrQuery)
00215  : m_table(dynamic_cast<TableSchema*>(&tableOrQuery))
00216  , m_query(dynamic_cast<QuerySchema*>(&tableOrQuery))
00217 {
00218     if (!m_table && !m_query)
00219         KexiDBWarn << "TableOrQuery(FieldList &tableOrQuery) : "
00220             " tableOrQuery is nether table nor query!" << endl;
00221 }
00222 
00223 TableOrQuerySchema::TableOrQuerySchema(Connection *conn, int id)
00224 {
00225     m_table = conn->tableSchema(id);
00226     m_query = m_table ? 0 : conn->querySchema(id);
00227     if (!m_table && !m_query)
00228         KexiDBWarn << "TableOrQuery(Connection *conn, int id) : no table or query found for id==" 
00229             << id << "!" << endl;
00230 }
00231 
00232 TableOrQuerySchema::TableOrQuerySchema(TableSchema* table)
00233  : m_table(table)
00234  , m_query(0)
00235 {
00236     if (!m_table)
00237         KexiDBWarn << "TableOrQuery(TableSchema* table) : no table specified!" << endl;
00238 }
00239 
00240 TableOrQuerySchema::TableOrQuerySchema(QuerySchema* query)
00241  : m_table(0)
00242  , m_query(query)
00243 {
00244     if (!m_query)
00245         KexiDBWarn << "TableOrQuery(QuerySchema* query) : no query specified!" << endl;
00246 }
00247 
00248 uint TableOrQuerySchema::fieldCount() const
00249 {
00250     if (m_table)
00251         return m_table->fieldCount();
00252     if (m_query)
00253         return m_query->fieldsExpanded().size();
00254     return 0;
00255 }
00256 
00257 const QueryColumnInfo::Vector TableOrQuerySchema::columns(bool unique)
00258 {
00259     if (m_table)
00260         return m_table->query()->fieldsExpanded(unique ? QuerySchema::Unique : QuerySchema::Default);
00261     
00262     if (m_query)
00263         return m_query->fieldsExpanded(unique ? QuerySchema::Unique : QuerySchema::Default);
00264 
00265     KexiDBWarn << "TableOrQuerySchema::column() : no query or table specified!" << endl;
00266     return QueryColumnInfo::Vector();
00267 }
00268 
00269 QCString TableOrQuerySchema::name() const
00270 {
00271     if (m_table)
00272         return m_table->name().latin1();
00273     if (m_query)
00274         return m_query->name().latin1();
00275     return m_name;
00276 }
00277 
00278 QString TableOrQuerySchema::captionOrName() const
00279 {
00280     SchemaData *sdata = m_table ? static_cast<SchemaData *>(m_table) : static_cast<SchemaData *>(m_query);
00281     if (!sdata)
00282         return m_name;
00283     return sdata->caption().isEmpty() ? sdata->name() : sdata->caption();
00284 }
00285 
00286 Field* TableOrQuerySchema::field(const QString& name)
00287 {
00288     if (m_table)
00289         return m_table->field(name);
00290     if (m_query)
00291         return m_query->field(name);
00292 
00293     return 0;
00294 }
00295 
00296 QueryColumnInfo* TableOrQuerySchema::columnInfo(const QString& name)
00297 {
00298     if (m_table)
00299         return m_table->query()->columnInfo(name);
00300     
00301     if (m_query)
00302         return m_query->columnInfo(name);
00303 
00304     return 0;
00305 }
00306 
00307 QString TableOrQuerySchema::debugString()
00308 {
00309     if (m_table)
00310         return m_table->debugString();
00311     else if (m_query)
00312         return m_query->debugString();
00313     return QString::null;
00314 }
00315 
00316 void TableOrQuerySchema::debug()
00317 {
00318     if (m_table)
00319         return m_table->debug();
00320     else if (m_query)
00321         return m_query->debug();
00322 }
00323 
00324 Connection* TableOrQuerySchema::connection() const
00325 {
00326     if (m_table)
00327         return m_table->connection();
00328     else if (m_query)
00329         return m_query->connection();
00330     return 0;
00331 }
00332 
00333 
00334 //------------------------------------------
00335 
00336 class ConnectionTestThread : public QThread {
00337     public:
00338         ConnectionTestThread(ConnectionTestDialog *dlg, const KexiDB::ConnectionData& connData);
00339         virtual void run();
00340     protected:
00341         ConnectionTestDialog* m_dlg;
00342         KexiDB::ConnectionData m_connData;
00343 };
00344 
00345 ConnectionTestThread::ConnectionTestThread(ConnectionTestDialog* dlg, const KexiDB::ConnectionData& connData)
00346  : m_dlg(dlg), m_connData(connData)
00347 {
00348 }
00349 
00350 void ConnectionTestThread::run()
00351 {
00352     KexiDB::DriverManager manager;
00353     KexiDB::Driver* drv = manager.driver(m_connData.driverName);
00354 //  KexiGUIMessageHandler msghdr;
00355     if (!drv || manager.error()) {
00356 //move      msghdr.showErrorMessage(&Kexi::driverManager());
00357         m_dlg->error(&manager);
00358         return;
00359     }
00360     KexiDB::Connection * conn = drv->createConnection(m_connData);
00361     if (!conn || drv->error()) {
00362 //move      msghdr.showErrorMessage(drv);
00363         delete conn;
00364         m_dlg->error(drv);
00365         return;
00366     }
00367     if (!conn->connect() || conn->error()) {
00368 //move      msghdr.showErrorMessage(conn);
00369         m_dlg->error(conn);
00370         delete conn;
00371         return;
00372     }
00373     // SQL database backends like PostgreSQL require executing "USE database" 
00374     // if we really want to know connection to the server succeeded.
00375     QString tmpDbName;
00376     if (!conn->useTemporaryDatabaseIfNeeded( tmpDbName )) {
00377         m_dlg->error(conn);
00378         delete conn;
00379         return;
00380     }
00381     delete conn;
00382     m_dlg->error(0);
00383 }
00384 
00385 ConnectionTestDialog::ConnectionTestDialog(QWidget* parent, 
00386     const KexiDB::ConnectionData& data,
00387     KexiDB::MessageHandler& msgHandler)
00388  : KProgressDialog(parent, "testconn_dlg",
00389     i18n("Test Connection"), i18n("<qt>Testing connection to <b>%1</b> database server...</qt>")
00390     .arg(data.serverInfoString(true)), true /*modal*/)
00391  , m_thread(new ConnectionTestThread(this, data))
00392  , m_connData(data)
00393  , m_msgHandler(&msgHandler)
00394  , m_elapsedTime(0)
00395  , m_errorObj(0)
00396  , m_stopWaiting(false)
00397 {
00398     showCancelButton(true);
00399     progressBar()->setPercentageVisible(false);
00400     progressBar()->setTotalSteps(0);
00401     connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
00402     adjustSize();
00403     resize(250, height());
00404 }
00405 
00406 ConnectionTestDialog::~ConnectionTestDialog()
00407 {
00408     m_wait.wakeAll();
00409     m_thread->terminate();
00410     delete m_thread;
00411 }
00412 
00413 int ConnectionTestDialog::exec()
00414 {
00415     m_timer.start(20);
00416     m_thread->start();
00417     const int res = KProgressDialog::exec();
00418     m_thread->wait();
00419     m_timer.stop();
00420     return res;
00421 }
00422 
00423 void ConnectionTestDialog::slotTimeout()
00424 {
00425 //  KexiDBDbg << "ConnectionTestDialog::slotTimeout() " << m_errorObj << endl;
00426     bool notResponding = false;
00427     if (m_elapsedTime >= 1000*5) {//5 seconds
00428         m_stopWaiting = true;
00429         notResponding = true;
00430     }
00431     if (m_stopWaiting) {
00432         m_timer.disconnect(this);
00433         m_timer.stop();
00434         slotCancel();
00435 //      reject();
00436 //      close();
00437         if (m_errorObj) {
00438             m_msgHandler->showErrorMessage(m_errorObj);
00439             m_errorObj = 0;
00440         }
00441         else if (notResponding) {
00442             KMessageBox::sorry(0, 
00443                 i18n("<qt>Test connection to <b>%1</b> database server failed. The server is not responding.</qt>")
00444                     .arg(m_connData.serverInfoString(true)),
00445                 i18n("Test Connection"));
00446         }
00447         else {
00448             KMessageBox::information(0, 
00449                 i18n("<qt>Test connection to <b>%1</b> database server established successfully.</qt>")
00450                     .arg(m_connData.serverInfoString(true)),
00451                 i18n("Test Connection"));
00452         }
00453 //      slotCancel();
00454 //      reject();
00455         m_wait.wakeAll();
00456         return;
00457     }
00458     m_elapsedTime += 20;
00459     progressBar()->setProgress( m_elapsedTime );
00460 }
00461 
00462 void ConnectionTestDialog::error(KexiDB::Object *obj)
00463 {
00464     KexiDBDbg << "ConnectionTestDialog::error()" << endl;
00465     m_stopWaiting = true;
00466     m_errorObj = obj;
00467 /*      reject();
00468         m_msgHandler->showErrorMessage(obj);
00469     if (obj) {
00470     }
00471     else {
00472         accept();
00473     }*/
00474     m_wait.wait();
00475 }
00476 
00477 void ConnectionTestDialog::slotCancel()
00478 {
00479 //  m_wait.wakeAll();
00480     m_thread->terminate();
00481     m_timer.disconnect(this);
00482     m_timer.stop();
00483     KProgressDialog::slotCancel();
00484 }
00485 
00486 void KexiDB::connectionTestDialog(QWidget* parent, const KexiDB::ConnectionData& data, 
00487     KexiDB::MessageHandler& msgHandler)
00488 {
00489     ConnectionTestDialog dlg(parent, data, msgHandler);
00490     dlg.exec();
00491 }
00492 
00493 int KexiDB::rowCount(Connection &conn, const QString& sql)
00494 {
00495     int count = -1; //will be changed only on success of querySingleNumber()
00496     QString selectSql( QString::fromLatin1("SELECT COUNT() FROM (") + sql + ")" );
00497     conn.querySingleNumber(selectSql, count);
00498     return count;
00499 }
00500 
00501 int KexiDB::rowCount(const KexiDB::TableSchema& tableSchema)
00502 {
00504     if (!tableSchema.connection()) {
00505         KexiDBWarn << "KexiDB::rowsCount(const KexiDB::TableSchema&): no tableSchema.connection() !" << endl;
00506         return -1;
00507     }
00508     int count = -1; //will be changed only on success of querySingleNumber()
00509     tableSchema.connection()->querySingleNumber(
00510         QString::fromLatin1("SELECT COUNT(*) FROM ") 
00511         + tableSchema.connection()->driver()->escapeIdentifier(tableSchema.name()), 
00512         count
00513     );
00514     return count;
00515 }
00516 
00517 int KexiDB::rowCount(KexiDB::QuerySchema& querySchema)
00518 {
00520     if (!querySchema.connection()) {
00521         KexiDBWarn << "KexiDB::rowsCount(const KexiDB::QuerySchema&): no querySchema.connection() !" << endl;
00522         return -1;
00523     }
00524     int count = -1; //will be changed only on success of querySingleNumber()
00525     querySchema.connection()->querySingleNumber(
00526         QString::fromLatin1("SELECT COUNT(*) FROM (") 
00527         + querySchema.connection()->selectStatement(querySchema) + ")",
00528         count
00529     );
00530     return count;
00531 }
00532 
00533 int KexiDB::rowCount(KexiDB::TableOrQuerySchema& tableOrQuery)
00534 {
00535     if (tableOrQuery.table())
00536         return rowCount( *tableOrQuery.table() );
00537     if (tableOrQuery.query())
00538         return rowCount( *tableOrQuery.query() );
00539     return -1;
00540 }
00541 
00542 int KexiDB::fieldCount(KexiDB::TableOrQuerySchema& tableOrQuery)
00543 {
00544     if (tableOrQuery.table())
00545         return tableOrQuery.table()->fieldCount();
00546     if (tableOrQuery.query())
00547         return tableOrQuery.query()->fieldsExpanded().count();
00548     return -1;
00549 }
00550 
00551 QMap<QString,QString> KexiDB::toMap( const ConnectionData& data )
00552 {
00553     QMap<QString,QString> m;
00554     m["caption"] = data.caption;
00555     m["description"] = data.description;
00556     m["driverName"] = data.driverName;
00557     m["hostName"] = data.hostName;
00558     m["port"] = QString::number(data.port);
00559     m["useLocalSocketFile"] = QString::number((int)data.useLocalSocketFile);
00560     m["localSocketFileName"] = data.localSocketFileName;
00561     m["password"] = data.password;
00562     m["savePassword"] = QString::number((int)data.savePassword);
00563     m["userName"] = data.userName;
00564     m["fileName"] = data.fileName();
00565     return m;
00566 }
00567 
00568 void KexiDB::fromMap( const QMap<QString,QString>& map, ConnectionData& data )
00569 {
00570     data.caption = map["caption"];
00571     data.description = map["description"];
00572     data.driverName = map["driverName"];
00573     data.hostName = map["hostName"];
00574     data.port = map["port"].toInt();
00575     data.useLocalSocketFile = map["useLocalSocketFile"].toInt()==1;
00576     data.localSocketFileName = map["localSocketFileName"];
00577     data.password = map["password"];
00578     data.savePassword = map["savePassword"].toInt()==1;
00579     data.userName = map["userName"];
00580     data.setFileName(map["fileName"]);
00581 }
00582 
00583 bool KexiDB::splitToTableAndFieldParts(const QString& string, 
00584     QString& tableName, QString& fieldName,
00585     SplitToTableAndFieldPartsOptions option)
00586 {
00587     const int id = string.find('.');
00588     if (option & SetFieldNameIfNoTableName && id==-1) {
00589         tableName = QString::null;
00590         fieldName = string;
00591         return !fieldName.isEmpty();
00592     }
00593     if (id<=0 || id==int(string.length()-1))
00594         return false;
00595     tableName = string.left(id);
00596     fieldName = string.mid(id+1);
00597     return !tableName.isEmpty() && !fieldName.isEmpty();
00598 }
00599 
00600 bool KexiDB::supportsVisibleDecimalPlacesProperty(Field::Type type)
00601 {
00603     return Field::isFPNumericType(type);
00604 }
00605 
00606 QString KexiDB::formatNumberForVisibleDecimalPlaces(double value, int decimalPlaces)
00607 {
00609     if (decimalPlaces < 0) {
00610         QString s( QString::number(value, 'f', 10 /*reasonable precision*/));
00611         uint i = s.length()-1;
00612         while (i>0 && s[i]=='0')
00613             i--;
00614         if (s[i]=='.') //remove '.'
00615             i--;
00616         s = s.left(i+1).replace('.', KGlobal::locale()->decimalSymbol());
00617         return s;
00618     }
00619     if (decimalPlaces == 0)
00620         return QString::number((int)value);
00621     return KGlobal::locale()->formatNumber(value, decimalPlaces);
00622 }
00623 
00624 KexiDB::Field::Type KexiDB::intToFieldType( int type )
00625 {
00626     if (type<(int)KexiDB::Field::InvalidType || type>(int)KexiDB::Field::LastType) {
00627         KexiDBWarn << "KexiDB::intToFieldType(): invalid type " << type << endl;
00628         return KexiDB::Field::InvalidType;
00629     }
00630     return (KexiDB::Field::Type)type;
00631 }
00632 
00633 static bool setIntToFieldType( Field& field, const QVariant& value )
00634 {
00635     bool ok;
00636     const int intType = value.toInt(&ok);
00637     if (!ok || KexiDB::Field::InvalidType == intToFieldType(intType)) {//for sanity
00638         KexiDBWarn << "KexiDB::setFieldProperties(): invalid type" << endl;
00639         return false;
00640     }
00641     field.setType((KexiDB::Field::Type)intType);
00642     return true;
00643 }
00644 
00646 static KStaticDeleter< QAsciiDict<char> > KexiDB_builtinFieldPropertiesDeleter;
00648 QAsciiDict<char>* KexiDB_builtinFieldProperties = 0;
00649 
00650 bool KexiDB::isBuiltinTableFieldProperty( const QCString& propertyName )
00651 {
00652     if (!KexiDB_builtinFieldProperties) {
00653         KexiDB_builtinFieldPropertiesDeleter.setObject( KexiDB_builtinFieldProperties, new QAsciiDict<char>(499) );
00654 #define ADD(name) KexiDB_builtinFieldProperties->insert(name, (char*)1)
00655         ADD("type");
00656         ADD("primaryKey");
00657         ADD("indexed");
00658         ADD("autoIncrement");
00659         ADD("unique");
00660         ADD("notNull");
00661         ADD("allowEmpty");
00662         ADD("unsigned");
00663         ADD("name");
00664         ADD("caption");
00665         ADD("description");
00666         ADD("length");
00667         ADD("precision");
00668         ADD("defaultValue");
00669         ADD("width");
00670         ADD("visibleDecimalPlaces");
00672 #undef ADD
00673     }
00674     return KexiDB_builtinFieldProperties->find( propertyName );
00675 }
00676 
00677 bool KexiDB::setFieldProperties( Field& field, const QMap<QCString, QVariant>& values )
00678 {
00679     QMapConstIterator<QCString, QVariant> it;
00680     if ( (it = values.find("type")) != values.constEnd() ) {
00681         if (!setIntToFieldType(field, *it))
00682             return false;
00683     }
00684 
00685 #define SET_BOOLEAN_FLAG(flag, value) { \
00686         constraints |= KexiDB::Field::flag; \
00687         if (!value) \
00688             constraints ^= KexiDB::Field::flag; \
00689     }
00690     
00691     uint constraints = field.constraints();
00692     bool ok = true;
00693     if ( (it = values.find("primaryKey")) != values.constEnd() )
00694         SET_BOOLEAN_FLAG(PrimaryKey, (*it).toBool());
00695     if ( (it = values.find("indexed")) != values.constEnd() )
00696         SET_BOOLEAN_FLAG(Indexed, (*it).toBool());
00697     if ( (it = values.find("autoIncrement")) != values.constEnd() 
00698         && KexiDB::Field::isAutoIncrementAllowed(field.type()) )
00699         SET_BOOLEAN_FLAG(AutoInc, (*it).toBool());
00700     if ( (it = values.find("unique")) != values.constEnd() )
00701         SET_BOOLEAN_FLAG(Unique, (*it).toBool());
00702     if ( (it = values.find("notNull")) != values.constEnd() )
00703         SET_BOOLEAN_FLAG(NotNull, (*it).toBool());
00704     if ( (it = values.find("allowEmpty")) != values.constEnd() )
00705         SET_BOOLEAN_FLAG(NotEmpty, !(*it).toBool());
00706     field.setConstraints( constraints );
00707 
00708     uint options = 0;
00709     if ( (it = values.find("unsigned")) != values.constEnd()) {
00710         options |= KexiDB::Field::Unsigned;
00711         if (!(*it).toBool())
00712             options ^= KexiDB::Field::Unsigned;
00713     }
00714     field.setOptions( options );
00715 
00716     if ( (it = values.find("name")) != values.constEnd())
00717         field.setName( (*it).toString() );
00718     if ( (it = values.find("caption")) != values.constEnd())
00719         field.setCaption( (*it).toString() );
00720     if ( (it = values.find("description")) != values.constEnd())
00721         field.setDescription( (*it).toString() );
00722     if ( (it = values.find("length")) != values.constEnd())
00723         field.setLength( (*it).isNull() ? 0/*default*/ : (*it).toUInt(&ok) );
00724     if (!ok)
00725         return false;
00726     if ( (it = values.find("precision")) != values.constEnd())
00727         field.setPrecision( (*it).isNull() ? 0/*default*/ : (*it).toUInt(&ok) );
00728     if (!ok)
00729         return false;
00730     if ( (it = values.find("defaultValue")) != values.constEnd())
00731         field.setDefaultValue( *it );
00732     if ( (it = values.find("width")) != values.constEnd())
00733         field.setWidth( (*it).isNull() ? 0/*default*/ : (*it).toUInt(&ok) );
00734     if (!ok)
00735         return false;
00736     if ( (it = values.find("visibleDecimalPlaces")) != values.constEnd() 
00737       && KexiDB::supportsVisibleDecimalPlacesProperty(field.type()) )
00738         field.setVisibleDecimalPlaces( (*it).isNull() ? -1/*default*/ : (*it).toInt(&ok) );
00739     if (!ok)
00740         return false;
00741 
00742     // set custom properties
00743     typedef QMap<QCString, QVariant> PropertiesMap;
00744     foreach( PropertiesMap::ConstIterator, it, values ) {
00745         if (!isBuiltinTableFieldProperty( it.key() ) && !isExtendedTableFieldProperty( it.key() )) {
00746             field.setCustomProperty( it.key(), it.data() );
00747         }
00748     }
00749     return true;
00750 #undef SET_BOOLEAN_FLAG
00751 }
00752 
00754 static KStaticDeleter< QAsciiDict<char> > KexiDB_extendedPropertiesDeleter;
00756 QAsciiDict<char>* KexiDB_extendedProperties = 0;
00757 
00758 bool KexiDB::isExtendedTableFieldProperty( const QCString& propertyName )
00759 {
00760     if (!KexiDB_extendedProperties) {
00761         KexiDB_extendedPropertiesDeleter.setObject( KexiDB_extendedProperties, new QAsciiDict<char>(499, false) );
00762 #define ADD(name) KexiDB_extendedProperties->insert(name, (char*)1)
00763         ADD("visibleDecimalPlaces");
00764         ADD("rowSource");
00765         ADD("rowSourceType");
00766         ADD("rowSourceValues");
00767         ADD("boundColumn");
00768         ADD("visibleColumn");
00769         ADD("columnWidths");
00770         ADD("showColumnHeaders");
00771         ADD("listRows");
00772         ADD("limitToList");
00773         ADD("displayWidget");
00774 #undef ADD
00775     }
00776     return KexiDB_extendedProperties->find( propertyName );
00777 }
00778 
00779 bool KexiDB::setFieldProperty( Field& field, const QCString& propertyName, const QVariant& value )
00780 {
00781 #define SET_BOOLEAN_FLAG(flag, value) { \
00782             constraints |= KexiDB::Field::flag; \
00783             if (!value) \
00784                 constraints ^= KexiDB::Field::flag; \
00785             field.setConstraints( constraints ); \
00786             return true; \
00787         }
00788 #define GET_INT(method) { \
00789             const uint ival = value.toUInt(&ok); \
00790             if (!ok) \
00791                 return false; \
00792             field.method( ival ); \
00793             return true; \
00794         }
00795 
00796     if (propertyName.isEmpty())
00797         return false;
00798 
00799     bool ok;
00800     if (KexiDB::isExtendedTableFieldProperty(propertyName)) {
00801         //a little speedup: identify extended property in O(1)
00802         if ( "visibleDecimalPlaces" == propertyName
00803             && KexiDB::supportsVisibleDecimalPlacesProperty(field.type()) )
00804         {
00805             GET_INT( setVisibleDecimalPlaces );
00806         }
00807         else {
00808             if (!field.table()) {
00809                 KexiDBWarn << QString("KexiDB::setFieldProperty() Cannot set \"%1\" property - no table assinged for field!")
00810                     .arg(propertyName) << endl;
00811             }
00812             else {
00813                 LookupFieldSchema *lookup = field.table()->lookupFieldSchema(field);
00814                 const bool hasLookup = lookup != 0;
00815                 if (!hasLookup)
00816                     lookup = new LookupFieldSchema();
00817                 if (LookupFieldSchema::setProperty( *lookup, propertyName, value )) {
00818                     if (!hasLookup && lookup)
00819                         field.table()->setLookupFieldSchema( field.name(), lookup );
00820                     return true;
00821                 }
00822                 delete lookup;
00823             }
00824         }
00825     }
00826     else {//non-extended
00827         if ( "type" == propertyName )
00828             return setIntToFieldType(field, value);
00829     
00830         uint constraints = field.constraints();
00831         if ( "primaryKey" == propertyName )
00832             SET_BOOLEAN_FLAG(PrimaryKey, value.toBool());
00833         if ( "indexed" == propertyName )
00834             SET_BOOLEAN_FLAG(Indexed, value.toBool());
00835         if ( "autoIncrement" == propertyName
00836             && KexiDB::Field::isAutoIncrementAllowed(field.type()) )
00837             SET_BOOLEAN_FLAG(AutoInc, value.toBool());
00838         if ( "unique" == propertyName )
00839             SET_BOOLEAN_FLAG(Unique, value.toBool());
00840         if ( "notNull" == propertyName )
00841             SET_BOOLEAN_FLAG(NotNull, value.toBool());
00842         if ( "allowEmpty" == propertyName )
00843             SET_BOOLEAN_FLAG(NotEmpty, !value.toBool());
00844 
00845         uint options = 0;
00846         if ( "unsigned" == propertyName ) {
00847             options |= KexiDB::Field::Unsigned;
00848             if (!value.toBool())
00849                 options ^= KexiDB::Field::Unsigned;
00850             field.setOptions( options );
00851             return true;
00852         }
00853 
00854         if ( "name" == propertyName ) {
00855             if (value.toString().isEmpty())
00856                 return false;
00857             field.setName( value.toString() );
00858             return true;
00859         }
00860         if ( "caption" == propertyName ) {
00861             field.setCaption( value.toString() );
00862             return true;
00863         }
00864         if ( "description" == propertyName ) {
00865             field.setDescription( value.toString() );
00866             return true;
00867         }
00868         if ( "length" == propertyName )
00869             GET_INT( setLength );
00870         if ( "precision" == propertyName )
00871             GET_INT( setPrecision );
00872         if ( "defaultValue" == propertyName ) {
00873             field.setDefaultValue( value );
00874             return true;
00875         }
00876         if ( "width" == propertyName )
00877             GET_INT( setWidth );
00878 
00879         // last chance that never fails: custom field property
00880         field.setCustomProperty(propertyName, value);
00881     }
00882 
00883     KexiDBWarn << "KexiDB::setFieldProperty() property \"" << propertyName << "\" not found!" << endl;
00884     return false;
00885 #undef SET_BOOLEAN_FLAG
00886 #undef GET_INT
00887 }
00888 
00889 int KexiDB::loadIntPropertyValueFromDom( const QDomNode& node, bool* ok )
00890 {
00891     QCString valueType = node.nodeName().latin1();
00892     if (valueType.isEmpty() || valueType!="number") {
00893         if (ok)
00894             *ok = false;
00895         return 0;
00896     }
00897     const QString text( QDomNode(node).toElement().text() );
00898     int val = text.toInt(ok);
00899     return val;
00900 }
00901 
00902 QString KexiDB::loadStringPropertyValueFromDom( const QDomNode& node, bool* ok )
00903 {
00904     QCString valueType = node.nodeName().latin1();
00905     if (valueType!="string") {
00906         if (ok)
00907             *ok = false;
00908         return 0;
00909     }
00910     return QDomNode(node).toElement().text();
00911 }
00912 
00913 QVariant KexiDB::loadPropertyValueFromDom( const QDomNode& node )
00914 {
00915     QCString valueType = node.nodeName().latin1();
00916     if (valueType.isEmpty())
00917         return QVariant();
00918     const QString text( QDomNode(node).toElement().text() );
00919     bool ok;
00920     if (valueType == "string") {
00921         return text;
00922     }
00923     else if (valueType == "cstring") {
00924         return QCString(text.latin1());
00925     }
00926     else if (valueType == "number") { // integer or double
00927         if (text.find('.')!=-1) {
00928             double val = text.toDouble(&ok);
00929             if (ok)
00930                 return val;
00931         }
00932         else {
00933             const int val = text.toInt(&ok);
00934             if (ok)
00935                 return val;
00936             const Q_LLONG valLong = text.toLongLong(&ok);
00937             if (ok)
00938                 return valLong;
00939         }
00940     }
00941     else if (valueType == "bool") {
00942         return QVariant(text.lower()=="true" || text=="1", 1);
00943     }
00945     KexiDBWarn << "loadPropertyValueFromDom(): unknown type '" << valueType << "'" << endl;
00946     return QVariant();
00947 }
00948 
00949 QDomElement KexiDB::saveNumberElementToDom(QDomDocument& doc, QDomElement& parentEl, 
00950     const QString& elementName, int value)
00951 {
00952     QDomElement el( doc.createElement(elementName) );
00953     parentEl.appendChild( el );
00954     QDomElement numberEl( doc.createElement("number") );
00955     el.appendChild( numberEl );
00956     numberEl.appendChild( doc.createTextNode( QString::number(value) ) );
00957     return el;
00958 }
00959 
00960 QDomElement KexiDB::saveBooleanElementToDom(QDomDocument& doc, QDomElement& parentEl, 
00961     const QString& elementName, bool value)
00962 {
00963     QDomElement el( doc.createElement(elementName) );
00964     parentEl.appendChild( el );
00965     QDomElement numberEl( doc.createElement("bool") );
00966     el.appendChild( numberEl );
00967     numberEl.appendChild( doc.createTextNode( 
00968         value ? QString::fromLatin1("true") : QString::fromLatin1("false") ) );
00969     return el;
00970 }
00971 
00973 static KStaticDeleter< QValueVector<QVariant> > KexiDB_emptyValueForTypeCacheDeleter;
00974 QValueVector<QVariant> *KexiDB_emptyValueForTypeCache = 0;
00975 
00976 QVariant KexiDB::emptyValueForType( KexiDB::Field::Type type )
00977 {
00978     if (!KexiDB_emptyValueForTypeCache) {
00979         KexiDB_emptyValueForTypeCacheDeleter.setObject( KexiDB_emptyValueForTypeCache, 
00980             new QValueVector<QVariant>(int(Field::LastType)+1) );
00981 #define ADD(t, value) (*KexiDB_emptyValueForTypeCache)[t]=value;
00982         ADD(Field::Byte, 0);
00983         ADD(Field::ShortInteger, 0);
00984         ADD(Field::Integer, 0);
00985         ADD(Field::BigInteger, 0);
00986         ADD(Field::Boolean, QVariant(false, 0));
00987         ADD(Field::Float, 0.0);
00988         ADD(Field::Double, 0.0);
00990         ADD(Field::Text, QString(" "));
00991         ADD(Field::LongText, QString(" "));
00992         ADD(Field::BLOB, QByteArray());
00993 #undef ADD
00994     }
00995     const QVariant val( KexiDB_emptyValueForTypeCache->at(
00996         (type<=Field::LastType) ? type : Field::InvalidType) );
00997     if (!val.isNull())
00998         return val;
00999     else { //special cases
01000         if (type==Field::Date)
01001             return QDate::currentDate();
01002         if (type==Field::DateTime)
01003             return QDateTime::currentDateTime();
01004         if (type==Field::Time)
01005             return QTime::currentTime();
01006     }
01007     KexiDBWarn << "KexiDB::emptyValueForType() no value for type " 
01008         << Field::typeName(type) << endl;
01009     return QVariant();
01010 }
01011 
01013 static KStaticDeleter< QValueVector<QVariant> > KexiDB_notEmptyValueForTypeCacheDeleter;
01014 QValueVector<QVariant> *KexiDB_notEmptyValueForTypeCache = 0;
01015 
01016 QVariant KexiDB::notEmptyValueForType( KexiDB::Field::Type type )
01017 {
01018     if (!KexiDB_notEmptyValueForTypeCache) {
01019         KexiDB_notEmptyValueForTypeCacheDeleter.setObject( KexiDB_notEmptyValueForTypeCache, 
01020             new QValueVector<QVariant>(int(Field::LastType)+1) );
01021 #define ADD(t, value) (*KexiDB_notEmptyValueForTypeCache)[t]=value;
01022         // copy most of the values
01023         for (int i = int(Field::InvalidType) + 1; i<=Field::LastType; i++) {
01024             if (i==Field::Date || i==Field::DateTime || i==Field::Time)
01025                 continue; //'current' value will be returned
01026             if (i==Field::Text || i==Field::LongText) {
01027                 ADD(i, QVariant(QString("")));
01028                 continue;
01029             }
01030             if (i==Field::BLOB) {
01032                 QByteArray ba;
01033                 QBuffer buffer( ba );
01034                 buffer.open( IO_WriteOnly );
01035                 QPixmap pm(SmallIcon("filenew"));
01036                 pm.save( &buffer, "PNG" );
01037                 ADD(i, ba);
01038                 continue;
01039             }
01040             ADD(i, KexiDB::emptyValueForType((Field::Type)i));
01041         }
01042 #undef ADD
01043     }
01044     const QVariant val( KexiDB_notEmptyValueForTypeCache->at(
01045         (type<=Field::LastType) ? type : Field::InvalidType) );
01046     if (!val.isNull())
01047         return val;
01048     else { //special cases
01049         if (type==Field::Date)
01050             return QDate::currentDate();
01051         if (type==Field::DateTime)
01052             return QDateTime::currentDateTime();
01053         if (type==Field::Time)
01054             return QTime::currentTime();
01055     }
01056     KexiDBWarn << "KexiDB::notEmptyValueForType() no value for type " 
01057         << Field::typeName(type) << endl;
01058     return QVariant();
01059 }
01060 
01061 QString KexiDB::escapeBLOB(const QByteArray& array, BLOBEscapingType type)
01062 {
01063     const int size = array.size();
01064     if (size==0)
01065         return QString::null;
01066     int escaped_length = size*2;
01067     if (type == BLOBEscape0xHex || type == BLOBEscapeOctal)
01068         escaped_length += 2/*0x or X'*/;
01069     else if (type == BLOBEscapeXHex)
01070         escaped_length += 3; //X' + '
01071     QString str;
01072     str.reserve(escaped_length);
01073     if (str.capacity() < (uint)escaped_length) {
01074         KexiDBWarn << "KexiDB::Driver::escapeBLOB(): no enough memory (cannot allocate "<< 
01075             escaped_length<<" chars)" << endl;
01076         return QString::null;
01077     }
01078     if (type == BLOBEscapeXHex)
01079         str = QString::fromLatin1("X'");
01080     else if (type == BLOBEscape0xHex)
01081         str = QString::fromLatin1("0x");
01082     else if (type == BLOBEscapeOctal)
01083         str = QString::fromLatin1("'");
01084     
01085     int new_length = str.length(); //after X' or 0x, etc.
01086     if (type == BLOBEscapeOctal) {
01087         // only escape nonprintable characters as in Table 8-7:
01088         // http://www.postgresql.org/docs/8.1/interactive/datatype-binary.html
01089         // i.e. escape for bytes: < 32, >= 127, 39 ('), 92(\). 
01090         for (int i = 0; i < size; i++) {
01091             const unsigned char val = array[i];
01092             if (val<32 || val>=127 || val==39 || val==92) {
01093                 str[new_length++] = '\\';
01094                 str[new_length++] = '\\';
01095                 str[new_length++] = '0' + val/64;
01096                 str[new_length++] = '0' + (val % 64) / 8;
01097                 str[new_length++] = '0' + val % 8;
01098             }
01099             else {
01100                 str[new_length++] = val;
01101             }
01102         }
01103     }
01104     else {
01105         for (int i = 0; i < size; i++) {
01106             const unsigned char val = array[i];
01107             str[new_length++] = (val/16) < 10 ? ('0'+(val/16)) : ('A'+(val/16)-10);
01108             str[new_length++] = (val%16) < 10 ? ('0'+(val%16)) : ('A'+(val%16)-10);
01109         }
01110     }
01111     if (type == BLOBEscapeXHex || type == BLOBEscapeOctal)
01112         str[new_length++] = '\'';
01113     return str;
01114 }
01115 
01116 QByteArray KexiDB::pgsqlByteaToByteArray(const char* data, int length)
01117 {
01118     QByteArray array;
01119     int output=0;
01120     for (int pass=0; pass<2; pass++) {//2 passes to avoid allocating buffer twice:
01121                                         //  0: count #of chars; 1: copy data
01122         const char* s = data;
01123         const char* end = s + length;
01124         if (pass==1) {
01125             KexiDBDbg << "processBinaryData(): real size == " << output << endl;
01126             array.resize(output);
01127             output=0;
01128         }
01129         for (int input=0; s < end; output++) {
01130     //      KexiDBDbg<<(int)s[0]<<" "<<(int)s[1]<<" "<<(int)s[2]<<" "<<(int)s[3]<<" "<<(int)s[4]<<endl;
01131             if (s[0]=='\\' && (s+1)<end) {
01132                 //special cases as in http://www.postgresql.org/docs/8.1/interactive/datatype-binary.html
01133                 if (s[1]=='\'') {// \'
01134                     if (pass==1)
01135                         array[output] = '\'';
01136                     s+=2;
01137                 }
01138                 else if (s[1]=='\\') { // 2 backslashes 
01139                     if (pass==1)
01140                         array[output] = '\\';
01141                     s+=2;
01142                 }
01143                 else if ((input+3)<length) {// \\xyz where xyz are 3 octal digits
01144                     if (pass==1)
01145                         array[output] = char( (int(s[1]-'0')*8+int(s[2]-'0'))*8+int(s[3]-'0') );
01146                     s+=4;
01147                 }
01148                 else {
01149                     KexiDBDrvWarn << "processBinaryData(): no octal value after backslash" << endl;
01150                     s++;
01151                 }
01152             }
01153             else {
01154                 if (pass==1)
01155                     array[output] = s[0];
01156                 s++;
01157             }
01158     //      KexiDBDbg<<output<<": "<<(int)array[output]<<endl;
01159         }
01160     }
01161     return array;
01162 }
01163 
01164 QString KexiDB::variantToString( const QVariant& v )
01165 {
01166     if (v.type()==QVariant::ByteArray)
01167         return KexiDB::escapeBLOB(v.toByteArray(), KexiDB::BLOBEscapeHex);
01168     return v.toString();
01169 }
01170 
01171 QVariant KexiDB::stringToVariant( const QString& s, QVariant::Type type, bool &ok )
01172 {
01173     if (s.isNull()) {
01174         ok = true;
01175         return QVariant();
01176     }
01177     if (QVariant::Invalid==type) {
01178         ok = false;
01179         return QVariant();
01180     }
01181     if (type==QVariant::ByteArray) {//special case: hex string
01182         const uint len = s.length();
01183         QByteArray ba(len/2 + len%2);
01184         for (uint i=0; i<(len-1); i+=2) {
01185             int c = s.mid(i,2).toInt(&ok, 16);
01186             if (!ok) {
01187                 KexiDBWarn << "KexiDB::stringToVariant(): Error in digit " << i << endl;
01188                 return QVariant();
01189             }
01190             ba[i/2] = (char)c;
01191         }
01192         ok = true;
01193         return ba;
01194     }
01195     QVariant result(s);
01196     if (!result.cast( type )) {
01197         ok = false;
01198         return QVariant();
01199     }
01200     ok = true;
01201     return result;
01202 }
01203 
01204 bool KexiDB::isDefaultValueAllowed( KexiDB::Field* field )
01205 {
01206     return field && !field->isUniqueKey();
01207 }
01208 
01209 void KexiDB::getLimitsForType(Field::Type type, int &minValue, int &maxValue)
01210 {
01211     switch (type) {
01212     case Field::Byte:
01214         minValue = 0;
01215         maxValue = 255;
01216         break;
01217     case Field::ShortInteger:
01218         minValue = -32768;
01219         maxValue = 32767;
01220         break;
01221     case Field::Integer:
01222     case Field::BigInteger: //cannot return anything larger
01223     default:
01224         minValue = (int)-0x07FFFFFFF;
01225         maxValue = (int)(0x080000000-1);
01226     }
01227 }
01228 
01229 void KexiDB::debugRowData(const RowData& rowData)
01230 {
01231     KexiDBDbg << QString("ROW DATA (%1 columns):").arg(rowData.count()) << endl;
01232     foreach(RowData::ConstIterator, it, rowData)
01233         KexiDBDbg << "- " << (*it) << endl;
01234 }
01235 
01236 Field::Type KexiDB::maximumForIntegerTypes(Field::Type t1, Field::Type t2)
01237 {
01238     if (!Field::isIntegerType(t1) || !Field::isIntegerType(t2))
01239         return Field::InvalidType;
01240     if (t1==t2)
01241         return t2;
01242     if (t1==Field::ShortInteger && t2!=Field::Integer && t2!=Field::BigInteger)
01243         return t1;
01244     if (t1==Field::Integer && t2!=Field::BigInteger)
01245         return t1;
01246     if (t1==Field::BigInteger)
01247         return t1;
01248     return KexiDB::maximumForIntegerTypes(t2, t1); //swap
01249 }
01250 
01251 QString KexiDB::simplifiedTypeName(const Field& field)
01252 {
01253     if (field.isNumericType())
01254         return i18n("Number"); //simplify
01255     else if (field.type() == Field::BLOB)
01257         return i18n("Image"); //simplify
01258 
01259     return field.typeGroupName();
01260 }
01261 
01262 #include "utils_p.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys