File indexing completed on 2024-04-28 15:58:54

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2015 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 "SqliteDriver.h"
0021 #include "SqliteConnection.h"
0022 #include "SqliteConnection_p.h"
0023 #include "SqliteAdmin.h"
0024 
0025 #include "KDbConnection.h"
0026 #include "KDbDriverManager.h"
0027 #include "KDbDriverBehavior.h"
0028 #include "KDbExpression.h"
0029 #include "KDb.h"
0030 
0031 #include <KPluginFactory>
0032 
0033 #include <sqlite3.h>
0034 
0035 KDB_DRIVER_PLUGIN_FACTORY(SqliteDriver, "kdb_sqlitedriver.json")
0036 
0037 //! driver specific private data
0038 //! @internal
0039 class SqliteDriverPrivate
0040 {
0041 public:
0042     SqliteDriverPrivate()
0043      : collate(QLatin1String(" COLLATE ''"))
0044     {
0045     }
0046     KDbEscapedString collate;
0047     Q_DISABLE_COPY(SqliteDriverPrivate)
0048 };
0049 
0050 SqliteDriver::SqliteDriver(QObject *parent, const QVariantList &args)
0051         : KDbDriver(parent, args)
0052         , dp(new SqliteDriverPrivate)
0053 {
0054     KDbDriverBehavior *beh = behavior();
0055     beh->features = SingleTransactions | CursorForward | CompactingDatabaseSupported;
0056 
0057     //special method for autoincrement definition
0058     beh->SPECIAL_AUTO_INCREMENT_DEF = true;
0059     beh->AUTO_INCREMENT_FIELD_OPTION = QString(); //not available
0060     beh->AUTO_INCREMENT_TYPE = QLatin1String("INTEGER");
0061     beh->AUTO_INCREMENT_PK_FIELD_OPTION = QLatin1String("PRIMARY KEY");
0062     beh->AUTO_INCREMENT_REQUIRES_PK = true;
0063     beh->ROW_ID_FIELD_NAME = QLatin1String("OID");
0064     beh->IS_DB_OPEN_AFTER_CREATE = true;
0065     beh->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY = true;
0066     beh->OPENING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER = '[';
0067     beh->CLOSING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER = ']';
0068     beh->SELECT_1_SUBQUERY_SUPPORTED = true;
0069     beh->CONNECTION_REQUIRED_TO_CHECK_DB_EXISTENCE = false;
0070     beh->CONNECTION_REQUIRED_TO_CREATE_DB = false;
0071     beh->CONNECTION_REQUIRED_TO_DROP_DB = false;
0072     beh->GET_TABLE_NAMES_SQL
0073         = KDbEscapedString("SELECT name FROM sqlite_master WHERE type='table'");
0074 
0075     initDriverSpecificKeywords(keywords);
0076 
0077     // internal properties
0078     beh->properties.insert("client_library_version", QLatin1String(sqlite3_libversion()));
0079     beh->properties.insert("default_server_encoding", QLatin1String("UTF8")); //OK?
0080 
0081     beh->typeNames[KDbField::Byte] = QLatin1String("Byte");
0082     beh->typeNames[KDbField::ShortInteger] = QLatin1String("ShortInteger");
0083     beh->typeNames[KDbField::Integer] = QLatin1String("Integer");
0084     beh->typeNames[KDbField::BigInteger] = QLatin1String("BigInteger");
0085     beh->typeNames[KDbField::Boolean] = QLatin1String("Boolean");
0086     beh->typeNames[KDbField::Date] = QLatin1String("Date"); // In fact date/time types could be declared as datetext etc.
0087     beh->typeNames[KDbField::DateTime] = QLatin1String("DateTime"); // to force text affinity..., see https://sqlite.org/datatype3.html
0088     beh->typeNames[KDbField::Time] = QLatin1String("Time");
0089     beh->typeNames[KDbField::Float] = QLatin1String("Float");
0090     beh->typeNames[KDbField::Double] = QLatin1String("Double");
0091     beh->typeNames[KDbField::Text] = QLatin1String("Text");
0092     beh->typeNames[KDbField::LongText] = QLatin1String("CLOB");
0093     beh->typeNames[KDbField::BLOB] = QLatin1String("BLOB");
0094 }
0095 
0096 SqliteDriver::~SqliteDriver()
0097 {
0098     delete dp;
0099 }
0100 
0101 
0102 KDbConnection*
0103 SqliteDriver::drv_createConnection(const KDbConnectionData& connData,
0104                                    const KDbConnectionOptions &options)
0105 {
0106     return new SqliteConnection(this, connData, options);
0107 }
0108 
0109 bool SqliteDriver::isSystemObjectName(const QString& name) const
0110 {
0111     return name.startsWith(QLatin1String("sqlite_"), Qt::CaseInsensitive);
0112 }
0113 
0114 bool SqliteDriver::isSystemDatabaseName(const QString& name) const
0115 {
0116     Q_UNUSED(name);
0117     return false;
0118 }
0119 
0120 bool SqliteDriver::drv_isSystemFieldName(const QString& name) const
0121 {
0122     return    0 == name.compare(QLatin1String("_rowid_"), Qt::CaseInsensitive)
0123            || 0 == name.compare(QLatin1String("rowid"), Qt::CaseInsensitive)
0124            || 0 == name.compare(QLatin1String("oid"), Qt::CaseInsensitive);
0125 }
0126 
0127 KDbEscapedString SqliteDriver::escapeString(const QString& str) const
0128 {
0129     return KDbEscapedString("'") + KDbEscapedString(str).replace('\'', "''") + '\'';
0130 }
0131 
0132 KDbEscapedString SqliteDriver::escapeString(const QByteArray& str) const
0133 {
0134     return KDbEscapedString("'") + KDbEscapedString(str).replace('\'', "''") + '\'';
0135 }
0136 
0137 KDbEscapedString SqliteDriver::escapeBLOB(const QByteArray& array) const
0138 {
0139     return KDbEscapedString(KDb::escapeBLOB(array, KDb::BLOBEscapingType::XHex));
0140 }
0141 
0142 QString SqliteDriver::drv_escapeIdentifier(const QString& str) const
0143 {
0144     return QString(str).replace(QLatin1Char('"'), QLatin1String("\"\""));
0145 }
0146 
0147 QByteArray SqliteDriver::drv_escapeIdentifier(const QByteArray& str) const
0148 {
0149     return QByteArray(str).replace('"', "\"\"");
0150 }
0151 
0152 KDbAdminTools* SqliteDriver::drv_createAdminTools() const
0153 {
0154     return new SqliteAdminTools();
0155 }
0156 
0157 KDbEscapedString SqliteDriver::collationSql() const
0158 {
0159     return dp->collate;
0160 }
0161 
0162 KDbEscapedString SqliteDriver::greatestOrLeastFunctionToString(const QString &name,
0163                                                       const KDbNArgExpression &args,
0164                                                       KDbQuerySchemaParameterValueListIterator* params,
0165                                                       KDb::ExpressionCallStack* callStack) const
0166 {
0167     Q_ASSERT(args.argCount() >= 2);
0168     static QString greatestString(QLatin1String("GREATEST"));
0169     static QString maxString(QLatin1String("MAX"));
0170     static QString minString(QLatin1String("MIN"));
0171     const QString realName(
0172         name == greatestString ? maxString : minString);
0173     if (args.argCount() >= 2 && KDbField::isTextType(args.arg(0).type())) {
0174         KDbEscapedString s;
0175         s.reserve(256);
0176         for(int i=0; i < args.argCount(); ++i) {
0177             if (!s.isEmpty()) {
0178                 s += ", ";
0179             }
0180             s += QLatin1Char('(') + args.arg(i).toString(this, params, callStack) + QLatin1String(") ") + collationSql();
0181         }
0182         return realName + QLatin1Char('(') + s + QLatin1Char(')');
0183     }
0184     return KDbFunctionExpression::toString(realName, this, args, params, callStack);
0185 }
0186 
0187 KDbEscapedString SqliteDriver::randomFunctionToString(const KDbNArgExpression &args,
0188                                              KDbQuerySchemaParameterValueListIterator* params,
0189                                              KDb::ExpressionCallStack* callStack) const
0190 {
0191     if (args.isNull() || args.argCount() < 1 ) {
0192         static KDbEscapedString randomStatic("((RANDOM()+9223372036854775807)/18446744073709551615)");
0193         return randomStatic;
0194     }
0195     Q_ASSERT(args.argCount() == 2);
0196     const KDbEscapedString x(args.arg(0).toString(this, params, callStack));
0197     const KDbEscapedString y(args.arg(1).toString(this, params, callStack));
0198     static KDbEscapedString floorRandomStatic("+CAST(((");
0199     static KDbEscapedString floorRandomStatic2("))*(RANDOM()+9223372036854775807)/18446744073709551615 AS INT))");
0200     //! (X + CAST((Y - X) * (RANDOM()+9223372036854775807)/18446744073709551615 AS INT)).
0201     return KDbEscapedString("((") + x + QLatin1Char(')') + floorRandomStatic + y + QLatin1Char(')')
0202             + QLatin1String("-(") + x + floorRandomStatic2;
0203 }
0204 
0205 KDbEscapedString SqliteDriver::ceilingOrFloorFunctionToString(const QString &name,
0206                                                      const KDbNArgExpression &args,
0207                                                      KDbQuerySchemaParameterValueListIterator* params,
0208                                                      KDb::ExpressionCallStack* callStack) const
0209 {
0210     Q_ASSERT(args.argCount() == 1);
0211     static QLatin1String ceilingString("CEILING");
0212     KDbEscapedString x(args.arg(0).toString(this, params, callStack));
0213     if (name == ceilingString) {
0214         return KDbEscapedString("(CASE WHEN ")
0215             + x + QLatin1String("=CAST(") + x + QLatin1String(" AS INT) THEN CAST(")
0216             + x + QLatin1String(" AS INT) WHEN ")
0217             + x + QLatin1String(">=0 THEN CAST(")
0218             + x + QLatin1String(" AS INT)+1 ELSE CAST(")
0219             + x + QLatin1String(" AS INT) END)");
0220     }
0221     // floor():
0222     return KDbEscapedString("(CASE WHEN ") + x + QLatin1String(">=0 OR ")
0223             + x + QLatin1String("=CAST(") + x + QLatin1String(" AS INT) THEN CAST(")
0224             + x + QLatin1String(" AS INT) ELSE CAST(")
0225             + x + QLatin1String(" AS INT)-1 END)");
0226 }
0227 
0228 #include "SqliteDriver.moc"