kexi

driver.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this program; see the file COPYING.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <kexidb/driver.h>
00021 #include <kexidb/driver_p.h>
00022 #include <kexidb/drivermanager.h>
00023 #include <kexidb/drivermanager_p.h>
00024 #include "error.h"
00025 #include "drivermanager.h"
00026 #include "connection.h"
00027 #include "connectiondata.h"
00028 #include "admin.h"
00029 
00030 #include <qfileinfo.h>
00031 
00032 #include <klocale.h>
00033 #include <kdebug.h>
00034 
00035 #include <assert.h>
00036 
00037 using namespace KexiDB;
00038 
00041 QValueVector<QString> dflt_typeNames;
00042 
00043 
00044 //---------------------------------------------
00045 
00046 
00047 DriverBehaviour::DriverBehaviour()
00048     : UNSIGNED_TYPE_KEYWORD("UNSIGNED")
00049     , AUTO_INCREMENT_FIELD_OPTION("AUTO_INCREMENT")
00050     , AUTO_INCREMENT_PK_FIELD_OPTION("AUTO_INCREMENT PRIMARY KEY")
00051     , SPECIAL_AUTO_INCREMENT_DEF(false)
00052     , AUTO_INCREMENT_REQUIRES_PK(false)
00053     , ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE(false)
00054     , QUOTATION_MARKS_FOR_IDENTIFIER('"')
00055     , USING_DATABASE_REQUIRED_TO_CONNECT(true)
00056     , _1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY(false)
00057     , SELECT_1_SUBQUERY_SUPPORTED(false)
00058     , SQL_KEYWORDS(0)
00059 {
00060 }
00061 
00062 //---------------------------------------------
00063 
00064 Driver::Info::Info()
00065  : fileBased(false)
00066  , allowImportingTo(true)
00067 {
00068 }
00069 
00070 //---------------------------------------------
00071 
00072 Driver::Driver( QObject *parent, const char *name, const QStringList & )
00073     : QObject( parent, name )
00074     , Object()
00075     , beh( new DriverBehaviour() )
00076     , d( new DriverPrivate() )
00077 {
00078     d->connections.setAutoDelete(false);
00079     //TODO: reasonable size
00080     d->connections.resize(101);
00081     d->typeNames.resize(Field::LastType + 1);
00082 
00083     d->initKexiKeywords();
00084 }
00085 
00086 
00087 Driver::~Driver()
00088 {
00089     DriverManagerInternal::self()->aboutDelete( this );
00090 //  KexiDBDbg << "Driver::~Driver()" << endl;
00091     QPtrDictIterator<Connection> it( d->connections );
00092     Connection *conn;
00093     while ( (conn = it.toFirst()) ) {
00094         delete conn;
00095     }
00096     delete beh;
00097     delete d;
00098 //  KexiDBDbg << "Driver::~Driver() ok" << endl;
00099 }
00100 
00101 bool Driver::isValid()
00102 {
00103     clearError();
00104     if (KexiDB::version().major != version().major
00105         || KexiDB::version().minor != version().minor)
00106     {
00107         setError(ERR_INCOMPAT_DRIVER_VERSION,
00108         i18n("Incompatible database driver's \"%1\" version: found version %2, expected version %3.")
00109         .arg(name())
00110         .arg(QString("%1.%2").arg(version().major).arg(version().minor))
00111         .arg(QString("%1.%2").arg(KexiDB::version().major).arg(KexiDB::version().minor)));
00112         return false;
00113     }
00114 
00115     QString inv_impl = i18n("Invalid database driver's \"%1\" implementation:\n").arg(name());
00116     QString not_init = i18n("Value of \"%1\" is not initialized for the driver.");
00117     if (beh->ROW_ID_FIELD_NAME.isEmpty()) {
00118         setError(ERR_INVALID_DRIVER_IMPL, inv_impl + not_init.arg("DriverBehaviour::ROW_ID_FIELD_NAME"));
00119         return false;
00120     }
00121 
00122     return true;
00123 }
00124 
00125 const QPtrList<Connection> Driver::connectionsList() const
00126 {
00127     QPtrList<Connection> clist;
00128     QPtrDictIterator<Connection> it( d->connections );
00129     for( ; it.current(); ++it )
00130         clist.append( &(*it) );
00131     return clist;
00132 }
00133 
00134 QString Driver::fileDBDriverMimeType() const
00135 { return d->fileDBDriverMimeType; }
00136 
00137 QString Driver::defaultFileBasedDriverMimeType()
00138 { return QString::fromLatin1("application/x-kexiproject-sqlite3"); }
00139 
00140 QString Driver::defaultFileBasedDriverName()
00141 {
00142     DriverManager dm;
00143     return dm.lookupByMime(Driver::defaultFileBasedDriverMimeType()).lower();
00144 }
00145 
00146 const KService* Driver::service() const
00147 { return d->service; }
00148 
00149 bool Driver::isFileDriver() const
00150 { return d->isFileDriver; }
00151 
00152 int Driver::features() const
00153 { return d->features; }
00154 
00155 bool Driver::transactionsSupported() const
00156 { return d->features & (SingleTransactions | MultipleTransactions); }
00157 
00158 AdminTools& Driver::adminTools() const
00159 {
00160     if (!d->adminTools)
00161         d->adminTools = drv_createAdminTools();
00162     return *d->adminTools;
00163 }
00164 
00165 AdminTools* Driver::drv_createAdminTools() const
00166 {
00167     return new AdminTools(); //empty impl.
00168 }
00169 
00170 QString Driver::sqlTypeName(int id_t, int /*p*/) const
00171 {
00172     if (id_t > Field::InvalidType && id_t <= Field::LastType)
00173         return d->typeNames[(id_t>0 && id_t<=Field::LastType) ? id_t : Field::InvalidType /*sanity*/];
00174 
00175     return d->typeNames[Field::InvalidType];
00176 }
00177 
00178 Connection *Driver::createConnection( ConnectionData &conn_data, int options )
00179 {
00180     clearError();
00181     if (!isValid())
00182         return 0;
00183 
00184     if (d->isFileDriver) {
00185         if (conn_data.fileName().isEmpty()) {
00186             setError(ERR_MISSING_DB_LOCATION, i18n("File name expected for file-based database driver.") );
00187             return 0;
00188         }
00189     }
00190 //  Connection *conn = new Connection( this, conn_data );
00191     Connection *conn = drv_createConnection( conn_data );
00192 
00193     conn->setReadOnly(options & ReadOnlyConnection);
00194 
00195     conn_data.driverName = name();
00196     d->connections.insert( conn, conn );
00197     return conn;
00198 }
00199 
00200 Connection* Driver::removeConnection( Connection *conn )
00201 {
00202     clearError();
00203     return d->connections.take( conn );
00204 }
00205 
00206 QString Driver::defaultSQLTypeName(int id_t)
00207 {
00208     if (id_t>=Field::Null)
00209         return "Null";
00210     if (dflt_typeNames.isEmpty()) {
00211         dflt_typeNames.resize(Field::LastType + 1);
00212         dflt_typeNames[Field::InvalidType]="InvalidType";
00213         dflt_typeNames[Field::Byte]="Byte";
00214         dflt_typeNames[Field::ShortInteger]="ShortInteger";
00215         dflt_typeNames[Field::Integer]="Integer";
00216         dflt_typeNames[Field::BigInteger]="BigInteger";
00217         dflt_typeNames[Field::Boolean]="Boolean";
00218         dflt_typeNames[Field::Date]="Date";
00219         dflt_typeNames[Field::DateTime]="DateTime";
00220         dflt_typeNames[Field::Time]="Time";
00221         dflt_typeNames[Field::Float]="Float";
00222         dflt_typeNames[Field::Double]="Double";
00223         dflt_typeNames[Field::Text]="Text";
00224         dflt_typeNames[Field::LongText]="LongText";
00225         dflt_typeNames[Field::BLOB]="BLOB";
00226     }
00227     return dflt_typeNames[id_t];
00228 }
00229 
00230 bool Driver::isSystemObjectName( const QString& n ) const
00231 {
00232     return Driver::isKexiDBSystemObjectName(n);
00233 }
00234 
00235 bool Driver::isKexiDBSystemObjectName( const QString& n )
00236 {
00237     if (!n.lower().startsWith("kexi__"))
00238         return false;
00239     const QStringList list( Connection::kexiDBSystemTableNames() );
00240     return list.find(n.lower())!=list.constEnd();
00241 }
00242 
00243 bool Driver::isSystemFieldName( const QString& n ) const
00244 {
00245     if (!beh->ROW_ID_FIELD_NAME.isEmpty() && n.lower()==beh->ROW_ID_FIELD_NAME.lower())
00246         return true;
00247     return drv_isSystemFieldName(n);
00248 }
00249 
00250 QString Driver::valueToSQL( uint ftype, const QVariant& v ) const
00251 {
00252     if (v.isNull())
00253         return "NULL";
00254     switch (ftype) {
00255         case Field::Text:
00256         case Field::LongText: {
00257             QString s = v.toString();
00258             return escapeString(s); //QString("'")+s.replace( '"', "\\\"" ) + "'";
00259         }
00260         case Field::Byte:
00261         case Field::ShortInteger:
00262         case Field::Integer:
00263         case Field::BigInteger:
00264             return v.toString();
00265         case Field::Float:
00266         case Field::Double: {
00267             if (v.type()==QVariant::String) {
00268                 //workaround for values stored as string that should be casted to floating-point
00269                 QString s(v.toString());
00270                 return s.replace(',', ".");
00271             }
00272             return v.toString();
00273         }
00274 //TODO: here special encoding method needed
00275         case Field::Boolean:
00276             return QString::number(v.toInt()?1:0); //0 or 1
00277         case Field::Time:
00278             return QString("\'")+v.toTime().toString(Qt::ISODate)+"\'";
00279         case Field::Date:
00280             return QString("\'")+v.toDate().toString(Qt::ISODate)+"\'";
00281         case Field::DateTime:
00282             return dateTimeToSQL( v.toDateTime() );
00283         case Field::BLOB: {
00284             if (v.toByteArray().isEmpty())
00285                 return QString::fromLatin1("NULL");
00286             if (v.type()==QVariant::String)
00287                 return escapeBLOB(v.toString().utf8());
00288             return escapeBLOB(v.toByteArray());
00289         }
00290         case Field::InvalidType:
00291             return "!INVALIDTYPE!";
00292         default:
00293             KexiDBDbg << "Driver::valueToSQL(): UNKNOWN!" << endl;
00294             return QString::null;
00295     }
00296     return QString::null;
00297 }
00298 
00299 QVariant Driver::propertyValue( const QCString& propName ) const
00300 {
00301     return d->properties[propName.lower()];
00302 }
00303 
00304 QString Driver::propertyCaption( const QCString& propName ) const
00305 {
00306     return d->propertyCaptions[propName.lower()];
00307 }
00308 
00309 QValueList<QCString> Driver::propertyNames() const
00310 {
00311     QValueList<QCString> names = d->properties.keys();
00312     qHeapSort(names);
00313     return names;
00314 }
00315 
00316 QString Driver::escapeIdentifier(const QString& str, int options) const
00317 {
00318     QCString cstr = str.latin1();
00319     return QString(escapeIdentifier(cstr, options));
00320 }
00321 
00322 QCString Driver::escapeIdentifier(const QCString& str, int options) const
00323 {
00324     bool needOuterQuotes = false;
00325 
00326 // Need to use quotes if ...
00327 // ... we have been told to, or ...
00328     if(options & EscapeAlways)
00329         needOuterQuotes = true;
00330 
00331 // ... or if the driver does not have a list of keywords,
00332     else if(!d->driverSQLDict)
00333         needOuterQuotes = true;
00334 
00335 // ... or if it's a keyword in Kexi's SQL dialect,
00336     else if(d->kexiSQLDict->find(str))
00337         needOuterQuotes = true;
00338 
00339 // ... or if it's a keyword in the backends SQL dialect,
00340 // (have already checked !d->driverSQLDict)
00341     else if((options & EscapeDriver) && d->driverSQLDict->find(str))
00342         needOuterQuotes = true;
00343 
00344 // ... or if the identifier has a space in it...
00345   else if(str.find(' ') != -1)
00346         needOuterQuotes = true;
00347 
00348     if(needOuterQuotes && (options & EscapeKexi)) {
00349         const char quote = '"';
00350         return quote + QCString(str).replace( quote, "\"\"" ) + quote;
00351     }
00352     else if (needOuterQuotes) {
00353         const char quote = beh->QUOTATION_MARKS_FOR_IDENTIFIER.latin1();
00354         return quote + drv_escapeIdentifier(str) + quote;
00355     } else {
00356         return drv_escapeIdentifier(str);
00357     }
00358 }
00359 
00360 void Driver::initSQLKeywords(int hashSize) {
00361 
00362     if(!d->driverSQLDict && beh->SQL_KEYWORDS != 0) {
00363       d->initDriverKeywords(beh->SQL_KEYWORDS, hashSize);
00364     }
00365 }
00366 
00367 #include "driver.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys