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
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
00124
00125
00126 if (!obj || !obj->error())
00127 return;
00128
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
00345 if (!drv || manager.error()) {
00346
00347 m_dlg->error(&manager);
00348 return;
00349 }
00350 KexiDB::Connection * conn = drv->createConnection(m_connData);
00351 if (!conn || drv->error()) {
00352
00353 delete conn;
00354 m_dlg->error(drv);
00355 return;
00356 }
00357 if (!conn->connect() || conn->error()) {
00358
00359 m_dlg->error(conn);
00360 delete conn;
00361 return;
00362 }
00363
00364
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 )
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
00416 bool notResponding = false;
00417 if (m_elapsedTime >= 1000*5) {
00418 m_stopWaiting = true;
00419 notResponding = true;
00420 }
00421 if (m_stopWaiting) {
00422 m_timer.disconnect(this);
00423 m_timer.stop();
00424 slotCancel();
00425
00426
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
00444
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
00458
00459
00460
00461
00462
00463
00464 m_wait.wait();
00465 }
00466
00467 void ConnectionTestDialog::slotCancel()
00468 {
00469
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;
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;
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;
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 ));
00601 uint i = s.length()-1;
00602 while (i>0 && s[i]=='0')
00603 i--;
00604 if (s[i]=='.')
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)) {
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 : (*it).toUInt(&ok) );
00714 if (!ok)
00715 return false;
00716 if ( (it = values.find("precision")) != values.constEnd())
00717 field.setPrecision( (*it).isNull() ? 0 : (*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 : (*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 : (*it).toInt(&ok) );
00729 if (!ok)
00730 return false;
00731
00732
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
00781 if ( "visibleDecimalPlaces" == propertyName
00782 && KexiDB::supportsVisibleDecimalPlacesProperty(field.type()) )
00783 GET_INT( setVisibleDecimalPlaces );
00784 }
00785 else {
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
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") {
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 {
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
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;
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 {
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;
01027 else if (type == BLOBEscapeXHex)
01028 escaped_length += 3;
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();
01044 if (type == BLOBEscapeOctal) {
01045
01046
01047
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++) {
01079
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
01089 if (s[0]=='\\' && (s+1)<end) {
01090
01091 if (s[1]=='\'') {
01092 if (pass==1)
01093 array[output] = '\'';
01094 s+=2;
01095 }
01096 else if (s[1]=='\\') {
01097 if (pass==1)
01098 array[output] = '\\';
01099 s+=2;
01100 }
01101 else if ((input+3)<length) {
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
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) {
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:
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);
01207 }
01208
01209 #include "utils_p.moc"