File indexing completed on 2024-05-05 16:47:14

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Adam Pigg <adam@piggz.co.uk>
0003    Copyright (C) 2010-2015 Jarosław Staniek <staniek@kde.org>
0004 
0005    This program is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This program is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this program; see the file COPYING.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "PostgresqlDriver.h"
0022 
0023 #include "KDbConnection.h"
0024 #include "KDbDriverManager.h"
0025 #include "KDbDriverBehavior.h"
0026 #include "KDbExpression.h"
0027 #include "KDb.h"
0028 
0029 #include "PostgresqlConnection.h"
0030 
0031 #include <KPluginFactory>
0032 
0033 #include <libpq-fe.h>
0034 
0035 KDB_DRIVER_PLUGIN_FACTORY(PostgresqlDriver, "kdb_postgresqldriver.json")
0036 
0037 PostgresqlDriver::PostgresqlDriver(QObject *parent, const QVariantList &args)
0038         : KDbDriver(parent, args)
0039 {
0040     KDbDriverBehavior *beh = behavior();
0041     beh->features = SingleTransactions | CursorForward | CursorBackward;
0042 //! @todo enable this when KDb supports multiple: beh->features = MultipleTransactions | CursorForward | CursorBackward;
0043 
0044     beh->UNSIGNED_TYPE_KEYWORD = QString();
0045     beh->ROW_ID_FIELD_NAME = QLatin1String("oid");
0046     beh->SPECIAL_AUTO_INCREMENT_DEF = false;
0047     beh->AUTO_INCREMENT_TYPE = QLatin1String("SERIAL");
0048     beh->AUTO_INCREMENT_FIELD_OPTION = QString();
0049     beh->AUTO_INCREMENT_PK_FIELD_OPTION = QLatin1String("PRIMARY KEY");
0050     beh->ALWAYS_AVAILABLE_DATABASE_NAME = QLatin1String("template1");
0051     beh->OPENING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER = '"';
0052     beh->CLOSING_QUOTATION_MARK_BEGIN_FOR_IDENTIFIER = '"';
0053     beh->LIKE_OPERATOR = QLatin1String("ILIKE");
0054     // Use SQL compliant TRUE or FALSE as described
0055     // at https://www.postgresql.org/docs/8.0/interactive/datatype-boolean.html
0056     // 1 or 0 does not work.
0057     beh->BOOLEAN_TRUE_LITERAL = QLatin1String("TRUE");
0058     beh->BOOLEAN_FALSE_LITERAL = QLatin1String("FALSE");
0059     beh->USE_TEMPORARY_DATABASE_FOR_CONNECTION_IF_NEEDED = true;
0060     beh->GET_TABLE_NAMES_SQL = KDbEscapedString(
0061         "SELECT table_name FROM information_schema.tables WHERE "
0062         "table_type='BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')");
0063 
0064     initDriverSpecificKeywords(m_keywords);
0065     initPgsqlToKDbMap();
0066 
0067     //predefined properties
0068     //https://www.postgresql.org/docs/9.5/static/libpq-misc.html#LIBPQ-PQLIBVERSION
0069 //! @todo use QLibrary to resolve PQlibVersion
0070     beh->properties.insert("client_library_version", PQlibVersion());
0071     //! @todo pgsql default_server_encoding: should be a property of connection
0072     //beh->properties["default_server_encoding"] = QString();
0073 
0074     beh->typeNames[KDbField::Byte] = QLatin1String("SMALLINT");
0075     beh->typeNames[KDbField::ShortInteger] = QLatin1String("SMALLINT");
0076     beh->typeNames[KDbField::Integer] = QLatin1String("INTEGER");
0077     beh->typeNames[KDbField::BigInteger] = QLatin1String("BIGINT");
0078     beh->typeNames[KDbField::Boolean] = QLatin1String("BOOLEAN");
0079     beh->typeNames[KDbField::Date] = QLatin1String("DATE");
0080     beh->typeNames[KDbField::DateTime] = QLatin1String("TIMESTAMP");
0081     beh->typeNames[KDbField::Time] = QLatin1String("TIME");
0082     beh->typeNames[KDbField::Float] = QLatin1String("REAL");
0083     beh->typeNames[KDbField::Double] = QLatin1String("DOUBLE PRECISION");
0084     beh->typeNames[KDbField::Text] = QLatin1String("CHARACTER VARYING");
0085     beh->typeNames[KDbField::LongText] = QLatin1String("TEXT");
0086     beh->typeNames[KDbField::BLOB] = QLatin1String("BYTEA");
0087 }
0088 
0089 PostgresqlDriver::~PostgresqlDriver()
0090 {
0091 }
0092 
0093 QString PostgresqlDriver::sqlTypeName(KDbField::Type type, const KDbField &field) const
0094 {
0095     if (type == KDbField::Null) {
0096         return QLatin1String("NULL");
0097     }
0098     if (type == KDbField::Float || type == KDbField::Double) {
0099         if (field.precision() > 0) {
0100             return QLatin1String("NUMERIC");
0101         }
0102     }
0103     return KDbDriver::sqlTypeName(type, field);
0104 }
0105 
0106 KDbConnection* PostgresqlDriver::drv_createConnection(const KDbConnectionData& connData,
0107                                                       const KDbConnectionOptions &options)
0108 {
0109     return new PostgresqlConnection(this, connData, options);
0110 }
0111 
0112 bool PostgresqlDriver::isSystemObjectName(const QString& name) const
0113 {
0114     Q_UNUSED(name);
0115     return false;
0116 }
0117 
0118 bool PostgresqlDriver::drv_isSystemFieldName(const QString& name) const
0119 {
0120     Q_UNUSED(name);
0121     return false;
0122 }
0123 
0124 bool PostgresqlDriver::isSystemDatabaseName(const QString& name) const
0125 {
0126     return    0 == name.compare(QLatin1String("template1"), Qt::CaseInsensitive)
0127            || 0 == name.compare(QLatin1String("template0"), Qt::CaseInsensitive)
0128            || 0 == name.compare(QLatin1String("postgres"), Qt::CaseInsensitive);
0129 }
0130 
0131 KDbEscapedString PostgresqlDriver::escapeString(const QString& str) const
0132 {
0133     //Cannot use libpq escape functions as they require a db connection
0134     //to escape using the char encoding of the database
0135     //see https://www.postgresql.org/docs/8.1/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING
0136     return KDbEscapedString("E'")
0137            + KDbEscapedString(str).replace("\\", "\\\\").replace("'", "\\\'")
0138            + "'";
0139 }
0140 
0141 KDbEscapedString PostgresqlDriver::escapeString(const QByteArray& str) const
0142 {
0143     //Cannot use libpq escape functions as they require a db connection
0144     //to escape using the char encoding of the database
0145     //see https://www.postgresql.org/docs/8.1/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING
0146     return KDbEscapedString("'")
0147            + QByteArray(str).replace("\\", "\\\\").replace("'", "\\\'")
0148            + "'";
0149 }
0150 
0151 QString PostgresqlDriver::drv_escapeIdentifier(const QString& str) const
0152 {
0153     return QString(str).replace(QLatin1Char('"'), QLatin1String("\"\""));
0154 }
0155 
0156 QByteArray PostgresqlDriver::drv_escapeIdentifier(const QByteArray& str) const
0157 {
0158     return QByteArray(str).replace('"', "\"\"");
0159 }
0160 
0161 KDbEscapedString PostgresqlDriver::escapeBLOB(const QByteArray& array) const
0162 {
0163     return KDbEscapedString(KDb::escapeBLOB(array, KDb::BLOBEscapingType::ByteaHex));
0164 }
0165 
0166 KDbEscapedString PostgresqlDriver::hexFunctionToString(const KDbNArgExpression &args,
0167                                                        KDbQuerySchemaParameterValueListIterator* params,
0168                                                        KDb::ExpressionCallStack* callStack) const
0169 {
0170     Q_ASSERT(args.argCount() == 1);
0171     return KDbEscapedString("UPPER(ENCODE(%1, 'hex'))").arg(args.arg(0).toString(this, params, callStack));
0172 }
0173 
0174 KDbEscapedString PostgresqlDriver::ifnullFunctionToString(const KDbNArgExpression &args,
0175                                                           KDbQuerySchemaParameterValueListIterator* params,
0176                                                           KDb::ExpressionCallStack* callStack) const
0177 {
0178     return KDbFunctionExpression::toString(QLatin1String("COALESCE"), this, args, params, callStack);
0179 }
0180 
0181 KDbEscapedString PostgresqlDriver::lengthFunctionToString(const KDbNArgExpression &args,
0182                                                           KDbQuerySchemaParameterValueListIterator* params,
0183                                                           KDb::ExpressionCallStack* callStack) const
0184 {
0185     Q_ASSERT(args.argCount() == 1);
0186     if (args.arg(0).type() == KDbField::BLOB) {
0187         return KDbFunctionExpression::toString(QLatin1String("OCTET_LENGTH"), this, args, params, callStack);
0188     }
0189     return KDbDriver::lengthFunctionToString(args, params, callStack); // default
0190 }
0191 
0192 KDbEscapedString PostgresqlDriver::greatestOrLeastFunctionToString(const QString &name,
0193                                                 const KDbNArgExpression &args,
0194                                                 KDbQuerySchemaParameterValueListIterator* params,
0195                                                 KDb::ExpressionCallStack* callStack) const
0196 {
0197     return KDbFunctionExpression::greatestOrLeastFunctionUsingCaseToString(
0198                 name, this, args, params, callStack);
0199 }
0200 
0201 KDbEscapedString PostgresqlDriver::unicodeFunctionToString(
0202                                                 const KDbNArgExpression &args,
0203                                                 KDbQuerySchemaParameterValueListIterator* params,
0204                                                 KDb::ExpressionCallStack* callStack) const
0205 {
0206     Q_ASSERT(args.argCount() == 1);
0207     return KDbEscapedString("ASCII(%1)").arg(args.arg(0).toString(this, params, callStack));
0208 }
0209 
0210 #include "PostgresqlDriver.moc"