Kexi API Documentation (2.0 alpha)

kexidb/utils.cpp

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