|
|
|
/* 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"
|