kexi

pqxxconnection.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003 Adam Pigg <adam@piggz.co.uk>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "pqxxconnection.h"
21#include <tqvariant.h>
22#include <tqfile.h>
23#include <kdebug.h>
24#include <kexidb/error.h>
25#include <kexidb/global.h>
26#include <tdelocale.h>
27#include <string>
28#include "pqxxpreparedstatement.h"
29#include "pqxxconnection_p.h"
30using namespace KexiDB;
31
32pqxxTransactionData::pqxxTransactionData(Connection *conn, bool nontransaction)
33 : TransactionData(conn)
34{
35 if (nontransaction)
36 data = new pqxx::nontransaction(*static_cast<pqxxSqlConnection*>(conn)->d->pqxxsql /* todo: add name? */);
37 else
38 data = new pqxx::transaction<>(*static_cast<pqxxSqlConnection*>(conn)->d->pqxxsql /* todo: add name? */);
39 if (!static_cast<pqxxSqlConnection*>(conn)->m_trans) {
40 static_cast<pqxxSqlConnection*>(conn)->m_trans = this;
41 }
42}
43
44pqxxTransactionData::~pqxxTransactionData()
45{
46 if (static_cast<pqxxSqlConnection*>(m_conn)->m_trans == this) {
47 static_cast<pqxxSqlConnection*>(m_conn)->m_trans = 0;
48 }
49 delete data;
50 data = 0;
51}
52
53//==================================================================================
54
55pqxxSqlConnection::pqxxSqlConnection(Driver *driver, ConnectionData &conn_data)
56 : Connection(driver,conn_data)
57 , d( new pqxxSqlConnectionInternal(this) )
58 , m_trans(0)
59{
60}
61
62//==================================================================================
63//Do any tidying up before the object is deleted
64pqxxSqlConnection::~pqxxSqlConnection()
65{
66 //delete m_trans;
67 destroy();
68 delete d;
69}
70
71//==================================================================================
72//Return a new query based on a query statment
73Cursor* pqxxSqlConnection::prepareQuery( const TQString& statement, uint cursor_options)
74{
75 Q_UNUSED(cursor_options);
76 return new pqxxSqlCursor(this, statement, 1); //Always used buffered cursor
77}
78
79//==================================================================================
80//Return a new query based on a query object
81Cursor* pqxxSqlConnection::prepareQuery( QuerySchema& query, uint cursor_options)
82{
83 Q_UNUSED(cursor_options);
84 return new pqxxSqlCursor(this, query, 1);//Always used buffered cursor
85}
86
87//==================================================================================
88//Properly escaped a database object name
89TQString pqxxSqlConnection::escapeName(const TQString &name) const
90{
91 return TQString("\"" + name + "\"");
92}
93
94//==================================================================================
95//Made this a noop
96//We tell kexi we are connected, but we wont actually connect until we use a database!
98{
99 KexiDBDrvDbg << "pqxxSqlConnection::drv_connect" << endl;
100 version.clear();
101 d->version = &version; //remember for later...
102#ifdef __GNUC__
103#warning pqxxSqlConnection::drv_connect implement setting version info when we drop libpqxx for libpq
104#endif
105 return true;
106}
107
108//==================================================================================
109//Made this a noop
110//We tell kexi wehave disconnected, but it is actually handled by closeDatabse
112{
113 KexiDBDrvDbg << "pqxxSqlConnection::drv_disconnect: " << endl;
114 return true;
115}
116
117//==================================================================================
118//Return a list of database names
120{
121// KexiDBDrvDbg << "pqxxSqlConnection::drv_getDatabaseList" << endl;
122
123 if (executeSQL("SELECT datname FROM pg_database WHERE datallowconn = TRUE"))
124 {
125 std::string N;
126 for (pqxx::result::const_iterator c = d->res->begin(); c != d->res->end(); ++c)
127 {
128 // Read value of column 0 into a string N
129 (*c)[0].to(N);
130 // Copy the result into the return list
131 list << TQString::fromLatin1 (N.c_str());
132 }
133 return true;
134 }
135
136 return false;
137}
138
139//==================================================================================
140//Create a new database
141bool pqxxSqlConnection::drv_createDatabase( const TQString &dbName )
142{
143 KexiDBDrvDbg << "pqxxSqlConnection::drv_createDatabase: " << dbName << endl;
144
145 if (executeSQL("CREATE DATABASE " + escapeName(dbName)))
146 return true;
147
148 return false;
149}
150
151//==================================================================================
152//Use this as our connection instead of connect
153bool pqxxSqlConnection::drv_useDatabase( const TQString &dbName, bool *cancelled,
154 MessageHandler* msgHandler )
155{
156 Q_UNUSED(cancelled);
157 Q_UNUSED(msgHandler);
158 KexiDBDrvDbg << "pqxxSqlConnection::drv_useDatabase: " << dbName << endl;
159
160 TQString conninfo;
161 TQString socket;
162 TQStringList sockets;
163
164 if (data()->hostName.isEmpty() || data()->hostName == "localhost")
165 {
166 if (data()->localSocketFileName.isEmpty())
167 {
168 sockets.append("/tmp/.s.PGSQL.5432");
169
170 for(TQStringList::ConstIterator it = sockets.constBegin(); it != sockets.constEnd(); it++)
171 {
172 if(TQFile(*it).exists())
173 {
174 socket = (*it);
175 break;
176 }
177 }
178 }
179 else
180 {
181 socket=data()->localSocketFileName; //data()->fileName();
182 }
183 }
184 else
185 {
186 conninfo = "host='" + data()->hostName + "'";
187 }
188
189 //Build up the connection string
190 if (data()->port == 0)
191 data()->port = 5432;
192
193 conninfo += TQString::fromLatin1(" port='%1'").arg(data()->port);
194
195 conninfo += TQString::fromLatin1(" dbname='%1'").arg(dbName);
196
197 if (!data()->userName.isNull())
198 conninfo += TQString::fromLatin1(" user='%1'").arg(data()->userName);
199
200 if (!data()->password.isNull())
201 conninfo += TQString::fromLatin1(" password='%1'").arg(data()->password);
202
203 try
204 {
205 d->pqxxsql = new pqxx::connection( conninfo.latin1() );
206
207 if (d->version) {
209 }
210 return true;
211 }
212 catch(const std::exception &e)
213 {
214 KexiDBDrvDbg << "pqxxSqlConnection::drv_useDatabase:exception - " << e.what() << endl;
215 d->errmsg = TQString::fromUtf8( e.what() );
216
217 }
218 catch(...)
219 {
220 d->errmsg = i18n("Unknown error.");
221 }
222 return false;
223}
224
225//==================================================================================
226//Here we close the database connection
228{
229 KexiDBDrvDbg << "pqxxSqlConnection::drv_closeDatabase" << endl;
230// if (isConnected())
231// {
232 delete d->pqxxsql;
233 return true;
234// }
235/* js: not needed, right?
236 else
237 {
238 d->errmsg = "Not connected to database backend";
239 d->res = ERR_NO_CONNECTION;
240 }
241 return false;*/
242}
243
244//==================================================================================
245//Drops the given database
246bool pqxxSqlConnection::drv_dropDatabase( const TQString &dbName )
247{
248 KexiDBDrvDbg << "pqxxSqlConnection::drv_dropDatabase: " << dbName << endl;
249
250 //FIXME Maybe should check that dbname is no the currentdb
251 if (executeSQL("DROP DATABASE " + escapeName(dbName)))
252 return true;
253
254 return false;
255}
256
257//==================================================================================
258//Execute an SQL statement
259bool pqxxSqlConnection::drv_executeSQL( const TQString& statement )
260{
261 std::string temp = std::string(statement.utf8());
262// Adding xmin result output for inserting rows
263 bool isInserted = false;
264 pqxx::result::const_iterator itRow;
265 if (statement.find("INSERT INTO") == 0 || statement.find("UPDATE") == 0) {
266 temp += " RETURNING xmin";
267 isInserted = true;
268 }
269
270// KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL: " << statement << endl;
271 bool ok = false;
272
273 // Clear the last result information...
274 delete d->res;
275 d->res = 0;
276
277// KexiDBDrvDbg << "About to try" << endl;
278 try
279 {
280 //Create a transaction
281 const bool implicityStarted = !m_trans;
282 if (implicityStarted)
283 (void)new pqxxTransactionData(this, true);
284
285 // m_trans = new pqxx::nontransaction(*m_pqxxsql);
286// KexiDBDrvDbg << "About to execute" << endl;
287 //Create a result object through the transaction
288 d->res = new pqxx::result(m_trans->data->exec(temp));
289
290// Get the xmin of the last inserted row
291 if (isInserted) {
292 itRow = d->res->begin();
293 (*itRow)[0].to(temp);
294 lastRowId = temp.c_str();
295 }
296
297// KexiDBDrvDbg << "Executed" << endl;
298 //Commit the transaction
299 if (implicityStarted) {
300 pqxxTransactionData *t = m_trans;
302 delete t;
303// m_trans = 0;
304 }
305
306 //If all went well then return true, errors picked up by the catch block
307 ok = true;
308 }
309 catch(const pqxx::sql_error& sqlerr) {
310 KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL: sql_error exception - " << std::string(sqlerr.query()).c_str() << endl;
311 }
312 catch (const pqxx::broken_connection& bcerr) {
313 KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL: broken_connection exception" << endl;
314 }
315 catch (const std::exception &e)
316 {
317 //If an error ocurred then put the error description into _dbError
318 d->errmsg = TQString::fromUtf8( e.what() );
319 KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL:exception - " << e.what() << endl;
320 }
321 catch(...)
322 {
323 d->errmsg = i18n("Unknown error.");
324 }
325 //KexiDBDrvDbg << "EXECUTE SQL OK: OID was " << (d->res ? d->res->inserted_oid() : 0) << endl;
326 return ok;
327}
328
329//==================================================================================
330//Return true if currently connected to a database, ignoring the m_is_connected falg.
332{
333 if (d->pqxxsql->is_open())
334 {
335 return true;
336 }
337 return false;
338}
339
340//==================================================================================
341//Return the oid of the last insert - only works if sql was insert of 1 row
343{
344 if (d->res)
345 {
346 if (!lastRowId.isEmpty())
347 {
348 return lastRowId.toULong();
349 }
350 else
351 {
352 return 0;
353 }
354 }
355 return 0;
356}
357
358//<queries taken from pqxxMigrate>
359bool pqxxSqlConnection::drv_containsTable( const TQString &tableName )
360{
361 bool success;
362 return resultExists(TQString("select 1 from pg_class where relkind='r' and relname LIKE %1")
363 .arg(driver()->escapeString(tableName)), success) && success;
364}
365
366bool pqxxSqlConnection::drv_getTablesList( TQStringList &list )
367{
368 KexiDB::Cursor *cursor;
369 m_sql = "select lower(relname) from pg_class where relkind='r'";
370 if (!(cursor = executeQuery( m_sql ))) {
371 KexiDBDrvWarn << "pqxxSqlConnection::drv_getTablesList(): !executeQuery()" << endl;
372 return false;
373 }
374 list.clear();
375 cursor->moveFirst();
376 while (!cursor->eof() && !cursor->error()) {
377 list += cursor->value(0).toString();
378 cursor->moveNext();
379 }
380 if (cursor->error()) {
381 deleteCursor(cursor);
382 return false;
383 }
384 return deleteCursor(cursor);
385}
386//</taken from pqxxMigrate>
387
389{
390 return new pqxxTransactionData(this, false);
391}
392
394{
395 bool result = true;
396 try {
397 static_cast<pqxxTransactionData*>(tdata)->data->commit();
398 }
399 catch (const std::exception &e)
400 {
401 //If an error ocurred then put the error description into _dbError
402 d->errmsg = TQString::fromUtf8( e.what() );
403 result = false;
404 }
405 catch (...) {
407 setError();
408 result = false;
409 }
410 if (m_trans == tdata)
411 m_trans = 0;
412 return result;
413}
414
416{
417 bool result = true;
418 try {
419 static_cast<pqxxTransactionData*>(tdata)->data->abort();
420 }
421 catch (const std::exception &e)
422 {
423 //If an error ocurred then put the error description into _dbError
424 d->errmsg = TQString::fromUtf8( e.what() );
425
426 result = false;
427 }
428 catch (...) {
429 d->errmsg = i18n("Unknown error.");
430 result = false;
431 }
432 if (m_trans == tdata)
433 m_trans = 0;
434 return result;
435}
436
438{
439 return d->resultCode;
440}
441
443{
444 return TQString();
445}
446
448{
449 d->resultCode = 0;
450}
451
453{
454 return d->errmsg;
455}
456
458 FieldList& fields)
459{
460 return new pqxxPreparedStatement(type, *d, fields);
461}
462#include "pqxxconnection.moc"
TQString localSocketFileName
Name of local (named) socket file.
unsigned short int port
Port used for the remote connection.
TQString hostName
Host name used for the remote connection.
Database specific connection data, e.g. host, port.
Provides database connection, allowing queries and data modification.
Definition connection.h:62
bool deleteCursor(Cursor *cursor)
bool executeSQL(const TQString &statement)
bool resultExists(const TQString &sql, bool &success, bool addLimitTo1=true)
Driver * driver() const
Definition connection.h:78
ConnectionData * data() const
Cursor * executeQuery(const TQString &statement, uint cursor_options=0)
Provides database cursor functionality.
Definition cursor.h:67
virtual bool moveNext()
Definition cursor.cpp:292
virtual TQVariant value(uint i)=0
bool eof() const
Definition cursor.cpp:339
bool moveFirst()
Definition cursor.cpp:205
Generic database abstraction.
Definition driver.h:67
bool error() const
Definition object.h:38
TQString m_sql
used to store of actually executed SQL statement
Definition object.h:161
virtual void setError(int code=ERR_OTHER, const TQString &msg=TQString())
Definition object.cpp:53
StatementType
Defines type of the prepared statement.
KexiDB::QuerySchema provides information about database query.
Contains version information about a database backend.
Definition global.h:126
virtual PreparedStatement::Ptr prepareStatement(PreparedStatement::StatementType type, FieldList &fields)
virtual bool drv_executeSQL(const TQString &statement)
virtual bool drv_getTablesList(TQStringList &list)
virtual void drv_clearServerResult()
virtual bool drv_rollbackTransaction(TransactionData *)
virtual bool drv_dropDatabase(const TQString &dbName=TQString())
virtual bool drv_isDatabaseUsed() const
virtual TQ_ULLONG drv_lastInsertRowID()
virtual bool drv_containsTable(const TQString &tableName)
virtual Cursor * prepareQuery(const TQString &statement=TQString(), uint cursor_options=0)
virtual bool drv_createDatabase(const TQString &dbName=TQString())
virtual bool drv_getDatabasesList(TQStringList &list)
virtual TQString serverResultName()
virtual bool drv_useDatabase(const TQString &dbName=TQString(), bool *cancelled=0, MessageHandler *msgHandler=0)
virtual bool drv_commitTransaction(TransactionData *)
virtual TransactionData * drv_beginTransaction()
virtual bool drv_connect(KexiDB::ServerVersionInfo &version)
virtual TQString serverErrorMsg()
High-level database connectivity library with database backend drivers.
KEXI_DB_EXPORT DatabaseVersionInfo version()
Definition global.cpp:55