File indexing completed on 2024-12-08 10:15:21

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KDbDriver.h"
0021 #include "KDbAdmin.h"
0022 #include "KDbConnectionData.h"
0023 #include "KDbConnection.h"
0024 #include "KDbConnectionOptions.h"
0025 #include "KDbDriverManager_p.h"
0026 #include "KDbDriverMetaData.h"
0027 #include "KDbDriver_p.h"
0028 #include "KDbDriverBehavior.h"
0029 #include "KDbError.h"
0030 #include "KDbExpression.h"
0031 #include "kdb_debug.h"
0032 
0033 #include <algorithm>
0034 
0035 /*! @internal Used in KDbDriver::defaultSqlTypeName(int)
0036  when we do not have KDbDriver instance yet, or when we cannot get one */
0037 static const char* const KDb_defaultSqlTypeNames[] = {
0038     "InvalidType",
0039     "Byte",
0040     "ShortInteger",
0041     "Integer",
0042     "BigInteger",
0043     "Boolean",
0044     "Date",
0045     "DateTime",
0046     "Time",
0047     "Float",
0048     "Double",
0049     "Text",
0050     "LongText",
0051     "BLOB"
0052 };
0053 
0054 //---------------------------------------------
0055 
0056 KDbDriver::KDbDriver(QObject *parent, const QVariantList &args)
0057  : QObject(parent)
0058  , d(new KDbDriverPrivate(this))
0059 {
0060     Q_UNUSED(args);
0061     d->driverBehavior.typeNames.resize(KDbField::LastType + 1);
0062 }
0063 
0064 KDbDriver::~KDbDriver()
0065 {
0066     // make a copy because d->connections will be touched by ~KDbConnection
0067     QSet<KDbConnection*> connections(d->connections);
0068     qDeleteAll(connections);
0069     d->connections.clear();
0070     delete d;
0071 // kdbDebug() << "ok";
0072 }
0073 
0074 KDbDriverBehavior *KDbDriver::behavior()
0075 {
0076     return &d->driverBehavior;
0077 }
0078 
0079 const KDbDriverBehavior *KDbDriver::behavior() const
0080 {
0081     return &d->driverBehavior;
0082 }
0083 
0084 bool KDbDriver::isValid()
0085 {
0086     clearResult();
0087     QString inv_impl(tr("Invalid database driver's \"%1\" implementation.").arg(metaData()->name()));
0088     QString not_init(tr("Value of \"%1\" is not initialized for the driver."));
0089     if (d->driverBehavior.ROW_ID_FIELD_NAME.isEmpty()) {
0090         m_result = KDbResult(ERR_INVALID_DRIVER_IMPL,
0091                           inv_impl + QLatin1Char(' ')
0092                           + not_init.arg(QLatin1String("KDbDriverBehavior::ROW_ID_FIELD_NAME")));
0093         return false;
0094     }
0095     return true;
0096 }
0097 
0098 const QSet<KDbConnection*> KDbDriver::connections() const
0099 {
0100     return d->connections;
0101 }
0102 
0103 const KDbDriverMetaData* KDbDriver::metaData() const
0104 {
0105     return d->metaData;
0106 }
0107 
0108 int KDbDriver::features() const
0109 {
0110     return d->driverBehavior.features;
0111 }
0112 
0113 bool KDbDriver::transactionsSupported() const
0114 {
0115     return d->driverBehavior.features & (SingleTransactions | MultipleTransactions);
0116 }
0117 
0118 KDbAdminTools& KDbDriver::adminTools() const
0119 {
0120     if (!d->adminTools)
0121         d->adminTools = drv_createAdminTools();
0122     return *d->adminTools;
0123 }
0124 
0125 KDbAdminTools* KDbDriver::drv_createAdminTools() const
0126 {
0127     return new KDbAdminTools(); //empty impl.
0128 }
0129 
0130 QString KDbDriver::sqlTypeName(KDbField::Type type, const KDbField &field) const
0131 {
0132     Q_UNUSED(field);
0133     if (type > KDbField::InvalidType && type <= KDbField::LastType) { /*sanity*/
0134         return d->driverBehavior.typeNames[type];
0135     }
0136     return d->driverBehavior.typeNames[KDbField::InvalidType];
0137 }
0138 
0139 KDbConnection *KDbDriver::createConnection(const KDbConnectionData& connData,
0140                                            const KDbConnectionOptions &options)
0141 {
0142     clearResult();
0143     if (!isValid())
0144         return nullptr;
0145 
0146     KDbConnection *conn = drv_createConnection(connData, options);
0147 
0148 //! @todo needed? connData->setDriverId(id());
0149     d->connections.insert(conn);
0150     return conn;
0151 }
0152 
0153 KDbConnection *KDbDriver::createConnection(const KDbConnectionData& connData)
0154 {
0155     return createConnection(connData, KDbConnectionOptions());
0156 }
0157 
0158 KDbConnection* KDbDriver::removeConnection(KDbConnection *conn)
0159 {
0160     clearResult();
0161     if (d->connections.remove(conn))
0162         return conn;
0163     return nullptr;
0164 }
0165 
0166 QString KDbDriver::defaultSqlTypeName(KDbField::Type type)
0167 {
0168     if (type > KDbField::LastType)
0169         return QLatin1String("Null");
0170     return QLatin1String(KDb_defaultSqlTypeNames[type]);
0171 }
0172 
0173 bool KDbDriver::isKDbSystemObjectName(const QString& name)
0174 {
0175     if (!name.startsWith(QLatin1String("kexi__"), Qt::CaseInsensitive))
0176         return false;
0177     return KDbConnection::kdbSystemTableNames().contains(name, Qt::CaseInsensitive);
0178 }
0179 
0180 bool KDbDriver::isSystemFieldName(const QString& name) const
0181 {
0182     if (!d->driverBehavior.ROW_ID_FIELD_NAME.isEmpty()
0183         && 0 == name.compare(d->driverBehavior.ROW_ID_FIELD_NAME, Qt::CaseInsensitive))
0184     {
0185         return true;
0186     }
0187     return drv_isSystemFieldName(name);
0188 }
0189 
0190 KDbEscapedString valueToSqlInternal(const KDbDriver *driver, KDbField::Type ftype, const QVariant& v)
0191 {
0192     if (v.isNull() || ftype == KDbField::Null) {
0193         return KDbEscapedString("NULL");
0194     }
0195     switch (ftype) {
0196     case KDbField::Text:
0197     case KDbField::LongText: {
0198         return driver ? driver->escapeString(v.toString())
0199                       : KDbEscapedString(KDb::escapeString(v.toString()));
0200     }
0201     case KDbField::Byte:
0202     case KDbField::ShortInteger:
0203     case KDbField::Integer:
0204     case KDbField::BigInteger:
0205         return KDbEscapedString(v.toByteArray());
0206     case KDbField::Float:
0207     case KDbField::Double: {
0208         if (v.type() == QVariant::String) {
0209             //workaround for values stored as string that should be casted to floating-point
0210             KDbEscapedString s(v.toByteArray());
0211             return s.replace(',', '.');
0212         }
0213         return KDbEscapedString(v.toByteArray());
0214     }
0215 //! @todo here special encoding method needed
0216     case KDbField::Boolean:
0217         return driver
0218             ? KDbEscapedString(v.toInt() == 0 ? KDbDriverPrivate::behavior(driver)->BOOLEAN_FALSE_LITERAL
0219                                               : KDbDriverPrivate::behavior(driver)->BOOLEAN_TRUE_LITERAL)
0220             : KDbEscapedString(v.toInt() == 0 ? "FALSE" : "TRUE");
0221     case KDbField::Time:
0222         return driver ? driver->timeToSql(v) : KDb::timeToSql(v);
0223     case KDbField::Date:
0224         return driver ? driver->dateToSql(v) : KDb::dateToSql(v);
0225     case KDbField::DateTime:
0226         return driver ? driver->dateTimeToSql(v) : KDb::dateTimeToSql(v);
0227     case KDbField::BLOB: {
0228         if (v.toByteArray().isEmpty()) {
0229             return KDbEscapedString("NULL");
0230         }
0231         if (v.type() == QVariant::String) {
0232             return driver ? driver->escapeBLOB(v.toString().toUtf8())
0233                           : KDbEscapedString(KDb::escapeBLOB(v.toString().toUtf8(), KDb::BLOBEscapingType::ZeroXHex));
0234         }
0235         return driver ? driver->escapeBLOB(v.toByteArray())
0236                       : KDbEscapedString(KDb::escapeBLOB(v.toByteArray(), KDb::BLOBEscapingType::ZeroXHex));
0237     }
0238     case KDbField::InvalidType:
0239         return KDbEscapedString("!INVALIDTYPE!");
0240     default:
0241         kdbDebug() << KDbEscapedString("UNKNOWN!");
0242     }
0243     return KDbEscapedString();
0244 }
0245 
0246 KDbEscapedString KDbDriver::valueToSql(KDbField::Type ftype, const QVariant& v) const
0247 {
0248     //! @note it was compatible with SQLite: https://www.sqlite.org/lang_datefunc.html
0249     return valueToSqlInternal(this, ftype, v);
0250 }
0251 
0252 KDbEscapedString KDbDriver::dateToSql(const QVariant &v) const
0253 {
0254     return KDb::dateToIsoString(v);
0255 }
0256 
0257 KDbEscapedString KDbDriver::timeToSql(const QVariant& v) const
0258 {
0259     return KDb::timeToIsoString(v);
0260 }
0261 
0262 KDbEscapedString KDbDriver::dateTimeToSql(const QVariant& v) const
0263 {
0264     return KDb::dateTimeToIsoString(v);
0265 }
0266 
0267 KDbEscapedString KDbDriver::dateTimeToSql(const QDateTime& v) const
0268 {
0269     return dateTimeToSql(QVariant(v));
0270 }
0271 
0272 QString KDbDriver::escapeIdentifier(const QString& str) const
0273 {
0274     return QLatin1Char(d->driverBehavior.OPENING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER)
0275             + drv_escapeIdentifier(str)
0276             + QLatin1Char(d->driverBehavior.CLOSING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER);
0277 }
0278 
0279 QByteArray KDbDriver::escapeIdentifier(const QByteArray& str) const
0280 {
0281     return d->driverBehavior.OPENING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER
0282             + drv_escapeIdentifier(str)
0283             + d->driverBehavior.CLOSING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER;
0284 }
0285 
0286 KDbUtils::Property KDbDriver::internalProperty(const QByteArray& name) const
0287 {
0288     return d->driverBehavior.properties.property(name);
0289 }
0290 
0291 QList<QByteArray> KDbDriver::internalPropertyNames() const
0292 {
0293     QList<QByteArray> names(d->driverBehavior.properties.names());
0294     std::sort(names.begin(), names.end());
0295     return names;
0296 }
0297 
0298 void KDbDriver::initDriverSpecificKeywords(const char* const* keywords)
0299 {
0300     d->driverSpecificSqlKeywords.setStrings(keywords);
0301 }
0302 
0303 KDbEscapedString KDbDriver::addLimitTo1(const KDbEscapedString& sql, bool add)
0304 {
0305     return add ? (sql + " LIMIT 1") : sql;
0306 }
0307 
0308 bool KDbDriver::isDriverSpecificKeyword(const QByteArray& word) const
0309 {
0310     return d->driverSpecificSqlKeywords.contains(word);
0311 }
0312 
0313 void KDbDriver::setMetaData(const KDbDriverMetaData *metaData)
0314 {
0315     d->metaData = metaData;
0316     d->driverBehavior.initInternalProperties();
0317 }
0318 
0319 KDbEscapedString KDbDriver::hexFunctionToString(
0320                                        const KDbNArgExpression &args,
0321                                        KDbQuerySchemaParameterValueListIterator* params,
0322                                        KDb::ExpressionCallStack* callStack) const
0323 {
0324     return KDbFunctionExpression::toString(QLatin1String("HEX"), this, args, params, callStack);
0325 }
0326 
0327 KDbEscapedString KDbDriver::ifnullFunctionToString(
0328                                           const KDbNArgExpression &args,
0329                                           KDbQuerySchemaParameterValueListIterator* params,
0330                                           KDb::ExpressionCallStack* callStack) const
0331 {
0332     return KDbFunctionExpression::toString(QLatin1String("IFNULL"), this, args, params, callStack);
0333 }
0334 
0335 KDbEscapedString KDbDriver::lengthFunctionToString(
0336                                           const KDbNArgExpression &args,
0337                                           KDbQuerySchemaParameterValueListIterator* params,
0338                                           KDb::ExpressionCallStack* callStack) const
0339 {
0340     return KDbFunctionExpression::toString(QLatin1String("LENGTH"), this, args, params, callStack);
0341 }
0342 
0343 KDbEscapedString KDbDriver::greatestOrLeastFunctionToString(
0344                                             const QString &name,
0345                                             const KDbNArgExpression &args,
0346                                             KDbQuerySchemaParameterValueListIterator* params,
0347                                             KDb::ExpressionCallStack* callStack) const
0348 {
0349     return KDbFunctionExpression::toString(name, this, args, params, callStack);
0350 }
0351 
0352 KDbEscapedString KDbDriver::randomFunctionToString(
0353                                             const KDbNArgExpression &args,
0354                                             KDbQuerySchemaParameterValueListIterator* params,
0355                                             KDb::ExpressionCallStack* callStack) const
0356 {
0357     static QLatin1String randomStatic("()");
0358     if (!args.isNull() || args.argCount() < 1 ) {
0359         return KDbEscapedString(d->driverBehavior.RANDOM_FUNCTION + randomStatic);
0360     }
0361     if (args.argCount() != 2) {
0362         return KDbEscapedString();
0363     }
0364     const KDbEscapedString x(args.arg(0).toString(this, params, callStack));
0365     const KDbEscapedString y(args.arg(1).toString(this, params, callStack));
0366     static KDbEscapedString floorRandomStatic("+FLOOR(");
0367     static KDbEscapedString floorRandomStatic2("()*(");
0368     static KDbEscapedString floorRandomStatic3(")))");
0369     return KDbEscapedString('(') + x + floorRandomStatic + d->driverBehavior.RANDOM_FUNCTION
0370             + floorRandomStatic2 + y + QLatin1Char('-') + x + floorRandomStatic3;
0371 }
0372 
0373 KDbEscapedString KDbDriver::ceilingOrFloorFunctionToString(
0374                                                 const QString &name,
0375                                                 const KDbNArgExpression &args,
0376                                                 KDbQuerySchemaParameterValueListIterator* params,
0377                                                 KDb::ExpressionCallStack* callStack) const
0378 {
0379     return KDbFunctionExpression::toString(name, this, args, params, callStack);
0380 }
0381 
0382 KDbEscapedString KDbDriver::unicodeFunctionToString(
0383                                         const KDbNArgExpression &args,
0384                                         KDbQuerySchemaParameterValueListIterator* params,
0385                                         KDb::ExpressionCallStack* callStack) const
0386 {
0387     return KDbFunctionExpression::toString(QLatin1String("UNICODE"), this, args, params, callStack);
0388 }
0389 
0390 KDbEscapedString KDbDriver::concatenateFunctionToString(const KDbBinaryExpression &args,
0391                                                KDbQuerySchemaParameterValueListIterator* params,
0392                                                KDb::ExpressionCallStack* callStack) const
0393 {
0394     return args.left().toString(this, params, callStack) + KDbEscapedString("||")
0395             + args.right().toString(this, params, callStack);
0396 }
0397 
0398 //---------------
0399 
0400 Q_GLOBAL_STATIC_WITH_ARGS(
0401     KDbUtils::StaticSetOfStrings,
0402     KDb_kdbSqlKeywords,
0403     (KDbDriverPrivate::kdbSQLKeywords) )
0404 
0405 KDB_EXPORT bool KDb::isKDbSqlKeyword(const QByteArray& word)
0406 {
0407     return KDb_kdbSqlKeywords->contains(word.toUpper());
0408 }
0409 
0410 KDB_EXPORT QString KDb::escapeIdentifier(const KDbDriver* driver,
0411                                          const QString& str)
0412 {
0413     return driver ? driver->escapeIdentifier(str)
0414                   : KDb::escapeIdentifier(str);
0415 }
0416 
0417 KDB_EXPORT QByteArray KDb::escapeIdentifier(const KDbDriver* driver,
0418                                             const QByteArray& str)
0419 {
0420     return driver ? driver->escapeIdentifier(str)
0421                   : KDb::escapeIdentifier(str);
0422 }