sqlite/sqliteconnection.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
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 "sqliteconnection.h"
21#include "sqliteconnection_p.h"
22#include "sqlitecursor.h"
23#include "sqlitepreparedstatement.h"
24
25#include "sqlite.h"
26
27#ifndef SQLITE2
28# include "kexisql.h" //for isReadOnly()
29#endif
30
31#include <kexidb/driver.h>
32#include <kexidb/cursor.h>
33#include <kexidb/error.h>
34#include <kexiutils/utils.h>
35
36#include <tqfile.h>
37#include <tqdir.h>
38#include <tqregexp.h>
39
40#include <kgenericfactory.h>
41#include <kdebug.h>
42
43//remove debug
44#undef KexiDBDrvDbg
45#define KexiDBDrvDbg if (0) kdDebug()
46
47using namespace KexiDB;
48
49SQLiteConnectionInternal::SQLiteConnectionInternal(Connection *connection)
50 : ConnectionInternal(connection)
51 , data(0)
52 , data_owned(true)
53 , errmsg_p(0)
54 , res(SQLITE_OK)
55 , temp_st(0x10000)
56#ifdef SQLITE3
57 , result_name(0)
58#endif
59{
60}
61
62SQLiteConnectionInternal::~SQLiteConnectionInternal()
63{
64 if (data_owned && data) {
65 free( data );
66 data = 0;
67 }
68//sqlite_freemem does this if (errmsg) {
69// free( errmsg );
70// errmsg = 0;
71// }
72}
73
74void SQLiteConnectionInternal::storeResult()
75{
76 if (errmsg_p) {
77 errmsg = errmsg_p;
78 sqlite_free(errmsg_p);
79 errmsg_p = 0;
80 }
81#ifdef SQLITE3
82 errmsg = (data && res!=SQLITE_OK) ? sqlite3_errmsg(data) : 0;
83#endif
84}
85
88 : Connection( driver, conn_data )
89 ,d(new SQLiteConnectionInternal(this))
90{
91}
92
93SQLiteConnection::~SQLiteConnection()
94{
95 KexiDBDrvDbg << "SQLiteConnection::~SQLiteConnection()" << endl;
96 //disconnect if was connected
97// disconnect();
98 destroy();
99 delete d;
100 KexiDBDrvDbg << "SQLiteConnection::~SQLiteConnection() ok" << endl;
101}
102
104{
105 KexiDBDrvDbg << "SQLiteConnection::connect()" << endl;
106 version.string = TQString(SQLITE_VERSION); //defined in sqlite3.h
107 TQRegExp re("(\\d+)\\.(\\d+)\\.(\\d+)");
108 if (re.exactMatch(version.string)) {
109 version.major = re.cap(1).toUInt();
110 version.minor = re.cap(2).toUInt();
111 version.release = re.cap(3).toUInt();
112 }
113 return true;
114}
115
117{
118 KexiDBDrvDbg << "SQLiteConnection::disconnect()" << endl;
119 return true;
120}
121
123{
124 //this is one-db-per-file database
125 list.append( data()->fileName() ); //more consistent than dbFileName() ?
126 return true;
127}
128
129bool SQLiteConnection::drv_containsTable( const TQString &tableName )
130{
131 bool success;
132 return resultExists(TQString("select name from sqlite_master where type='table' and name LIKE %1")
133 .arg(driver()->escapeString(tableName)), success) && success;
134}
135
136bool SQLiteConnection::drv_getTablesList( TQStringList &list )
137{
138 KexiDB::Cursor *cursor;
139 m_sql = "select lower(name) from sqlite_master where type='table'";
140 if (!(cursor = executeQuery( m_sql ))) {
141 KexiDBWarn << "Connection::drv_getTablesList(): !executeQuery()" << endl;
142 return false;
143 }
144 list.clear();
145 cursor->moveFirst();
146 while (!cursor->eof() && !cursor->error()) {
147 list += cursor->value(0).toString();
148 cursor->moveNext();
149 }
150 if (cursor->error()) {
151 deleteCursor(cursor);
152 return false;
153 }
154 return deleteCursor(cursor);
155}
156
157bool SQLiteConnection::drv_createDatabase( const TQString &dbName )
158{
159 // SQLite creates a new db is it does not exist
160 return drv_useDatabase(dbName);
161#if 0
162 d->data = sqlite_open( TQFile::encodeName( data()->fileName() ), 0/*mode: unused*/,
163 &d->errmsg_p );
164 d->storeResult();
165 return d->data != 0;
166#endif
167}
168
169bool SQLiteConnection::drv_useDatabase( const TQString &dbName, bool *cancelled,
170 MessageHandler* msgHandler )
171{
172 Q_UNUSED(dbName);
173// KexiDBDrvDbg << "drv_useDatabase(): " << data()->fileName() << endl;
174#ifdef SQLITE2
175 Q_UNUSED(cancelled);
176 Q_UNUSED(msgHandler);
177 d->data = sqlite_open( TQFile::encodeName( data()->fileName() ), 0/*mode: unused*/,
178 &d->errmsg_p );
179 d->storeResult();
180 return d->data != 0;
181#else //SQLITE3
182 //TODO: perhaps allow to use sqlite3_open16() as well for SQLite ~ 3.3 ?
184 int exclusiveFlag = Connection::isReadOnly() ? SQLITE_OPEN_READONLY : SQLITE_OPEN_WRITE_LOCKED; // <-- shared read + (if !r/o): exclusive write
186 int allowReadonly = 1;
187 const bool wasReadOnly = Connection::isReadOnly();
188
189 d->res = sqlite3_open(
190 //TQFile::encodeName( data()->fileName() ),
191 data()->fileName().utf8(), /* unicode expected since SQLite 3.1 */
192 &d->data,
193 exclusiveFlag,
194 allowReadonly /* If 1 and locking fails, try opening in read-only mode */
195 );
196 d->storeResult();
197
198 if (d->res == SQLITE_OK && cancelled && !wasReadOnly && allowReadonly && isReadOnly()) {
199 //opened as read only, ask
200 if (KMessageBox::Continue !=
202 i18n("Do you want to open file \"%1\" as read-only?")
203 .arg(TQDir::convertSeparators(data()->fileName()))
204 + "\n\n"
205 + i18n("The file is probably already open on this or another computer.") + " "
206 + i18n("Could not gain exclusive access for writing the file."),
207 KMessageBox::WarningContinueCancel, KMessageBox::Continue,
208 KGuiItem(i18n("Open As Read-Only"), "document-open"), KStdGuiItem::cancel(),
209 "askBeforeOpeningFileReadOnly", KMessageBox::Notify, msgHandler ))
210 {
211 clearError();
212 if (!drv_closeDatabase())
213 return false;
214 *cancelled = true;
215 return false;
216 }
217 }
218
219 if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_READWRITE) {
220 setError(ERR_ACCESS_RIGHTS,
221 i18n("The file is probably already open on this or another computer.")+"\n\n"
222 + i18n("Could not gain exclusive access for reading and writing the file.") + " "
223 + i18n("Check the file's permissions and whether it is already opened and locked by another application."));
224 }
225 else if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_WRITE) {
226 setError(ERR_ACCESS_RIGHTS,
227 i18n("The file is probably already open on this or another computer.")+"\n\n"
228 + i18n("Could not gain exclusive access for writing the file.") + " "
229 + i18n("Check the file's permissions and whether it is already opened and locked by another application."));
230 }
231 return d->res == SQLITE_OK;
232#endif
233}
234
236{
237 if (!d->data)
238 return false;
239
240#ifdef SQLITE2
241 sqlite_close(d->data);
242 d->data = 0;
243 return true;
244#else
245 const int res = sqlite_close(d->data);
246 if (SQLITE_OK == res) {
247 d->data = 0;
248 return true;
249 }
250 if (SQLITE_BUSY==res) {
251#if 0 //this is ANNOYING, needs fixing (by closing cursors or waiting)
252 setError(ERR_CLOSE_FAILED, i18n("Could not close busy database."));
253#else
254 return true;
255#endif
256 }
257 return false;
258#endif
259}
260
261bool SQLiteConnection::drv_dropDatabase( const TQString &dbName )
262{
263 Q_UNUSED(dbName); // Each database is one single SQLite file.
264 const TQString filename = data()->fileName();
265 if (TQFile(filename).exists() && !TQDir().remove(filename)) {
266 setError(ERR_ACCESS_RIGHTS, i18n("Could not remove file \"%1\".")
267 .arg(TQDir::convertSeparators(filename)) + " "
268 + i18n("Check the file's permissions and whether it is already opened and locked by another application."));
269 return false;
270 }
271 return true;
272}
273
274//CursorData* SQLiteConnection::drv_createCursor( const TQString& statement )
275Cursor* SQLiteConnection::prepareQuery( const TQString& statement, uint cursor_options )
276{
277 return new SQLiteCursor( this, statement, cursor_options );
278}
279
280Cursor* SQLiteConnection::prepareQuery( QuerySchema& query, uint cursor_options )
281{
282 return new SQLiteCursor( this, query, cursor_options );
283}
284
285bool SQLiteConnection::drv_executeSQL( const TQString& statement )
286{
287// KexiDBDrvDbg << "SQLiteConnection::drv_executeSQL(" << statement << ")" <<endl;
288// TQCString st(statement.length()*2);
289// st = escapeString( statement.local8Bit() ); //?
290#ifdef SQLITE_UTF8
291 d->temp_st = statement.utf8();
292#else
293 d->temp_st = statement.local8Bit(); //latin1 only
294#endif
295
296#ifdef KEXI_DEBUG_GUI
297 KexiUtils::addKexiDBDebug(TQString("ExecuteSQL (SQLite): ")+statement);
298#endif
299
300 d->res = sqlite_exec(
301 d->data,
302 (const char*)d->temp_st,
303 0/*callback*/,
304 0,
305 &d->errmsg_p );
306 d->storeResult();
307#ifdef KEXI_DEBUG_GUI
308 KexiUtils::addKexiDBDebug(d->res==SQLITE_OK ? " Success" : " Failure");
309#endif
310 return d->res==SQLITE_OK;
311}
312
314{
315 return (TQ_ULLONG)sqlite_last_insert_rowid(d->data);
316}
317
319{
320 return d->res==0 ? Connection::serverResult() : d->res;
321}
322
324{
325 TQString r =
326#ifdef SQLITE2
327 TQString::fromLatin1( sqlite_error_string(d->res) );
328#else //SQLITE3
329 TQString(); //fromLatin1( d->result_name );
330#endif
331 return r.isEmpty() ? Connection::serverResultName() : r;
332}
333
335{
336 if (!d)
337 return;
338 d->res = SQLITE_OK;
339#ifdef SQLITE2
340 d->errmsg_p = 0;
341#else
342// d->result_name = 0;
343#endif
344}
345
347{
348 return d->errmsg.isEmpty() ? Connection::serverErrorMsg() : d->errmsg;
349}
350
352 FieldList& fields)
353{
354//#ifndef SQLITE2 //TEMP IFDEF!
355 return new SQLitePreparedStatement(type, *d, fields);
356//#endif
357}
358
360{
361#ifdef SQLITE2
362 return Connection::isReadOnly();
363#else
364 return d->data ? sqlite3_is_readonly(d->data) : false;
365#endif
366}
367
368#ifdef SQLITE2
369bool SQLiteConnection::drv_alterTableName(TableSchema& tableSchema, const TQString& newName, bool replace)
370{
371 const TQString oldTableName = tableSchema.name();
372 const bool destTableExists = this->tableSchema( newName ) != 0;
373
374 //1. drop the table
375 if (destTableExists) {
376 if (!replace)
377 return false;
378 if (!drv_dropTable( newName ))
379 return false;
380 }
381
382 //2. create a copy of the table
383//TODO: move this code to drv_copyTable()
384 tableSchema.setName(newName);
385
386//helper:
387#define drv_alterTableName_ERR \
388 tableSchema.setName(oldTableName) //restore old name
389
391 drv_alterTableName_ERR;
392 return false;
393 }
394
395//TODO indices, etc.???
396
397 // 3. copy all rows to the new table
398 if (!executeSQL(TQString::fromLatin1("INSERT INTO %1 SELECT * FROM %2")
399 .arg(escapeIdentifier(tableSchema.name())).arg(escapeIdentifier(oldTableName))))
400 {
401 drv_alterTableName_ERR;
402 return false;
403 }
404
405 // 4. drop old table.
406 if (!drv_dropTable( oldTableName )) {
407 drv_alterTableName_ERR;
408 return false;
409 }
410 return true;
411}
412#endif
413
414#include "sqliteconnection.moc"
Database specific connection data, e.g. host, port.
TQString fileName() const
Get the filename used by the connection.
Provides database connection, allowing queries and data modification.
Definition connection.h:62
TQString escapeIdentifier(const TQString &id, int escaping=Driver::EscapeDriver|Driver::EscapeAsNecessary) const
Identifier escaping function in the associated Driver.
TableSchema * tableSchema(int tableId)
bool deleteCursor(Cursor *cursor)
bool executeSQL(const TQString &statement)
virtual bool drv_createTable(const TableSchema &tableSchema)
virtual bool isReadOnly() const
Cursor * executeQuery(const TQString &statement, uint cursor_options=0)
virtual void clearError()
bool resultExists(const TQString &sql, bool &success, bool addLimitTo1=true)
Driver * driver() const
Definition connection.h:78
virtual bool drv_alterTableName(TableSchema &tableSchema, const TQString &newName)
ConnectionData * data() const
virtual bool drv_dropTable(const TQString &name)
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
uint major
Major version number, e.g. 1 for 1.8.
Definition global.h:115
uint minor
Minor version number, e.g. 8 for 1.8.
Definition global.h:118
Generic database abstraction.
Definition driver.h:67
bool error() const
Definition object.h:38
virtual TQString serverResultName()
Definition object.cpp:154
virtual int askQuestion(const TQString &message, KMessageBox::DialogType dlgType, KMessageBox::ButtonCode defaultResult, const KGuiItem &buttonYes=KStdGuiItem::yes(), const KGuiItem &buttonNo=KStdGuiItem::no(), const TQString &dontShowAskAgainName=TQString(), int options=KMessageBox::Notify, MessageHandler *msgHandler=0)
Definition object.cpp:174
TQString m_sql
used to store of actually executed SQL statement
Definition object.h:161
virtual TQString serverErrorMsg()
Definition object.cpp:144
virtual void setError(int code=ERR_OTHER, const TQString &msg=TQString())
Definition object.cpp:53
virtual int serverResult()
Definition object.cpp:149
StatementType
Defines type of the prepared statement.
KexiDB::QuerySchema provides information about database query.
virtual bool drv_executeSQL(const TQString &statement)
virtual bool drv_connect(KexiDB::ServerVersionInfo &version)
virtual bool drv_getTablesList(TQStringList &list)
virtual bool drv_createDatabase(const TQString &dbName=TQString())
virtual bool drv_getDatabasesList(TQStringList &list)
virtual bool drv_dropDatabase(const TQString &dbName=TQString())
virtual bool drv_containsTable(const TQString &tableName)
virtual PreparedStatement::Ptr prepareStatement(PreparedStatement::StatementType type, FieldList &fields)
virtual Cursor * prepareQuery(const TQString &statement, uint cursor_options=0)
SQLiteConnection(Driver *driver, ConnectionData &conn_data)
virtual bool drv_useDatabase(const TQString &dbName=TQString(), bool *cancelled=0, MessageHandler *msgHandler=0)
Contains version information about a database backend.
Definition global.h:126
High-level database connectivity library with database backend drivers.
KEXI_DB_EXPORT DatabaseVersionInfo version()
Definition global.cpp:55
type * findParent(TQObject *o, const char *className)