diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/kexidb/drivers/sqlite | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kexi/kexidb/drivers/sqlite')
20 files changed, 3073 insertions, 0 deletions
diff --git a/kexi/kexidb/drivers/sqlite/Makefile.am b/kexi/kexidb/drivers/sqlite/Makefile.am new file mode 100644 index 00000000..fc0ad677 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/Makefile.am @@ -0,0 +1,27 @@ +include $(top_srcdir)/kexi/Makefile.global + +kde_module_LTLIBRARIES = kexidb_sqlite3driver.la + +INCLUDES = -I$(top_srcdir)/kexi/3rdparty/kexisql3/src -I$(srcdir)/../.. \ + -I$(top_srcdir)/kexi $(all_includes) + +kexidb_sqlite3driver_la_METASOURCES = AUTO + +kexidb_sqlite3driver_la_SOURCES = sqliteconnection.cpp sqlitedriver.cpp sqlitecursor.cpp \ +sqlitekeywords.cpp sqlitepreparedstatement.cpp sqlitevacuum.cpp sqliteadmin.cpp \ +sqlitealter.cpp + +kexidb_sqlite3driver_la_LIBADD = $(LIB_KPARTS) $(LIB_QT) \ + $(top_builddir)/kexi/3rdparty/kexisql3/src/libkexisql3.la \ + $(top_builddir)/kexi/kexidb/libkexidb.la \ + $(top_builddir)/kexi/kexidb/parser/libkexidbparser.la + +kexidb_sqlite3driver_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) $(VER_INFO) + + +kde_services_DATA = kexidb_sqlite3driver.desktop + + +KDE_CXXFLAGS += -DKEXIDB_SQLITE_DRIVER_EXPORT= -D__KEXIDB__= \ + -include $(top_srcdir)/kexi/kexidb/global.h + diff --git a/kexi/kexidb/drivers/sqlite/driver/sqlite.h b/kexi/kexidb/drivers/sqlite/driver/sqlite.h new file mode 100644 index 00000000..680f81e2 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/driver/sqlite.h @@ -0,0 +1,687 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the SQLite library +** presents to client programs. +** +** @(#) $Id: sqlite.h 614463 2006-12-17 21:08:15Z staniek $ +*/ +#ifndef _SQLITE_H_ +#define _SQLITE_H_ +#include <stdarg.h> /* Needed for the definition of va_list */ + +/* +** Make sure we can call this stuff from C++. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +/* +** The version of the SQLite library. +*/ +#define SQLITE_VERSION "2.8.2" + +/* +** The version string is also compiled into the library so that a program +** can check to make sure that the lib*.a file and the *.h file are from +** the same version. +*/ +extern const char sqlite_version[]; + +/* +** The SQLITE_UTF8 macro is defined if the library expects to see +** UTF-8 encoded data. The SQLITE_ISO8859 macro is defined if the +** iso8859 encoded should be used. +*/ +#define SQLITE_ISO8859 1 + +/* +** The following constant holds one of two strings, "UTF-8" or "iso8859", +** depending on which character encoding the SQLite library expects to +** see. The character encoding makes a difference for the LIKE and GLOB +** operators and for the LENGTH() and SUBSTR() functions. +*/ +extern const char sqlite_encoding[]; + +/* +** Each open sqlite database is represented by an instance of the +** following opaque structure. +*/ +typedef struct sqlite sqlite; + +/* +** A function to open a new sqlite database. +** +** If the database does not exist and mode indicates write +** permission, then a new database is created. If the database +** does not exist and mode does not indicate write permission, +** then the open fails, an error message generated (if errmsg!=0) +** and the function returns 0. +** +** If mode does not indicates user write permission, then the +** database is opened read-only. +** +** The Truth: As currently implemented, all databases are opened +** for writing all the time. Maybe someday we will provide the +** ability to open a database readonly. The mode parameters is +** provided in anticipation of that enhancement. +*/ +sqlite *sqlite_open(const char *filename, int mode, char **errmsg); + +/* +** A function to close the database. +** +** Call this function with a pointer to a structure that was previously +** returned from sqlite_open() and the corresponding database will by closed. +*/ +void sqlite_close(sqlite *); + +/* +** The type for a callback function. +*/ +typedef int (*sqlite_callback)(void*,int,char**, char**); + +/* +** A function to executes one or more statements of SQL. +** +** If one or more of the SQL statements are queries, then +** the callback function specified by the 3rd parameter is +** invoked once for each row of the query result. This callback +** should normally return 0. If the callback returns a non-zero +** value then the query is aborted, all subsequent SQL statements +** are skipped and the sqlite_exec() function returns the SQLITE_ABORT. +** +** The 4th parameter is an arbitrary pointer that is passed +** to the callback function as its first parameter. +** +** The 2nd parameter to the callback function is the number of +** columns in the query result. The 3rd parameter to the callback +** is an array of strings holding the values for each column. +** The 4th parameter to the callback is an array of strings holding +** the names of each column. +** +** The callback function may be NULL, even for queries. A NULL +** callback is not an error. It just means that no callback +** will be invoked. +** +** If an error occurs while parsing or evaluating the SQL (but +** not while executing the callback) then an appropriate error +** message is written into memory obtained from malloc() and +** *errmsg is made to point to that message. The calling function +** is responsible for freeing the memory that holds the error +** message. Use sqlite_freemem() for this. If errmsg==NULL, +** then no error message is ever written. +** +** The return value is is SQLITE_OK if there are no errors and +** some other return code if there is an error. The particular +** return value depends on the type of error. +** +** If the query could not be executed because a database file is +** locked or busy, then this function returns SQLITE_BUSY. (This +** behavior can be modified somewhat using the sqlite_busy_handler() +** and sqlite_busy_timeout() functions below.) +*/ +int sqlite_exec( + sqlite*, /* An open database */ + const char *sql, /* SQL to be executed */ + sqlite_callback, /* Callback function */ + void *, /* 1st argument to callback function */ + char **errmsg /* Error msg written here */ +); + +/* +** Return values for sqlite_exec() and sqlite_step() +*/ +#define SQLITE_OK 0 /* Successful result */ +#define SQLITE_ERROR 1 /* SQL error or missing database */ +#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */ +#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ +#define SQLITE_MISUSE 21 /* Library used incorrectly */ +#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ +#define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Auxiliary database format error */ +#define SQLITE_ROW 100 /* sqlite_step() has another row ready */ +#define SQLITE_DONE 101 /* sqlite_step() has finished executing */ + +/* +** Each entry in an SQLite table has a unique integer key. (The key is +** the value of the INTEGER PRIMARY KEY column if there is such a column, +** otherwise the key is generated at random. The unique key is always +** available as the ROWID, OID, or _ROWID_ column.) The following routine +** returns the integer key of the most recent insert in the database. +** +** This function is similar to the mysql_insert_id() function from MySQL. +*/ +int sqlite_last_insert_rowid(sqlite*); + +/* +** This function returns the number of database rows that were changed +** (or inserted or deleted) by the most recent called sqlite_exec(). +** +** All changes are counted, even if they were later undone by a +** ROLLBACK or ABORT. Except, changes associated with creating and +** dropping tables are not counted. +** +** If a callback invokes sqlite_exec() recursively, then the changes +** in the inner, recursive call are counted together with the changes +** in the outer call. +** +** SQLite implements the command "DELETE FROM table" without a WHERE clause +** by dropping and recreating the table. (This is much faster than going +** through and deleting individual elements form the table.) Because of +** this optimization, the change count for "DELETE FROM table" will be +** zero regardless of the number of elements that were originally in the +** table. To get an accurate count of the number of rows deleted, use +** "DELETE FROM table WHERE 1" instead. +*/ +int sqlite_changes(sqlite*); + +/* If the parameter to this routine is one of the return value constants +** defined above, then this routine returns a constant text string which +** descripts (in English) the meaning of the return value. +*/ +const char *sqlite_error_string(int); +#define sqliteErrStr sqlite_error_string /* Legacy. Do not use in new code. */ + +/* This function causes any pending database operation to abort and +** return at its earliest opportunity. This routine is typically +** called in response to a user action such as pressing "Cancel" +** or Ctrl-C where the user wants a long query operation to halt +** immediately. +*/ +void sqlite_interrupt(sqlite*); + + +/* This function returns true if the given input string comprises +** one or more complete SQL statements. +** +** The algorithm is simple. If the last token other than spaces +** and comments is a semicolon, then return true. otherwise return +** false. +*/ +int sqlite_complete(const char *sql); + +/* +** This routine identifies a callback function that is invoked +** whenever an attempt is made to open a database table that is +** currently locked by another process or thread. If the busy callback +** is NULL, then sqlite_exec() returns SQLITE_BUSY immediately if +** it finds a locked table. If the busy callback is not NULL, then +** sqlite_exec() invokes the callback with three arguments. The +** second argument is the name of the locked table and the third +** argument is the number of times the table has been busy. If the +** busy callback returns 0, then sqlite_exec() immediately returns +** SQLITE_BUSY. If the callback returns non-zero, then sqlite_exec() +** tries to open the table again and the cycle repeats. +** +** The default busy callback is NULL. +** +** Sqlite is re-entrant, so the busy handler may start a new query. +** (It is not clear why anyone would every want to do this, but it +** is allowed, in theory.) But the busy handler may not close the +** database. Closing the database from a busy handler will delete +** data structures out from under the executing query and will +** probably result in a coredump. +*/ +void sqlite_busy_handler(sqlite*, int(*)(void*,const char*,int), void*); + +/* +** This routine sets a busy handler that sleeps for a while when a +** table is locked. The handler will sleep multiple times until +** at least "ms" milleseconds of sleeping have been done. After +** "ms" milleseconds of sleeping, the handler returns 0 which +** causes sqlite_exec() to return SQLITE_BUSY. +** +** Calling this routine with an argument less than or equal to zero +** turns off all busy handlers. +*/ +void sqlite_busy_timeout(sqlite*, int ms); + +/* +** This next routine is really just a wrapper around sqlite_exec(). +** Instead of invoking a user-supplied callback for each row of the +** result, this routine remembers each row of the result in memory +** obtained from malloc(), then returns all of the result after the +** query has finished. +** +** As an example, suppose the query result where this table: +** +** Name | Age +** ----------------------- +** Alice | 43 +** Bob | 28 +** Cindy | 21 +** +** If the 3rd argument were &azResult then after the function returns +** azResult will contain the following data: +** +** azResult[0] = "Name"; +** azResult[1] = "Age"; +** azResult[2] = "Alice"; +** azResult[3] = "43"; +** azResult[4] = "Bob"; +** azResult[5] = "28"; +** azResult[6] = "Cindy"; +** azResult[7] = "21"; +** +** Notice that there is an extra row of data containing the column +** headers. But the *nrow return value is still 3. *ncolumn is +** set to 2. In general, the number of values inserted into azResult +** will be ((*nrow) + 1)*(*ncolumn). +** +** After the calling function has finished using the result, it should +** pass the result data pointer to sqlite_free_table() in order to +** release the memory that was malloc-ed. Because of the way the +** malloc() happens, the calling function must not try to call +** malloc() directly. Only sqlite_free_table() is able to release +** the memory properly and safely. +** +** The return value of this routine is the same as from sqlite_exec(). +*/ +int sqlite_get_table( + sqlite*, /* An open database */ + const char *sql, /* SQL to be executed */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncolumn, /* Number of result columns written here */ + char **errmsg /* Error msg written here */ +); + +/* +** Call this routine to free the memory that sqlite_get_table() allocated. +*/ +void sqlite_free_table(char **result); + +/* +** The following routines are wrappers around sqlite_exec() and +** sqlite_get_table(). The only difference between the routines that +** follow and the originals is that the second argument to the +** routines that follow is really a printf()-style format +** string describing the SQL to be executed. Arguments to the format +** string appear at the end of the argument list. +** +** All of the usual printf formatting options apply. In addition, there +** is a "%q" option. %q works like %s in that it substitutes a null-terminated +** string from the argument list. But %q also doubles every '\'' character. +** %q is designed for use inside a string literal. By doubling each '\'' +** character it escapes that character and allows it to be inserted into +** the string. +** +** For example, so some string variable contains text as follows: +** +** char *zText = "It's a happy day!"; +** +** We can use this text in an SQL statement as follows: +** +** sqlite_exec_printf(db, "INSERT INTO table VALUES('%q')", +** callback1, 0, 0, zText); +** +** Because the %q format string is used, the '\'' character in zText +** is escaped and the SQL generated is as follows: +** +** INSERT INTO table1 VALUES('It''s a happy day!') +** +** This is correct. Had we used %s instead of %q, the generated SQL +** would have looked like this: +** +** INSERT INTO table1 VALUES('It's a happy day!'); +** +** This second example is an SQL syntax error. As a general rule you +** should always use %q instead of %s when inserting text into a string +** literal. +*/ +int sqlite_exec_printf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + sqlite_callback, /* Callback function */ + void *, /* 1st argument to callback function */ + char **errmsg, /* Error msg written here */ + ... /* Arguments to the format string. */ +); +int sqlite_exec_vprintf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + sqlite_callback, /* Callback function */ + void *, /* 1st argument to callback function */ + char **errmsg, /* Error msg written here */ + va_list ap /* Arguments to the format string. */ +); +int sqlite_get_table_printf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncolumn, /* Number of result columns written here */ + char **errmsg, /* Error msg written here */ + ... /* Arguments to the format string */ +); +int sqlite_get_table_vprintf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncolumn, /* Number of result columns written here */ + char **errmsg, /* Error msg written here */ + va_list ap /* Arguments to the format string */ +); +char *sqlite_mprintf(const char*,...); + +/* +** Windows systems should call this routine to free memory that +** is returned in the in the errmsg parameter of sqlite_open() when +** SQLite is a DLL. For some reason, it does not work to call free() +** directly. +*/ +void sqlite_freemem(void *p); + +/* +** Windows systems need functions to call to return the sqlite_version +** and sqlite_encoding strings. +*/ +const char *sqlite_libversion(void); +const char *sqlite_libencoding(void); + +/* +** A pointer to the following structure is used to communicate with +** the implementations of user-defined functions. +*/ +typedef struct sqlite_func sqlite_func; + +/* +** Use the following routines to create new user-defined functions. See +** the documentation for details. +*/ +int sqlite_create_function( + sqlite*, /* Database where the new function is registered */ + const char *zName, /* Name of the new function */ + int nArg, /* Number of arguments. -1 means any number */ + void (*xFunc)(sqlite_func*,int,const char**), /* C code to implement */ + void *pUserData /* Available via the sqlite_user_data() call */ +); +int sqlite_create_aggregate( + sqlite*, /* Database where the new function is registered */ + const char *zName, /* Name of the function */ + int nArg, /* Number of arguments */ + void (*xStep)(sqlite_func*,int,const char**), /* Called for each row */ + void (*xFinalize)(sqlite_func*), /* Called once to get final result */ + void *pUserData /* Available via the sqlite_user_data() call */ +); + +/* +** Use the following routine to define the datatype returned by a +** user-defined function. The second argument can be one of the +** constants SQLITE_NUMERIC, SQLITE_TEXT, or SQLITE_ARGS or it +** can be an integer greater than or equal to zero. The datatype +** will be numeric or text (the only two types supported) if the +** argument is SQLITE_NUMERIC or SQLITE_TEXT. If the argument is +** SQLITE_ARGS, then the datatype is numeric if any argument to the +** function is numeric and is text otherwise. If the second argument +** is an integer, then the datatype of the result is the same as the +** parameter to the function that corresponds to that integer. +*/ +int sqlite_function_type( + sqlite *db, /* The database there the function is registered */ + const char *zName, /* Name of the function */ + int datatype /* The datatype for this function */ +); +#define SQLITE_NUMERIC (-1) +#define SQLITE_TEXT (-2) +#define SQLITE_ARGS (-3) + +/* +** The user function implementations call one of the following four routines +** in order to return their results. The first parameter to each of these +** routines is a copy of the first argument to xFunc() or xFinialize(). +** The second parameter to these routines is the result to be returned. +** A NULL can be passed as the second parameter to sqlite_set_result_string() +** in order to return a NULL result. +** +** The 3rd argument to _string and _error is the number of characters to +** take from the string. If this argument is negative, then all characters +** up to and including the first '\000' are used. +** +** The sqlite_set_result_string() function allocates a buffer to hold the +** result and returns a pointer to this buffer. The calling routine +** (that is, the implementation of a user function) can alter the content +** of this buffer if desired. +*/ +char *sqlite_set_result_string(sqlite_func*,const char*,int); +void sqlite_set_result_int(sqlite_func*,int); +void sqlite_set_result_double(sqlite_func*,double); +void sqlite_set_result_error(sqlite_func*,const char*,int); + +/* +** The pUserData parameter to the sqlite_create_function() and +** sqlite_create_aggregate() routines used to register user functions +** is available to the implementation of the function using this +** call. +*/ +void *sqlite_user_data(sqlite_func*); + +/* +** Aggregate functions use the following routine to allocate +** a structure for storing their state. The first time this routine +** is called for a particular aggregate, a new structure of size nBytes +** is allocated, zeroed, and returned. On subsequent calls (for the +** same aggregate instance) the same buffer is returned. The implementation +** of the aggregate can use the returned buffer to accumulate data. +** +** The buffer allocated is freed automatically be SQLite. +*/ +void *sqlite_aggregate_context(sqlite_func*, int nBytes); + +/* +** The next routine returns the number of calls to xStep for a particular +** aggregate function instance. The current call to xStep counts so this +** routine always returns at least 1. +*/ +int sqlite_aggregate_count(sqlite_func*); + +/* +** This routine registers a callback with the SQLite library. The +** callback is invoked (at compile-time, not at run-time) for each +** attempt to access a column of a table in the database. The callback +** returns SQLITE_OK if access is allowed, SQLITE_DENY if the entire +** SQL statement should be aborted with an error and SQLITE_IGNORE +** if the column should be treated as a NULL value. +*/ +int sqlite_set_authorizer( + sqlite*, + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), + void *pUserData +); + +/* +** The second parameter to the access authorization function above will +** be one of the values below. These values signify what kind of operation +** is to be authorized. The 3rd and 4th parameters to the authorization +** function will be parameters or NULL depending on which of the following +** codes is used as the second parameter. The 5th parameter is the name +** of the database ("main", "temp", etc.) if applicable. The 6th parameter +** is the name of the inner-most trigger or view that is responsible for +** the access attempt or NULL if this access attempt is directly from +** input SQL code. +** +** Arg-3 Arg-4 +*/ +#define SQLITE_COPY 0 /* Table Name File Name */ +#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ +#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ +#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ +#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ +#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ +#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ +#define SQLITE_DELETE 9 /* Table Name NULL */ +#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ +#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ +#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ +#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ +#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ +#define SQLITE_DROP_VIEW 17 /* View Name NULL */ +#define SQLITE_INSERT 18 /* Table Name NULL */ +#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ +#define SQLITE_READ 20 /* Table Name Column Name */ +#define SQLITE_SELECT 21 /* NULL NULL */ +#define SQLITE_TRANSACTION 22 /* NULL NULL */ +#define SQLITE_UPDATE 23 /* Table Name Column Name */ + +/* +** The return value of the authorization function should be one of the +** following constants: +*/ +/* #define SQLITE_OK 0 // Allow access (This is actually defined above) */ +#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ +#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ + +/* +** Register a function that is called at every invocation of sqlite_exec() +** or sqlite_compile(). This function can be used (for example) to generate +** a log file of all SQL executed against a database. +*/ +void *sqlite_trace(sqlite*, void(*xTrace)(void*,const char*), void*); + +/*** The Callback-Free API +** +** The following routines implement a new way to access SQLite that does not +** involve the use of callbacks. +** +** An sqlite_vm is an opaque object that represents a single SQL statement +** that is ready to be executed. +*/ +typedef struct sqlite_vm sqlite_vm; + +/* +** To execute an SQLite query without the use of callbacks, you first have +** to compile the SQL using this routine. The 1st parameter "db" is a pointer +** to an sqlite object obtained from sqlite_open(). The 2nd parameter +** "zSql" is the text of the SQL to be compiled. The remaining parameters +** are all outputs. +** +** *pzTail is made to point to the first character past the end of the first +** SQL statement in zSql. This routine only compiles the first statement +** in zSql, so *pzTail is left pointing to what remains uncompiled. +** +** *ppVm is left pointing to a "virtual machine" that can be used to execute +** the compiled statement. Or if there is an error, *ppVm may be set to NULL. +** If the input text contained no SQL (if the input is and empty string or +** a comment) then *ppVm is set to NULL. +** +** If any errors are detected during compilation, an error message is written +** into space obtained from malloc() and *pzErrMsg is made to point to that +** error message. The calling routine is responsible for freeing the text +** of this message when it has finished with it. Use sqlite_freemem() to +** free the message. pzErrMsg may be NULL in which case no error message +** will be generated. +** +** On success, SQLITE_OK is returned. Otherwise and error code is returned. +*/ +int sqlite_compile( + sqlite *db, /* The open database */ + const char *zSql, /* SQL statement to be compiled */ + const char **pzTail, /* OUT: uncompiled tail of zSql */ + sqlite_vm **ppVm, /* OUT: the virtual machine to execute zSql */ + char **pzErrmsg /* OUT: Error message. */ +); + +/* +** After an SQL statement has been compiled, it is handed to this routine +** to be executed. This routine executes the statement as far as it can +** go then returns. The return value will be one of SQLITE_DONE, +** SQLITE_ERROR, SQLITE_BUSY, SQLITE_ROW, or SQLITE_MISUSE. +** +** SQLITE_DONE means that the execute of the SQL statement is complete +** an no errors have occurred. sqlite_step() should not be called again +** for the same virtual machine. *pN is set to the number of columns in +** the result set and *pazColName is set to an array of strings that +** describe the column names and datatypes. The name of the i-th column +** is (*pazColName)[i] and the datatype of the i-th column is +** (*pazColName)[i+*pN]. *pazValue is set to NULL. +** +** SQLITE_ERROR means that the virtual machine encountered a run-time +** error. sqlite_step() should not be called again for the same +** virtual machine. *pN is set to 0 and *pazColName and *pazValue are set +** to NULL. Use sqlite_finalize() to obtain the specific error code +** and the error message text for the error. +** +** SQLITE_BUSY means that an attempt to open the database failed because +** another thread or process is holding a lock. The calling routine +** can try again to open the database by calling sqlite_step() again. +** The return code will only be SQLITE_BUSY if no busy handler is registered +** using the sqlite_busy_handler() or sqlite_busy_timeout() routines. If +** a busy handler callback has been registered but returns 0, then this +** routine will return SQLITE_ERROR and sqltie_finalize() will return +** SQLITE_BUSY when it is called. +** +** SQLITE_ROW means that a single row of the result is now available. +** The data is contained in *pazValue. The value of the i-th column is +** (*azValue)[i]. *pN and *pazColName are set as described in SQLITE_DONE. +** Invoke sqlite_step() again to advance to the next row. +** +** SQLITE_MISUSE is returned if sqlite_step() is called incorrectly. +** For example, if you call sqlite_step() after the virtual machine +** has halted (after a prior call to sqlite_step() has returned SQLITE_DONE) +** or if you call sqlite_step() with an incorrectly initialized virtual +** machine or a virtual machine that has been deleted or that is associated +** with an sqlite structure that has been closed. +*/ +int sqlite_step( + sqlite_vm *pVm, /* The virtual machine to execute */ + int *pN, /* OUT: Number of columns in result */ + const char ***pazValue, /* OUT: Column data */ + const char ***pazColName /* OUT: Column names and datatypes */ +); + +/* +** This routine is called to delete a virtual machine after it has finished +** executing. The return value is the result code. SQLITE_OK is returned +** if the statement executed successfully and some other value is returned if +** there was any kind of error. If an error occurred and pzErrMsg is not +** NULL, then an error message is written into memory obtained from malloc() +** and *pzErrMsg is made to point to that error message. The calling routine +** should use sqlite_freemem() to delete this message when it has finished +** with it. +** +** This routine can be called at any point during the execution of the +** virtual machine. If the virtual machine has not completed execution +** when this routine is called, that is like encountering an error or +** an interrupt. (See sqlite_interrupt().) Incomplete updates may be +** rolled back and transactions cancelled, depending on the circumstances, +** and the result code returned will be SQLITE_ABORT. +*/ +int sqlite_finalize(sqlite_vm*, char **pzErrMsg); + +#ifdef __cplusplus +} /* End of the 'extern "C"' block */ +#endif + +#endif /* _SQLITE_H_ */ diff --git a/kexi/kexidb/drivers/sqlite/kexidb_sqlite3driver.desktop b/kexi/kexidb/drivers/sqlite/kexidb_sqlite3driver.desktop new file mode 100644 index 00000000..92e59931 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/kexidb_sqlite3driver.desktop @@ -0,0 +1,56 @@ +[Desktop Entry] +Name=SQLite3 +Name[sv]=Sqlite 3 +Comment=SQLite is default Kexi embedded SQL engine +Comment[bg]=SQLite е СУБД по подразбиране в Kexi , с вградено SQL ядро +Comment[ca]=SQLite és el motor SQL encastat i per defecte de Kexi +Comment[cy]=Y peiriant SQL mewnadeiledig rhagosod Kexi yw SQLite +Comment[da]=SQLite er en standard Kexi-indlejret SQL-motor +Comment[de]=SQLite ist der standardmäßig in Kexi eingebettete SQL-Treiber +Comment[el]=Η SQLite είναι η προκαθορισμένη ενσωματωμένη μηχανή SQL του Kexi +Comment[es]=SQLite es el motor SQL incrustado en Kexi de forma predefinida +Comment[et]=SQLite on Kexi vaikimisi kasutatav SQL mootor +Comment[eu]=SQLite Kexi-ren kapsultatutako SQL motore lehenetsia da +Comment[fa]=SQLite، پیشفرض موتور SQL نهفتۀ Kexi است +Comment[fi]=SQLite on Kexin käyttämä sisäänrakennetti SQL-moottori. +Comment[fr]=SQLite est le moteur SQL par défaut intégré à Kexi +Comment[fy]=SQLite is de standert SQL-databank foar Kexi +Comment[gl]=SQLite é o motor embebido de SQL de Kexi +Comment[he]=SQLite הוא מנוע ה־SQL המוטבע של Kexi המשמש כברירת מחדל +Comment[hi]=केएक्साई एम्बेडेड एसक्यूएल इंजिन के लिए एसक्यूएललाइट डिफ़ॉल्ट है +Comment[hr]=SQLite je zadani Kexi ugrađeni pogon SQL pogona +Comment[hu]=Az SQLite a Kexi alapértelmezett, beépített SQL-motorja +Comment[is]=SQLite er sjálfgefna Kexi SQL vélin +Comment[it]=SQLite è il motore predefinito integrato in Kexi +Comment[ja]=SQLite は Kexi の標準埋め込み SQL エンジンです。 +Comment[km]=SQLite គឺជាម៉ាស៊ីន SQL ដែលបានបង្កប់ក្នុង Kexi តាមលំនាំដើម +Comment[lv]=SQLite ir Kexi noklusējuma SQL dzinējs +Comment[ms]=SQLite adalah KeXi piawai yang dipasang dalam enjin SQL +Comment[nb]=SQLite er den innebygde SQL-motoren i Kexi +Comment[nds]=SQLite is de standardinbett SQL-Driever för Kexi +Comment[ne]=SQLite पूर्वनिर्धारित केक्सी सम्मिलित SQL इन्जिन हो +Comment[nl]=SQLite is de standaard SQL-database voor Kexi +Comment[nn]=SQLite er den innebygde SQL-motoren i Kexi +Comment[pl]=SQLite jest domyślnym wbudowanym silnikiem SQL dla Kexi +Comment[pt]=SQLite é o motor embebido de SQL do Kexi +Comment[pt_BR]=SQLite é o mecanismo SQL embutido padrão do Kexi +Comment[ru]=SQLite -- движок встроенного SQL по умолчанию в Kexi +Comment[se]=SQLite lea Kexi:a sisahuksejuvvon SQL-mohtor +Comment[sk]=SQLite je štandardný Kexi embedded SQL systém +Comment[sl]=SQLite je privzet vključen pogon SQL za Kexi +Comment[sr]=SQLite је подразумевани Kexi-јев уграђен SQL мотор +Comment[sr@Latn]=SQLite je podrazumevani Kexi-jev ugrađen SQL motor +Comment[sv]=Sqlite är inbyggd SQL-standarddatabas i Kexi +Comment[tg]=SQLite -- движоки SQL, ки дар дар дохили Kexi бо пешфарз сохта шудааст +Comment[tr]=SQlite Kexi'ye gömülü varsayılan SQL motorudur +Comment[uk]=SQLite - це типовий рушій SQL вбудований в Kexi +Comment[zh_CN]=SQLite 是嵌入 Kexi 的默认 SQL 引擎 +Comment[zh_TW]=SQLite 是 Kexi 預設內建的 SQL 引擎 +X-KDE-Library=kexidb_sqlite3driver +ServiceTypes=Kexi/DBDriver +Type=Service +InitialPreference=8 +X-Kexi-DriverName=SQLite3 +X-Kexi-DriverType=File +X-Kexi-FileDBDriverMimeList=application/x-sqlite3,application/x-kexiproject-sqlite3,application/x-hk_classes-sqlite3 +X-Kexi-KexiDBVersion=1.8 diff --git a/kexi/kexidb/drivers/sqlite/sqlite.pro b/kexi/kexidb/drivers/sqlite/sqlite.pro new file mode 100644 index 00000000..7fde2926 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlite.pro @@ -0,0 +1,10 @@ +include( sqlite_common.pro ) + +win32:LIBS += $$KDELIBDESTDIR/kexisql3$$KDELIBDEBUGLIB + +INCLUDEPATH += $(KEXI)/3rdparty/kexisql3/src + +TARGET = kexidb_sqlite3driver$$KDELIBDEBUG + +SOURCES += sqlitekeywords.cpp \ +sqlitevacuum.cpp diff --git a/kexi/kexidb/drivers/sqlite/sqlite_common.pro b/kexi/kexidb/drivers/sqlite/sqlite_common.pro new file mode 100644 index 00000000..81fb85b3 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlite_common.pro @@ -0,0 +1,16 @@ +include( ../common.pro ) + +DEFINES += MAKE_KEXIDB_SQLITE_DRIVER_LIB + +system( bash kmoc ) + +SOURCES = \ +sqliteconnection.cpp \ +sqlitedriver.cpp \ +sqliteadmin.cpp \ +sqlitecursor.cpp \ +sqlitepreparedstatement.cpp \ +sqlitealter.cpp + +HEADERS = + diff --git a/kexi/kexidb/drivers/sqlite/sqliteadmin.cpp b/kexi/kexidb/drivers/sqlite/sqliteadmin.cpp new file mode 100644 index 00000000..8bd8085a --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqliteadmin.cpp @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <qdir.h> + +#include "sqliteadmin.h" +#include <kexidb/drivermanager.h> +#include <kexidb/driver_p.h> + +#ifndef SQLITE2 +# include "sqlitevacuum.h" +#endif + +SQLiteAdminTools::SQLiteAdminTools() + : KexiDB::AdminTools() +{ +} + +SQLiteAdminTools::~SQLiteAdminTools() +{ +} + +bool SQLiteAdminTools::vacuum(const KexiDB::ConnectionData& data, const QString& databaseName) +{ + clearError(); +#ifdef SQLITE2 + Q_UNUSED(data); + Q_UNUSED(databaseName); + return false; +#else + KexiDB::DriverManager manager; + KexiDB::Driver *drv = manager.driver(data.driverName); + QString title( i18n("Could not compact database \"%1\".").arg(QDir::convertSeparators(databaseName)) ); + if (!drv) { + setError(&manager, title); + return false; + } + SQLiteVacuum vacuum(data.dbPath()+QDir::separator()+databaseName); + tristate result = vacuum.run(); + if (!result) { + setError(title); + return false; + } + else //success or cancelled + return true; +#endif +} + diff --git a/kexi/kexidb/drivers/sqlite/sqliteadmin.h b/kexi/kexidb/drivers/sqlite/sqliteadmin.h new file mode 100644 index 00000000..d9f8a6b6 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqliteadmin.h @@ -0,0 +1,36 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_SQLITEADMIN_H +#define KEXIDB_SQLITEADMIN_H + +#include <kexidb/admin.h> + +//! @short An interface containing a set of tools for SQLite database administration. +class SQLiteAdminTools : public KexiDB::AdminTools +{ + public: + SQLiteAdminTools(); + virtual ~SQLiteAdminTools(); + + /*! Performs vacuum (compacting) for connection \a conn. */ + virtual bool vacuum(const KexiDB::ConnectionData& data, const QString& databaseName); +}; + +#endif diff --git a/kexi/kexidb/drivers/sqlite/sqlitealter.cpp b/kexi/kexidb/drivers/sqlite/sqlitealter.cpp new file mode 100644 index 00000000..cc72e48a --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitealter.cpp @@ -0,0 +1,114 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +// ** bits of SQLiteConnection related to table altering ** + +#include "sqliteconnection.h" +#include <kexidb/utils.h> + +#include <kstaticdeleter.h> + +#include <qmap.h> + +using namespace KexiDB; + +enum SQLiteTypeAffinity { //as defined here: 2.1 Determination Of Column Affinity (http://sqlite.org/datatype3.html) + NoAffinity = 0, IntAffinity = 1, TextAffinity = 2, BLOBAffinity = 3 +}; + +//! helper for affinityForType() +static KStaticDeleter< QMap<int,int> > KexiDB_SQLite_affinityForType_deleter; +QMap<int,int> *KexiDB_SQLite_affinityForType = 0; + +//! \return SQLite type affinity for \a type +//! See doc/dev/alter_table_type_conversions.ods, page 2 for more info +static SQLiteTypeAffinity affinityForType(Field::Type type) +{ + if (!KexiDB_SQLite_affinityForType) { + KexiDB_SQLite_affinityForType_deleter.setObject( KexiDB_SQLite_affinityForType, new QMap<int,int>() ); + KexiDB_SQLite_affinityForType->insert(Field::Byte, IntAffinity); + KexiDB_SQLite_affinityForType->insert(Field::ShortInteger, IntAffinity); + KexiDB_SQLite_affinityForType->insert(Field::Integer, IntAffinity); + KexiDB_SQLite_affinityForType->insert(Field::BigInteger, IntAffinity); + KexiDB_SQLite_affinityForType->insert(Field::Boolean, IntAffinity); + KexiDB_SQLite_affinityForType->insert(Field::Date, TextAffinity); + KexiDB_SQLite_affinityForType->insert(Field::DateTime, TextAffinity); + KexiDB_SQLite_affinityForType->insert(Field::Time, TextAffinity); + KexiDB_SQLite_affinityForType->insert(Field::Float, IntAffinity); + KexiDB_SQLite_affinityForType->insert(Field::Double, IntAffinity); + KexiDB_SQLite_affinityForType->insert(Field::Text, TextAffinity); + KexiDB_SQLite_affinityForType->insert(Field::LongText, TextAffinity); + KexiDB_SQLite_affinityForType->insert(Field::BLOB, BLOBAffinity); + } + return static_cast<SQLiteTypeAffinity>((*KexiDB_SQLite_affinityForType)[(int)type]); +} + +tristate SQLiteConnection::drv_changeFieldProperty(TableSchema &table, Field& field, + const QString& propertyName, const QVariant& value) +{ +/* if (propertyName=="name") { + + }*/ + if (propertyName=="type") { + bool ok; + Field::Type type = KexiDB::intToFieldType( value.toUInt(&ok) ); + if (!ok || Field::InvalidType == type) { + //! @todo msg + return false; + } + return changeFieldType(table, field, type); + } + // not found + return cancelled; +} + +/*! + From http://sqlite.org/datatype3.html : + Version 3 enhances provides the ability to store integer and real numbers in a more compact + format and the capability to store BLOB data. + + Each value stored in an SQLite database (or manipulated by the database engine) has one + of the following storage classes: + * NULL. The value is a NULL value. + * INTEGER. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending + on the magnitude of the value. + * REAL. The value is a floating point value, stored as an 8-byte IEEE floating point number. + * TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16-LE). + * BLOB. The value is a blob of data, stored exactly as it was input. + + Column Affinity + In SQLite version 3, the type of a value is associated with the value itself, + not with the column or variable in which the value is stored. +.The type affinity of a column is the recommended type for data stored in that column. + + See alter_table_type_conversions.ods for details. +*/ +tristate SQLiteConnection::changeFieldType(TableSchema &table, Field& field, + Field::Type type) +{ + Q_UNUSED(table); + const Field::Type oldType = field.type(); + const SQLiteTypeAffinity oldAffinity = affinityForType(oldType); + const SQLiteTypeAffinity newAffinity = affinityForType(type); + if (oldAffinity!=newAffinity) { + //type affinity will be changed + } + + return cancelled; +} diff --git a/kexi/kexidb/drivers/sqlite/sqliteconnection.cpp b/kexi/kexidb/drivers/sqlite/sqliteconnection.cpp new file mode 100644 index 00000000..6de41c59 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqliteconnection.cpp @@ -0,0 +1,414 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "sqliteconnection.h" +#include "sqliteconnection_p.h" +#include "sqlitecursor.h" +#include "sqlitepreparedstatement.h" + +#include "sqlite.h" + +#ifndef SQLITE2 +# include "kexisql.h" //for isReadOnly() +#endif + +#include <kexidb/driver.h> +#include <kexidb/cursor.h> +#include <kexidb/error.h> +#include <kexiutils/utils.h> + +#include <qfile.h> +#include <qdir.h> +#include <qregexp.h> + +#include <kgenericfactory.h> +#include <kdebug.h> + +//remove debug +#undef KexiDBDrvDbg +#define KexiDBDrvDbg if (0) kdDebug() + +using namespace KexiDB; + +SQLiteConnectionInternal::SQLiteConnectionInternal(Connection *connection) + : ConnectionInternal(connection) + , data(0) + , data_owned(true) + , errmsg_p(0) + , res(SQLITE_OK) + , temp_st(0x10000) +#ifdef SQLITE3 + , result_name(0) +#endif +{ +} + +SQLiteConnectionInternal::~SQLiteConnectionInternal() +{ + if (data_owned && data) { + free( data ); + data = 0; + } +//sqlite_freemem does this if (errmsg) { +// free( errmsg ); +// errmsg = 0; +// } +} + +void SQLiteConnectionInternal::storeResult() +{ + if (errmsg_p) { + errmsg = errmsg_p; + sqlite_free(errmsg_p); + errmsg_p = 0; + } +#ifdef SQLITE3 + errmsg = (data && res!=SQLITE_OK) ? sqlite3_errmsg(data) : 0; +#endif +} + +/*! Used by driver */ +SQLiteConnection::SQLiteConnection( Driver *driver, ConnectionData &conn_data ) + : Connection( driver, conn_data ) + ,d(new SQLiteConnectionInternal(this)) +{ +} + +SQLiteConnection::~SQLiteConnection() +{ + KexiDBDrvDbg << "SQLiteConnection::~SQLiteConnection()" << endl; + //disconnect if was connected +// disconnect(); + destroy(); + delete d; + KexiDBDrvDbg << "SQLiteConnection::~SQLiteConnection() ok" << endl; +} + +bool SQLiteConnection::drv_connect(KexiDB::ServerVersionInfo& version) +{ + KexiDBDrvDbg << "SQLiteConnection::connect()" << endl; + version.string = QString(SQLITE_VERSION); //defined in sqlite3.h + QRegExp re("(\\d+)\\.(\\d+)\\.(\\d+)"); + if (re.exactMatch(version.string)) { + version.major = re.cap(1).toUInt(); + version.minor = re.cap(2).toUInt(); + version.release = re.cap(3).toUInt(); + } + return true; +} + +bool SQLiteConnection::drv_disconnect() +{ + KexiDBDrvDbg << "SQLiteConnection::disconnect()" << endl; + return true; +} + +bool SQLiteConnection::drv_getDatabasesList( QStringList &list ) +{ + //this is one-db-per-file database + list.append( data()->fileName() ); //more consistent than dbFileName() ? + return true; +} + +bool SQLiteConnection::drv_containsTable( const QString &tableName ) +{ + bool success; + return resultExists(QString("select name from sqlite_master where type='table' and name LIKE %1") + .arg(driver()->escapeString(tableName)), success) && success; +} + +bool SQLiteConnection::drv_getTablesList( QStringList &list ) +{ + KexiDB::Cursor *cursor; + m_sql = "select lower(name) from sqlite_master where type='table'"; + if (!(cursor = executeQuery( m_sql ))) { + KexiDBWarn << "Connection::drv_getTablesList(): !executeQuery()" << endl; + return false; + } + list.clear(); + cursor->moveFirst(); + while (!cursor->eof() && !cursor->error()) { + list += cursor->value(0).toString(); + cursor->moveNext(); + } + if (cursor->error()) { + deleteCursor(cursor); + return false; + } + return deleteCursor(cursor); +} + +bool SQLiteConnection::drv_createDatabase( const QString &dbName ) +{ + // SQLite creates a new db is it does not exist + return drv_useDatabase(dbName); +#if 0 + d->data = sqlite_open( QFile::encodeName( data()->fileName() ), 0/*mode: unused*/, + &d->errmsg_p ); + d->storeResult(); + return d->data != 0; +#endif +} + +bool SQLiteConnection::drv_useDatabase( const QString &dbName, bool *cancelled, + MessageHandler* msgHandler ) +{ + Q_UNUSED(dbName); +// KexiDBDrvDbg << "drv_useDatabase(): " << data()->fileName() << endl; +#ifdef SQLITE2 + Q_UNUSED(cancelled); + Q_UNUSED(msgHandler); + d->data = sqlite_open( QFile::encodeName( data()->fileName() ), 0/*mode: unused*/, + &d->errmsg_p ); + d->storeResult(); + return d->data != 0; +#else //SQLITE3 + //TODO: perhaps allow to use sqlite3_open16() as well for SQLite ~ 3.3 ? +//! @todo add option (command line or in kexirc?) + int exclusiveFlag = Connection::isReadOnly() ? SQLITE_OPEN_READONLY : SQLITE_OPEN_WRITE_LOCKED; // <-- shared read + (if !r/o): exclusive write +//! @todo add option + int allowReadonly = 1; + const bool wasReadOnly = Connection::isReadOnly(); + + d->res = sqlite3_open( + //QFile::encodeName( data()->fileName() ), + data()->fileName().utf8(), /* unicode expected since SQLite 3.1 */ + &d->data, + exclusiveFlag, + allowReadonly /* If 1 and locking fails, try opening in read-only mode */ + ); + d->storeResult(); + + if (d->res == SQLITE_OK && cancelled && !wasReadOnly && allowReadonly && isReadOnly()) { + //opened as read only, ask + if (KMessageBox::Continue != + askQuestion( + i18n("Do you want to open file \"%1\" as read-only?") + .arg(QDir::convertSeparators(data()->fileName())) + + "\n\n" + + i18n("The file is probably already open on this or another computer.") + " " + + i18n("Could not gain exclusive access for writing the file."), + KMessageBox::WarningContinueCancel, KMessageBox::Continue, + KGuiItem(i18n("Open As Read-Only"), "fileopen"), KStdGuiItem::cancel(), + "askBeforeOpeningFileReadOnly", KMessageBox::Notify, msgHandler )) + { + clearError(); + if (!drv_closeDatabase()) + return false; + *cancelled = true; + return false; + } + } + + if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_READWRITE) { + setError(ERR_ACCESS_RIGHTS, + i18n("The file is probably already open on this or another computer.")+"\n\n" + + i18n("Could not gain exclusive access for reading and writing the file.") + " " + + i18n("Check the file's permissions and whether it is already opened and locked by another application.")); + } + else if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_WRITE) { + setError(ERR_ACCESS_RIGHTS, + i18n("The file is probably already open on this or another computer.")+"\n\n" + + i18n("Could not gain exclusive access for writing the file.") + " " + + i18n("Check the file's permissions and whether it is already opened and locked by another application.")); + } + return d->res == SQLITE_OK; +#endif +} + +bool SQLiteConnection::drv_closeDatabase() +{ + if (!d->data) + return false; + +#ifdef SQLITE2 + sqlite_close(d->data); + d->data = 0; + return true; +#else + const int res = sqlite_close(d->data); + if (SQLITE_OK == res) { + d->data = 0; + return true; + } + if (SQLITE_BUSY==res) { +#if 0 //this is ANNOYING, needs fixing (by closing cursors or waiting) + setError(ERR_CLOSE_FAILED, i18n("Could not close busy database.")); +#else + return true; +#endif + } + return false; +#endif +} + +bool SQLiteConnection::drv_dropDatabase( const QString &dbName ) +{ + Q_UNUSED(dbName); // Each database is one single SQLite file. + const QString filename = data()->fileName(); + if (QFile(filename).exists() && !QDir().remove(filename)) { + setError(ERR_ACCESS_RIGHTS, i18n("Could not remove file \"%1\".") + .arg(QDir::convertSeparators(filename)) + " " + + i18n("Check the file's permissions and whether it is already opened and locked by another application.")); + return false; + } + return true; +} + +//CursorData* SQLiteConnection::drv_createCursor( const QString& statement ) +Cursor* SQLiteConnection::prepareQuery( const QString& statement, uint cursor_options ) +{ + return new SQLiteCursor( this, statement, cursor_options ); +} + +Cursor* SQLiteConnection::prepareQuery( QuerySchema& query, uint cursor_options ) +{ + return new SQLiteCursor( this, query, cursor_options ); +} + +bool SQLiteConnection::drv_executeSQL( const QString& statement ) +{ +// KexiDBDrvDbg << "SQLiteConnection::drv_executeSQL(" << statement << ")" <<endl; +// QCString st(statement.length()*2); +// st = escapeString( statement.local8Bit() ); //? +#ifdef SQLITE_UTF8 + d->temp_st = statement.utf8(); +#else + d->temp_st = statement.local8Bit(); //latin1 only +#endif + +#ifdef KEXI_DEBUG_GUI + KexiUtils::addKexiDBDebug(QString("ExecuteSQL (SQLite): ")+statement); +#endif + + d->res = sqlite_exec( + d->data, + (const char*)d->temp_st, + 0/*callback*/, + 0, + &d->errmsg_p ); + d->storeResult(); +#ifdef KEXI_DEBUG_GUI + KexiUtils::addKexiDBDebug(d->res==SQLITE_OK ? " Success" : " Failure"); +#endif + return d->res==SQLITE_OK; +} + +Q_ULLONG SQLiteConnection::drv_lastInsertRowID() +{ + return (Q_ULLONG)sqlite_last_insert_rowid(d->data); +} + +int SQLiteConnection::serverResult() +{ + return d->res==0 ? Connection::serverResult() : d->res; +} + +QString SQLiteConnection::serverResultName() +{ + QString r = +#ifdef SQLITE2 + QString::fromLatin1( sqlite_error_string(d->res) ); +#else //SQLITE3 + QString::null; //fromLatin1( d->result_name ); +#endif + return r.isEmpty() ? Connection::serverResultName() : r; +} + +void SQLiteConnection::drv_clearServerResult() +{ + if (!d) + return; + d->res = SQLITE_OK; +#ifdef SQLITE2 + d->errmsg_p = 0; +#else +// d->result_name = 0; +#endif +} + +QString SQLiteConnection::serverErrorMsg() +{ + return d->errmsg.isEmpty() ? Connection::serverErrorMsg() : d->errmsg; +} + +PreparedStatement::Ptr SQLiteConnection::prepareStatement(PreparedStatement::StatementType type, + FieldList& fields) +{ +//#ifndef SQLITE2 //TEMP IFDEF! + return new SQLitePreparedStatement(type, *d, fields); +//#endif +} + +bool SQLiteConnection::isReadOnly() const +{ +#ifdef SQLITE2 + return Connection::isReadOnly(); +#else + return d->data ? sqlite3_is_readonly(d->data) : false; +#endif +} + +#ifdef SQLITE2 +bool SQLiteConnection::drv_alterTableName(TableSchema& tableSchema, const QString& newName, bool replace) +{ + const QString oldTableName = tableSchema.name(); + const bool destTableExists = this->tableSchema( newName ) != 0; + + //1. drop the table + if (destTableExists) { + if (!replace) + return false; + if (!drv_dropTable( newName )) + return false; + } + + //2. create a copy of the table +//TODO: move this code to drv_copyTable() + tableSchema.setName(newName); + +//helper: +#define drv_alterTableName_ERR \ + tableSchema.setName(oldTableName) //restore old name + + if (!drv_createTable( tableSchema )) { + drv_alterTableName_ERR; + return false; + } + +//TODO indices, etc.??? + + // 3. copy all rows to the new table + if (!executeSQL(QString::fromLatin1("INSERT INTO %1 SELECT * FROM %2") + .arg(escapeIdentifier(tableSchema.name())).arg(escapeIdentifier(oldTableName)))) + { + drv_alterTableName_ERR; + return false; + } + + // 4. drop old table. + if (!drv_dropTable( oldTableName )) { + drv_alterTableName_ERR; + return false; + } + return true; +} +#endif + +#include "sqliteconnection.moc" diff --git a/kexi/kexidb/drivers/sqlite/sqliteconnection.h b/kexi/kexidb/drivers/sqlite/sqliteconnection.h new file mode 100644 index 00000000..ba0d3b5a --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqliteconnection.h @@ -0,0 +1,125 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_CONN_SQLITE_H +#define KEXIDB_CONN_SQLITE_H + +#include <qstringlist.h> + +#include <kexidb/connection.h> + +/*! + */ + +namespace KexiDB +{ + +class SQLiteConnectionInternal; +class Driver; + +//! sqlite-specific connection +class SQLiteConnection : public Connection +{ + Q_OBJECT + + public: + virtual ~SQLiteConnection(); + + virtual Cursor* prepareQuery( const QString& statement, uint cursor_options = 0 ); + virtual Cursor* prepareQuery( QuerySchema& query, uint cursor_options = 0 ); + +//#ifndef SQLITE2 //TEMP IFDEF! + virtual PreparedStatement::Ptr prepareStatement(PreparedStatement::StatementType type, + FieldList& fields); +//#endif + /*! Reimplemented to provide real read-only flag of the connection */ + virtual bool isReadOnly() const; + + protected: + /*! Used by driver */ + SQLiteConnection( Driver *driver, ConnectionData &conn_data ); + + virtual bool drv_connect(KexiDB::ServerVersionInfo& version); + virtual bool drv_disconnect(); + virtual bool drv_getDatabasesList( QStringList &list ); + +//TODO: move this somewhere to low level class (MIGRATION?) + virtual bool drv_getTablesList( QStringList &list ); + +//TODO: move this somewhere to low level class (MIGRATION?) + virtual bool drv_containsTable( const QString &tableName ); + + /*! Creates new database using connection. Note: Do not pass \a dbName + arg because for file-based engine (that has one database per connection) + it is defined during connection. */ + virtual bool drv_createDatabase( const QString &dbName = QString::null ); + + /*! Opens existing database using connection. Do not pass \a dbName + arg because for file-based engine (that has one database per connection) + it is defined during connection. If you pass it, + database file name will be changed. */ + virtual bool drv_useDatabase( const QString &dbName = QString::null, bool *cancelled = 0, + MessageHandler* msgHandler = 0 ); + + virtual bool drv_closeDatabase(); + + /*! Drops database from the server using connection. + After drop, database shouldn't be accessible + anymore, so database file is just removed. See note from drv_useDatabase(). */ + virtual bool drv_dropDatabase( const QString &dbName = QString::null ); + + //virtual bool drv_createTable( const KexiDB::Table& table ); + + virtual bool drv_executeSQL( const QString& statement ); +// virtual bool drv_executeQuery( const QString& statement ); + + virtual Q_ULLONG drv_lastInsertRowID(); + + virtual int serverResult(); + virtual QString serverResultName(); + virtual QString serverErrorMsg(); + virtual void drv_clearServerResult(); + virtual tristate drv_changeFieldProperty(TableSchema &table, Field& field, + const QString& propertyName, const QVariant& value); + +#ifdef SQLITE2 + /*! Alters table's described \a tableSchema name to \a newName. + This implementation is ineffective but works. + - creates a copy of the table + - copies all rows + - drops old table. + All the above should be performed within single transaction. + \return true on success. + More advanced server backends implement this using "ALTER TABLE .. RENAME TO". + */ + virtual bool drv_alterTableName(TableSchema& tableSchema, const QString& newName, bool replace = false); +#endif + + //! for drv_changeFieldProperty() + tristate changeFieldType(TableSchema &table, Field& field, Field::Type type); + + SQLiteConnectionInternal* d; + + friend class SQLiteDriver; + friend class SQLiteCursor; +}; + +} + +#endif diff --git a/kexi/kexidb/drivers/sqlite/sqliteconnection_p.h b/kexi/kexidb/drivers/sqlite/sqliteconnection_p.h new file mode 100644 index 00000000..f295573d --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqliteconnection_p.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_SQLITECONN_P_H +#define KEXIDB_SQLITECONN_P_H + +#include <kexidb/connection_p.h> + +#include "sqlite.h" + +//for compatibility +#ifdef _SQLITE3_H_ +# define SQLITE3 + typedef sqlite3 sqlite_struct; +# define sqlite_free sqlite3_free +# define sqlite_close sqlite3_close +# define sqlite_exec sqlite3_exec +# define sqlite_last_insert_rowid sqlite3_last_insert_rowid +# define sqlite_error_string sqlite3_last_insert_row_id +# define sqlite_libversion sqlite3_libversion +# define sqlite_libencoding sqlite3_libencoding +#else +# ifndef SQLITE2 +# define SQLITE2 +# endif + typedef struct sqlite sqlite_struct; +# define sqlite_free sqlite_freemem +#endif + +namespace KexiDB +{ + +/*! Internal SQLite connection data. Also used inside SQLiteCursor. */ +class SQLiteConnectionInternal : public ConnectionInternal +{ + public: + SQLiteConnectionInternal(Connection* connection); + virtual ~SQLiteConnectionInternal(); + + //! stores last result's message + virtual void storeResult(); + + sqlite_struct *data; + bool data_owned; //!< true if data pointer should be freed on destruction + QString errmsg; //<! server-specific message of last operation + char *errmsg_p; //<! temporary: server-specific message of last operation + int res; //<! result code of last operation on server + + QCString temp_st; +#ifdef SQLITE3 + const char *result_name; +#endif +}; + +} + +#endif diff --git a/kexi/kexidb/drivers/sqlite/sqlitecursor.cpp b/kexi/kexidb/drivers/sqlite/sqlitecursor.cpp new file mode 100644 index 00000000..4b18b437 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitecursor.cpp @@ -0,0 +1,567 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "sqlitecursor.h" + +#include "sqliteconnection.h" +#include "sqliteconnection_p.h" + +#include <kexidb/error.h> +#include <kexidb/driver.h> +#include <kexiutils/utils.h> + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <qptrvector.h> +#include <qdatetime.h> + +using namespace KexiDB; + +//! safer interpretations of boolean values for SQLite +static bool sqliteStringToBool(const QString& s) +{ + return s.lower()=="yes" || (s.lower()!="no" && s!="0"); +} + +//---------------------------------------------------- + +class KexiDB::SQLiteCursorData : public SQLiteConnectionInternal +{ + public: + SQLiteCursorData(Connection* conn) + : + SQLiteConnectionInternal(conn) +// : curr_cols(0) +// errmsg_p(0) +// , res(SQLITE_OK) + , curr_coldata(0) + , curr_colname(0) + , cols_pointers_mem_size(0) +// , rec_stored(false) +/* MOVED TO Cursor: + , cols_pointers_mem_size(0) + , records_in_buf(0) + , buffering_completed(false) + , at_buffer(false)*/ +//#ifdef SQLITE3 +// , rowDataReadyToFetch(false) +//#endif + { + data_owned = false; + } + +/*#ifdef SQLITE3 + void fetchRowDataIfNeeded() + { + if (!rowDataReadyToFetch) + return true; + rowDataReadyToFetch = false; + m_fieldCount = sqlite3_data_count(data); + for (int i=0; i<m_fieldCount; i++) { + + } + } +#endif*/ + + QCString st; + //for sqlite: +// sqlite_struct *data; //! taken from SQLiteConnection +#ifdef SQLITE2 + sqlite_vm *prepared_st_handle; //vm +#else //SQLITE3 + sqlite3_stmt *prepared_st_handle; +#endif + + char *utail; + +// QString errmsg; //<! server-specific message of last operation +// char *errmsg_p; //<! temporary: server-specific message of last operation +// int res; //<! result code of last operation on server + +// int curr_cols; + const char **curr_coldata; + const char **curr_colname; + + int next_cols; +// const char **next_coldata; +// const char **next_colname; +// bool rec_stored : 1; //! true, current record is stored in next_coldata + +/* MOVED TO Cursor: + uint cols_pointers_mem_size; //! size of record's array of pointers to values + int records_in_buf; //! number of records currently stored in the buffer + bool buffering_completed; //! true if we have already all records stored in the buffer + QPtrVector<const char*> records; //buffer data + bool at_buffer; //! true if we already point to the buffer with curr_coldata +*/ + +/* int prev_cols; + const char **prev_coldata; + const char **prev_colname;*/ + + uint cols_pointers_mem_size; //! size of record's array of pointers to values + QPtrVector<const char*> records;//! buffer data +//#ifdef SQLITE3 +// bool rowDataReadyToFetch : 1; +//#endif + +#ifdef SQLITE3 + inline QVariant getValue(Field *f, int i) + { + int type = sqlite3_column_type(prepared_st_handle, i); + if (type==SQLITE_NULL) { + return QVariant(); + } + else if (!f || type==SQLITE_TEXT) { +//TODO: support for UTF-16 +#define GET_sqlite3_column_text QString::fromUtf8( (const char*)sqlite3_column_text(prepared_st_handle, i) ) + if (!f || f->isTextType()) + return GET_sqlite3_column_text; + else { + switch (f->type()) { + case Field::Date: + return QDate::fromString( GET_sqlite3_column_text, Qt::ISODate ); + case Field::Time: + //QDateTime - a hack needed because QVariant(QTime) has broken isNull() + return KexiUtils::stringToHackedQTime(GET_sqlite3_column_text); + case Field::DateTime: { + QString tmp( GET_sqlite3_column_text ); + tmp[10] = 'T'; //for ISODate compatibility + return QDateTime::fromString( tmp, Qt::ISODate ); + } + case Field::Boolean: + return QVariant(sqliteStringToBool(GET_sqlite3_column_text), 1); + default: + return QVariant(); //TODO + } + } + } + else if (type==SQLITE_INTEGER) { + switch (f->type()) { + case Field::Byte: + case Field::ShortInteger: + case Field::Integer: + return QVariant( sqlite3_column_int(prepared_st_handle, i) ); + case Field::BigInteger: + return QVariant( (Q_LLONG)sqlite3_column_int64(prepared_st_handle, i) ); + case Field::Boolean: + return QVariant( sqlite3_column_int(prepared_st_handle, i)!=0, 1 ); + default:; + } + if (f->isFPNumericType()) //WEIRD, YEAH? + return QVariant( (double)sqlite3_column_int(prepared_st_handle, i) ); + else + return QVariant(); //TODO + } + else if (type==SQLITE_FLOAT) { + if (f && f->isFPNumericType()) + return QVariant( sqlite3_column_double(prepared_st_handle, i) ); + else if (!f || f->isIntegerType()) + return QVariant( (double)sqlite3_column_double(prepared_st_handle, i) ); + else + return QVariant(); //TODO + } + else if (type==SQLITE_BLOB) { + if (f && f->type()==Field::BLOB) { + QByteArray ba; +//! @todo efficient enough? + ba.duplicate((const char*)sqlite3_column_blob(prepared_st_handle, i), + sqlite3_column_bytes(prepared_st_handle, i)); + return ba; + } else + return QVariant(); //TODO + } + return QVariant(); + } +#endif //SQLITE3 +}; + +SQLiteCursor::SQLiteCursor(Connection* conn, const QString& statement, uint options) + : Cursor( conn, statement, options ) + , d( new SQLiteCursorData(conn) ) +{ + d->data = static_cast<SQLiteConnection*>(conn)->d->data; +} + +SQLiteCursor::SQLiteCursor(Connection* conn, QuerySchema& query, uint options ) + : Cursor( conn, query, options ) + , d( new SQLiteCursorData(conn) ) +{ + d->data = static_cast<SQLiteConnection*>(conn)->d->data; +} + +SQLiteCursor::~SQLiteCursor() +{ + close(); + delete d; +} + +bool SQLiteCursor::drv_open() +{ +// d->st.resize(statement.length()*2); + //TODO: decode +// d->st = statement.local8Bit(); +// d->st = m_conn->driver()->escapeString( statement.local8Bit() ); + + if(! d->data) { + // this may as example be the case if SQLiteConnection::drv_useDatabase() + // wasn't called before. Normaly sqlite_compile/sqlite3_prepare + // should handle it, but it crashes in in sqlite3SafetyOn at util.c:786 + kdWarning() << "SQLiteCursor::drv_open(): Database handle undefined." << endl; + return false; + } + +#ifdef SQLITE2 + d->st = m_sql.local8Bit(); + d->res = sqlite_compile( + d->data, + d->st.data(), + (const char**)&d->utail, + &d->prepared_st_handle, + &d->errmsg_p ); +#else //SQLITE3 + d->st = m_sql.utf8(); + d->res = sqlite3_prepare( + d->data, /* Database handle */ + d->st.data(), /* SQL statement, UTF-8 encoded */ + d->st.length(), /* Length of zSql in bytes. */ + &d->prepared_st_handle, /* OUT: Statement handle */ + 0/*const char **pzTail*/ /* OUT: Pointer to unused portion of zSql */ + ); +#endif + if (d->res!=SQLITE_OK) { + d->storeResult(); + return false; + } +//cursor is automatically @ first record +// m_beforeFirst = true; + + if (isBuffered()) { + d->records.resize(128); //TODO: manage size dynamically + } + + return true; +} + +/*bool SQLiteCursor::drv_getFirstRecord() +{ + bool ok = drv_getNextRecord();*/ +/* if ((m_options & Buffered) && ok) { //1st record is there: + //compute parameters for cursor's buffer: + //-size of record's array of pointer to values + d->cols_pointers_mem_size = d->curr_cols * sizeof(char*); + d->records_in_buf = 1; + }*/ + /*return ok; +}*/ + +bool SQLiteCursor::drv_close() +{ +#ifdef SQLITE2 + d->res = sqlite_finalize( d->prepared_st_handle, &d->errmsg_p ); +#else //SQLITE3 + d->res = sqlite3_finalize( d->prepared_st_handle ); +#endif + if (d->res!=SQLITE_OK) { + d->storeResult(); + return false; + } + return true; +} + +void SQLiteCursor::drv_getNextRecord() +{ +#ifdef SQLITE2 + static int _fieldCount; + d->res = sqlite_step( + d->prepared_st_handle, + &_fieldCount, + &d->curr_coldata, + &d->curr_colname); +#else //SQLITE3 + d->res = sqlite3_step( d->prepared_st_handle ); +#endif + if (d->res == SQLITE_ROW) { + m_result = FetchOK; +#ifdef SQLITE2 + m_fieldCount = (uint)_fieldCount; +#else + m_fieldCount = sqlite3_data_count(d->prepared_st_handle); +//#else //for SQLITE3 data fetching is delayed. Now we even do not take field count information +// // -- just set a flag that we've a data not fetched but available +// d->rowDataReadyToFetch = true; +#endif + //(m_logicalFieldCount introduced) m_fieldCount -= (m_containsROWIDInfo ? 1 : 0); + } else { +//#ifdef SQLITE3 +// d->rowDataReadyToFetch = false; +//#endif + if (d->res==SQLITE_DONE) + m_result = FetchEnd; + else + m_result = FetchError; + } + + //debug +/* + if (m_result == FetchOK && d->curr_coldata) { + for (uint i=0;i<m_fieldCount;i++) { + KexiDBDrvDbg<<"col."<< i<<": "<< d->curr_colname[i]<<" "<< d->curr_colname[m_fieldCount+i] + << " = " << (d->curr_coldata[i] ? QString::fromLocal8Bit(d->curr_coldata[i]) : "(NULL)") <<endl; + } +// KexiDBDrvDbg << "SQLiteCursor::drv_getNextRecord(): "<<m_fieldCount<<" col(s) fetched"<<endl; + }*/ +} + +void SQLiteCursor::drv_appendCurrentRecordToBuffer() +{ +// KexiDBDrvDbg << "SQLiteCursor::drv_appendCurrentRecordToBuffer():" <<endl; + if (!d->cols_pointers_mem_size) + d->cols_pointers_mem_size = m_fieldCount * sizeof(char*); + const char **record = (const char**)malloc(d->cols_pointers_mem_size); + const char **src_col = d->curr_coldata; + const char **dest_col = record; + for (uint i=0; i<m_fieldCount; i++,src_col++,dest_col++) { +// KexiDBDrvDbg << i <<": '" << *src_col << "'" <<endl; +// KexiDBDrvDbg << "src_col: " << src_col << endl; + *dest_col = *src_col ? strdup(*src_col) : 0; + } + d->records.insert(m_records_in_buf,record); +// KexiDBDrvDbg << "SQLiteCursor::drv_appendCurrentRecordToBuffer() ok." <<endl; +} + +void SQLiteCursor::drv_bufferMovePointerNext() +{ + d->curr_coldata++; //move to next record in the buffer +} + +void SQLiteCursor::drv_bufferMovePointerPrev() +{ + d->curr_coldata--; //move to prev record in the buffer +} + +//compute a place in the buffer that contain next record's data +//and move internal buffer pointer to that place +void SQLiteCursor::drv_bufferMovePointerTo(Q_LLONG at) +{ + d->curr_coldata = d->records.at(at); +} + +void SQLiteCursor::drv_clearBuffer() +{ + if (d->cols_pointers_mem_size>0) { + const uint records_in_buf = m_records_in_buf; + const char ***r_ptr = d->records.data(); + for (uint i=0; i<records_in_buf; i++, r_ptr++) { + // const char **record = m_records.at(i); + const char **field_data = *r_ptr; + // for (int col=0; col<d->curr_cols; col++, field_data++) { + for (uint col=0; col<m_fieldCount; col++, field_data++) { + free((void*)*field_data); //free field memory + } + free(*r_ptr); //free pointers to fields array + } + } +// d->curr_cols=0; +// m_fieldCount=0; + m_records_in_buf=0; + d->cols_pointers_mem_size=0; +// m_at_buffer=false; + d->records.clear(); +} + +/* +void SQLiteCursor::drv_storeCurrentRecord() +{ +#if 0 + assert(!m_data->rec_stored); + m_data->rec_stored = true; + m_data->next_cols = m_data->curr_cols; + for (int i=0;i<m_data->curr_cols;i++) { + KexiDBDrvDbg<<"[COPY] "<<i<<": "<< m_data->curr_coldata[i]<<endl; + if (m_data->curr_coldata[i]) + m_data->next_coldata[i] = strdup( m_data->curr_coldata[i] ); + else + m_data->next_coldata[i] = 0; + } +#endif +} +*/ + +/*TODO +const char *** SQLiteCursor::bufferData() +{ + if (!isBuffered()) + return 0; + return m_records.data(); +}*/ + +const char ** SQLiteCursor::rowData() const +{ + return d->curr_coldata; +} + +void SQLiteCursor::storeCurrentRow(RowData &data) const +{ +#ifdef SQLITE2 + const char **col = d->curr_coldata; +#endif + //const uint realCount = m_fieldCount + (m_containsROWIDInfo ? 1 : 0); + data.resize(m_fieldCount); + if (!m_fieldsExpanded) {//simple version: without types + for( uint i=0; i<m_fieldCount; i++ ) { +#ifdef SQLITE2 + data[i] = QVariant( *col ); + col++; +#else //SQLITE3 + data[i] = QString::fromUtf8( (const char*)sqlite3_column_text(d->prepared_st_handle, i) ); +#endif + } + return; + } + + //const uint fieldsExpandedCount = m_fieldsExpanded->count(); + const uint maxCount = QMIN(m_fieldCount, m_fieldsExpanded->count()); + // i - visible field's index, j - physical index + for( uint i=0, j=0; i<m_fieldCount; i++, j++ ) { +// while (j < m_detailedVisibility.count() && !m_detailedVisibility[j]) //!m_query->isColumnVisible(j)) +// j++; + while (j < maxCount && !m_fieldsExpanded->at(j)->visible) + j++; + if (j >= (maxCount /*+(m_containsROWIDInfo ? 1 : 0)*/)) { + //ERR! + break; + } + //(m_logicalFieldCount introduced) Field *f = (m_containsROWIDInfo && i>=m_fieldCount) ? 0 : m_fieldsExpanded->at(j)->field; + Field *f = (i>=m_fieldCount) ? 0 : m_fieldsExpanded->at(j)->field; +// KexiDBDrvDbg << "SQLiteCursor::storeCurrentRow(): col=" << (col ? *col : 0) << endl; + +#ifdef SQLITE2 + if (!*col) + data[i] = QVariant(); + else if (f && f->isTextType()) +# ifdef SQLITE_UTF8 + data[i] = QString::fromUtf8( *col ); +# else + data[i] = QVariant( *col ); //only latin1 +# endif + else if (f && f->isFPNumericType()) + data[i] = QVariant( QCString(*col).toDouble() ); + else { + switch (f ? f->type() : Field::Integer/*ROWINFO*/) { +//todo: use short, etc. + case Field::Byte: + case Field::ShortInteger: + case Field::Integer: + data[i] = QVariant( QCString(*col).toInt() ); + case Field::BigInteger: + data[i] = QVariant( QString::fromLatin1(*col).toLongLong() ); + case Field::Boolean: + data[i] = QVariant( sqliteStringToBool(QString::fromLatin1(*col)), 1 ); + break; + case Field::Date: + data[i] = QDate::fromString( QString::fromLatin1(*col), Qt::ISODate ); + break; + case Field::Time: + //QDateTime - a hack needed because QVariant(QTime) has broken isNull() + data[i] = KexiUtils::stringToHackedQTime(QString::fromLatin1(*col)); + break; + case Field::DateTime: { + QString tmp( QString::fromLatin1(*col) ); + tmp[10] = 'T'; + data[i] = QDateTime::fromString( tmp, Qt::ISODate ); + break; + } + default: + data[i] = QVariant( *col ); + } + } + + col++; +#else //SQLITE3 + data[i] = d->getValue(f, i); //, !f /*!f means ROWID*/); +#endif + } +} + +QVariant SQLiteCursor::value(uint i) +{ +// if (i > (m_fieldCount-1+(m_containsROWIDInfo?1:0))) //range checking + if (i > (m_fieldCount-1)) //range checking + return QVariant(); +//TODO: allow disable range checking! - performance reasons +// const KexiDB::Field *f = m_query ? m_query->field(i) : 0; + KexiDB::Field *f = (m_fieldsExpanded && i<m_fieldsExpanded->count()) + ? m_fieldsExpanded->at(i)->field : 0; +#ifdef SQLITE2 + //from most to least frequently used types: +//(m_logicalFieldCount introduced) if (i==m_fieldCount || f && f->isIntegerType()) + if (!f || f->isIntegerType()) + return QVariant( QCString(d->curr_coldata[i]).toInt() ); + else if (!f || f->isTextType()) + return QVariant( d->curr_coldata[i] ); + else if (f->isFPNumericType()) + return QVariant( QCString(d->curr_coldata[i]).toDouble() ); + + return QVariant( d->curr_coldata[i] ); //default +#else + return d->getValue(f, i); //, i==m_logicalFieldCount/*ROWID*/); +#endif +} + +/*! Stores string value taken from field number \a i to \a str. + \return false when range checking failed. +bool SQLiteCursor::storeStringValue(uint i, QString &str) +{ + if (i > (m_fieldCount-1)) //range checking + return false; + str = d->curr_coldata[i]; + return true; +}*/ + +int SQLiteCursor::serverResult() +{ + return d->res; +} + +QString SQLiteCursor::serverResultName() +{ +#ifdef SQLITE2 + return QString::fromLatin1( sqlite_error_string(d->res) ); +#else //SQLITE3 + return QString::fromLatin1( d->result_name ); +#endif +} + +QString SQLiteCursor::serverErrorMsg() +{ + return d->errmsg; +} + +void SQLiteCursor::drv_clearServerResult() +{ + d->res = SQLITE_OK; + d->errmsg_p = 0; +} + diff --git a/kexi/kexidb/drivers/sqlite/sqlitecursor.h b/kexi/kexidb/drivers/sqlite/sqlitecursor.h new file mode 100644 index 00000000..b7170f67 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitecursor.h @@ -0,0 +1,92 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_SQLITECURSOR_H +#define KEXIDB_SQLITECURSOR_H + +#include <qstring.h> + +#include <kexidb/cursor.h> +#include "connection.h" + +namespace KexiDB { + +class SQLiteCursorData; + +/*! + +*/ +class SQLiteCursor : public Cursor +{ + public: + virtual ~SQLiteCursor(); + virtual QVariant value(uint i); + + /*! [PROTOTYPE] \return internal buffer data. */ +//TODO virtual const char *** bufferData() + /*! [PROTOTYPE] \return current record data or NULL if there is no current records. */ + virtual const char ** rowData() const; + + virtual void storeCurrentRow(RowData &data) const; + +// virtual bool save(RowData& data, RowEditBuffer& buf); + + virtual int serverResult(); + virtual QString serverResultName(); + virtual QString serverErrorMsg(); + + protected: + /*! Cursor will operate on \a conn, raw \a statement will be used to execute query. */ + SQLiteCursor(Connection* conn, const QString& statement, uint options = NoOptions ); + + /*! Cursor will operate on \a conn, \a query schema will be used to execute query. */ + SQLiteCursor(Connection* conn, QuerySchema& query, + uint options = NoOptions ); + + virtual bool drv_open(); + + virtual bool drv_close(); +// virtual bool drv_moveFirst(); + virtual void drv_getNextRecord(); +//unused virtual bool drv_getPrevRecord(); + + virtual void drv_appendCurrentRecordToBuffer(); + virtual void drv_bufferMovePointerNext(); + virtual void drv_bufferMovePointerPrev(); + virtual void drv_bufferMovePointerTo(Q_LLONG at); + +//TODO virtual void drv_storeCurrentRecord(); + + //PROTOTYPE: + /*! Method called when cursor's buffer need to be cleared + (only for buffered cursor type), eg. in close(). */ + virtual void drv_clearBuffer(); + + virtual void drv_clearServerResult(); + + SQLiteCursorData *d; + + friend class SQLiteConnection; +}; + +} //namespace KexiDB + +#endif + + diff --git a/kexi/kexidb/drivers/sqlite/sqlitedriver.cpp b/kexi/kexidb/drivers/sqlite/sqlitedriver.cpp new file mode 100644 index 00000000..e2abc246 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitedriver.cpp @@ -0,0 +1,159 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <kexidb/connection.h> +#include <kexidb/drivermanager.h> +#include <kexidb/driver_p.h> +#include <kexidb/utils.h> + +#include "sqlite.h" +#include "sqlitedriver.h" +#include "sqliteconnection.h" +#include "sqliteconnection_p.h" +#include "sqliteadmin.h" + +#include <kdebug.h> + +using namespace KexiDB; + +#ifdef SQLITE2 +KEXIDB_DRIVER_INFO( SQLiteDriver, sqlite2 ) +#else +KEXIDB_DRIVER_INFO( SQLiteDriver, sqlite3 ) +#endif + +//! driver specific private data +//! @internal +class KexiDB::SQLiteDriverPrivate +{ + public: + SQLiteDriverPrivate() + { + } +}; + +//PgSqlDB::PgSqlDB(QObject *parent, const char *name, const QStringList &) +SQLiteDriver::SQLiteDriver( QObject *parent, const char *name, const QStringList &args ) + : Driver( parent, name, args ) + ,dp( new SQLiteDriverPrivate() ) +{ + d->isFileDriver = true; + d->isDBOpenedAfterCreate = true; + d->features = SingleTransactions | CursorForward +#ifndef SQLITE2 + | CompactingDatabaseSupported; +#endif + ; + + //special method for autoincrement definition + beh->SPECIAL_AUTO_INCREMENT_DEF = true; + beh->AUTO_INCREMENT_FIELD_OPTION = ""; //not available + beh->AUTO_INCREMENT_TYPE = "INTEGER"; + beh->AUTO_INCREMENT_PK_FIELD_OPTION = "PRIMARY KEY"; + beh->AUTO_INCREMENT_REQUIRES_PK = true; + beh->ROW_ID_FIELD_NAME = "OID"; + beh->_1ST_ROW_READ_AHEAD_REQUIRED_TO_KNOW_IF_THE_RESULT_IS_EMPTY=true; + beh->QUOTATION_MARKS_FOR_IDENTIFIER='"'; + beh->SELECT_1_SUBQUERY_SUPPORTED = true; + beh->SQL_KEYWORDS = keywords; + initSQLKeywords(29); + + //predefined properties + d->properties["client_library_version"] = sqlite_libversion(); + d->properties["default_server_encoding"] = +#ifdef SQLITE2 + sqlite_libencoding(); +#else //SQLITE3 + "UTF8"; //OK? +#endif + + d->typeNames[Field::Byte]="Byte"; + d->typeNames[Field::ShortInteger]="ShortInteger"; + d->typeNames[Field::Integer]="Integer"; + d->typeNames[Field::BigInteger]="BigInteger"; + d->typeNames[Field::Boolean]="Boolean"; + d->typeNames[Field::Date]="Date"; // In fact date/time types could be declared as datetext etc. + d->typeNames[Field::DateTime]="DateTime"; // to force text affinity..., see http://sqlite.org/datatype3.html + d->typeNames[Field::Time]="Time"; // + d->typeNames[Field::Float]="Float"; + d->typeNames[Field::Double]="Double"; + d->typeNames[Field::Text]="Text"; + d->typeNames[Field::LongText]="CLOB"; + d->typeNames[Field::BLOB]="BLOB"; +} + +SQLiteDriver::~SQLiteDriver() +{ + delete dp; +} + + +KexiDB::Connection* +SQLiteDriver::drv_createConnection( ConnectionData &conn_data ) +{ + return new SQLiteConnection( this, conn_data ); +} + +bool SQLiteDriver::isSystemObjectName( const QString& n ) const +{ + return Driver::isSystemObjectName(n) || n.lower().startsWith("sqlite_"); +} + +bool SQLiteDriver::drv_isSystemFieldName( const QString& n ) const +{ + return n.lower()=="_rowid_" + || n.lower()=="rowid" + || n.lower()=="oid"; +} + +QString SQLiteDriver::escapeString(const QString& str) const +{ + return QString("'")+QString(str).replace( '\'', "''" ) + "'"; +} + +QCString SQLiteDriver::escapeString(const QCString& str) const +{ + return QCString("'")+QCString(str).replace( '\'', "''" )+"'"; +} + +QString SQLiteDriver::escapeBLOB(const QByteArray& array) const +{ + return KexiDB::escapeBLOB(array, KexiDB::BLOBEscapeXHex); +} + +QString SQLiteDriver::drv_escapeIdentifier( const QString& str) const +{ + return QString(str).replace( '"', "\"\"" ); +} + +QCString SQLiteDriver::drv_escapeIdentifier( const QCString& str) const +{ + return QCString(str).replace( '"', "\"\"" ); +} + +AdminTools* SQLiteDriver::drv_createAdminTools() const +{ +#ifdef SQLITE2 + return new AdminTools(); //empty impl. +#else + return new SQLiteAdminTools(); +#endif +} + +#include "sqlitedriver.moc" diff --git a/kexi/kexidb/drivers/sqlite/sqlitedriver.h b/kexi/kexidb/drivers/sqlite/sqlitedriver.h new file mode 100644 index 00000000..66d4d4d2 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitedriver.h @@ -0,0 +1,82 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_DRIVER_SQLITE_H +#define KEXIDB_DRIVER_SQLITE_H + +#include <qstringlist.h> + +#include <kexidb/driver.h> + +namespace KexiDB +{ + +class Connection; +class DriverManager; +class SQLiteDriverPrivate; + +//! SQLite database driver. +class SQLiteDriver : public Driver +{ + Q_OBJECT + KEXIDB_DRIVER + + public: + SQLiteDriver( QObject *parent, const char *name, const QStringList &args = QStringList() ); + virtual ~SQLiteDriver(); + + /*! \return true if \a n is a system object name; + for this driver any object with name prefixed with "sqlite_" + is considered as system object. + */ + virtual bool isSystemObjectName( const QString& n ) const; + + /*! \return false for this driver. */ + virtual bool isSystemDatabaseName( const QString& ) const { return false; } + + //! Escape a string for use as a value + virtual QString escapeString(const QString& str) const; + virtual QCString escapeString(const QCString& str) const; + + //! Escape BLOB value \a array + virtual QString escapeBLOB(const QByteArray& array) const; + + protected: + virtual QString drv_escapeIdentifier( const QString& str) const; + virtual QCString drv_escapeIdentifier( const QCString& str) const; + virtual Connection *drv_createConnection( ConnectionData &conn_data ); + virtual AdminTools* drv_createAdminTools() const; + + /*! \return true if \a n is a system field name; + for this driver fields with name equal "_ROWID_" + is considered as system field. + */ + virtual bool drv_isSystemFieldName( const QString& n ) const; + + SQLiteDriverPrivate *dp; + + private: + static const char *keywords[]; + +}; + +} + +#endif + diff --git a/kexi/kexidb/drivers/sqlite/sqlitekeywords.cpp b/kexi/kexidb/drivers/sqlite/sqlitekeywords.cpp new file mode 100644 index 00000000..a3d6095b --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitekeywords.cpp @@ -0,0 +1,39 @@ + /* + * This file has been automatically generated from + * koffice/kexi/tools/sql_keywords/sql_keywords.sh and + * ../../3rdparty/kexisql3/src/tokenize.c. + * + * Please edit the sql_keywords.sh, not this file! + */ +#include <sqlitedriver.h> + +namespace KexiDB { + const char* SQLiteDriver::keywords[] = { + "ABORT", + "ATTACH", + "CLUSTER", + "CONFLICT", + "DEFERRED", + "DEFERRABLE", + "DETACH", + "EACH", + "EXCEPT", + "FAIL", + "GLOB", + "IMMEDIATE", + "INITIALLY", + "INSTEAD", + "INTERSECT", + "ISNULL", + "NOTNULL", + "OF", + "PRAGMA", + "RAISE", + "STATEMENT", + "TEMP", + "TRIGGER", + "VACUUM", + "VIEW", + 0 + }; +} diff --git a/kexi/kexidb/drivers/sqlite/sqlitepreparedstatement.cpp b/kexi/kexidb/drivers/sqlite/sqlitepreparedstatement.cpp new file mode 100644 index 00000000..9103b131 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitepreparedstatement.cpp @@ -0,0 +1,242 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "sqlitepreparedstatement.h" + +#include <kdebug.h> +#include <assert.h> + +using namespace KexiDB; + +SQLitePreparedStatement::SQLitePreparedStatement(StatementType type, ConnectionInternal& conn, + FieldList& fields) + : KexiDB::PreparedStatement(type, conn, fields) + , SQLiteConnectionInternal(conn.connection) + , prepared_st_handle(0) + , m_resetRequired(false) +{ + data_owned = false; + data = dynamic_cast<KexiDB::SQLiteConnectionInternal&>(conn).data; //copy + + temp_st = generateStatementString(); +#ifdef SQLITE2 + //! @todo +#else + if (!temp_st.isEmpty()) { + res = sqlite3_prepare( + data, /* Database handle */ + temp_st, //const char *zSql, /* SQL statement, UTF-8 encoded */ + temp_st.length(), //int nBytes, /* Length of zSql in bytes. */ + &prepared_st_handle, //sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + 0 //const char **pzTail /* OUT: Pointer to unused portion of zSql */ + ); + if (SQLITE_OK != res) { +//! @todo copy error msg + } + } +#endif +} + +SQLitePreparedStatement::~SQLitePreparedStatement() +{ +#ifdef SQLITE2 +//! @todo +#else + sqlite3_finalize(prepared_st_handle); + prepared_st_handle = 0; +#endif +} + +bool SQLitePreparedStatement::execute() +{ +#ifdef SQLITE2 +//! @todo +#else + if (!prepared_st_handle) + return false; + if (m_resetRequired) { + res = sqlite3_reset(prepared_st_handle); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + m_resetRequired = false; + } + + int arg=1; //arg index counted from 1 + KexiDB::Field *field; + + Field::List _dummy; + Field::ListIterator itFields(_dummy); + //for INSERT, we're iterating over inserting values + //for SELECT, we're iterating over WHERE conditions + if (m_type == SelectStatement) + itFields = *m_whereFields; + else if (m_type == InsertStatement) + itFields = m_fields->fieldsIterator(); + else + assert(0); //impl. error + + for (QValueListConstIterator<QVariant> it = m_args.constBegin(); + (field = itFields.current()); ++it, ++itFields, arg++) + { + if (it==m_args.constEnd() || (*it).isNull()) {//no value to bind or the value is null: bind NULL + res = sqlite3_bind_null(prepared_st_handle, arg); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + continue; + } + if (field->isTextType()) { + //! @todo optimize: make a static copy so SQLITE_STATIC can be used + QCString utf8String((*it).toString().utf8()); + res = sqlite3_bind_text(prepared_st_handle, arg, + (const char*)utf8String, utf8String.length(), SQLITE_TRANSIENT /*??*/); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + } + else switch (field->type()) { + case KexiDB::Field::Byte: + case KexiDB::Field::ShortInteger: + case KexiDB::Field::Integer: + { +//! @todo what about unsigned > INT_MAX ? + bool ok; + const int value = (*it).toInt(&ok); + if (ok) { + res = sqlite3_bind_int(prepared_st_handle, arg, value); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + } + else { + res = sqlite3_bind_null(prepared_st_handle, arg); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + } + break; + } + case KexiDB::Field::Float: + case KexiDB::Field::Double: + res = sqlite3_bind_double(prepared_st_handle, arg, (*it).toDouble()); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + break; + case KexiDB::Field::BigInteger: + { +//! @todo what about unsigned > LLONG_MAX ? + bool ok; + Q_LLONG value = (*it).toLongLong(&ok); + if (ok) { + res = sqlite3_bind_int64(prepared_st_handle, arg, value); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + } + else { + res = sqlite3_bind_null(prepared_st_handle, arg); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + } + break; + } + case KexiDB::Field::Boolean: + res = sqlite3_bind_text(prepared_st_handle, arg, + QString::number((*it).toBool() ? 1 : 0).latin1(), + 1, SQLITE_TRANSIENT /*??*/); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + break; + case KexiDB::Field::Time: + res = sqlite3_bind_text(prepared_st_handle, arg, + (*it).toTime().toString(Qt::ISODate).latin1(), + sizeof("HH:MM:SS"), SQLITE_TRANSIENT /*??*/); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + break; + case KexiDB::Field::Date: + res = sqlite3_bind_text(prepared_st_handle, arg, + (*it).toDate().toString(Qt::ISODate).latin1(), + sizeof("YYYY-MM-DD"), SQLITE_TRANSIENT /*??*/); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + break; + case KexiDB::Field::DateTime: + res = sqlite3_bind_text(prepared_st_handle, arg, + (*it).toDateTime().toString(Qt::ISODate).latin1(), + sizeof("YYYY-MM-DDTHH:MM:SS"), SQLITE_TRANSIENT /*??*/); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + break; + case KexiDB::Field::BLOB: + { + const QByteArray byteArray((*it).toByteArray()); + res = sqlite3_bind_blob(prepared_st_handle, arg, + (const char*)byteArray, byteArray.size(), SQLITE_TRANSIENT /*??*/); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + break; + } + default: + KexiDBWarn << "PreparedStatement::execute(): unsupported field type: " + << field->type() << " - NULL value bound to column #" << arg << endl; + res = sqlite3_bind_null(prepared_st_handle, arg); + if (SQLITE_OK != res) { + //! @todo msg? + return false; + } + } //switch + } + + //real execution + res = sqlite3_step(prepared_st_handle); + m_resetRequired = true; + if (m_type == InsertStatement && res == SQLITE_DONE) { + return true; + } + if (m_type == SelectStatement) { + //fetch result + + //todo + } +#endif + return false; +} + diff --git a/kexi/kexidb/drivers/sqlite/sqlitepreparedstatement.h b/kexi/kexidb/drivers/sqlite/sqlitepreparedstatement.h new file mode 100644 index 00000000..b8b9c376 --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitepreparedstatement.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_SQLITEPREPAREDSTATEMENT_H +//&& !defined SQLITE2 +#define KEXIDB_SQLITEPREPAREDSTATEMENT_H + +#include <kexidb/preparedstatement.h> +#include "sqliteconnection_p.h" + +namespace KexiDB { + +/*! Implementation of prepared statements for SQLite driver. */ +class SQLitePreparedStatement : public PreparedStatement, SQLiteConnectionInternal +{ + public: + SQLitePreparedStatement(StatementType type, ConnectionInternal& conn, + FieldList& fields); + + virtual ~SQLitePreparedStatement(); + + virtual bool execute(); + +#ifdef SQLITE2 + sqlite_vm *prepared_st_handle; +#else //SQLITE3 + sqlite3_stmt *prepared_st_handle; +#endif + bool m_resetRequired : 1; +}; + +} + +#endif diff --git a/kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp b/kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp new file mode 100644 index 00000000..adf8709a --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp @@ -0,0 +1,150 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <kexidb/global.h> +#include "sqlitevacuum.h" + +#include <kstandarddirs.h> +#include <kprogress.h> +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <kmessagebox.h> +#include <kio/global.h> + +#include <qfileinfo.h> +#include <qdir.h> +#include <qapplication.h> +#include <qprocess.h> +#include <qcursor.h> + +#include <unistd.h> + +SQLiteVacuum::SQLiteVacuum(const QString& filePath) +: m_filePath(filePath) +{ + m_process = 0; + m_percent = 0; + m_dlg = 0; + m_result = true; +} + +SQLiteVacuum::~SQLiteVacuum() +{ + delete m_process; + if (m_dlg) + m_dlg->close(); + delete m_dlg; +} + +tristate SQLiteVacuum::run() +{ + const QString ksqlite_app = KStandardDirs::findExe( "ksqlite" ); + if (ksqlite_app.isEmpty()) { + m_result = false; + return m_result; + } + QFileInfo fi(m_filePath); + if (!fi.isReadable()) { + KexiDBDrvWarn << "SQLiteVacuum::run(): No such file" << m_filePath << endl; + return false; + } + const uint origSize = fi.size(); + + QStringList args; + args << ksqlite_app << "-verbose-vacuum" << m_filePath << "vacuum"; + m_process = new QProcess(args, this, "process"); + m_process->setWorkingDirectory( QFileInfo(m_filePath).dir(true) ); + connect( m_process, SIGNAL(readyReadStdout()), this, SLOT(readFromStdout()) ); + connect( m_process, SIGNAL(processExited()), this, SLOT(processExited()) ); + if (!m_process->start()) { + m_result = false; + return m_result; + } + m_dlg = new KProgressDialog(0, 0, i18n("Compacting database"), + "<qt>"+i18n("Compacting database \"%1\"...") + .arg("<nobr>"+QDir::convertSeparators(QFileInfo(m_filePath).fileName())+"</nobr>") + ); + m_dlg->adjustSize(); + m_dlg->resize(300, m_dlg->height()); + connect(m_dlg, SIGNAL(cancelClicked()), this, SLOT(cancelClicked())); + m_dlg->setMinimumDuration(1000); + m_dlg->setAutoClose(true); + m_dlg->progressBar()->setTotalSteps(100); + m_dlg->exec(); + while (m_process->isRunning()) { + readFromStdout(); + usleep(50000); + } + delete m_process; + m_process = 0; + if (m_result == true) { + const uint newSize = QFileInfo(m_filePath).size(); + const uint decrease = 100-100*newSize/origSize; + KMessageBox::information(0, i18n("The database has been compacted. Current size decreased by %1% to %2.") + .arg(decrease).arg(KIO::convertSize(newSize))); + } + return m_result; +} + +void SQLiteVacuum::readFromStdout() +{ + while (true) { + QString s( m_process->readLineStdout() ); //readStdout(); + if (s.isEmpty()) + break; + m_dlg->progressBar()->setProgress(m_percent); +// KexiDBDrvDbg << m_percent << " " << s << endl; + if (s.startsWith("VACUUM: ")) { + //set previously known progress + m_dlg->progressBar()->setProgress(m_percent); + //update progress info + if (s.mid(8,4)=="100%") { + m_percent = 100; + m_dlg->setAllowCancel(false); + m_dlg->setCursor(QCursor(Qt::WaitCursor)); + } + else if (s.mid(9,1)=="%") { + m_percent = s.mid(8,1).toInt(); + } + else if (s.mid(10,1)=="%") { + m_percent = s.mid(8,2).toInt(); + } + m_process->writeToStdin(" "); + } + } +} + +void SQLiteVacuum::processExited() +{ +// KexiDBDrvDbg << sender()->name() << " EXIT" << endl; + m_dlg->close(); + delete m_dlg; + m_dlg = 0; +} + +void SQLiteVacuum::cancelClicked() +{ + if (!m_process->normalExit()) { + m_process->writeToStdin("q"); //quit + m_result = cancelled; + } +} + +#include "sqlitevacuum.moc" diff --git a/kexi/kexidb/drivers/sqlite/sqlitevacuum.h b/kexi/kexidb/drivers/sqlite/sqlitevacuum.h new file mode 100644 index 00000000..4424b7fc --- /dev/null +++ b/kexi/kexidb/drivers/sqlite/sqlitevacuum.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef SQLITE_VACUUM_H +#define SQLITE_VACUUM_H + +#include <qobject.h> +#include <qstring.h> + +#include <kexiutils/tristate.h> + +class QProcess; +class KTempFile; +class KProgressDialog; + +//! @short Helper class performing interactive compacting (VACUUM) of the SQLite database +/*! Proved SQLite database filename in the constructor. + Then execute run() should be executed. + + KProgressDialog will be displayed. Its progress bar will be updated whenever another + table's data compacting is performed. User can click "Cancel" button in any time + (except the final committing) to cancel the operation. In this case, + it's guaranteed that the original file remains unchanged. + + This is possible because we rely on SQLite's VACUUM SQL command, which itself temporarily + creates a copy of the original database file, and replaces the orginal with the new only + on success. +*/ +class SQLiteVacuum : public QObject +{ + Q_OBJECT + public: + SQLiteVacuum(const QString& filePath); + ~SQLiteVacuum(); + + /*! Performs compacting procedure. + \return true on success, false on failure and cancelled if user + clicked "Cancel" button in the progress dialog. */ + tristate run(); + + public slots: + void readFromStdout(); + void processExited(); + void cancelClicked(); + + protected: + QString m_filePath; + QProcess *m_process; + KProgressDialog* m_dlg; + int m_percent; + tristate m_result; +}; + +#endif |