KDb

PostgresqlConnection.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003 Adam Pigg <adam@piggz.co.uk>
3 Copyright (C) 2010-2016 Jarosław Staniek <staniek@kde.org>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "PostgresqlConnection.h"
22#include "PostgresqlConnection_p.h"
23#include "PostgresqlPreparedStatement.h"
24#include "PostgresqlCursor.h"
25#include "postgresql_debug.h"
26#include "KDbConnectionData.h"
27#include "KDbError.h"
28#include "KDbGlobal.h"
29#include "KDbVersionInfo.h"
30
31#include <QFileInfo>
32#include <QHostAddress>
33
34#define MIN_SERVER_VERSION_MAJOR 7
35#define MIN_SERVER_VERSION_MINOR 1
36
37inline static QByteArray parameter(PGconn *conn, const char *paramName)
38{
39 return PQparameterStatus(conn, paramName);
40}
41
42PostgresqlTransactionData::PostgresqlTransactionData(KDbConnection *conn)
43 : KDbTransactionData(conn)
44{
45}
46
47PostgresqlTransactionData::~PostgresqlTransactionData()
48{
49}
50
51//==================================================================================
52
53PostgresqlConnection::PostgresqlConnection(KDbDriver *driver, const KDbConnectionData& connData,
54 const KDbConnectionOptions &options)
55 : KDbConnection(driver, connData, options)
56 , d(new PostgresqlConnectionInternal(this))
57{
58}
59
60PostgresqlConnection::~PostgresqlConnection()
61{
62 //delete m_trans;
63 destroy();
64 delete d;
65}
66
67KDbCursor* PostgresqlConnection::prepareQuery(const KDbEscapedString& sql, KDbCursor::Options options)
68{
69 return new PostgresqlCursor(this, sql, options);
70}
71
72KDbCursor* PostgresqlConnection::prepareQuery(KDbQuerySchema* query, KDbCursor::Options options)
73{
74 return new PostgresqlCursor(this, query, options);
75}
76
77bool PostgresqlConnection::drv_connect()
78{
79 return true;
80}
81
82bool PostgresqlConnection::drv_getServerVersion(KDbServerVersionInfo* version)
83{
84 // https://www.postgresql.org/docs/8.4/static/libpq-status.html
85 //postgresqlDebug() << "server_version:" << d->parameter("server_version");
86 version->setString(QString::fromLatin1(parameter(d->conn, "server_version")));
87 const int versionNumber = PQserverVersion(d->conn);
88 if (versionNumber > 0) {
89 version->setMajor(versionNumber / 10000);
90 version->setMinor((versionNumber % 1000) / 100);
91 version->setRelease(versionNumber % 100);
92 }
93
94 if ( version->major() < MIN_SERVER_VERSION_MAJOR
95 || (version->major() == MIN_SERVER_VERSION_MAJOR && version->minor() < MIN_SERVER_VERSION_MINOR))
96 {
97 postgresqlWarning()
98 << QString::fromLatin1("PostgreSQL %d.%d is not supported and may not work. The minimum is %d.%d")
99 .arg(version->major()).arg(version->minor()).arg(MIN_SERVER_VERSION_MAJOR).arg(MIN_SERVER_VERSION_MINOR);
100 }
101 return true;
102}
103
104bool PostgresqlConnection::drv_disconnect()
105{
106 return true;
107}
108
109bool PostgresqlConnection::drv_getDatabasesList(QStringList* list)
110{
111 return queryStringList(KDbEscapedString("SELECT datname FROM pg_database WHERE datallowconn = TRUE"), list);
112}
113
114bool PostgresqlConnection::drv_createDatabase(const QString &dbName)
115{
116 return executeSql(KDbEscapedString("CREATE DATABASE ") + escapeIdentifier(dbName));
117}
118
119QByteArray buildConnParameter(const QByteArray& key, const QVariant& value)
120{
121 QByteArray result = key;
122//! @todo optimize
123 result.replace('\\', "\\\\").replace('\'', "\\'");
124 return key + "='" + value.toString().toUtf8() + "' ";
125}
126
127bool PostgresqlConnection::drv_useDatabase(const QString &dbName, bool *cancelled,
128 KDbMessageHandler* msgHandler)
129{
130 Q_UNUSED(cancelled);
131 Q_UNUSED(msgHandler);
132
133 QByteArray conninfo;
134
135 if (data().hostName().isEmpty()
136 || 0 == QString::compare(data().hostName(), QLatin1String("localhost"), Qt::CaseInsensitive))
137 {
138 if (!data().localSocketFileName().isEmpty()) {
139 QFileInfo fileInfo(data().localSocketFileName());
140 if (fileInfo.exists()) {
141 conninfo += buildConnParameter("host", fileInfo.absolutePath());
142 }
143 }
144 }
145 else {
146 const QHostAddress ip(data().hostName());
147 if (ip.isNull()) {
148 conninfo += buildConnParameter("host", data().hostName());
149 }
150 else {
151 conninfo += buildConnParameter("hostaddr", ip.toString());
152 }
153 }
154
155 //Build up the connection string
156 if (data().port() > 0)
157 conninfo += buildConnParameter("port", data().port());
158
159 QString myDbName = dbName;
160 if (myDbName.isEmpty())
161 myDbName = data().databaseName();
162 if (!myDbName.isEmpty())
163 conninfo += buildConnParameter("dbname", myDbName);
164
165 if (!data().userName().isEmpty())
166 conninfo += buildConnParameter("user", data().userName());
167
168 if (!data().password().isEmpty())
169 conninfo += buildConnParameter("password", data().password());
170
171 //postgresqlDebug() << conninfo;
172
173 //! @todo other parameters: connect_timeout, options, options, sslmode, sslcert, sslkey, sslrootcert, sslcrl, krbsrvname, gsslib, service
174 // https://www.postgresql.org/docs/8.4/interactive/libpq-connect.html
175 d->conn = PQconnectdb(conninfo.constData());
176
177 if (!d->connectionOK()) {
178 PQfinish(d->conn);
179 d->conn = nullptr;
180 return false;
181 }
182
183 // pgsql 8.1 changed the default to no oids but we need them
184 PGresult* result = PQexec(d->conn, "SET DEFAULT_WITH_OIDS TO ON");
185 int status = PQresultStatus(result);
186 Q_UNUSED(status)
187 PQclear(result);
188
189 // initialize encoding
190 result = PQexec(d->conn, "SET CLIENT_ENCODING TO 'UNICODE'");
191 status = PQresultStatus(result);
192 PQclear(result);
193 d->unicode = status == PGRES_COMMAND_OK;
194
195 result = PQexec(d->conn, "SET DATESTYLE TO 'ISO'");
196 status = PQresultStatus(result);
197 if (status != PGRES_COMMAND_OK) {
198 postgresqlWarning() << "Failed to set DATESTYLE to 'ISO':" << PQerrorMessage(d->conn);
199 }
200 //! @todo call on first use of SOUNDEX(), etc.;
201 //! it's not possible now because we don't have connection context in KDbFunctionExpressionData
202 if (!d->fuzzystrmatchExtensionCreated) {
203 d->fuzzystrmatchExtensionCreated
204 = drv_executeSql(KDbEscapedString("CREATE EXTENSION IF NOT EXISTS fuzzystrmatch"));
205 }
206 PQclear(result);
207 return true;
208}
209
210bool PostgresqlConnection::drv_closeDatabase()
211{
212 PQfinish(d->conn);
213 d->conn = nullptr;
214 return true;
215}
216
217bool PostgresqlConnection::drv_dropDatabase(const QString &dbName)
218{
219 //postgresqlDebug() << dbName;
220
221 //! @todo Maybe should check that dbname is no the currentdb
222 if (executeSql(KDbEscapedString("DROP DATABASE ") + escapeIdentifier(dbName)))
223 return true;
224
225 return false;
226}
227
228KDbSqlResult* PostgresqlConnection::drv_prepareSql(const KDbEscapedString& sql)
229{
230 PGresult* result = d->executeSql(sql);
231 const ExecStatusType status = PQresultStatus(result);
232 if (status == PGRES_TUPLES_OK || status == PGRES_COMMAND_OK) {
233 return new PostgresqlSqlResult(this, result, status);
234 }
235 storeResult(result, status);
236 return nullptr;
237}
238
239bool PostgresqlConnection::drv_executeSql(const KDbEscapedString& sql)
240{
241 PGresult* result = d->executeSql(sql);
242 const ExecStatusType status = PQresultStatus(result);
243 d->storeResultAndClear(&m_result, &result, status);
244 return status == PGRES_TUPLES_OK || status == PGRES_COMMAND_OK;
245}
246
247bool PostgresqlConnection::drv_isDatabaseUsed() const
248{
249 return d->conn;
250}
251
252tristate PostgresqlConnection::drv_containsTable(const QString &tableName)
253{
254 return resultExists(KDbEscapedString("SELECT 1 FROM pg_class WHERE relkind='r' AND relname LIKE %1")
255 .arg(escapeString(tableName)));
256}
257
258QString PostgresqlConnection::serverResultName() const
259{
260 if (m_result.code() >= 0 && m_result.code() <= PGRES_SINGLE_TUPLE) {
261 return QString::fromLatin1(PQresStatus(ExecStatusType(m_result.code())));
262 }
263 return QString();
264}
265
266KDbPreparedStatementInterface* PostgresqlConnection::prepareStatementInternal()
267{
268 return new PostgresqlPreparedStatement(d);
269}
270
271KDbEscapedString PostgresqlConnection::escapeString(const QByteArray& str) const
272{
273 int error;
274 d->escapingBuffer.resize(str.length() * 2 + 1);
275 size_t count = PQescapeStringConn(d->conn,
276 d->escapingBuffer.data(), str.constData(), str.length(),
277 &error);
278 d->escapingBuffer.resize(count);
279
280 if (error != 0) {
281 d->storeResult(const_cast<KDbResult*>(&m_result));
282 const_cast<KDbResult&>(m_result) = KDbResult(ERR_INVALID_ENCODING,
283 PostgresqlConnection::tr("Escaping string failed. Invalid multibyte encoding."));
284 return KDbEscapedString();
285 }
286 return KDbEscapedString("\'") + d->escapingBuffer + '\'';
287}
288
289KDbEscapedString PostgresqlConnection::escapeString(const QString& str) const
290{
291 return escapeString(d->unicode ? str.toUtf8() : str.toLocal8Bit());
292}
293
294void PostgresqlConnection::storeResult(PGresult *pgResult, ExecStatusType execStatus)
295{
296 d->storeResultAndClear(&m_result, &pgResult, execStatus);
297}
Database specific connection data, e.g. host, port.
Generic options for a single connection. The options are accessible using key/value pairs....
Provides database connection, allowing queries and data modification.
bool queryStringList(const KDbEscapedString &sql, QStringList *list, int column=0)
bool executeSql(const KDbEscapedString &sql)
Executes a new native (raw, backend-specific) SQL query.
virtual QString escapeIdentifier(const QString &id) const
Identifier escaping function in the associated KDbDriver.
KDbConnectionData data() const
tristate isEmpty(KDbTableSchema *table)
tristate resultExists(const KDbEscapedString &sql, QueryRecordOptions options=QueryRecordOption::Default)
KDbConnectionOptions * options()
Provides database cursor functionality.
Definition KDbCursor.h:69
Database driver's abstraction.
Definition KDbDriver.h:50
Specialized string for escaping.
Prepared statement interface for backend-dependent implementations.
KDbQuerySchema provides information about database query.
The KDbSqlResult class abstracts result of a raw SQL query preparation by KDbConnection::prepareSql()
Internal prototype for storing transaction handle for KDbTransaction object.
3-state logical type with three values: true, false and cancelled and convenient operators.
Q_SCRIPTABLE CaptureState status()
KCOREADDONS_EXPORT unsigned int version()
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
const char * constData() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
QString arg(Args &&... args) const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
const QChar * constData() const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QByteArray toUtf8() const const
CaseInsensitive
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.