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::Text:
00235         case Field::LongText: {
00236             QString s = v.toString();
00237             return escapeString(s); //QString("'")+s.replace( '"', "\\\"" ) + "'";
00238         }
00239         case Field::Byte:
00240         case Field::ShortInteger:
00241         case Field::Integer:
00242         case Field::BigInteger:
00243             return v.toString();
00244         case Field::Float:
00245         case Field::Double: {
00246             if (v.type()==QVariant::String) {
00247                 //workaround for values stored as string but should be casted to floating-point
00248                 QString s(v.toString());
00249                 return s.replace(',', ".");
00250             }
00251             return v.toString();
00252         }
00253 //TODO: here special encoding method needed
00254         case Field::Boolean:
00255             return QString::number(v.toInt()?1:0); //0 or 1
00256         case Field::Time:
00257             return QString("\'")+v.toTime().toString(Qt::ISODate)+"\'";
00258         case Field::Date:
00259             return QString("\'")+v.toDate().toString(Qt::ISODate)+"\'";
00260         case Field::DateTime:
00261             return dateTimeToSQL( v.toDateTime() );
00262         case Field::BLOB: {
00263             if (v.type()==QVariant::String)
00264                 return escapeBLOB(v.toString().utf8());
00265             return escapeBLOB(v.toByteArray());
00266         }
00267         case Field::InvalidType:
00268             return "!INVALIDTYPE!";
00269         default:
00270             KexiDBDbg << "Driver::valueToSQL(): UNKNOWN!" << endl;
00271             return QString::null;
00272     }
00273     return QString::null;
00274 }
00275 
00276 QVariant Driver::propertyValue( const QCString& propName ) const
00277 {
00278     return d->properties[propName.lower()];
00279 }
00280 
00281 QString Driver::propertyCaption( const QCString& propName ) const
00282 {
00283     return d->propertyCaptions[propName.lower()];
00284 }
00285 
00286 QValueList<QCString> Driver::propertyNames() const
00287 {
00288     QValueList<QCString> names = d->properties.keys();
00289     qHeapSort(names);
00290     return names;
00291 }
00292 
00293 QString Driver::escapeIdentifier(const QString& str, int options) const
00294 {
00295     QCString cstr = str.latin1();
00296     return QString(escapeIdentifier(cstr, options));
00297 }
00298 
00299 QCString Driver::escapeIdentifier(const QCString& str, int options) const
00300 {
00301     bool needOuterQuotes = false;
00302 
00303 // Need to use quotes if ...
00304 // ... we have been told to, or ...
00305     if(options & EscapeAlways)
00306         needOuterQuotes = true;
00307 
00308 // ... or if the driver does not have a list of keywords,
00309     else if(!d->driverSQLDict)
00310         needOuterQuotes = true;
00311 
00312 // ... or if it's a keyword in Kexi's SQL dialect,
00313     else if(d->kexiSQLDict->find(str))
00314         needOuterQuotes = true;
00315 
00316 // ... or if it's a keyword in the backends SQL dialect,
00317 // (have already checked !d->driverSQLDict)
00318     else if((options & EscapeDriver) && d->driverSQLDict->find(str))
00319         needOuterQuotes = true;
00320 
00321 // ... or if the identifier has a space in it...
00322   else if(str.find(' ') != -1)
00323         needOuterQuotes = true;
00324 
00325     if(needOuterQuotes && (options & EscapeKexi)) {
00326         const char quote = '"';
00327         return quote + QCString(str).replace( quote, "\"\"" ) + quote;
00328     }
00329     else if (needOuterQuotes) {
00330         const char quote = beh->QUOTATION_MARKS_FOR_IDENTIFIER.latin1();
00331         return quote + drv_escapeIdentifier(str) + quote;
00332     } else {
00333         return drv_escapeIdentifier(str);
00334     }
00335 }
00336 
00337 void Driver::initSQLKeywords(int hashSize) {
00338 
00339     if(!d->driverSQLDict && beh->SQL_KEYWORDS != 0) {
00340       d->initDriverKeywords(beh->SQL_KEYWORDS, hashSize);
00341     }
00342 }
00343 
00344 #define BLOB_ESCAPING_TYPE_USE_X     0 
00345 #define BLOB_ESCAPING_TYPE_USE_0x    1 
00346 #define BLOB_ESCAPING_TYPE_USE_OCTAL 2 
00347 
00348 QString Driver::escapeBLOBInternal(const QByteArray& array, int type) const
00349 {
00350     const int size = array.size();
00351     int escaped_length = size*2 + 2/*0x or X'*/;
00352     if (type == BLOB_ESCAPING_TYPE_USE_X)
00353         escaped_length += 1; //last char: '
00354     QString str;
00355     str.reserve(escaped_length);
00356     if (str.capacity() < (uint)escaped_length) {
00357         KexiDBWarn << "KexiDB::Driver::escapeBLOB(): no enough memory (cannot allocate "<< \
00358             escaped_length<<" chars)" << endl;
00359         return QString::fromLatin1("NULL");
00360     }
00361     if (type == BLOB_ESCAPING_TYPE_USE_X)
00362         str = QString::fromLatin1("X'");
00363     else if (type == BLOB_ESCAPING_TYPE_USE_0x)
00364         str = QString::fromLatin1("0x");
00365     else if (type == BLOB_ESCAPING_TYPE_USE_OCTAL)
00366         str = QString::fromLatin1("'");
00367     
00368     int new_length = str.length(); //after X' or 0x, etc.
00369     if (type == BLOB_ESCAPING_TYPE_USE_OCTAL) {
00370         // only escape nonprintable characters as in Table 8-7:
00371         // http://www.postgresql.org/docs/8.1/interactive/datatype-binary.html
00372         // i.e. escape for bytes: < 32, >= 127, 39 ('), 92(\). 
00373         for (int i = 0; i < size; i++) {
00374             const unsigned char val = array[i];
00375             if (val<32 || val>=127 || val==39 || val==92) {
00376                 str[new_length++] = '\\';
00377                 str[new_length++] = '\\';
00378                 str[new_length++] = '0' + val/64;
00379                 str[new_length++] = '0' + (val % 64) / 8;
00380                 str[new_length++] = '0' + val % 8;
00381             }
00382             else {
00383                 str[new_length++] = val;
00384             }
00385         }
00386     }
00387     else {
00388         for (int i = 0; i < size; i++) {
00389             const unsigned char val = array[i];
00390             str[new_length++] = (val/16) < 10 ? ('0'+(val/16)) : ('A'+(val/16)-10);
00391             str[new_length++] = (val%16) < 10 ? ('0'+(val%16)) : ('A'+(val%16)-10);
00392         }
00393     }
00394     if (type == BLOB_ESCAPING_TYPE_USE_X || type == BLOB_ESCAPING_TYPE_USE_OCTAL)
00395         str[new_length++] = '\'';
00396     return str;
00397 }
00398 
00399 #include "driver.moc"
00400 
KDE Home | KDE Accessibility Home | Description of Access Keys