/* This file is part of the KDE project Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> This program 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 program 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 program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include <kexidb/driver.h> #include <kexidb/driver_p.h> #include <kexidb/drivermanager.h> #include <kexidb/drivermanager_p.h> #include "error.h" #include "drivermanager.h" #include "connection.h" #include "connectiondata.h" #include "admin.h" #include <tqfileinfo.h> #include <tdelocale.h> #include <kdebug.h> #include <assert.h> using namespace KexiDB; /*! used when we do not have Driver instance yet, or when we cannot get one */ TQValueVector<TQString> dflt_typeNames; //--------------------------------------------- DriverBehaviour::DriverBehaviour() : UNSIGNED_TYPE_KEYWORD("UNSIGNED") , AUTO_INCREMENT_FIELD_OPTION("AUTO_INCREMENT") , AUTO_INCREMENT_PK_FIELD_OPTION("AUTO_INCREMENT PRIMARY KEY") , SPECIAL_AUTO_INCREMENT_DEF(false) , AUTO_INCREMENT_REQUIRES_PK(false) , ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE(false) , QUOTATION_MARKS_FOR_IDENTIFIER('"') , USING_DATABASE_REQUIRED_TO_CONNECT(true) , _1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY(false) , SELECT_1_SUBQUERY_SUPPORTED(false) , SQL_KEYWORDS(0) { } //--------------------------------------------- Driver::Info::Info() : fileBased(false) , allowImportingTo(true) { } //--------------------------------------------- Driver::Driver( TQObject *parent, const char *name, const TQStringList & ) : TQObject( parent, name ) , Object() , beh( new DriverBehaviour() ) , d( new DriverPrivate() ) { d->connections.setAutoDelete(false); //TODO: reasonable size d->connections.resize(101); d->typeNames.resize(Field::LastType + 1); d->initKexiKeywords(); } Driver::~Driver() { DriverManagerInternal::self()->aboutDelete( this ); // KexiDBDbg << "Driver::~Driver()" << endl; TQPtrDictIterator<Connection> it( d->connections ); Connection *conn; while ( (conn = it.toFirst()) ) { delete conn; } delete beh; delete d; // KexiDBDbg << "Driver::~Driver() ok" << endl; } bool Driver::isValid() { clearError(); if (KexiDB::version().major != version().major || KexiDB::version().minor != version().minor) { setError(ERR_INCOMPAT_DRIVER_VERSION, i18n("Incompatible database driver's \"%1\" version: found version %2, expected version %3.") .arg(name()) .arg(TQString("%1.%2").arg(version().major).arg(version().minor)) .arg(TQString("%1.%2").arg(KexiDB::version().major).arg(KexiDB::version().minor))); return false; } TQString inv_impl = i18n("Invalid database driver's \"%1\" implementation:\n").arg(name()); TQString not_init = i18n("Value of \"%1\" is not initialized for the driver."); if (beh->ROW_ID_FIELD_NAME.isEmpty()) { setError(ERR_INVALID_DRIVER_IMPL, inv_impl + not_init.arg("DriverBehaviour::ROW_ID_FIELD_NAME")); return false; } return true; } const TQPtrList<Connection> Driver::connectionsList() const { TQPtrList<Connection> clist; TQPtrDictIterator<Connection> it( d->connections ); for( ; it.current(); ++it ) clist.append( &(*it) ); return clist; } TQString Driver::fileDBDriverMimeType() const { return d->fileDBDriverMimeType; } TQString Driver::defaultFileBasedDriverMimeType() { return TQString::fromLatin1("application/x-kexiproject-sqlite3"); } TQString Driver::defaultFileBasedDriverName() { DriverManager dm; return dm.lookupByMime(Driver::defaultFileBasedDriverMimeType()).lower(); } const KService* Driver::service() const { return d->service; } bool Driver::isFileDriver() const { return d->isFileDriver; } int Driver::features() const { return d->features; } bool Driver::transactionsSupported() const { return d->features & (SingleTransactions | MultipleTransactions); } AdminTools& Driver::adminTools() const { if (!d->adminTools) d->adminTools = drv_createAdminTools(); return *d->adminTools; } AdminTools* Driver::drv_createAdminTools() const { return new AdminTools(); //empty impl. } TQString Driver::sqlTypeName(int id_t, int /*p*/) const { if (id_t > Field::InvalidType && id_t <= Field::LastType) return d->typeNames[(id_t>0 && id_t<=Field::LastType) ? id_t : Field::InvalidType /*sanity*/]; return d->typeNames[Field::InvalidType]; } Connection *Driver::createConnection( ConnectionData &conn_data, int options ) { clearError(); if (!isValid()) return 0; if (d->isFileDriver) { if (conn_data.fileName().isEmpty()) { setError(ERR_MISSING_DB_LOCATION, i18n("File name expected for file-based database driver.") ); return 0; } } // Connection *conn = new Connection( this, conn_data ); Connection *conn = drv_createConnection( conn_data ); conn->setReadOnly(options & ReadOnlyConnection); conn_data.driverName = name(); d->connections.insert( conn, conn ); return conn; } Connection* Driver::removeConnection( Connection *conn ) { clearError(); return d->connections.take( conn ); } TQString Driver::defaultSQLTypeName(int id_t) { if (id_t>=Field::Null) return "Null"; if (dflt_typeNames.isEmpty()) { dflt_typeNames.resize(Field::LastType + 1); dflt_typeNames[Field::InvalidType]="InvalidType"; dflt_typeNames[Field::Byte]="Byte"; dflt_typeNames[Field::ShortInteger]="ShortInteger"; dflt_typeNames[Field::Integer]="Integer"; dflt_typeNames[Field::BigInteger]="BigInteger"; dflt_typeNames[Field::Boolean]="Boolean"; dflt_typeNames[Field::Date]="Date"; dflt_typeNames[Field::DateTime]="DateTime"; dflt_typeNames[Field::Time]="Time"; dflt_typeNames[Field::Float]="Float"; dflt_typeNames[Field::Double]="Double"; dflt_typeNames[Field::Text]="Text"; dflt_typeNames[Field::LongText]="LongText"; dflt_typeNames[Field::BLOB]="BLOB"; } return dflt_typeNames[id_t]; } bool Driver::isSystemObjectName( const TQString& n ) const { return Driver::isKexiDBSystemObjectName(n); } bool Driver::isKexiDBSystemObjectName( const TQString& n ) { if (!n.lower().startsWith("kexi__")) return false; const TQStringList list( Connection::kexiDBSystemTableNames() ); return list.find(n.lower())!=list.constEnd(); } bool Driver::isSystemFieldName( const TQString& n ) const { if (!beh->ROW_ID_FIELD_NAME.isEmpty() && n.lower()==beh->ROW_ID_FIELD_NAME.lower()) return true; return drv_isSystemFieldName(n); } TQString Driver::valueToSQL( uint ftype, const TQVariant& v ) const { if (v.isNull()) return "NULL"; switch (ftype) { case Field::Text: case Field::LongText: { TQString s = v.toString(); return escapeString(s); //TQString("'")+s.replace( '"', "\\\"" ) + "'"; } case Field::Byte: case Field::ShortInteger: case Field::Integer: case Field::BigInteger: return v.toString(); case Field::Float: case Field::Double: { if (v.type()==TQVariant::String) { //workaround for values stored as string that should be casted to floating-point TQString s(v.toString()); return s.replace(',', "."); } return v.toString(); } //TODO: here special encoding method needed case Field::Boolean: return TQString::number(v.toInt()?1:0); //0 or 1 case Field::Time: return TQString("\'")+v.toTime().toString(Qt::ISODate)+"\'"; case Field::Date: return TQString("\'")+v.toDate().toString(Qt::ISODate)+"\'"; case Field::DateTime: return dateTimeToSQL( v.toDateTime() ); case Field::BLOB: { if (v.toByteArray().isEmpty()) return TQString::fromLatin1("NULL"); if (v.type()==TQVariant::String) return escapeBLOB(v.toString().utf8()); return escapeBLOB(v.toByteArray()); } case Field::InvalidType: return "!INVALIDTYPE!"; default: KexiDBDbg << "Driver::valueToSQL(): UNKNOWN!" << endl; return TQString(); } return TQString(); } TQVariant Driver::propertyValue( const TQCString& propName ) const { return d->properties[propName.lower()]; } TQString Driver::propertyCaption( const TQCString& propName ) const { return d->propertyCaptions[propName.lower()]; } TQValueList<TQCString> Driver::propertyNames() const { TQValueList<TQCString> names = d->properties.keys(); qHeapSort(names); return names; } TQString Driver::escapeIdentifier(const TQString& str, int options) const { TQCString cstr = str.latin1(); return TQString(escapeIdentifier(cstr, options)); } TQCString Driver::escapeIdentifier(const TQCString& str, int options) const { bool needOuterQuotes = false; // Need to use quotes if ... // ... we have been told to, or ... if(options & EscapeAlways) needOuterQuotes = true; // ... or if the driver does not have a list of keywords, else if(!d->driverSQLDict) needOuterQuotes = true; // ... or if it's a keyword in Kexi's SQL dialect, else if(d->kexiSQLDict->find(str)) needOuterQuotes = true; // ... or if it's a keyword in the backends SQL dialect, // (have already checked !d->driverSQLDict) else if((options & EscapeDriver) && d->driverSQLDict->find(str)) needOuterQuotes = true; // ... or if the identifier has a space in it... else if(str.find(' ') != -1) needOuterQuotes = true; if(needOuterQuotes && (options & EscapeKexi)) { const char quote = '"'; return quote + TQCString(str).replace( quote, "\"\"" ) + quote; } else if (needOuterQuotes) { const char quote = beh->QUOTATION_MARKS_FOR_IDENTIFIER.latin1(); return quote + drv_escapeIdentifier(str) + quote; } else { return drv_escapeIdentifier(str); } } void Driver::initSQLKeywords(int hashSize) { if(!d->driverSQLDict && beh->SQL_KEYWORDS != 0) { d->initDriverKeywords(beh->SQL_KEYWORDS, hashSize); } } #include "driver.moc"