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
qsizetype length() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
QString arg(Args &&... args) const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString fromLatin1(QByteArrayView str)
bool isEmpty() 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 Fri Jul 26 2024 11:59:38 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.