From 68354c7d41085d1f976a5b1d7ee542479a85f621 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 13 Feb 2010 09:56:58 +0100 Subject: imported trunk from sourceforge SVN --- src/GNUmakefile | 47 ++++++ src/Makefile.W32 | 64 ++++++++ src/connection.cpp | 175 ++++++++++++++++++++++ src/except.cpp | 52 +++++++ src/port/sleep.c | 43 ++++++ src/port/sleep.h | 36 +++++ src/port/sqlite.h | 32 ++++ src/port/sqlite3xx.cpp | 42 ++++++ src/port/string.c | 43 ++++++ src/port/string.h | 57 ++++++++ src/port/sys_internal.h | 136 +++++++++++++++++ src/port/unused.h | 40 +++++ src/prepared_statement.cpp | 221 ++++++++++++++++++++++++++++ src/result.cpp | 354 +++++++++++++++++++++++++++++++++++++++++++++ src/transaction.cpp | 142 ++++++++++++++++++ 15 files changed, 1484 insertions(+) create mode 100644 src/GNUmakefile create mode 100644 src/Makefile.W32 create mode 100644 src/connection.cpp create mode 100644 src/except.cpp create mode 100644 src/port/sleep.c create mode 100644 src/port/sleep.h create mode 100644 src/port/sqlite.h create mode 100755 src/port/sqlite3xx.cpp create mode 100644 src/port/string.c create mode 100644 src/port/string.h create mode 100644 src/port/sys_internal.h create mode 100644 src/port/unused.h create mode 100644 src/prepared_statement.cpp create mode 100644 src/result.cpp create mode 100644 src/transaction.cpp (limited to 'src') diff --git a/src/GNUmakefile b/src/GNUmakefile new file mode 100644 index 0000000..ce527be --- /dev/null +++ b/src/GNUmakefile @@ -0,0 +1,47 @@ +TOPDIR = .. + +SUBDIRS = + +INCLUDE_DIRS = \ + $(INCLUDE_FLAGS_SQLITE3) \ + -I$(TOPDIR)/include \ + -I. + +INCLUDE_LDFLAGS = \ + $(LDFLAGS_SQLITE3) + +INCLUDE_LIBS = \ + $(LIBS_SQLITE3) + +PORT_OBJS = \ + port/string.o \ + port/sleep.o + +OBJS = \ + $(PORT_OBJS) + +CPP_OBJS = \ + connection.o \ + prepared_statement.o \ + transaction.o \ + result.o \ + except.o + +STATIC_LIB = libsqlite3xx.a + +DYNAMIC_LIB = libsqlite3xx.so +DYNAMIC_LIB_MAJOR = 0 +DYNAMIC_LIB_MINOR = 0 +DYNAMIC_LIB_PATCH = 0 + +-include $(TOPDIR)/makefiles/gmake/sub.mk + +local_all: + +local_clean: + +local_distclean: + +local_install: + +local_uninstall: diff --git a/src/Makefile.W32 b/src/Makefile.W32 new file mode 100644 index 0000000..a16a0f5 --- /dev/null +++ b/src/Makefile.W32 @@ -0,0 +1,64 @@ +TOPDIR = .. + +SUBDIRS = + +INCLUDE_DIRS = \ + /I$(TOPDIR)\include /I. \ + /I$(TOPDIR)\sqlite-3.6.22 \ + /D_WIN32_WINNT=0x400 /I"$(PLATFORM_SDK_DIR)\Include" + +INCLUDE_LDFLAGS = \ + /LIBPATH:"$(PLATFORM_SDK_DIR)\Lib" + +INCLUDE_LIBS = \ + $(TOPDIR)\sqlite-3.6.22\sqlite3.lib + +BINS = + +LIBRARIES = \ + sqlite3xx.lib \ + sqlite3xx.dll + +PORT_OBJS = \ + port\string.obj \ + port\sleep.obj + +OBJS = \ + $(PORT_OBJS) \ + connection.obj \ + prepared_statement.obj \ + transaction.obj \ + result.obj \ + except.obj + +DLL_PORT_OBJS = \ + port\string.dllobj \ + port\sleep.dllobj + +DLL_OBJS = \ + $(DLL_PORT_OBJS) \ + connection.dllobj \ + prepared_statement.dllobj \ + transaction.dllobj \ + result.dllobj \ + except.dllobj \ + port\sqlite3xx.dllobj + +local_all: $(LIBRARIES) + +!INCLUDE $(TOPDIR)\makefiles\nmake\sub.mk + +sqlite3xx.lib: $(OBJS) + $(LINK) /lib /nologo /out:$@ $(LDFLAGS) $(LIBS) $? + +sqlite3xx.dll: $(DLL_OBJS) + $(LINK) /DLL /nologo /out:$@ $(LDFLAGS) $(LIBS) $? + +local_all: + +local_clean: + @-erase $(LIBRARIES) 2>NUL + +local_distclean: + +local_test: diff --git a/src/connection.cpp b/src/connection.cpp new file mode 100644 index 0000000..ceb5d3e --- /dev/null +++ b/src/connection.cpp @@ -0,0 +1,175 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#include "sqlite3xx/connection.hpp" +#include "sqlite3xx/except.hpp" + +#include "port/sqlite.h" +#include "port/unused.h" + +#include +#include +#include + +#include + +namespace sqlite3xx { + +connection::connection( ) { + db = NULL; + _internal_tno = 0; + _trace = false; +} + +connection::connection( string filename ) : + _filename( filename) { + _internal_tno = 0; + _trace = false; + sqlite3_open( filename.c_str( ), &db ); +} + +connection::connection( const connection& c ) { + db = c.db; + prepared_stmts = c.prepared_stmts; + _internal_tno = 0; + _trace = false; +} + +connection::~connection( ) { + assert( db != NULL ); + sqlite3_close( db ); +} + +extern "C" void profiling_callback( void *a, const char *b, sqlite3_uint64 c ) { + SQLITEXX_UNUSED( a ); + cout << "TRACE: " << b << " (time: " << c / 1000 << " ms)" << endl; +} + +void connection::trace( bool __trace ) { + _trace = __trace; + if( _trace ) { + sqlite3_profile( db, profiling_callback, NULL ); + } else { + sqlite3_profile( db, NULL, NULL ); + } +} + +ostream& operator<<( ostream& o, const connection& c ) { + o << "sqlite3xx::connection(" << c._filename << ")"; + return o; +} + +result connection::exec( string sql ) { + ostringstream s; + s << "internal_" << *this << "_" << ++_internal_tno; + prepare::declaration decl = prepare( s.str( ), sql ); + return prepared_exec( s.str( ) ); +} + +result connection::prepared_exec( const string& name ) { + PSMap::iterator it; + prepared_stmt* stmt = find_prepared( name ); + if( _trace ) cout << "TRACE: exec '" << name << "'" << endl; + return result( stmt->getStmt( ) ); + // TODO: remove the prepared statement again! +} + +prepared_stmt* connection::find_prepared( const string& name ) { + PSMap::iterator it; + it = prepared_stmts.find( name ); + if( it == prepared_stmts.end( ) ) { + throw invalid_argument( "Unknown prepared statement '" + name + "'" ); + } + return it->second; +} + +prepare::declaration connection::prepare( const string& name, string sql ) { + PSMap::iterator it; + it = prepared_stmts.find( name ); + if( it != prepared_stmts.end( ) ) { + if( it->second->sql( ) == sql ) { + ostringstream s; + s << "Inconsistent redefinition " + "of prepared statement " << name + << "(SQL: " << sql << ")"; + throw new invalid_argument( s.str( ) ); + } + } else { + prepared_stmts.insert( make_pair( name, + new prepared_stmt( this->db, sql.c_str( ) ) ) ); + } + if( _trace ) cout << "TRACE: prepared '" << name << "' as '" << sql << "'" << endl; + return prepare::declaration( *this, name ); +} + +void connection::prepare_param_declare( const string& name, const string& sqltype, prepare::param_treatment& treatment ) { + prepared_stmt* stmt = find_prepared( name ); + stmt->addparam( sqltype, treatment ); +} + +void connection::unprepare( string name ) { + prepared_stmt* stmt = find_prepared( name ); + delete stmt; + prepared_stmts.erase( name ); +} + +void connection::prepared_reset( const string& name ) { + prepared_stmt* stmt = find_prepared( name ); + stmt->reset( ); +} + +void connection::prepare_setparam( const string& name, const int pos, const int value ) { + prepared_stmt* stmt = find_prepared( name ); + if( _trace ) cout << "TRACE: prepared '" << name << "' param '" << + pos << "'" << " value: " << value << endl; + stmt->setparam( pos, value ); +} + +void connection::prepare_setparam( const string& name, const int pos, const char* value ) { + prepared_stmt* stmt = find_prepared( name ); + if( _trace ) cout << "TRACE: prepared '" << name << "' param '" << + pos << "'" << " value: " << value << endl; + stmt->setparam( pos, value ); +} + +void connection::prepare_setparam( const string& name, const int pos, const double value ) { + prepared_stmt* stmt = find_prepared( name ); + if( _trace ) cout << "TRACE: prepared '" << name << "' param '" << + pos << "'" << " value: " << value << endl; + stmt->setparam( pos, value ); +} + +void connection::prepare_setparam( const string& name, const int pos, const long value ) { + prepared_stmt* stmt = find_prepared( name ); + if( _trace ) cout << "TRACE: prepared '" << name << "' param '" << + pos << "'" << " value: " << value << endl; + stmt->setparam( pos, value ); +} + +void connection::prepare_setparam( const string& name, const int pos, const uintmax_t value ) { + prepared_stmt* stmt = find_prepared( name ); + if( _trace ) cout << "TRACE: prepared '" << name << "' param '" << + pos << "'" << " value: " << value << endl; + stmt->setparam( pos, value ); +} + +} /* namespace sqlite3xx */ diff --git a/src/except.cpp b/src/except.cpp new file mode 100644 index 0000000..cda8c7f --- /dev/null +++ b/src/except.cpp @@ -0,0 +1,52 @@ +/* + sqlite3xx - sqlite3 C++ layer, following ideas of libpqxx + Copyright (C) 2009 Andreas Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "sqlite3xx/except.hpp" + +namespace sqlite3xx { + +sql_error::sql_error( ) : + runtime_error( "failed query" ), + _m( ), + _q( ) { +} + +sql_error::sql_error( string& __m ) : + runtime_error( __m ), + _m( __m ), + _q( ) { +} + +sql_error::sql_error( string& __m, string& q ) : + runtime_error( __m ), + _m( __m ), + _q( q ) { +} + +sql_error::~sql_error( ) throw( ){ +} + +const string& sql_error::msg( ) const throw( ) { + return _m; +} + +const string& sql_error::query( ) const throw( ) { + return _q; +} + +} /* namespace sqlite3xx */ diff --git a/src/port/sleep.c b/src/port/sleep.c new file mode 100644 index 0000000..4ab1966 --- /dev/null +++ b/src/port/sleep.c @@ -0,0 +1,43 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#include "port/sleep.h" + +/** + * There are different sleep functions on POSIX and WIN32 systems. + */ + +#ifdef _WIN32 +#define WIN32_MEAN_AND_LEAN +#include /* for Sleep */ +#else +#include /* for sleep */ +#endif + +void sqlitexx_port_sleep( const unsigned int seconds ) { +#ifdef _WIN32 + Sleep( seconds * 1000 ); +#else + sleep( seconds ); +#endif +} + diff --git a/src/port/sleep.h b/src/port/sleep.h new file mode 100644 index 0000000..8320f43 --- /dev/null +++ b/src/port/sleep.h @@ -0,0 +1,36 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#ifndef __PORT_SLEEP_H +#define __PORT_SLEEP_H + +#ifdef __cplusplus +extern "C" { +#endif + +void sqlitexx_port_sleep( const unsigned int seconds ); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef __PORT_SLEEP_H */ diff --git a/src/port/sqlite.h b/src/port/sqlite.h new file mode 100644 index 0000000..23bdc86 --- /dev/null +++ b/src/port/sqlite.h @@ -0,0 +1,32 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#ifndef __PORT_SQLITE_H +#define __PORT_SQLITE_H + +/* sqlite3_xxx since 3.5.0 */ +#if SQLITE_VERSION_NUMBER < 3005000 +typedef sqlite_int64 sqlite3_int64; +typedef sqlite_uint64 sqlite3_uint64; +#endif + +#endif /* ifndef __PORT_SQLITE_H */ diff --git a/src/port/sqlite3xx.cpp b/src/port/sqlite3xx.cpp new file mode 100755 index 0000000..384a1d4 --- /dev/null +++ b/src/port/sqlite3xx.cpp @@ -0,0 +1,42 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#ifdef _WIN32 + +#ifdef BUILD_SHARED + +/* entry point for DLL on Windows */ + +#define WIN32_LEAN_AND_MEAN +#include + +BOOL APIENTRY DllMain( HANDLE, DWORD, LPVOID ) { + return TRUE; +} + +#else +#error DLL entry poing makesonly sense for a DLL, not for a static library! +#endif /* defined BUILD_SHARED */ + +#else +#error DLL building makes no sence on a non-Windows platform! +#endif /* !defined _WIN32 */ diff --git a/src/port/string.c b/src/port/string.c new file mode 100644 index 0000000..e561ebb --- /dev/null +++ b/src/port/string.c @@ -0,0 +1,43 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#include "port/string.h" + +/** + * strdup is not always part of the C library or they need insane flags to + * be set which would enable too many non-standard things. + */ + +#if !defined HAVE_STRDUP || defined TEST_STRDUP + +#include /* for malloc, NULL */ + +char *sqlitexx_port_strdup( const char *s ) { + char *d; + d = (char *)malloc( strlen( s ) + 1 ); + if( d == NULL ) return NULL; + strcpy( d, s ); + return d; +} + +#endif /* !defined HAVE_STRDUP || defined TEST_STRDUP */ + diff --git a/src/port/string.h b/src/port/string.h new file mode 100644 index 0000000..2b4668b --- /dev/null +++ b/src/port/string.h @@ -0,0 +1,57 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#ifndef __PORT_STRING_H +#define __PORT_STRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "port/sys_internal.h" + +#include + +/* on some platforms we must also include the old strings.h to get + * functions like str(n)casecmp */ +#ifdef HAVE_STRINGS_H +#include /* for strcasecmp and strncasecmp */ +#endif + +#include /* for size_t */ + +#if defined _WIN32 +#include /* for memcpy */ +#endif + +#if !defined HAVE_STRDUP || defined TEST_STRDUP +char *sqlitexx_port_strdup( const char *s ); +#endif +#if !defined HAVE_STRDUP +#define strdup sqlitexx_port_strdup +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef __PORT_STRING_H */ diff --git a/src/port/sys_internal.h b/src/port/sys_internal.h new file mode 100644 index 0000000..5b6a0b2 --- /dev/null +++ b/src/port/sys_internal.h @@ -0,0 +1,136 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#ifndef SQLITE3XX_SYS_INTERNAL_H +#define SQLITE3XX_SYS_INTERNAL_H + +#if defined LINUX +#if OS_MAJOR_VERSION == 2 +#if OS_MINOR_VERSION == 6 +#define _XOPEN_SOURCE 600 +#define HAVE_STRDUP +#else + #error unknown platform +#endif /* defined OS_MINOR_VERSION == 6 */ +#else + #error unknown platform +#endif /* defined OS_MAJOR_VERSION == 2 */ +#endif /* defined LINUX */ + +#if defined FREEBSD +#if OS_MAJOR_VERSION == 7 +#if OS_MINOR_VERSION >= 0 && OS_MINOR_VERSION <= 2 +#define _XOPEN_SOURCE 600 +#define HAVE_STRDUP +#else + #error unknown platform +#endif /* defined OS_MINOR_VERSION >= 0 && OS_MINOR_VERSION <= 2 */ +#else +#if OS_MAJOR_VERSION == 6 +#if OS_MINOR_VERSION == 2 +#define _XOPEN_SOURCE 600 +#define HAVE_STRDUP +#else + #error unknown platform +#endif /* defined OS_MINOR_VERSION == 2 */ +#else + #error unknown platform +#endif /* defined OS_MAJOR_VERSION == 6 */ +#endif /* defined OS_MAJOR_VERSION == 7 */ +#endif /* defined FREEBSD */ + +#if defined OPENBSD +#if OS_MAJOR_VERSION == 4 +#if OS_MINOR_VERSION >= 2 && OS_MINOR_VERSION <= 5 +#define _XOPEN_SOURCE 600 +#define HAVE_STRDUP +#else + #error unknown platform +#endif /* defined OS_MINOR_VERSION >= 2 && OS_MINOR_VERSION <= 3 */ +#else + #error unknown platform +#endif /* defined OS_MAJOR_VERSION == 4 */ +#endif /* defined OPENBSD */ + +#if defined NETBSD +#if OS_MAJOR_VERSION == 5 +#if OS_MINOR_VERSION == 0 +/* don't enable XOPEN_SOURCE here, we get compatibility problems + * with libstdc++: vgwscanf undefined + */ +#define HAVE_STRDUP +#else + #error unknown platform +#endif /* defined OS_MINOR_VERSION == 2 */ +#else /* defined OS_MAJOR_VERSION == 4 */ + +#if OS_MAJOR_VERSION == 4 +#if OS_MINOR_VERSION == 0 +#define _XOPEN_SOURCE 600 +#define HAVE_STRDUP +#else + #error unknown platform +#endif /* defined OS_MINOR_VERSION == 2 */ +#else + #error unknown platform +#endif /* defined OS_MAJOR_VERSION == 4 */ +#endif /* defined OS_MAJOR_VERSION == 5 */ +#endif /* defined NETBSD */ + +#if defined SUNOS +#if OS_MAJOR_VERSION == 5 +#if OS_MINOR_VERSION == 8 +#if !defined __cplusplus +#define _XOPEN_SOURCE 600 +#define __EXTENSIONS__ +#endif +#define HAVE_STRDUP +#else +#if OS_MINOR_VERSION == 10 +#if !defined __cplusplus +#define _XOPEN_SOURCE 600 +#define __EXTENSIONS__ +#endif +#define HAVE_STRDUP +#else + #error unknown platform +#endif /* OS_MINOR_VERSION == 10 */ +#endif /* OS_MINOR_VERSION == 8 */ +#else + #error unknown platform +#endif /* OS_MAJOR_VERSION == 5 */ +#endif /* defined SUNOS */ + +#if defined CYGWIN +#if OS_MAJOR_VERSION == 5 +#if OS_MINOR_VERSION >= 0 && OS_MINOR_VERSION <= 1 +#define _XOPEN_SOURCE 600 +#else + #error unknown platform +#endif /* OS_MINOR_VERSION >= 0 && OS_MINOR_VERSION <= 1 */ +#else + #error unknown platform +#endif /* OS_MAJOR_VERSION == 5 */ +#endif /* defined CYGWIN */ + +#endif /* ifndef SQLITE3XX_SYS_INTERNAL_H */ diff --git a/src/port/unused.h b/src/port/unused.h new file mode 100644 index 0000000..241ae2d --- /dev/null +++ b/src/port/unused.h @@ -0,0 +1,40 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +/* mark variables as being unused */ + +#ifndef __PORT_UNUSED_H +#define __PORT_UNUSED_H + +/** + * @brief Macro to avoid unused parameter messages in functions + * + * We could use __attribute__(unused) but I run into some problems + * on OpenBSD 4.5 with it.. + */ +#ifdef __GNUC__ +#define SQLITEXX_UNUSED( x ) if( 0 && (x) ) { } +#else +#define SQLITEXX_UNUSED( x ) { } +#endif + +#endif /* ifndef __PORT_UNUSED_H */ diff --git a/src/prepared_statement.cpp b/src/prepared_statement.cpp new file mode 100644 index 0000000..4843a86 --- /dev/null +++ b/src/prepared_statement.cpp @@ -0,0 +1,221 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#include "sqlite3xx/prepared_statement.hpp" + +#include +#include +#include +#include + +#include "sqlite3xx/except.hpp" +#include "sqlite3xx/connection.hpp" + +#include "port/unused.h" + +namespace sqlite3xx { + +/* declaration */ + +prepare::declaration::declaration( connection& c, const string& stmt ) : + _c( c ), + _stmt( stmt ) { +} + +const prepare::declaration& +prepare::declaration::operator( )( const string& sql_type, param_treatment treat ) const { + _c.prepare_param_declare( _stmt, sql_type, treat ); + return *this; +} + +/* invocation */ + +prepare::invocation::invocation( connection& c, transaction& t, const string& stmt ) : + _c( c ), + _t( t ), + _stmt( stmt ) { + _c.prepared_reset( _stmt ); + _pos = 0; +} + +result prepare::invocation::exec( ) const { + return _c.prepared_exec( _stmt ); +} + +prepare::invocation& prepare::invocation::setparam( const int& value, bool nonnull ) { + SQLITEXX_UNUSED( nonnull ); + _pos++; + _c.prepare_setparam( _stmt, _pos, value ); + return *this; +} + +prepare::invocation& prepare::invocation::setparam( const char* value, bool nonnull ) { + SQLITEXX_UNUSED( nonnull ); + _pos++; + _c.prepare_setparam( _stmt, _pos, value ); + return *this; +} + +prepare::invocation& prepare::invocation::setparam( const string& value, bool nonnull ) { + SQLITEXX_UNUSED( nonnull ); + _pos++; + _c.prepare_setparam( _stmt, _pos, value.c_str( ) ); + return *this; +} + +prepare::invocation& prepare::invocation::setparam( const double& value, bool nonnull ) { + SQLITEXX_UNUSED( nonnull ); + _pos++; + _c.prepare_setparam( _stmt, _pos, value ); + return *this; +} + +prepare::invocation& prepare::invocation::setparam( const long& value, bool nonnull ) { + SQLITEXX_UNUSED( nonnull ); + _pos++; + _c.prepare_setparam( _stmt, _pos, value ); + return *this; +} + +prepare::invocation& prepare::invocation::setparam( const uintmax_t& value, bool nonnull ) { + SQLITEXX_UNUSED( nonnull ); + _pos++; + _c.prepare_setparam( _stmt, _pos, value ); + return *this; +} + +/* prepared statement */ + +prepared_stmt::prepared_stmt( sqlite3 *db, string __sql ) : + _db( db ) { + int rc; + const char *tail; + _sql = __sql; +#if SQLITE_VERSION_NUMBER >= 3005000 + rc = sqlite3_prepare_v2( db, _sql.c_str( ), -1, &_stmt, &tail ); +#else + rc = sqlite3_prepare( db, _sql.c_str( ), -1, &_stmt, &tail ); +#endif + if( rc != SQLITE_OK ) { + ostringstream s; + s << "sqlite3::prepared_stmt::prepared_stmt error: " << sqlite3_errmsg( _db ); + string msg = s.str( ); + throw sql_error( msg, _sql ); + } +} + +prepared_stmt::~prepared_stmt( ) { + assert( _stmt != NULL ); + sqlite3_finalize( _stmt ); + _stmt = NULL; +} + +string prepared_stmt::sql( ) { + return _sql; +} + +void prepared_stmt::addparam( const string& sqltype, prepare::param_treatment& treatment ) { + parameters.push_back( param( sqltype, treatment ) ); +} + +void prepared_stmt::reset( ) { + int rc; + assert( _stmt != NULL ); + rc = sqlite3_reset( _stmt ); + if( rc != SQLITE_OK ) { + ostringstream s; + s << "sqlite3::prepared_stmt::reset error: " << sqlite3_errmsg( _db ); + string msg = s.str( ); + throw sql_error( msg, _sql ); + } +} + +void prepared_stmt::setparam( const int pos, const int v ) { + int rc; + assert( _stmt != NULL ); + rc = sqlite3_bind_int( _stmt, pos, v ); + if( rc != SQLITE_OK ) { + ostringstream s; + s << "sqlite3::prepared_stmt::setparam bind error: " + << sqlite3_errmsg( _db ) << " (rc: " << rc << ")"; + string msg = s.str( ); + throw sql_error( msg, _sql ); + } +} + +void prepared_stmt::setparam( const int pos, const char* v ) { + int rc; + assert( _stmt != NULL ); + rc = sqlite3_bind_text( _stmt, pos, v, strlen( v ), SQLITE_STATIC ); + if( rc != SQLITE_OK ) { + ostringstream s; + s << "sqlite3::prepared_stmt::setparam bind error: " + << sqlite3_errmsg( _db ) << " (rc: " << rc << ")"; + string msg = s.str( ); + throw sql_error( msg, _sql ); + } +} + +void prepared_stmt::setparam( const int pos, const double v ) { + int rc; + assert( _stmt != NULL ); + rc = sqlite3_bind_double( _stmt, pos, v ); + if( rc != SQLITE_OK ) { + ostringstream s; + s << "sqlite3::prepared_stmt::setparam bind error: " + << sqlite3_errmsg( _db ) << " (rc: " << rc << ")"; + string msg = s.str( ); + throw sql_error( msg, _sql ); + } +} + +void prepared_stmt::setparam( const int pos, const long v ) { + int rc; + assert( _stmt != NULL ); + rc = sqlite3_bind_int64( _stmt, pos, v ); + if( rc != SQLITE_OK ) { + ostringstream s; + s << "sqlite3::prepared_stmt::setparam bind error: " + << sqlite3_errmsg( _db ) << " (rc: " << rc << ")"; + string msg = s.str( ); + throw sql_error( msg, _sql ); + } +} + +void prepared_stmt::setparam( const int pos, const uintmax_t v ) { + int rc; + assert( _stmt != NULL ); + rc = sqlite3_bind_int64( _stmt, pos, v ); + if( rc != SQLITE_OK ) { + ostringstream s; + s << "sqlite3::prepared_stmt::setparam bind error: " + << sqlite3_errmsg( _db ) << " (rc: " << rc << ")"; + string msg = s.str( ); + throw sql_error( msg, _sql ); + } +} + +sqlite3_stmt* prepared_stmt::getStmt( ) const { + return _stmt; +} + +} /* namespace sqlite3xx */ diff --git a/src/result.cpp b/src/result.cpp new file mode 100644 index 0000000..80406af --- /dev/null +++ b/src/result.cpp @@ -0,0 +1,354 @@ +/* + sqlite3xx - sqlite3 C++ layer, following ideas of libpqxx + Copyright (C) 2009 Andreas Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "port/string.h" + +#include "sqlite3xx/result.hpp" + +#include +#include +#include + +#include +#include + +#include "sqlite3xx/except.hpp" + +#include "port/unused.h" +#include "port/sleep.h" + +using namespace std; + +namespace sqlite3xx { + +/* field */ + +ostream& operator<<( ostream& o, const result::field& f ) { + int type = f._t.column_type( f._c ); + switch( type ) { + case SQLITE_INTEGER: { + int value = f._t.GetValueInt( f._c ); + o << value; + } + break; + + case SQLITE3_TEXT: { + const unsigned char* value = f._t.GetValueText( f._c ); + o << value; + } + break; + + case SQLITE_FLOAT: { + double value = f._t.GetValueDouble( f._c ); + o << value; + } + break; + + case SQLITE_NULL: + o << "(NULL)"; + break; + + default: + o << "(Unknown type " << type << "!)"; + } + return o; +} + +/* result */ + +void result::Step( ) { + int rc; + +TRY_AGAIN_STEP: + rc = sqlite3_step( _stmt ); + switch( rc ) { + case SQLITE_DONE: + if( _crow == 0 ) { + /* never had a result, will never read-ahead and buffer */ + _status = st_nodata; + } else { + /* last row */ + _status = st_lastrow; + _crow++; + } + break; + + case SQLITE_ROW: + _status = st_hasdata; + _crow++; + break; + + /* hotfix for ticket #13 */ + case SQLITE_BUSY: + sqlitexx_port_sleep( 1 ); + goto TRY_AGAIN_STEP; + + default: { + ostringstream s; + s << "Illegal state after sqlite3_step (sqlite code: " << rc << ")"; + string str = s.str( ); + string msg = sqlite3_errmsg( sqlite3_db_handle( _stmt ) ); + throw sql_error( str, msg ); + } + } +} + +void result::BufferData( ) { + const char *tmp = NULL; + + _cache.resize( columns( ) + 1 ); + + for( size_type i = 0; i < columns( ); i++ ) { + CachedValue value; + value.type = column_type( i ); + switch( value.type ) { + case SQLITE_INTEGER: + value.value.i = sqlite3_column_int( _stmt, i ); + break; + + case SQLITE3_TEXT: + /* KLUDGE: the documentation is not specific about returning NULL here (but + * it happens, see ticket #16). I get the feeling this is undefined behaviour, + * so people should not relly on it from outside. */ + tmp = (const char *)sqlite3_column_text( _stmt, i ); + if( tmp != NULL ) { + value.value.s = (unsigned char *)strdup( tmp ); + } else { + value.type = SQLITE_NULL; + value.value.s = NULL; + } + break; + + case SQLITE_FLOAT: + value.value.d = sqlite3_column_double( _stmt, i ); + break; + + case SQLITE_NULL: + value.value.s = NULL; + break; + + default: { + ostringstream s; + s << "Illegal type " << value.type << " when buffering row " << _row; + string msg = s.str( ); + throw logic_error( msg ); + } + } + _cache.at( i ) = value; + } +} + +void result::FillColNameMap( ) { + size_type i; + const char* colname; + for( i = 0; i < columns( ); i++ ) { + colname = sqlite3_column_name( _stmt, i ); + _colmap.insert( make_pair( string( colname ), i ) ); + } +} + +void result::FillColTypeMap( ) { + size_type i; + _coltype.resize( columns( ) + 1 ); + for( i = 0; i < columns( ); i++ ) { + _coltype.at( i ) = sqlite3_column_type( _stmt, i ); + } +} + +result::result( const result& r ) { + _stmt = r._stmt; + _status = r._status; + _colmap = r._colmap; + _coltype = r._coltype; + _cache = r._cache; + _row = r._row; + _crow = r._crow; +} + + +result::result( sqlite3_stmt* stmt ) { + _stmt = stmt; + _status = st_nascent; + _row = 0; + _crow = 0; + Step( ); + if( _status == st_hasdata || _status == st_lastrow ) { + FillColNameMap( ); + FillColTypeMap( ); + BufferData( ); + Step( ); + } +} + +result::~result( ) { + switch( _status ) { + case st_nascent: /* created no result fetched, weird, but ok */ + break; + + case st_nodata: + /* a command without result data, just reset the prepared statement + * so it can be used again for the next set of parameters + */ + break; + + case st_lastrow: + break; + + case st_hasdata: + case st_nomoredata: + /* TODO: free row table data */ + break; + + default: + throw logic_error( "Illegal state in destructor" ); + } +} + +result& result::operator=( const result& r ) { + if( this != &r ) { + _stmt = r._stmt; + _status = r._status; + _colmap = r._colmap; + _coltype = r._coltype; + _cache = r._cache; + _row = r._row; + _crow = r._crow; + } + return *this; +} + +result::size_type result::affected_rows( ) const { + switch( _status ) { + case st_nascent: + throw logic_error( "Called affected_rows() before sqlite3_step!" ); + + case st_nodata: + return sqlite3_changes( sqlite3_db_handle( _stmt ) ); + + case st_hasdata: + case st_lastrow: + case st_nomoredata: + throw logic_error( "Affected rows doesn't make sence in a query!" ); + + default: + throw logic_error( "Illegal state in affected_rows()" ); + } +} + +result::size_type result::size( ) const { + /* TODO: can we get this value with sqlite3_step, I doubt! + * at least we know from the state wheter it's 0 or 1 and more + */ + switch( _status ) { + case st_nascent: + throw logic_error( "Called size() before sqlite3_step!" ); + + case st_nodata: + return 0; + + case st_hasdata: + /* ..but we know that the result must be at least _row */ + return _row + 2; + + case st_nomoredata: + return _row + 1; + + case st_lastrow: + return _row + 1; + + default: + throw logic_error( "Illegal state in size()" ); + } +} + +result::size_type result::columns( ) const { + return sqlite3_column_count( _stmt ); +} + +const result::tuple result::operator[]( result::size_type i ) throw( ) { + if( _row == i ) { + return tuple( this, i ); + } else if( i == _row + 1 ) { + _row++; + BufferData( ); + Step( ); + return tuple( this, i ); + } else { + throw logic_error( "Illegal access outside scope of cursor!" ); + } +} + +result::size_type result::column_number( string name ) const { + ColMap::const_iterator it = _colmap.find( name ); + if( it == _colmap.end( ) ) { + throw invalid_argument( "Unknown column '" + name + "' requested" ); + } + return it->second; +} + +int result::column_type( size_type i ) const { + if( _status != st_hasdata && _status != st_lastrow ) { + throw logic_error( "Fetching row after sqlite returned no data!" ); + } + return _coltype[i]; +} + +/* value caching */ + +result::CachedValue::CachedValue( const CachedValue& v ) { + type = v.type; + value = v.value; + if( type == SQLITE3_TEXT ) { + value.s = (unsigned char *)strdup( (const char *)v.value.s ); + } +} + +result::CachedValue::~CachedValue( ) { + if( type == SQLITE3_TEXT && value.s != NULL ) { + free( value.s ); + value.s = NULL; + } +} + +result::CachedValue& result::CachedValue::operator= ( const result::CachedValue& v ) { + if( this != &v ) { + type = v.type; + value = v.value; + if( type == SQLITE3_TEXT ) { + value.s = (unsigned char *)strdup( (const char *)v.value.s ); + } + } + return *this; +} + +/* const iterator for tuples */ + +result::const_iterator result::const_iterator::operator++( int incr ) { + SQLITEXX_UNUSED( incr ); + const_iterator old( *this ); + // idx++; + return old; +} + +bool result::const_iterator::operator<( const result::const_iterator& i ) const { + SQLITEXX_UNUSED( i.dummy ); + // idx < i.idx + return false; +} + +} /* namespace sqlite3xx */ diff --git a/src/transaction.cpp b/src/transaction.cpp new file mode 100644 index 0000000..a06f223 --- /dev/null +++ b/src/transaction.cpp @@ -0,0 +1,142 @@ +/* + * sqlite3xx - sqlite3 C++ layer, following the ideas of libpqxx + * Copyright (C) 2009 Andreas Baumann + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions of + * the GNU Lesser General Public License, as published by the Free Software + * Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ + +#include "sqlite3xx/transaction.hpp" + +#include +#include +#include + +using namespace std; + +namespace sqlite3xx { + +transaction::transaction( connection &c, string name ) : + _c( c ), + _name( name ), + _status( st_nascent ) { +} + +transaction::~transaction( ) { +} + +void transaction::commit( ) { + switch( _status ) { + case st_nascent: + // skip, nothing to do + return; + + case st_active: + // continue below, this is what we expect + break; + + case st_committed: + // already commited, skip + // TODO: we should warn the user about double commits + break; + + case st_aborted: + throw logic_error( "Attempt to commit previously aborted transaction!" ); + + default: + throw logic_error( "Illegal state in sqlite3::transaction::commit( )!" ); + } + + ostringstream s; + s << "COMMIT TRANSACTION trans_" << _name; + _c.exec( s.str( ).c_str( ) ); + _status = st_committed; +} + +void transaction::abort( ) { + switch( _status ) { + case st_nascent: + // skip, nothing to do + return; + + case st_active: + // continue below, this is what we expect + break; + + case st_committed: + throw logic_error( "Attempt to abort previously committed transaction!" ); + + case st_aborted: + // already aborted, skip + // TODO: we should warn the user about double commits + + default: + throw logic_error( "Illegal state in sqlite3::transaction::abort( )!" ); + } + + ostringstream s; + s << "ROLLBACK TRANSACTION trans_" << _name; + _c.exec( s.str( ).c_str( ) ); + _status = st_aborted; +} + +void transaction::Begin( ) { + switch( _status ) { + case st_nascent: + break; + + case st_active: + case st_committed: + case st_aborted: + default: + throw logic_error( "Illegal state in sqlite3::transaction::Begin( )!" ); + } + + ostringstream s; + s << "BEGIN TRANSACTION trans_" << _name; + _c.exec( s.str( ).c_str( ) ); +} + +result transaction::exec( string sql ) { + switch( _status ) { + case st_nascent: + Begin( ); + break; + + case st_active: + break; + + case st_committed: + case st_aborted: + throw logic_error( "Attempt to execute query " + sql + " in a transaction " + + "which is already closed" ); + + default: + throw logic_error( "Illegal state in sqlite3::transaction::exec( )!" ); + } + + result r = _c.exec( sql ); + _status = st_active; + + return r; +} + +prepare::invocation transaction::prepared( const string& name ) { + return prepare::invocation( _c, *this, name ); +} + +} /* namespace sqlite3xx */ -- cgit v1.2.3-54-g00ecf