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 
00029 #include <qfileinfo.h>
00030 
00031 #include <klocale.h>
00032 #include <kdebug.h>
00033 
00034 #include <assert.h>
00035 
00036 using namespace KexiDB;
00037 
00040 QValueVector<QString> dflt_typeNames;
00041 
00042 
00043 //---------------------------------------------
00044 
00045 
00046 DriverBehaviour::DriverBehaviour()
00047     : UNSIGNED_TYPE_KEYWORD("UNSIGNED")
00048     , AUTO_INCREMENT_FIELD_OPTION("AUTO_INCREMENT")
00049     , AUTO_INCREMENT_PK_FIELD_OPTION("AUTO_INCREMENT PRIMARY KEY")
00050     , SPECIAL_AUTO_INCREMENT_DEF(false)
00051     , AUTO_INCREMENT_REQUIRES_PK(false)
00052     , ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE(false)
00053     , QUOTATION_MARKS_FOR_IDENTIFIER('"')
00054     , USING_DATABASE_REQUIRED_TO_CONNECT(true)
00055     , _1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY(false)
00056     , SELECT_1_SUBQUERY_SUPPORTED(false)
00057     , SQL_KEYWORDS(0)
00058 {
00059 
00060 }
00061 
00062 //---------------------------------------------
00063 
00064 Driver::Driver( QObject *parent, const char *name, const QStringList & )
00065     : QObject( parent, name )
00066     , Object()
00067     , beh( new DriverBehaviour() )
00068     , d( new DriverPrivate() )
00069 {
00070     d->connections.setAutoDelete(false);
00071     //TODO: reasonable size
00072     d->connections.resize(101);
00073     d->typeNames.resize(Field::LastType + 1);
00074 
00075     d->initKexiKeywords();
00076 }
00077 
00078 
00079 Driver::~Driver()
00080 {
00081     DriverManagerInternal::self()->aboutDelete( this );
00082 //  KexiDBDbg << "Driver::~Driver()" << endl;
00083     QPtrDictIterator<Connection> it( d->connections );
00084     Connection *conn;
00085     while ( (conn = it.toFirst()) ) {
00086         delete conn;
00087     }
00088     delete beh;
00089     delete d;
00090 //  KexiDBDbg << "Driver::~Driver() ok" << endl;
00091 }
00092 
00093 bool Driver::isValid()
00094 {
00095     clearError();
00096     if (KexiDB::versionMajor() != versionMajor()
00097         || KexiDB::versionMinor() != versionMinor())
00098     {
00099         setError(ERR_INCOMPAT_DRIVER_VERSION,
00100         i18n("Incompatible database driver's \"%1\" version: found version %2, expected version %3.")
00101         .arg(name())
00102         .arg(QString("%1.%2").arg(versionMajor()).arg(versionMinor()))
00103         .arg(QString("%1.%2").arg(KexiDB::versionMajor()).arg(KexiDB::versionMinor())));
00104         return false;
00105     }
00106 
00107     QString inv_impl = i18n("Invalid database driver's \"%1\" implementation:\n").arg(name());
00108     QString not_init = i18n("Value of \"%1\" is not initialized for the driver.");
00109     if (beh->ROW_ID_FIELD_NAME.isEmpty()) {
00110         setError(ERR_INVALID_DRIVER_IMPL, inv_impl + not_init.arg("DriverBehaviour::ROW_ID_FIELD_NAME"));
00111         return false;
00112     }
00113 
00114     return true;
00115 }
00116 
00117 const QPtrList<Connection> Driver::connectionsList() const
00118 {
00119     QPtrList<Connection> clist;
00120     QPtrDictIterator<Connection> it( d->connections );
00121     for( ; it.current(); ++it )
00122         clist.append( &(*it) );
00123     return clist;
00124 }
00125 
00126 QString Driver::fileDBDriverMimeType() const
00127 { return d->fileDBDriverMimeType; }
00128 
00129 QString Driver::defaultFileBasedDriverMimeType()
00130 { return QString::fromLatin1("application/x-kexiproject-sqlite3"); }
00131 
00132 QString Driver::defaultFileBasedDriverName()
00133 {
00134     DriverManager dm;
00135     return dm.lookupByMime(Driver::defaultFileBasedDriverMimeType()).lower();
00136 }
00137 
00138 const KService* Driver::service() const
00139 { return d->service; }
00140 
00141 bool Driver::isFileDriver() const
00142 { return d->isFileDriver; }
00143 
00144 int Driver::features() const
00145 { return d->features; }
00146 
00147 bool Driver::transactionsSupported() const
00148 { return d->features & (SingleTransactions | MultipleTransactions); }
00149 
00150 QString Driver::sqlTypeName(int id_t, int /*p*/) const
00151 {
00152     if (id_t > Field::InvalidType && id_t <= Field::LastType)
00153         return d->typeNames[(id_t>0 && id_t<=Field::LastType) ? id_t : Field::InvalidType /*sanity*/];
00154 
00155     return d->typeNames[Field::InvalidType];
00156 }
00157 
00158 Connection *Driver::createConnection( ConnectionData &conn_data, int options )
00159 {
00160     clearError();
00161     if (!isValid())
00162         return 0;
00163 
00164     if (d->isFileDriver) {
00165         if (conn_data.fileName().isEmpty()) {
00166             setError(ERR_MISSING_DB_LOCATION, i18n("File name expected for file-based database driver.") );
00167             return 0;
00168         }
00169     }
00170 //  Connection *conn = new Connection( this, conn_data );
00171     Connection *conn = drv_createConnection( conn_data );
00172 
00173     conn->setReadOnly(options & ReadOnlyConnection);
00174 
00175     conn_data.driverName = name();
00176     d->connections.insert( conn, conn );
00177     return conn;
00178 }
00179 
00180 Connection* Driver::removeConnection( Connection *conn )
00181 {
00182     clearError();
00183     return d->connections.take( conn );
00184 }
00185 
00186 QString Driver::defaultSQLTypeName(int id_t)
00187 {
00188     if (id_t==Field::Null)
00189         return "Null";
00190     if (dflt_typeNames.isEmpty()) {
00191         dflt_typeNames.resize(Field::LastType + 1);
00192         dflt_typeNames[Field::Byte]="Byte";
00193         dflt_typeNames[Field::ShortInteger]="ShortInteger";
00194         dflt_typeNames[Field::Integer]="Integer";
00195         dflt_typeNames[Field::BigInteger]="BigInteger";
00196         dflt_typeNames[Field::Boolean]="Boolean";
00197         dflt_typeNames[Field::Date]="Date";
00198         dflt_typeNames[Field::DateTime]="DateTime";
00199         dflt_typeNames[Field::Time]="Time";
00200         dflt_typeNames[Field::Float]="Float";
00201         dflt_typeNames[Field::Double]="Double";
00202         dflt_typeNames[Field::Text]="Text";
00203         dflt_typeNames[Field::LongText]="Text";
00204         dflt_typeNames[Field::BLOB]="BLOB";
00205     }
00206     return dflt_typeNames[id_t];
00207 }
00208 
00209 bool Driver::isSystemObjectName( const QString& n ) const
00210 {
00211     return Driver::isKexiDBSystemObjectName(n);
00212 }
00213 
00214 bool Driver::isKexiDBSystemObjectName( const QString& n )
00215 {
00216     if (!n.lower().startsWith("kexi__"))
00217         return false;
00218     const QStringList list( Connection::kexiDBSystemTableNames() );
00219     return list.find(n.lower())!=list.constEnd();
00220 }
00221 
00222 bool Driver::isSystemFieldName( const QString& n ) const
00223 {
00224     if (!beh->ROW_ID_FIELD_NAME.isEmpty() && n.lower()==beh->ROW_ID_FIELD_NAME.lower())
00225         return true;
00226     return drv_isSystemFieldName(n);
00227 }
00228 
00229 QString Driver::valueToSQL( uint ftype, const QVariant& v ) const
00230 {
00231     if (v.isNull())
00232         return "NULL";
00233     switch (ftype) {
00234         case Field::Byte:
00235         case Field::ShortInteger:
00236         case Field::Integer:
00237         case Field::Float:
00238         case Field::Double:
00239         case Field::BigInteger:
00240             return v.toString();
00241 //TODO: here special encoding method needed
00242         case Field::Boolean:
00243             return QString::number(v.toInt()?1:0); //0 or 1
00244         case Field::Time:
00245             return QString("\'")+v.toTime().toString(Qt::ISODate)+"\'";
00246         case Field::Date:
00247             return QString("\'")+v.toDate().toString(Qt::ISODate)+"\'";
00248         case Field::DateTime:
00249             return dateTimeToSQL( v.toDateTime() );
00250         case Field::Text:
00251         case Field::LongText: {
00252             QString s = v.toString();
00253             return escapeString(s); //QString("'")+s.replace( '"', "\\\"" ) + "'";
00254         }
00255         case Field::BLOB: {
00256             if (v.type()==QVariant::String)
00257                 return escapeBLOB(v.toString().utf8());
00258             return escapeBLOB(v.toByteArray());
00259         }
00260         case Field::InvalidType:
00261             return "!INVALIDTYPE!";
00262         default:
00263             KexiDBDbg << "Driver::valueToSQL(): UNKNOWN!" << endl;
00264             return QString::null;
00265     }
00266     return QString::null;
00267 }
00268 
00269 QVariant Driver::propertyValue( const QCString& propName ) const
00270 {
00271     return d->properties[propName.lower()];
00272 }
00273 
00274 QString Driver::propertyCaption( const QCString& propName ) const
00275 {
00276     return d->propertyCaptions[propName.lower()];
00277 }
00278 
00279 QValueList<QCString> Driver::propertyNames() const
00280 {
00281     QValueList<QCString> names = d->properties.keys();
00282     qHeapSort(names);
00283     return names;
00284 }
00285 
00286 QString Driver::escapeIdentifier(const QString& str, int options) const
00287 {
00288     QCString cstr = str.latin1();
00289     return QString(escapeIdentifier(cstr, options));
00290 }
00291 
00292 QCString Driver::escapeIdentifier(const QCString& str, int options) const
00293 {
00294     bool needOuterQuotes = false;
00295 
00296 // Need to use quotes if ...
00297 // ... we have been told to, or ...
00298     if(options & EscapeAlways)
00299         needOuterQuotes = true;
00300 
00301 // ... or if the driver does not have a list of keywords,
00302     else if(!d->driverSQLDict)
00303         needOuterQuotes = true;
00304 
00305 // ... or if it's a keyword in Kexi's SQL dialect,
00306     else if(d->kexiSQLDict->find(str))
00307         needOuterQuotes = true;
00308 
00309 // ... or if it's a keyword in the backends SQL dialect,
00310 // (have already checked !d->driverSQLDict)
00311     else if((options & EscapeDriver) && d->driverSQLDict->find(str))
00312         needOuterQuotes = true;
00313 
00314 // ... or if the identifier has a space in it...
00315   else if(str.find(' ') != -1)
00316         needOuterQuotes = true;
00317 
00318     if(needOuterQuotes && (options & EscapeKexi)) {
00319         const char quote = '"';
00320         return quote + QCString(str).replace( quote, "\"\"" ) + quote;
00321     }
00322     else if (needOuterQuotes) {
00323         const char quote = beh->QUOTATION_MARKS_FOR_IDENTIFIER.latin1();
00324         return quote + drv_escapeIdentifier(str) + quote;
00325     } else {
00326         return drv_escapeIdentifier(str);
00327     }
00328 }
00329 
00330 void Driver::initSQLKeywords(int hashSize) {
00331 
00332     if(!d->driverSQLDict && beh->SQL_KEYWORDS != 0) {
00333       d->initDriverKeywords(beh->SQL_KEYWORDS, hashSize);
00334     }
00335 }
00336 
00337 #define BLOB_ESCAPING_TYPE_USE_X     0 
00338 #define BLOB_ESCAPING_TYPE_USE_0x    1 
00339 #define BLOB_ESCAPING_TYPE_USE_OCTAL 2 
00340 
00341 QString Driver::escapeBLOBInternal(const QByteArray& array, int type) const
00342 {
00343     const int size = array.size();
00344     int escaped_length = size*2 + 2/*0x or X'*/;
00345     if (type == BLOB_ESCAPING_TYPE_USE_X)
00346         escaped_length += 1; //last char: '
00347     QString str;
00348     str.reserve(escaped_length);
00349     if (str.capacity() < (uint)escaped_length) {
00350         KexiDBWarn << "KexiDB::Driver::escapeBLOB(): no enough memory (cannot allocate "<< \
00351             escaped_length<<" chars)" << endl;
00352         return QString::fromLatin1("NULL");
00353     }
00354     if (type == BLOB_ESCAPING_TYPE_USE_X)
00355         str = QString::fromLatin1("X'");
00356     else if (type == BLOB_ESCAPING_TYPE_USE_0x)
00357         str = QString::fromLatin1("0x");
00358     else if (type == BLOB_ESCAPING_TYPE_USE_OCTAL)
00359         str = QString::fromLatin1("'");
00360     
00361     int new_length = str.length(); //after X' or 0x, etc.
00362     if (type == BLOB_ESCAPING_TYPE_USE_OCTAL) {
00363         // only escape nonprintable characters as in Table 8-7:
00364         // http://www.postgresql.org/docs/8.1/interactive/datatype-binary.html
00365         // i.e. escape for bytes: < 32, >= 127, 39 ('), 92(\). 
00366         for (int i = 0; i < size; i++) {
00367             const unsigned char val = array[i];
00368             if (val<32 || val>=127 || val==39 || val==92) {
00369                 str[new_length++] = '\\';
00370                 str[new_length++] = '\\';
00371                 str[new_length++] = '0' + val/64;
00372                 str[new_length++] = '0' + (val % 64) / 8;
00373                 str[new_length++] = '0' + val % 8;
00374             }
00375             else {
00376                 str[new_length++] = val;
00377             }
00378         }
00379     }
00380     else {
00381         for (int i = 0; i < size; i++) {
00382             const unsigned char val = array[i];
00383             str[new_length++] = (val/16) < 10 ? ('0'+(val/16)) : ('A'+(val/16)-10);
00384             str[new_length++] = (val%16) < 10 ? ('0'+(val%16)) : ('A'+(val%16)-10);
00385         }
00386     }
00387     if (type == BLOB_ESCAPING_TYPE_USE_X || type == BLOB_ESCAPING_TYPE_USE_OCTAL)
00388         str[new_length++] = '\'';
00389     return str;
00390 }
00391 
00392 #include "driver.moc"
00393 
KDE Home | KDE Accessibility Home | Description of Access Keys