00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00125
00126
00127 if (!obj || !obj->error())
00128 return;
00129
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
00355 if (!drv || manager.error()) {
00356
00357 m_dlg->error(&manager);
00358 return;
00359 }
00360 KexiDB::Connection * conn = drv->createConnection(m_connData);
00361 if (!conn || drv->error()) {
00362
00363 delete conn;
00364 m_dlg->error(drv);
00365 return;
00366 }
00367 if (!conn->connect() || conn->error()) {
00368
00369 m_dlg->error(conn);
00370 delete conn;
00371 return;
00372 }
00373
00374
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 )
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
00426 bool notResponding = false;
00427 if (m_elapsedTime >= 1000*5) {
00428 m_stopWaiting = true;
00429 notResponding = true;
00430 }
00431 if (m_stopWaiting) {
00432 m_timer.disconnect(this);
00433 m_timer.stop();
00434 slotCancel();
00435
00436
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
00454
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
00468
00469
00470
00471
00472
00473
00474 m_wait.wait();
00475 }
00476
00477 void ConnectionTestDialog::slotCancel()
00478 {
00479
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;
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;
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;
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 ));
00611 uint i = s.length()-1;
00612 while (i>0 && s[i]=='0')
00613 i--;
00614 if (s[i]=='.')
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)) {
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 : (*it).toUInt(&ok) );
00724 if (!ok)
00725 return false;
00726 if ( (it = values.find("precision")) != values.constEnd())
00727 field.setPrecision( (*it).isNull() ? 0 : (*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 : (*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 : (*it).toInt(&ok) );
00739 if (!ok)
00740 return false;
00741
00742
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
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 {
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
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") {
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 {
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
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;
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 {
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;
01069 else if (type == BLOBEscapeXHex)
01070 escaped_length += 3;
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();
01086 if (type == BLOBEscapeOctal) {
01087
01088
01089
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++) {
01121
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
01131 if (s[0]=='\\' && (s+1)<end) {
01132
01133 if (s[1]=='\'') {
01134 if (pass==1)
01135 array[output] = '\'';
01136 s+=2;
01137 }
01138 else if (s[1]=='\\') {
01139 if (pass==1)
01140 array[output] = '\\';
01141 s+=2;
01142 }
01143 else if ((input+3)<length) {
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
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) {
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:
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);
01249 }
01250
01251 #include "utils_p.moc"