/* This file is part of the KDE project Copyright (C) 2004-2007 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "utils.h" #include "cursor.h" #include "drivermanager.h" #include "lookupfieldschema.h" #include #include #include #include #include #include #include #include #include #include #include #include "utils_p.h" using namespace KexiDB; //! Cache struct TypeCache { TQMap< uint, TypeGroupList > tlist; TQMap< uint, TQStringList > nlist; TQMap< uint, TQStringList > slist; TQMap< uint, Field::Type > def_tlist; }; static KStaticDeleter KexiDB_typeCacheDeleter; TypeCache *KexiDB_typeCache = 0; static void initList() { KexiDB_typeCacheDeleter.setObject( KexiDB_typeCache, new TypeCache() ); for (uint t=0; t<=KexiDB::Field::LastType; t++) { const uint tg = KexiDB::Field::typeGroup( t ); TypeGroupList list; TQStringList name_list, str_list; if (KexiDB_typeCache->tlist.find( tg )!=KexiDB_typeCache->tlist.end()) { list = KexiDB_typeCache->tlist[ tg ]; name_list = KexiDB_typeCache->nlist[ tg ]; str_list = KexiDB_typeCache->slist[ tg ]; } list+= t; name_list += KexiDB::Field::typeName( t ); str_list += KexiDB::Field::typeString( t ); KexiDB_typeCache->tlist[ tg ] = list; KexiDB_typeCache->nlist[ tg ] = name_list; KexiDB_typeCache->slist[ tg ] = str_list; } KexiDB_typeCache->def_tlist[ Field::InvalidGroup ] = Field::InvalidType; KexiDB_typeCache->def_tlist[ Field::TextGroup ] = Field::Text; KexiDB_typeCache->def_tlist[ Field::IntegerGroup ] = Field::Integer; KexiDB_typeCache->def_tlist[ Field::FloatGroup ] = Field::Double; KexiDB_typeCache->def_tlist[ Field::BooleanGroup ] = Field::Boolean; KexiDB_typeCache->def_tlist[ Field::DateTimeGroup ] = Field::Date; KexiDB_typeCache->def_tlist[ Field::BLOBGroup ] = Field::BLOB; } const TypeGroupList KexiDB::typesForGroup(KexiDB::Field::TypeGroup typeGroup) { if (!KexiDB_typeCache) initList(); return KexiDB_typeCache->tlist[ typeGroup ]; } TQStringList KexiDB::typeNamesForGroup(KexiDB::Field::TypeGroup typeGroup) { if (!KexiDB_typeCache) initList(); return KexiDB_typeCache->nlist[ typeGroup ]; } TQStringList KexiDB::typeStringsForGroup(KexiDB::Field::TypeGroup typeGroup) { if (!KexiDB_typeCache) initList(); return KexiDB_typeCache->slist[ typeGroup ]; } KexiDB::Field::Type KexiDB::defaultTypeForGroup(KexiDB::Field::TypeGroup typeGroup) { if (!KexiDB_typeCache) initList(); return (typeGroup <= Field::LastTypeGroup) ? KexiDB_typeCache->def_tlist[ typeGroup ] : Field::InvalidType; } void KexiDB::getHTMLErrorMesage(Object* obj, TQString& msg, TQString &details) { Connection *conn = 0; if (!obj || !obj->error()) { if (dynamic_cast(obj)) { conn = dynamic_cast(obj)->connection(); obj = conn; } else { return; } } // if (dynamic_cast(obj)) { // conn = dynamic_cast(obj); //} if (!obj || !obj->error()) return; //lower level message is added to the details, if there is alread message specified if (!obj->msgTitle().isEmpty()) msg += "

" + obj->msgTitle(); if (msg.isEmpty()) msg = "

" + obj->errorMsg(); else details += "

" + obj->errorMsg(); if (!obj->serverErrorMsg().isEmpty()) details += "

" +i18n("Message from server:") + "
" + obj->serverErrorMsg(); if (!obj->recentSQLString().isEmpty()) details += "

" +i18n("SQL statement:") + TQString("
%1").arg(obj->recentSQLString()); int serverResult; TQString serverResultName; if (obj->serverResult()!=0) { serverResult = obj->serverResult(); serverResultName = obj->serverResultName(); } else { serverResult = obj->previousServerResult(); serverResultName = obj->previousServerResultName(); } if (!serverResultName.isEmpty()) details += (TQString("

")+i18n("Server result name:")+"
"+serverResultName); if (!details.isEmpty() && (!obj->serverErrorMsg().isEmpty() || !obj->recentSQLString().isEmpty() || !serverResultName.isEmpty() || serverResult!=0) ) { details += (TQString("

")+i18n("Server result number:")+"
"+TQString::number(serverResult)); } if (!details.isEmpty() && !details.startsWith("")) { if (details.startsWith("

")) details = TQString::fromLatin1("")+details; else details = TQString::fromLatin1("

")+details; } } void KexiDB::getHTMLErrorMesage(Object* obj, TQString& msg) { getHTMLErrorMesage(obj, msg, msg); } void KexiDB::getHTMLErrorMesage(Object* obj, ResultInfo *result) { getHTMLErrorMesage(obj, result->msg, result->desc); } int KexiDB::idForObjectName( Connection &conn, const TQString& objName, int objType ) { RowData data; if (true!=conn.querySingleRecord(TQString("select o_id from kexi__objects where lower(o_name)='%1' and o_type=%2") .arg(objName.lower()).arg(objType), data)) return 0; bool ok; int id = data[0].toInt(&ok); return ok ? id : 0; } //----------------------------------------- TableOrQuerySchema::TableOrQuerySchema(Connection *conn, const TQCString& name) : m_name(name) { m_table = conn->tableSchema(TQString(name)); m_query = m_table ? 0 : conn->querySchema(TQString(name)); if (!m_table && !m_query) KexiDBWarn << "TableOrQuery(FieldList &tableOrQuery) : " " tableOrQuery is neither table nor query!" << endl; } TableOrQuerySchema::TableOrQuerySchema(Connection *conn, const TQCString& name, bool table) : m_name(name) , m_table(table ? conn->tableSchema(TQString(name)) : 0) , m_query(table ? 0 : conn->querySchema(TQString(name))) { if (table && !m_table) KexiDBWarn << "TableOrQuery(Connection *conn, const TQCString& name, bool table) : " "no table specified!" << endl; if (!table && !m_query) KexiDBWarn << "TableOrQuery(Connection *conn, const TQCString& name, bool table) : " "no query specified!" << endl; } TableOrQuerySchema::TableOrQuerySchema(FieldList &tableOrQuery) : m_table(dynamic_cast(&tableOrQuery)) , m_query(dynamic_cast(&tableOrQuery)) { if (!m_table && !m_query) KexiDBWarn << "TableOrQuery(FieldList &tableOrQuery) : " " tableOrQuery is nether table nor query!" << endl; } TableOrQuerySchema::TableOrQuerySchema(Connection *conn, int id) { m_table = conn->tableSchema(id); m_query = m_table ? 0 : conn->querySchema(id); if (!m_table && !m_query) KexiDBWarn << "TableOrQuery(Connection *conn, int id) : no table or query found for id==" << id << "!" << endl; } TableOrQuerySchema::TableOrQuerySchema(TableSchema* table) : m_table(table) , m_query(0) { if (!m_table) KexiDBWarn << "TableOrQuery(TableSchema* table) : no table specified!" << endl; } TableOrQuerySchema::TableOrQuerySchema(QuerySchema* query) : m_table(0) , m_query(query) { if (!m_query) KexiDBWarn << "TableOrQuery(QuerySchema* query) : no query specified!" << endl; } uint TableOrQuerySchema::fieldCount() const { if (m_table) return m_table->fieldCount(); if (m_query) return m_query->fieldsExpanded().size(); return 0; } const QueryColumnInfo::Vector TableOrQuerySchema::columns(bool unique) { if (m_table) return m_table->query()->fieldsExpanded(unique ? QuerySchema::Unique : QuerySchema::Default); if (m_query) return m_query->fieldsExpanded(unique ? QuerySchema::Unique : QuerySchema::Default); KexiDBWarn << "TableOrQuerySchema::column() : no query or table specified!" << endl; return QueryColumnInfo::Vector(); } TQCString TableOrQuerySchema::name() const { if (m_table) return m_table->name().latin1(); if (m_query) return m_query->name().latin1(); return m_name; } TQString TableOrQuerySchema::captionOrName() const { SchemaData *sdata = m_table ? static_cast(m_table) : static_cast(m_query); if (!sdata) return m_name; return sdata->caption().isEmpty() ? sdata->name() : sdata->caption(); } Field* TableOrQuerySchema::field(const TQString& name) { if (m_table) return m_table->field(name); if (m_query) return m_query->field(name); return 0; } QueryColumnInfo* TableOrQuerySchema::columnInfo(const TQString& name) { if (m_table) return m_table->query()->columnInfo(name); if (m_query) return m_query->columnInfo(name); return 0; } TQString TableOrQuerySchema::debugString() { if (m_table) return m_table->debugString(); else if (m_query) return m_query->debugString(); return TQString(); } void TableOrQuerySchema::debug() { if (m_table) return m_table->debug(); else if (m_query) return m_query->debug(); } Connection* TableOrQuerySchema::connection() const { if (m_table) return m_table->connection(); else if (m_query) return m_query->connection(); return 0; } //------------------------------------------ class ConnectionTestThread : public TQThread { public: ConnectionTestThread(ConnectionTestDialog *dlg, const KexiDB::ConnectionData& connData); virtual void run(); protected: ConnectionTestDialog* m_dlg; KexiDB::ConnectionData m_connData; }; ConnectionTestThread::ConnectionTestThread(ConnectionTestDialog* dlg, const KexiDB::ConnectionData& connData) : m_dlg(dlg), m_connData(connData) { } void ConnectionTestThread::run() { KexiDB::DriverManager manager; KexiDB::Driver* drv = manager.driver(m_connData.driverName); // KexiGUIMessageHandler msghdr; if (!drv || manager.error()) { //move msghdr.showErrorMessage(&Kexi::driverManager()); m_dlg->error(&manager); return; } KexiDB::Connection * conn = drv->createConnection(m_connData); if (!conn || drv->error()) { //move msghdr.showErrorMessage(drv); delete conn; m_dlg->error(drv); return; } if (!conn->connect() || conn->error()) { //move msghdr.showErrorMessage(conn); m_dlg->error(conn); delete conn; return; } // SQL database backends like PostgreSQL require executing "USE database" // if we really want to know connection to the server succeeded. TQString tmpDbName; if (!conn->useTemporaryDatabaseIfNeeded( tmpDbName )) { m_dlg->error(conn); delete conn; return; } delete conn; m_dlg->error(0); } ConnectionTestDialog::ConnectionTestDialog(TQWidget* parent, const KexiDB::ConnectionData& data, KexiDB::MessageHandler& msgHandler) : KProgressDialog(parent, "testconn_dlg", i18n("Test Connection"), i18n("Testing connection to %1 database server...") .arg(data.serverInfoString(true)), true /*modal*/) , m_thread(new ConnectionTestThread(this, data)) , m_connData(data) , m_msgHandler(&msgHandler) , m_elapsedTime(0) , m_errorObj(0) , m_stopWaiting(false) { showCancelButton(true); progressBar()->setPercentageVisible(false); progressBar()->setTotalSteps(0); connect(&m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotTimeout())); adjustSize(); resize(250, height()); } ConnectionTestDialog::~ConnectionTestDialog() { m_wait.wakeAll(); m_thread->terminate(); delete m_thread; } int ConnectionTestDialog::exec() { m_timer.start(20); m_thread->start(); const int res = KProgressDialog::exec(); m_thread->wait(); m_timer.stop(); return res; } void ConnectionTestDialog::slotTimeout() { // KexiDBDbg << "ConnectionTestDialog::slotTimeout() " << m_errorObj << endl; bool notResponding = false; if (m_elapsedTime >= 1000*5) {//5 seconds m_stopWaiting = true; notResponding = true; } if (m_stopWaiting) { m_timer.disconnect(this); m_timer.stop(); slotCancel(); // reject(); // close(); if (m_errorObj) { m_msgHandler->showErrorMessage(m_errorObj); m_errorObj = 0; } else if (notResponding) { KMessageBox::sorry(0, i18n("Test connection to %1 database server failed. The server is not responding.") .arg(m_connData.serverInfoString(true)), i18n("Test Connection")); } else { KMessageBox::information(0, i18n("Test connection to %1 database server established successfully.") .arg(m_connData.serverInfoString(true)), i18n("Test Connection")); } // slotCancel(); // reject(); m_wait.wakeAll(); return; } m_elapsedTime += 20; progressBar()->setProgress( m_elapsedTime ); } void ConnectionTestDialog::error(KexiDB::Object *obj) { KexiDBDbg << "ConnectionTestDialog::error()" << endl; m_stopWaiting = true; m_errorObj = obj; /* reject(); m_msgHandler->showErrorMessage(obj); if (obj) { } else { accept(); }*/ m_wait.wait(); } void ConnectionTestDialog::slotCancel() { // m_wait.wakeAll(); m_thread->terminate(); m_timer.disconnect(this); m_timer.stop(); KProgressDialog::slotCancel(); } void KexiDB::connectionTestDialog(TQWidget* parent, const KexiDB::ConnectionData& data, KexiDB::MessageHandler& msgHandler) { ConnectionTestDialog dlg(parent, data, msgHandler); dlg.exec(); } int KexiDB::rowCount(Connection &conn, const TQString& sql) { int count = -1; //will be changed only on success of querySingleNumber() TQString selectSql( TQString::fromLatin1("SELECT COUNT() FROM (") + sql + ")" ); conn.querySingleNumber(selectSql, count); return count; } int KexiDB::rowCount(const KexiDB::TableSchema& tableSchema) { //! @todo does not work with non-SQL data sources if (!tableSchema.connection()) { KexiDBWarn << "KexiDB::rowsCount(const KexiDB::TableSchema&): no tableSchema.connection() !" << endl; return -1; } int count = -1; //will be changed only on success of querySingleNumber() tableSchema.connection()->querySingleNumber( TQString::fromLatin1("SELECT COUNT(*) FROM ") + tableSchema.connection()->driver()->escapeIdentifier(tableSchema.name()), count ); return count; } int KexiDB::rowCount(KexiDB::QuerySchema& querySchema) { //! @todo does not work with non-SQL data sources if (!querySchema.connection()) { KexiDBWarn << "KexiDB::rowsCount(const KexiDB::QuerySchema&): no querySchema.connection() !" << endl; return -1; } int count = -1; //will be changed only on success of querySingleNumber() querySchema.connection()->querySingleNumber( TQString::fromLatin1("SELECT COUNT(*) FROM (") + querySchema.connection()->selectStatement(querySchema) + ")", count ); return count; } int KexiDB::rowCount(KexiDB::TableOrQuerySchema& tableOrQuery) { if (tableOrQuery.table()) return rowCount( *tableOrQuery.table() ); if (tableOrQuery.query()) return rowCount( *tableOrQuery.query() ); return -1; } int KexiDB::fieldCount(KexiDB::TableOrQuerySchema& tableOrQuery) { if (tableOrQuery.table()) return tableOrQuery.table()->fieldCount(); if (tableOrQuery.query()) return tableOrQuery.query()->fieldsExpanded().count(); return -1; } TQMap KexiDB::toMap( const ConnectionData& data ) { TQMap m; m["caption"] = data.caption; m["description"] = data.description; m["driverName"] = data.driverName; m["hostName"] = data.hostName; m["port"] = TQString::number(data.port); m["useLocalSocketFile"] = TQString::number((int)data.useLocalSocketFile); m["localSocketFileName"] = data.localSocketFileName; m["password"] = data.password; m["savePassword"] = TQString::number((int)data.savePassword); m["userName"] = data.userName; m["fileName"] = data.fileName(); return m; } void KexiDB::fromMap( const TQMap& map, ConnectionData& data ) { data.caption = map["caption"]; data.description = map["description"]; data.driverName = map["driverName"]; data.hostName = map["hostName"]; data.port = map["port"].toInt(); data.useLocalSocketFile = map["useLocalSocketFile"].toInt()==1; data.localSocketFileName = map["localSocketFileName"]; data.password = map["password"]; data.savePassword = map["savePassword"].toInt()==1; data.userName = map["userName"]; data.setFileName(map["fileName"]); } bool KexiDB::splitToTableAndFieldParts(const TQString& string, TQString& tableName, TQString& fieldName, SplitToTableAndFieldPartsOptions option) { const int id = string.find('.'); if (option & SetFieldNameIfNoTableName && id==-1) { tableName = TQString(); fieldName = string; return !fieldName.isEmpty(); } if (id<=0 || id==int(string.length()-1)) return false; tableName = string.left(id); fieldName = string.mid(id+1); return !tableName.isEmpty() && !fieldName.isEmpty(); } bool KexiDB::supportsVisibleDecimalPlacesProperty(Field::Type type) { //! @todo add check for decimal type as well return Field::isFPNumericType(type); } TQString KexiDB::formatNumberForVisibleDecimalPlaces(double value, int decimalPlaces) { //! @todo round? if (decimalPlaces < 0) { TQString s( TQString::number(value, 'f', 10 /*reasonable precision*/)); uint i = s.length()-1; while (i>0 && s[i]=='0') i--; if (s[i]=='.') //remove '.' i--; s = s.left(i+1).replace('.', TDEGlobal::locale()->decimalSymbol()); return s; } if (decimalPlaces == 0) return TQString::number((int)value); return TDEGlobal::locale()->formatNumber(value, decimalPlaces); } KexiDB::Field::Type KexiDB::intToFieldType( int type ) { if (type<(int)KexiDB::Field::InvalidType || type>(int)KexiDB::Field::LastType) { KexiDBWarn << "KexiDB::intToFieldType(): invalid type " << type << endl; return KexiDB::Field::InvalidType; } return (KexiDB::Field::Type)type; } static bool setIntToFieldType( Field& field, const TQVariant& value ) { bool ok; const int intType = value.toInt(&ok); if (!ok || KexiDB::Field::InvalidType == intToFieldType(intType)) {//for sanity KexiDBWarn << "KexiDB::setFieldProperties(): invalid type" << endl; return false; } field.setType((KexiDB::Field::Type)intType); return true; } //! for KexiDB::isBuiltinTableFieldProperty() static KStaticDeleter< TQAsciiDict > KexiDB_builtinFieldPropertiesDeleter; //! for KexiDB::isBuiltinTableFieldProperty() TQAsciiDict* KexiDB_builtinFieldProperties = 0; bool KexiDB::isBuiltinTableFieldProperty( const TQCString& propertyName ) { if (!KexiDB_builtinFieldProperties) { KexiDB_builtinFieldPropertiesDeleter.setObject( KexiDB_builtinFieldProperties, new TQAsciiDict(499) ); #define ADD(name) KexiDB_builtinFieldProperties->insert(name, (char*)1) ADD("type"); ADD("primaryKey"); ADD("indexed"); ADD("autoIncrement"); ADD("unique"); ADD("notNull"); ADD("allowEmpty"); ADD("unsigned"); ADD("name"); ADD("caption"); ADD("description"); ADD("length"); ADD("precision"); ADD("defaultValue"); ADD("width"); ADD("visibleDecimalPlaces"); //! @todo always update this when new builtins appear! #undef ADD } return KexiDB_builtinFieldProperties->find( propertyName ); } bool KexiDB::setFieldProperties( Field& field, const TQMap& values ) { TQMapConstIterator it; if ( (it = values.find("type")) != values.constEnd() ) { if (!setIntToFieldType(field, *it)) return false; } #define SET_BOOLEAN_FLAG(flag, value) { \ constraints |= KexiDB::Field::flag; \ if (!value) \ constraints ^= KexiDB::Field::flag; \ } uint constraints = field.constraints(); bool ok = true; if ( (it = values.find("primaryKey")) != values.constEnd() ) SET_BOOLEAN_FLAG(PrimaryKey, (*it).toBool()); if ( (it = values.find("indexed")) != values.constEnd() ) SET_BOOLEAN_FLAG(Indexed, (*it).toBool()); if ( (it = values.find("autoIncrement")) != values.constEnd() && KexiDB::Field::isAutoIncrementAllowed(field.type()) ) SET_BOOLEAN_FLAG(AutoInc, (*it).toBool()); if ( (it = values.find("unique")) != values.constEnd() ) SET_BOOLEAN_FLAG(Unique, (*it).toBool()); if ( (it = values.find("notNull")) != values.constEnd() ) SET_BOOLEAN_FLAG(NotNull, (*it).toBool()); if ( (it = values.find("allowEmpty")) != values.constEnd() ) SET_BOOLEAN_FLAG(NotEmpty, !(*it).toBool()); field.setConstraints( constraints ); uint options = 0; if ( (it = values.find("unsigned")) != values.constEnd()) { options |= KexiDB::Field::Unsigned; if (!(*it).toBool()) options ^= KexiDB::Field::Unsigned; } field.setOptions( options ); if ( (it = values.find("name")) != values.constEnd()) field.setName( (*it).toString() ); if ( (it = values.find("caption")) != values.constEnd()) field.setCaption( (*it).toString() ); if ( (it = values.find("description")) != values.constEnd()) field.setDescription( (*it).toString() ); if ( (it = values.find("length")) != values.constEnd()) field.setLength( (*it).isNull() ? 0/*default*/ : (*it).toUInt(&ok) ); if (!ok) return false; if ( (it = values.find("precision")) != values.constEnd()) field.setPrecision( (*it).isNull() ? 0/*default*/ : (*it).toUInt(&ok) ); if (!ok) return false; if ( (it = values.find("defaultValue")) != values.constEnd()) field.setDefaultValue( *it ); if ( (it = values.find("width")) != values.constEnd()) field.setWidth( (*it).isNull() ? 0/*default*/ : (*it).toUInt(&ok) ); if (!ok) return false; if ( (it = values.find("visibleDecimalPlaces")) != values.constEnd() && KexiDB::supportsVisibleDecimalPlacesProperty(field.type()) ) field.setVisibleDecimalPlaces( (*it).isNull() ? -1/*default*/ : (*it).toInt(&ok) ); if (!ok) return false; // set custom properties typedef TQMap PropertiesMap; foreach( PropertiesMap::ConstIterator, it, values ) { if (!isBuiltinTableFieldProperty( it.key() ) && !isExtendedTableFieldProperty( it.key() )) { field.setCustomProperty( it.key(), it.data() ); } } return true; #undef SET_BOOLEAN_FLAG } //! for KexiDB::isExtendedTableFieldProperty() static KStaticDeleter< TQAsciiDict > KexiDB_extendedPropertiesDeleter; //! for KexiDB::isExtendedTableFieldProperty() TQAsciiDict* KexiDB_extendedProperties = 0; bool KexiDB::isExtendedTableFieldProperty( const TQCString& propertyName ) { if (!KexiDB_extendedProperties) { KexiDB_extendedPropertiesDeleter.setObject( KexiDB_extendedProperties, new TQAsciiDict(499, false) ); #define ADD(name) KexiDB_extendedProperties->insert(name, (char*)1) ADD("visibleDecimalPlaces"); ADD("rowSource"); ADD("rowSourceType"); ADD("rowSourceValues"); ADD("boundColumn"); ADD("visibleColumn"); ADD("columnWidths"); ADD("showColumnHeaders"); ADD("listRows"); ADD("limitToList"); ADD("displayWidget"); #undef ADD } return KexiDB_extendedProperties->find( propertyName ); } bool KexiDB::setFieldProperty( Field& field, const TQCString& propertyName, const TQVariant& value ) { #define SET_BOOLEAN_FLAG(flag, value) { \ constraints |= KexiDB::Field::flag; \ if (!value) \ constraints ^= KexiDB::Field::flag; \ field.setConstraints( constraints ); \ return true; \ } #define GET_INT(method) { \ const uint ival = value.toUInt(&ok); \ if (!ok) \ return false; \ field.method( ival ); \ return true; \ } if (propertyName.isEmpty()) return false; bool ok; if (KexiDB::isExtendedTableFieldProperty(propertyName)) { //a little speedup: identify extended property in O(1) if ( "visibleDecimalPlaces" == propertyName && KexiDB::supportsVisibleDecimalPlacesProperty(field.type()) ) { GET_INT( setVisibleDecimalPlaces ); } else { if (!field.table()) { KexiDBWarn << TQString("KexiDB::setFieldProperty() Cannot set \"%1\" property - no table assinged for field!") .arg(propertyName.data()) << endl; } else { LookupFieldSchema *lookup = field.table()->lookupFieldSchema(field); const bool hasLookup = lookup != 0; if (!hasLookup) lookup = new LookupFieldSchema(); if (LookupFieldSchema::setProperty( *lookup, propertyName, value )) { if (!hasLookup && lookup) field.table()->setLookupFieldSchema( field.name(), lookup ); return true; } delete lookup; } } } else {//non-extended if ( "type" == propertyName ) return setIntToFieldType(field, value); uint constraints = field.constraints(); if ( "primaryKey" == propertyName ) SET_BOOLEAN_FLAG(PrimaryKey, value.toBool()); if ( "indexed" == propertyName ) SET_BOOLEAN_FLAG(Indexed, value.toBool()); if ( "autoIncrement" == propertyName && KexiDB::Field::isAutoIncrementAllowed(field.type()) ) SET_BOOLEAN_FLAG(AutoInc, value.toBool()); if ( "unique" == propertyName ) SET_BOOLEAN_FLAG(Unique, value.toBool()); if ( "notNull" == propertyName ) SET_BOOLEAN_FLAG(NotNull, value.toBool()); if ( "allowEmpty" == propertyName ) SET_BOOLEAN_FLAG(NotEmpty, !value.toBool()); uint options = 0; if ( "unsigned" == propertyName ) { options |= KexiDB::Field::Unsigned; if (!value.toBool()) options ^= KexiDB::Field::Unsigned; field.setOptions( options ); return true; } if ( "name" == propertyName ) { if (value.toString().isEmpty()) return false; field.setName( value.toString() ); return true; } if ( "caption" == propertyName ) { field.setCaption( value.toString() ); return true; } if ( "description" == propertyName ) { field.setDescription( value.toString() ); return true; } if ( "length" == propertyName ) GET_INT( setLength ); if ( "precision" == propertyName ) GET_INT( setPrecision ); if ( "defaultValue" == propertyName ) { field.setDefaultValue( value ); return true; } if ( "width" == propertyName ) GET_INT( setWidth ); // last chance that never fails: custom field property field.setCustomProperty(propertyName, value); } KexiDBWarn << "KexiDB::setFieldProperty() property \"" << propertyName << "\" not found!" << endl; return false; #undef SET_BOOLEAN_FLAG #undef GET_INT } int KexiDB::loadIntPropertyValueFromDom( const TQDomNode& node, bool* ok ) { TQCString valueType = node.nodeName().latin1(); if (valueType.isEmpty() || valueType!="number") { if (ok) *ok = false; return 0; } const TQString text( TQDomNode(node).toElement().text() ); int val = text.toInt(ok); return val; } TQString KexiDB::loadStringPropertyValueFromDom( const TQDomNode& node, bool* ok ) { TQCString valueType = node.nodeName().latin1(); if (valueType!="string") { if (ok) *ok = false; return 0; } return TQDomNode(node).toElement().text(); } TQVariant KexiDB::loadPropertyValueFromDom( const TQDomNode& node ) { TQCString valueType = node.nodeName().latin1(); if (valueType.isEmpty()) return TQVariant(); const TQString text( TQDomNode(node).toElement().text() ); bool ok; if (valueType == "string") { return text; } else if (valueType == "cstring") { return TQCString(text.latin1()); } else if (valueType == "number") { // integer or double if (text.find('.')!=-1) { double val = text.toDouble(&ok); if (ok) return val; } else { const int val = text.toInt(&ok); if (ok) return val; const TQ_LLONG valLong = text.toLongLong(&ok); if (ok) return valLong; } } else if (valueType == "bool") { return TQVariant(text.lower()=="true" || text=="1", 1); } //! @todo add more TQVariant types KexiDBWarn << "loadPropertyValueFromDom(): unknown type '" << valueType << "'" << endl; return TQVariant(); } TQDomElement KexiDB::saveNumberElementToDom(TQDomDocument& doc, TQDomElement& parentEl, const TQString& elementName, int value) { TQDomElement el( doc.createElement(elementName) ); parentEl.appendChild( el ); TQDomElement numberEl( doc.createElement("number") ); el.appendChild( numberEl ); numberEl.appendChild( doc.createTextNode( TQString::number(value) ) ); return el; } TQDomElement KexiDB::saveBooleanElementToDom(TQDomDocument& doc, TQDomElement& parentEl, const TQString& elementName, bool value) { TQDomElement el( doc.createElement(elementName) ); parentEl.appendChild( el ); TQDomElement numberEl( doc.createElement("bool") ); el.appendChild( numberEl ); numberEl.appendChild( doc.createTextNode( value ? TQString::fromLatin1("true") : TQString::fromLatin1("false") ) ); return el; } //! Used in KexiDB::emptyValueForType() static KStaticDeleter< TQValueVector > KexiDB_emptyValueForTypeCacheDeleter; TQValueVector *KexiDB_emptyValueForTypeCache = 0; TQVariant KexiDB::emptyValueForType( KexiDB::Field::Type type ) { if (!KexiDB_emptyValueForTypeCache) { KexiDB_emptyValueForTypeCacheDeleter.setObject( KexiDB_emptyValueForTypeCache, new TQValueVector(int(Field::LastType)+1) ); #define ADD(t, value) (*KexiDB_emptyValueForTypeCache)[t]=value; ADD(Field::Byte, 0); ADD(Field::ShortInteger, 0); ADD(Field::Integer, 0); ADD(Field::BigInteger, 0); ADD(Field::Boolean, TQVariant(false, 0)); ADD(Field::Float, 0.0); ADD(Field::Double, 0.0); //! @todo ok? we have no better defaults ADD(Field::Text, TQString(" ")); ADD(Field::LongText, TQString(" ")); ADD(Field::BLOB, TQByteArray()); #undef ADD } const TQVariant val( KexiDB_emptyValueForTypeCache->at( (type<=Field::LastType) ? type : Field::InvalidType) ); if (!val.isNull()) return val; else { //special cases if (type==Field::Date) return TQDate::currentDate(); if (type==Field::DateTime) return TQDateTime::currentDateTime(); if (type==Field::Time) return TQTime::currentTime(); } KexiDBWarn << "KexiDB::emptyValueForType() no value for type " << Field::typeName(type) << endl; return TQVariant(); } //! Used in KexiDB::notEmptyValueForType() static KStaticDeleter< TQValueVector > KexiDB_notEmptyValueForTypeCacheDeleter; TQValueVector *KexiDB_notEmptyValueForTypeCache = 0; TQVariant KexiDB::notEmptyValueForType( KexiDB::Field::Type type ) { if (!KexiDB_notEmptyValueForTypeCache) { KexiDB_notEmptyValueForTypeCacheDeleter.setObject( KexiDB_notEmptyValueForTypeCache, new TQValueVector(int(Field::LastType)+1) ); #define ADD(t, value) (*KexiDB_notEmptyValueForTypeCache)[t]=value; // copy most of the values for (int i = int(Field::InvalidType) + 1; i<=Field::LastType; i++) { if (i==Field::Date || i==Field::DateTime || i==Field::Time) continue; //'current' value will be returned if (i==Field::Text || i==Field::LongText) { ADD(i, TQVariant(TQString(""))); continue; } if (i==Field::BLOB) { //! @todo blobs will contain other mime types too TQByteArray ba; TQBuffer buffer( ba ); buffer.open( IO_WriteOnly ); TQPixmap pm(SmallIcon("document-new")); pm.save( &buffer, "PNG"/*! @todo default? */ ); ADD(i, ba); continue; } ADD(i, KexiDB::emptyValueForType((Field::Type)i)); } #undef ADD } const TQVariant val( KexiDB_notEmptyValueForTypeCache->at( (type<=Field::LastType) ? type : Field::InvalidType) ); if (!val.isNull()) return val; else { //special cases if (type==Field::Date) return TQDate::currentDate(); if (type==Field::DateTime) return TQDateTime::currentDateTime(); if (type==Field::Time) return TQTime::currentTime(); } KexiDBWarn << "KexiDB::notEmptyValueForType() no value for type " << Field::typeName(type) << endl; return TQVariant(); } TQString KexiDB::escapeBLOB(const TQByteArray& array, BLOBEscapingType type) { const int size = array.size(); if (size==0) return TQString(); int escaped_length = size*2; if (type == BLOBEscape0xHex || type == BLOBEscapeOctal) escaped_length += 2/*0x or X'*/; else if (type == BLOBEscapeXHex) escaped_length += 3; //X' + ' TQString str; str.reserve(escaped_length); if (str.capacity() < (uint)escaped_length) { KexiDBWarn << "KexiDB::Driver::escapeBLOB(): no enough memory (cannot allocate "<< escaped_length<<" chars)" << endl; return TQString(); } if (type == BLOBEscapeXHex) str = TQString::fromLatin1("X'"); else if (type == BLOBEscape0xHex) str = TQString::fromLatin1("0x"); else if (type == BLOBEscapeOctal) str = TQString::fromLatin1("'"); int new_length = str.length(); //after X' or 0x, etc. if (type == BLOBEscapeOctal) { // only escape nonprintable characters as in Table 8-7: // http://www.postgresql.org/docs/8.1/interactive/datatype-binary.html // i.e. escape for bytes: < 32, >= 127, 39 ('), 92(\). for (int i = 0; i < size; i++) { const unsigned char val = array[i]; if (val<32 || val>=127 || val==39 || val==92) { str[new_length++] = '\\'; str[new_length++] = '\\'; str[new_length++] = '0' + val/64; str[new_length++] = '0' + (val % 64) / 8; str[new_length++] = '0' + val % 8; } else { str[new_length++] = val; } } } else { for (int i = 0; i < size; i++) { const unsigned char val = array[i]; str[new_length++] = (val/16) < 10 ? ('0'+(val/16)) : ('A'+(val/16)-10); str[new_length++] = (val%16) < 10 ? ('0'+(val%16)) : ('A'+(val%16)-10); } } if (type == BLOBEscapeXHex || type == BLOBEscapeOctal) str[new_length++] = '\''; return str; } TQByteArray KexiDB::pgsqlByteaToByteArray(const char* data, int length) { TQByteArray array; int output=0; for (int pass=0; pass<2; pass++) {//2 passes to avoid allocating buffer twice: // 0: count #of chars; 1: copy data const char* s = data; const char* end = s + length; if (pass==1) { KexiDBDbg << "processBinaryData(): real size == " << output << endl; array.resize(output); output=0; } for (int input=0; s < end; output++) { // KexiDBDbg<<(int)s[0]<<" "<<(int)s[1]<<" "<<(int)s[2]<<" "<<(int)s[3]<<" "<<(int)s[4]<isUniqueKey(); } void KexiDB::getLimitsForType(Field::Type type, int &minValue, int &maxValue) { switch (type) { case Field::Byte: //! @todo always ok? minValue = 0; maxValue = 255; break; case Field::ShortInteger: minValue = -32768; maxValue = 32767; break; case Field::Integer: case Field::BigInteger: //cannot return anything larger default: minValue = (int)-0x07FFFFFFF; maxValue = (int)(0x080000000-1); } } void KexiDB::debugRowData(const RowData& rowData) { KexiDBDbg << TQString("ROW DATA (%1 columns):").arg(rowData.count()) << endl; foreach(RowData::ConstIterator, it, rowData) KexiDBDbg << "- " << (*it) << endl; } Field::Type KexiDB::maximumForIntegerTypes(Field::Type t1, Field::Type t2) { if (!Field::isIntegerType(t1) || !Field::isIntegerType(t2)) return Field::InvalidType; if (t1==t2) return t2; if (t1==Field::ShortInteger && t2!=Field::Integer && t2!=Field::BigInteger) return t1; if (t1==Field::Integer && t2!=Field::BigInteger) return t1; if (t1==Field::BigInteger) return t1; return KexiDB::maximumForIntegerTypes(t2, t1); //swap } TQString KexiDB::simplifiedTypeName(const Field& field) { if (field.isNumericType()) return i18n("Number"); //simplify else if (field.type() == Field::BLOB) //! @todo support names of other BLOB subtypes return i18n("Image"); //simplify return field.typeGroupName(); } #include "utils_p.moc"