Revision: 2249
Author: sgbeal
Date: Sat Jun 30 09:31:23 2012
Log: Initial support for sqlite4 (which is as-yet unreleased).
http://code.google.com/p/v8-juice/source/detail?r=2249
Modified:
/convert/addons/jspdo/Makefile
/convert/addons/jspdo/cpdo_amalgamation.cpp
/convert/addons/jspdo/cpdo_amalgamation.hpp
=======================================
--- /convert/addons/jspdo/Makefile Thu Apr 12 06:23:08 2012
+++ /convert/addons/jspdo/Makefile Sat Jun 30 09:31:23 2012
@@ -34,9 +34,11 @@
#
# FIXME: port in the whiki makefile code which allows us to drop in
# our own sqlite3.c (from the sqlite3 amalgamation build).
+SQLITE4_HOME ?= $(HOME)
CPDO_ENABLE_SQLITE3 ?= $(shell whereis sqlite3.h | cut -d: -f2 | grep
include/sqlite3.h >/dev/null && echo 1)
+CPDO_ENABLE_SQLITE4 ?= $(shell test -e $(SQLITE4_HOME)/include/sqlite4.h
>/dev/null && echo 1)
LDFLAGS_SQLITE3 ?= -lsqlite3
-
+LDFLAGS_SQLITE4 ?= -L$(SQLITE4_HOME)/lib -lsqlite4
########################################################################
# Try to find mysql_config. If we find it we assume that the dev files
# are installed and that mysql_config can tell us how to link to
@@ -59,6 +61,14 @@
cpdo_amalgamation.o: CPPFLAGS+=-Wno-error=long-long
endif
+ifneq (1,$(CPDO_ENABLE_SQLITE4))
+ CPDO_ENABLE_SQLITE4 := 0
+else
+ # gcc: work around a portability bug in sqlite:
+ cpdo_amalgamation.o: CPPFLAGS+=-Wno-error=long-long
+ cpdo_amalgamation.o: CPPFLAGS+=-I$(SQLITE4_HOME)/include
+endif
+
ifneq (1,$(CPDO_ENABLE_MYSQL5))
CPDO_ENABLE_MYSQL5 := 0
endif
@@ -96,6 +106,10 @@
LDFLAGS_DRIVERS += $(LDFLAGS_SQLITE3)
cpdo_amalgamation.o: CFLAGS+=$(CFLAGS_SQLITE3)
endif
+ifeq (1,$(CPDO_ENABLE_SQLITE4))
+ LDFLAGS_DRIVERS += $(LDFLAGS_SQLITE4)
+ cpdo_amalgamation.o: CFLAGS+=$(CFLAGS_SQLITE4)
+endif
ifeq (1,$(CPDO_ENABLE_MYSQL5))
LDFLAGS_DRIVERS += $(LDFLAGS_MYSQL5)
@@ -124,6 +138,7 @@
OBJS_NEEDING_DB_CPPFLAGS := cpdo_amalgamation.o
# jspdo.o
$(OBJS_NEEDING_DB_CPPFLAGS):
CPPFLAGS+=-DCPDO_ENABLE_SQLITE3=$(CPDO_ENABLE_SQLITE3)
+$(OBJS_NEEDING_DB_CPPFLAGS):
CPPFLAGS+=-DCPDO_ENABLE_SQLITE4=$(CPDO_ENABLE_SQLITE4)
$(OBJS_NEEDING_DB_CPPFLAGS):
CPPFLAGS+=-DCPDO_ENABLE_MYSQL5=$(CPDO_ENABLE_MYSQL5)
=======================================
--- /convert/addons/jspdo/cpdo_amalgamation.cpp Thu Apr 12 06:23:08 2012
+++ /convert/addons/jspdo/cpdo_amalgamation.cpp Sat Jun 30 09:31:23 2012
@@ -1,14 +1,4 @@
#include "cpdo_amalgamation.hpp"
-#if CPDO_ENABLE_SQLITE3
-namespace {
- static const int registration_sq3 = cpdo_driver_sqlite3_register();
-}
-#endif /*CPDO_ENABLE_SQLITE3*/
-#if CPDO_ENABLE_MYSQL5
-namespace {
- static const int registration_my5 = cpdo_driver_mysql5_register();
-}
-#endif /*CPDO_ENABLE_MYSQL5*/
/* start of file cpdo_amalgamation.c */
#if !defined(__STDC_FORMAT_MACROS) /* required for PRIi32 and friends.*/
# define __STDC_FORMAT_MACROS
@@ -110,6 +100,7 @@
X,X,X,X,X
#undef X
};
+static char cpdo_bind_val_empty_blob = 0;
/**
Searches for a registered driver with the given name. If one is
@@ -404,7 +395,19 @@
break;
case CPDO_TYPE_BLOB:
case CPDO_TYPE_STRING:
- free( b->valu.blob.mem );
+#if 1
+ if( &cpdo_bind_val_empty_blob != b->valu.blob.mem ) {
+ free( b->valu.blob.mem );
+#if 0
+ assert(b->valu.blob.length && "WTF is this coming
from?");
+#endif
+ }
+#else
+ /* this is leaking from somewhere! */
+ if( b->valu.blob.length ) {
+ free( b->valu.blob.mem );
+ }
+#endif
b->valu.blob.length = 0;
b->valu.blob.mem = NULL;
/* fall through */
@@ -554,35 +557,17 @@
}
}
cpdo_bind_val_clean(b);
- if( NULL != v
- /* even if len is 0, so we treat literal null and empty
- string differently*/
- )
- {
- mem = malloc(len+1);
- if( NULL == mem ) return cpdo_rc.AllocError;
- else
- {
- memcpy( mem, v, len+1 );
+ mem = len ? malloc(len+1) : &cpdo_bind_val_empty_blob;
+ if( NULL == mem ) return cpdo_rc.AllocError;
+ else
+ {
+ if(len){
+ if(v) memcpy( mem, v, len );
+ else memset( mem, 0, len );
((char *)mem)[len] = 0;
- b->type = isString ? CPDO_TYPE_STRING : CPDO_TYPE_BLOB;
- b->valu.blob.mem = mem;
- b->valu.blob.length = len;
- return 0;
- }
- }
- else if( ! len )
- {
+ }
b->type = isString ? CPDO_TYPE_STRING : CPDO_TYPE_BLOB;
- b->valu.blob.mem = NULL;
- b->valu.blob.length = 0;
- return 0;
- }
- else /* allocate the blob ourselves. */
- {
- b->type = isString ? CPDO_TYPE_STRING : CPDO_TYPE_BLOB;
- b->valu.blob.mem = calloc(len+1,1);
- if( ! b->valu.blob.mem ) return cpdo_rc.AllocError;
+ b->valu.blob.mem = mem;
b->valu.blob.length = len;
return 0;
}
@@ -3760,19 +3745,39 @@
}
}
-
+#define TRY_SHARED_STRINGS 1
+#if TRY_SHARED_STRINGS
+static struct {
+ char sql_null[5];
+ char quoted_empty[3];
+} sq3_shared_strings = {
+{'N','U','L','L',0},
+{'\'','\'',0}
+};
+#endif
static int cpdo_sq3_sql_quote( cpdo_driver * self, char const * str,
uint32_t * len, char ** dest )
{
DRV_DECL(cpdo_rc.ArgError);
if( ! len || !dest ) return cpdo_rc.ArgError;
else if( NULL == str )
{
+#if TRY_SHARED_STRINGS
+ *dest = sq3_shared_strings.sql_null;
+ *len = 4;
+ return 0;
+#else
char * tmp = (char *)malloc(5);
if( ! tmp ) return cpdo_rc.AllocError;
strcpy( tmp, "NULL" );
*dest = tmp;
*len = 4;
return 0;
+#endif
+ }
+ else if(!*str || !*len){
+ *dest = sq3_shared_strings.quoted_empty;
+ *len = 2;
+ return 0;
}
else
{
@@ -3810,7 +3815,18 @@
static int cpdo_sq3_free_string( cpdo_driver * self, char * str)
{
- return str ? (free(str),0) : cpdo_rc.ArgError;
+ if(!self || !str) return cpdo_rc.ArgError;
+#if TRY_SHARED_STRINGS
+ else if( ((void const *)str >= (void const *)&sq3_shared_strings)
+ && ((void const *)str < (void const *)((unsigned char
*)&sq3_shared_strings + sizeof(sq3_shared_strings)))){
+ return 0;
+ }
+ else
+#endif
+ {
+ free(str);
+ return 0;
+ }
}
static int cpdo_sq3_prepare( cpdo_driver * self, cpdo_stmt ** tgt, char
const * sql, uint32_t len )
@@ -4132,31 +4148,27 @@
{
int rc;
STMT_DECL(cpdo_rc.ArgError);
- if( v )
- {
- rc = sqlite3_bind_text( stmt->stmt, (int)ndx, (char const *)v,
(int)len, SQLITE_TRANSIENT );
- }
- else
- {
+ if(!v){
rc = sqlite3_bind_null( stmt->stmt, (int)ndx );
}
+ else {
+ rc = sqlite3_bind_text( stmt->stmt, (int)ndx, v, len,
SQLITE_TRANSIENT );
+ }
SQ3_TO_CPDO_BIND_RC(rc);
}
static int cpdo_sq3_stmt_bind_blob( cpdo_stmt * self, uint16_t ndx, void
const * v, uint32_t len )
{
-
- if( (NULL == v) || (0==len) )
- {
- return cpdo_sq3_stmt_bind_null( self, ndx );
- }
- else
- {
- int rc;
- STMT_DECL(cpdo_rc.ArgError);
+ int rc;
+ STMT_DECL(cpdo_rc.ArgError);
+ if(!v){
+ rc = sqlite3_bind_null( stmt->stmt, (int)ndx );
+ }
+ else {
+ if( len != (uint32_t) ((int)len) ) return cpdo_rc.RangeError;
rc = sqlite3_bind_blob( stmt->stmt, (int)ndx, v, (int)len,
SQLITE_TRANSIENT );
- SQ3_TO_CPDO_BIND_RC(rc);
- }
+ }
+ SQ3_TO_CPDO_BIND_RC(rc);
}
#undef SQ3_TO_CPDO_BIND_RC
@@ -4269,16 +4281,36 @@
else STMT_CHECK_GET_NDX;
else
{
- *val = sqlite3_column_blob( stmt->stmt, (int)ndx )
- /* reminder: sqlite3_column_blob() returns NULL for
- length-zero blobs. */
- ;
- if( len )
- {
- *len = *val
- ? (uint32_t)sqlite3_column_bytes( stmt->stmt, (int)ndx )
- : 0
- ;
+ static char zeroLenBlob = {0};
+ cpdo_data_type ty = CPDO_TYPE_ERROR;
+ int const rc = cpdo_sq3_stmt_get_type_ndx( self, ndx, &ty );
+ assert(0 == rc);
+ assert( CPDO_TYPE_ERROR != ty );
+ switch( ty ){
+ case CPDO_TYPE_NULL:
+ *val = NULL;
+ if(len) *len = 0;
+ break;
+ default:
+ *val = sqlite3_column_blob( stmt->stmt, (int)ndx )
+ /* reminder: sqlite3_column_blob() returns NULL for
+ length-zero blobs. */
+ ;
+ if(!*val){
+ if(len) *len = 0;
+ *val = &zeroLenBlob;
+ }
+ else if( len ){
+ *len = (uint32_t)sqlite3_column_bytes( stmt->stmt,
(int)ndx )
+ /*
+ reminder: sqlite3_column_bytes()'s return
+ value can potentially be way off if the value
+ is UTF16:
+
+
http://www.sqlite.org/c3ref/column_blob.html
+ */
+ ;
+ }
}
return 0;
}
@@ -4304,7 +4336,7 @@
{
static const cpdo_driver_details bob = {
CPDO_DRIVER_NAME/*driver_name*/,
- "20110131"/*driver_version*/,
+ "20120413"/*driver_version*/,
"Dual Public Domain/MIT"/*license*/,
"
http://fossil.wanderinghorse.net/repos/cpdo/",
"Stephan Beal (
http://wanderinghorse.net)"
@@ -4327,7 +4359,994 @@
#undef DRV_DECL
#undef STMT_DECL
#undef CPDO_DRIVER_NAME
-#endif /*CPDO_ENABLE_SQLITE3*/
+#undef TRY_SHARED_STRINGS
+#endif
+/*CPDO_ENABLE_SQLITE3*/
+#if CPDO_ENABLE_SQLITE4
+/** @file cpdo_sqlite4.c
+
+ sqlite4 driver implementation for cpdo_driver interface.
+
+ Peculiarities vis-a-vis the interface specification:
+
+ - This is the reference driver implementation, and has no known
+ incompatibilities with the interface's required features.
+
+
+ Using this driver:
+
+ The simplest approach is to link it to you app and do:
+
+ @code
+ extern int cpdo_driver_sqlite4_register();
+ ...
+ cpdo_driver_sqlite4_register();
+ @endcode
+
+ If you are using C++, or can use C++ for one file of your
+ project, you can have that code automatically run by assigning
+ a dummy static variable like this:
+
+ @code
+ namespace { static int reg_sqlite4 = cpdo_driver_sqlite4_register(); }
+ @endcode
+
+
+*/
+#include <assert.h>
+#include <sqlite4.h>
+#include <stdlib.h> /* malloc()/free() */
+#include <string.h> /* strlen() */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/************************************************************************
+ cpdo_driver_api members:
+************************************************************************/
+#define CPDO_DRIVER_NAME "sqlite4"
+int cpdo_sq4_connect( cpdo_driver * self, cpdo_connect_opt const * opt );
+static int cpdo_sq4_sql_quote( cpdo_driver * self, char const * src,
uint32_t * len, char ** dest );
+static int cpdo_sq4_sql_qualify( cpdo_driver * self, char const * src,
uint32_t * len, char ** dest );
+static int cpdo_sq4_free_string( cpdo_driver * self, char * str);
+static int cpdo_sq4_prepare( cpdo_driver * self, cpdo_stmt ** tgt, char
const * sql, uint32_t len );
+static int cpdo_sq4_error_info( cpdo_driver * self, char const ** dest,
uint32_t * len, int * errorCode );
+static char cpdo_sq4_is_connected( cpdo_driver * self );
+static int cpdo_sq4_close( cpdo_driver * self );
+static int cpdo_sq4_last_insert_id( cpdo_driver * self, uint64_t * v, char
const * hint );
+static cpdo_driver_details const * cpdo_sq4_driver_details();
+
+static int cpdo_sq4_driver_begin_transaction( cpdo_driver * self );
+static int cpdo_sq4_driver_commit( cpdo_driver * self );
+static int cpdo_sq4_driver_rollback( cpdo_driver * self );
+static char cpdo_sq4_driver_in_trans( cpdo_driver * self );
+
+static int cpdo_sq4_driver_opt_set( cpdo_driver * self, char const * key,
va_list vargs );
+static int cpdo_sq4_driver_opt_get( cpdo_driver * self, char const * key,
va_list vargs );
+
+const cpdo_driver_api cpdo_sq4_driver_api =
+{
+ cpdo_sq4_driver_details,
+ cpdo_sq4_connect,
+ cpdo_sq4_sql_quote,
+ cpdo_sq4_sql_qualify,
+ cpdo_sq4_free_string,
+ cpdo_sq4_prepare,
+ cpdo_sq4_error_info,
+ cpdo_sq4_is_connected,
+ cpdo_sq4_close,
+ cpdo_sq4_last_insert_id,
+ {/*transaction*/
+ cpdo_sq4_driver_begin_transaction,
+ cpdo_sq4_driver_commit,
+ cpdo_sq4_driver_rollback,
+ cpdo_sq4_driver_in_trans
+ },
+ {/*opt*/
+ cpdo_sq4_driver_opt_set,
+ cpdo_sq4_driver_opt_get
+ },
+ {/*constants*/
+ CPDO_DRIVER_NAME /*driver_name*/
+ }
+};
+
+/************************************************************************
+cpdo_stmt_api members...
+************************************************************************/
+static cpdo_step_code cpdo_sq4_stmt_step( cpdo_stmt * self );
+static int cpdo_sq4_stmt_error_info( cpdo_stmt * self, char const ** dest,
uint32_t * len, int * errorCode );
+static uint16_t cpdo_sq4_stmt_column_count( cpdo_stmt * self );
+static char const * cpdo_sq4_stmt_column_name( cpdo_stmt * self, uint16_t
ndx );
+static int cpdo_sq4_stmt_reset( cpdo_stmt * self );
+static uint16_t cpdo_sq4_stmt_bind_count( cpdo_stmt * self );
+static uint16_t cpdo_sq4_stmt_param_index( cpdo_stmt * self, char const *
name );
+static char const * cpdo_sq4_stmt_param_name( cpdo_stmt * self, uint16_t
ndx );
+static int cpdo_sq4_stmt_bind_null( cpdo_stmt * self, uint16_t ndx );
+static int cpdo_sq4_stmt_bind_int8( cpdo_stmt * self, uint16_t ndx, int8_t
v );
+static int cpdo_sq4_stmt_bind_int16( cpdo_stmt * self, uint16_t ndx,
int16_t v );
+static int cpdo_sq4_stmt_bind_int32( cpdo_stmt * self, uint16_t ndx,
int32_t v );
+static int cpdo_sq4_stmt_bind_int64( cpdo_stmt * self, uint16_t ndx,
int64_t v );
+static int cpdo_sq4_stmt_bind_float( cpdo_stmt * self, uint16_t ndx, float
v );
+static int cpdo_sq4_stmt_bind_double( cpdo_stmt * self, uint16_t ndx,
double v );
+static int cpdo_sq4_stmt_bind_string( cpdo_stmt * self, uint16_t ndx, char
const * v, uint32_t len );
+static int cpdo_sq4_stmt_bind_blob( cpdo_stmt * self, uint16_t ndx, void
const * v, uint32_t len );
+static int cpdo_sq4_stmt_get_type_ndx( cpdo_stmt * self, uint16_t ndx,
cpdo_data_type * val );
+static int cpdo_sq4_stmt_get_int8_ndx( cpdo_stmt * self, uint16_t ndx,
int8_t * val );
+static int cpdo_sq4_stmt_get_int16_ndx( cpdo_stmt * self, uint16_t ndx,
int16_t * val );
+static int cpdo_sq4_stmt_get_int32_ndx( cpdo_stmt * self, uint16_t ndx,
int32_t * val );
+static int cpdo_sq4_stmt_get_int64_ndx( cpdo_stmt * self, uint16_t ndx,
int64_t * val );
+static int cpdo_sq4_stmt_get_float_ndx( cpdo_stmt * self, uint16_t ndx,
float * val );
+static int cpdo_sq4_stmt_get_double_ndx( cpdo_stmt * self, uint16_t ndx,
double * val );
+static int cpdo_sq4_stmt_get_string_ndx( cpdo_stmt * self, uint16_t ndx,
char const ** val, uint32_t * len );
+static int cpdo_sq4_stmt_get_blob_ndx( cpdo_stmt * self, uint16_t ndx,
void const ** v, uint32_t * len );
+static int cpdo_sq4_stmt_finalize( cpdo_stmt * self );
+const cpdo_stmt_api cpdo_sq4_stmt_api = {
+ cpdo_sq4_stmt_step,
+ cpdo_sq4_stmt_error_info,
+ cpdo_sq4_stmt_finalize,
+ {/*bind*/
+ cpdo_sq4_stmt_reset,
+ cpdo_sq4_stmt_bind_count,
+ cpdo_sq4_stmt_param_index,
+ cpdo_sq4_stmt_param_name,
+ cpdo_sq4_stmt_bind_null,
+ cpdo_sq4_stmt_bind_int8,
+ cpdo_sq4_stmt_bind_int16,
+ cpdo_sq4_stmt_bind_int32,
+ cpdo_sq4_stmt_bind_int64,
+ cpdo_sq4_stmt_bind_float,
+ cpdo_sq4_stmt_bind_double,
+ cpdo_sq4_stmt_bind_string,
+ cpdo_sq4_stmt_bind_blob
+ },
+ {/*get*/
+ cpdo_sq4_stmt_column_count,
+ cpdo_sq4_stmt_column_name,
+ cpdo_sq4_stmt_get_type_ndx,
+ cpdo_sq4_stmt_get_int8_ndx,
+ cpdo_sq4_stmt_get_int16_ndx,
+ cpdo_sq4_stmt_get_int32_ndx,
+ cpdo_sq4_stmt_get_int64_ndx,
+ cpdo_sq4_stmt_get_float_ndx,
+ cpdo_sq4_stmt_get_double_ndx,
+ cpdo_sq4_stmt_get_string_ndx,
+ cpdo_sq4_stmt_get_blob_ndx
+ }
+};
+
+
+
+typedef struct cpdo_sq4_stmt cpdo_sq4_stmt;
+static int cpdo_sq4_stmt_free(cpdo_sq4_stmt *s);
+static cpdo_sq4_stmt * cpdo_sq4_stmt_alloc();
+
+typedef struct cpdo_sq4_driver cpdo_sq4_driver;
+static int cpdo_sq4_driver_free(cpdo_sq4_driver *d);
+static cpdo_sq4_driver * cpdo_sq4_driver_alloc();
+
+static struct {
+ int envInitCount;
+} Sq4State = {
+0 /* envInitCount */
+};
+
+struct cpdo_sq4_driver
+{
+ sqlite4 * db;
+ char inTransaction;
+ cpdo_driver self;
+};
+
+
+struct cpdo_sq4_stmt
+{
+ sqlite4_stmt * stmt;
+ cpdo_sq4_driver * driver;
+ cpdo_stmt self;
+};
+
+const cpdo_sq4_driver cpdo_sq4_driver_empty = {
+ NULL /*db*/,
+ 0/*inTransaction*/,
+ {/*self*/
+ &cpdo_sq4_driver_api /*api*/,
+ NULL /*impl*/
+ }
+};
+
+const cpdo_sq4_stmt cpdo_sq4_stmt_empty = {
+ NULL /*stmt*/,
+ NULL /*driver*/,
+ {/*self*/
+ &cpdo_sq4_stmt_api /*api*/,
+ NULL /*impl*/
+ }
+};
+
+static cpdo_sq4_driver * cpdo_sq4_driver_alloc()
+{
+ cpdo_sq4_driver * s =
(cpdo_sq4_driver*)malloc(sizeof(cpdo_sq4_driver));
+ if( s )
+ {
+ *s = cpdo_sq4_driver_empty;
+ s->self.impl = s;
+ }
+ return s;
+}
+
+static int cpdo_sq4_driver_free(cpdo_sq4_driver *d)
+{
+ int rc = cpdo_rc.ArgError;
+ if( d )
+ {
+ rc = 0;
+ if( d->db )
+ {
+ rc = sqlite4_close(d->db);
+ if(rc) rc = cpdo_rc.UnknownError
+ /* we can't use CheckDbError
+ here because we're destroying the
+ db the client would be checking.
+ */
+ ;
+ }
+ *d = cpdo_sq4_driver_empty;
+ free(d);
+ }
+ if(0==--Sq4State.envInitCount){
+ sqlite4_shutdown(NULL);
+ }
+ return rc;
+}
+
+
+/**
+ Allocates a new cpdo_sq4_stmt and initializes
+ its self.impl member to point to the returned
+ object.
+*/
+static cpdo_sq4_stmt * cpdo_sq4_stmt_alloc()
+{
+ cpdo_sq4_stmt * s = (cpdo_sq4_stmt*)malloc(sizeof(cpdo_sq4_stmt));
+ if( s )
+ {
+ *s = cpdo_sq4_stmt_empty;
+ s->self.impl = s;
+ }
+ return s;
+}
+
+/**
+ Frees all resources belonging to this statement. It can return
+ non-0, but there is no generic recovery strategy for this, and s is
+ freed regardless of whether or not sqlite4_finalize() succeeds.
+*/
+static int cpdo_sq4_stmt_free(cpdo_sq4_stmt *s)
+{
+ int rc = cpdo_rc.ArgError;
+ if( s )
+ {
+ rc = 0;
+ if( s->stmt )
+ {
+ rc = sqlite4_finalize(s->stmt);
+ if(0 != rc ) rc = cpdo_rc.CheckDbError;
+ }
+ *s = cpdo_sq4_stmt_empty;
+ free(s);
+ }
+ return rc;
+}
+
+
+int cpdo_sq4_driver_new( cpdo_driver ** tgt )
+{
+ if( ! tgt ) return cpdo_rc.ArgError;
+ else
+ {
+ cpdo_sq4_driver * d = cpdo_sq4_driver_alloc();
+ if( d )
+ {
+ *tgt = &d->self;
+ return 0;
+ }
+ else return cpdo_rc.AllocError;
+ }
+}
+
+#define DRV_DECL(RC) cpdo_sq4_driver * drv = (self && self->impl &&
(self->api==&cpdo_sq4_driver_api)) \
+ ? (cpdo_sq4_driver *)self->impl : NULL; \
+ if( ! drv ) return RC
+#define STMT_DECL(RC) cpdo_sq4_stmt * stmt = (self && self->impl &&
(self->api==&cpdo_sq4_stmt_api)) \
+ ? (cpdo_sq4_stmt *)self->impl : NULL; \
+ if( ! stmt ) return RC
+
+static int cpdo_sq4_last_insert_id( cpdo_driver * self, uint64_t * v, char
const * hint )
+{
+#if 1
+ return cpdo_rc.UnsupportedError;
+#else
+ DRV_DECL(cpdo_rc.ArgError);
+ if( ! v ) return cpdo_rc.ArgError;
+#if 0 /* enabling this adds LOTS of mallocs()! */
+ else if(hint && *hint) { /* check sqlite_sequence table... */
+ char const * sql = "SELECT seq FROM sqlite_sequence WHERE name=?";
+ sqlite4_stmt * st3 = NULL;
+ int rc = sqlite4_prepare( drv->db, sql, (int)strlen(sql), &st3,
NULL );
+ if(rc){
+ return cpdo_rc.CheckDbError;
+ }
+ rc = sqlite4_bind_text( st3, 1, hint, (int)strlen(hint),
SQLITE4_STATIC );
+ if(rc) goto end;
+ rc = sqlite4_step( st3 );
+ if( SQLITE4_ROW != rc ) {
+ *v = sqlite4_last_insert_rowid(drv->db);
+ rc = 0;
+ goto end;
+ }
+ rc = 0;
+ *v = (uint64_t) sqlite4_column_int64( st3, 0 );
+ end:
+ sqlite4_finalize(st3);
+ return rc ? cpdo_rc.CheckDbError : 0;;
+ }
+#endif
+ else
+ {
+ *v = sqlite4_last_insert_rowid(drv->db);
+ return 0;
+ }
+#endif
+}
+static int cpdo_sq4_close( cpdo_driver * self )
+{
+ DRV_DECL(cpdo_rc.ArgError);
+ return cpdo_sq4_driver_free(drv);
+}
+
+static char cpdo_sq4_is_connected( cpdo_driver * self )
+{
+ DRV_DECL(0);
+ return drv->db ? 1 : 0;
+}
+
+static int cpdo_sq4_error_info( cpdo_driver * self, char const ** dest,
uint32_t * len, int * errorCode )
+{
+ DRV_DECL(cpdo_rc.ArgError);
+ if( ! drv->db ) return cpdo_rc.ConnectionError;
+ else
+ {
+ if( errorCode ) *errorCode = sqlite4_errcode(drv->db);
+ if( dest )
+ {
+ *dest = sqlite4_errmsg(drv->db);
+ if( len )
+ {
+ *len = *dest ? strlen(*dest) : 0;
+ }
+ }
+ return 0;
+ }
+}
+
+
+#define TRY_SHARED_STRINGS 1
+#if TRY_SHARED_STRINGS
+static struct {
+ char sql_null[5];
+ char quoted_empty[3];
+} sq4_shared_strings = {
+{'N','U','L','L',0},
+{'\'','\'',0}
+};
+#endif
+static int cpdo_sq4_sql_quote( cpdo_driver * self, char const * str,
uint32_t * len, char ** dest )
+{
+ DRV_DECL(cpdo_rc.ArgError);
+ if( ! len || !dest ) return cpdo_rc.ArgError;
+ else if( NULL == str )
+ {
+#if TRY_SHARED_STRINGS
+ *dest = sq4_shared_strings.sql_null;
+ *len = 4;
+ return 0;
+#else
+ char * tmp = (char *)malloc(5);
+ if( ! tmp ) return cpdo_rc.AllocError;
+ strcpy( tmp, "NULL" );
+ *dest = tmp;
+ *len = 4;
+ return 0;
+#endif
+ }
+ else if(!*str || !*len){
+ *dest = sq4_shared_strings.quoted_empty;
+ *len = 2;
+ return 0;
+ }
+ else
+ {
+ return cpdo_sql_escape( str, len, dest,
+ '\'',
+ '\'',
+ 1 );
+ }
+}
+
+static int cpdo_sq4_sql_qualify( cpdo_driver * self, char const * str,
uint32_t * len, char ** dest )
+{
+ if(!str || !dest || !*str) return cpdo_rc.ArgError;
+ else if( (NULL != strstr(str, ";"))
+ || (NULL != strstr(str, "'"))
+ || (NULL != strstr(str, "["))
+ )
+ {
+ return cpdo_rc.RangeError;
+ }
+ else
+ {
+ int sz = 0;
+ char * rc = cpdo_mprintf("[%s]%n", str, &sz);
+ if(!rc) return cpdo_rc.AllocError;
+ else
+ {
+ assert( sz > 0 );
+ *dest = rc;
+ if(len) *len = (uint32_t) sz; /*strlen(rc);*/
+ return 0;
+ }
+ }
+}
+
+static int cpdo_sq4_free_string( cpdo_driver * self, char * str)
+{
+ if(!self || !str) return cpdo_rc.ArgError;
+#if TRY_SHARED_STRINGS
+ else if( ((void const *)str >= (void const *)&sq4_shared_strings)
+ && ((void const *)str < (void const *)((unsigned char
*)&sq4_shared_strings + sizeof(sq4_shared_strings)))){
+ return 0;
+ }
+ else
+#endif
+ {
+ free(str);
+ return 0;
+ }
+}
+
+static int cpdo_sq4_prepare( cpdo_driver * self, cpdo_stmt ** tgt, char
const * sql, uint32_t len )
+{
+ int rc;
+ sqlite4_stmt * st3 = NULL;
+ cpdo_sq4_stmt * stmt = NULL;
+ DRV_DECL(cpdo_rc.ArgError);
+ if(!drv->db) return cpdo_rc.ConnectionError;
+ else if( ! tgt ) return cpdo_rc.ArgError;
+ rc =
+ sqlite4_prepare( drv->db, sql, (int)len, &st3, NULL );
+ if( 0 != rc ) return cpdo_rc.CheckDbError;
+ stmt = cpdo_sq4_stmt_alloc();
+ if( ! stmt )
+ {
+ sqlite4_finalize(st3);
+ return cpdo_rc.AllocError;
+ }
+ stmt->stmt = st3;
+ stmt->driver = drv;
+ *tgt = &stmt->self;
+ return 0;
+}
+
+static int cpdo_sq4_mode_to_flags( char const * m ){
+
+ if(!m || !*m) return 0;
+ else if(0 == strcmp("rwc",m)) return SQLITE4_OPEN_READWRITE |
SQLITE4_OPEN_CREATE;
+ else if(0 == strcmp("rw",m)) return SQLITE4_OPEN_READWRITE;
+ else if(0 == strcmp("ro",m)) return SQLITE4_OPEN_READONLY;
+ else return -1;
+}
+
+int cpdo_sq4_connect( cpdo_driver * self, cpdo_connect_opt const * opt )
+{
+ int rc;
+ DRV_DECL(cpdo_rc.ArgError);
+ if( ! opt ) return cpdo_rc.ArgError;
+ else if( drv->db ) return cpdo_rc.ConnectionError;
+ {
+ enum { BufSize = 256U };
+ char buf[BufSize];
+ char const * tokBegin = NULL;
+ char const * tokEnd = NULL;
+ char kbuf[BufSize] = {0,0};
+ char nameBuf[BufSize] = {0,0};
+ char * pos;
+ char const * key = NULL;
+#if (SQLITE4_VERSION_NUMBER >= 3005001)
+ int flags; flags = 0;
+#endif
+ rc = cpdo_split_dsn( opt->dsn, buf, BufSize, &tokBegin );
+ if( rc ) return rc;
+ assert( NULL != tokBegin );
+ pos = nameBuf;
+ while( *tokBegin && (';'!=*tokBegin) ){ /* skip filename part */
+ if((pos-nameBuf) >= (BufSize-1)){
+ return cpdo_rc.RangeError;
+ }
+ *(pos++) = *(tokBegin++);
+ }
+ *pos = 0; /* NUL-terminate file name */
+ if(';'==*tokBegin) ++tokBegin;
+ while( cpdo_next_token( &tokBegin, ';', &tokEnd ) ){
+ if(tokBegin==tokEnd) break;
+ else {
+ char const * value = NULL;
+ char * at = kbuf;
+ if( (tokEnd - tokBegin) >= (BufSize-1) ) return
cpdo_rc.RangeError;
+ memset( kbuf, 0, BufSize );
+ key = tokBegin;
+ /* Write the key part to the buffer... */
+ for( ; (key<tokEnd) && *key && ('='!=*key); ++key ) {
+ *(at++) = *key;
+ }
+ *(at++) = 0;
+ value = at;
+ if( '=' == *key ) {
+ ++key;
+ }
+ /* Write the value part to the buffer... */
+ for( ; (key<tokEnd) && *key; ++key ) {
+ *(at++) = *key;
+ }
+ key = kbuf;
+ /* Done parsing. Now see if we understand how to use
+ this option... */
+ if( 0 == strcmp("openmode",key) )
+ {
+#if (SQLITE4_VERSION_NUMBER >= 3005001)
+ flags = cpdo_sq4_mode_to_flags( value );
+ if(flags<0){
+ /* FIXME: add error string to db class */
+ return cpdo_rc.RangeError;
+ }
+#else
+ /* TODO: emit a warning here. */
+#endif
+ }
+ else
+ {
+ /* ignore unknown keys: this is optional in the CPDO
+ interface. If we add warning support, i'll add the
+ warning here. Or if i'm feeling pedantic later i'll
+ throw the error here.
+ */
+ }
+ /* set up for the next token... */
+ tokBegin = tokEnd;
+ tokEnd = NULL;
+ }
+ }
+ if(1==++Sq4State.envInitCount){
+ sqlite4_initialize(NULL);
+ }
+
+ /*
+ FIXME: strip any parameters after the first ';' separator
+ (just replace the first ';' with a NUL).
+ */
+ rc = /*flags
+ ? sqlite4_open_v2( NULL, nameBuf, &drv->db, flags, NULL )
+ : */sqlite4_open( NULL, nameBuf, &drv->db, flags, 0 )
+ /* reminder: don't close so the caller can get error info. */;
+ return rc ? cpdo_rc.CheckDbError : 0;
+ }
+}
+
+
+static int cpdo_sq4_driver_begin_transaction( cpdo_driver * self )
+{
+ char const * sql = NULL;
+ int rc;
+ DRV_DECL(cpdo_rc.ArgError);
+ if( drv->inTransaction ) return cpdo_rc.UnsupportedError;
+ sql = "BEGIN TRANSACTION";
+ rc = cpdo_exec( self, sql, strlen(sql) );
+ if( 0 == rc ) drv->inTransaction = 1;
+ return rc;
+}
+
+static int cpdo_sq4_driver_commit( cpdo_driver * self )
+{
+ char const * sql = NULL;
+ int rc;
+ DRV_DECL(cpdo_rc.ArgError);
+ sql = "COMMIT";
+ rc = cpdo_exec( self, sql, strlen(sql) );
+ drv->inTransaction = 0;
+ return rc;
+}
+
+static int cpdo_sq4_driver_rollback( cpdo_driver * self )
+{
+ char const * sql = NULL;
+ int rc;
+ DRV_DECL(cpdo_rc.ArgError);
+ sql = "ROLLBACK";
+ rc = cpdo_exec( self, sql, strlen(sql) );
+ drv->inTransaction = 0;
+ return rc;
+}
+
+static char cpdo_sq4_driver_in_trans( cpdo_driver * self )
+{
+ DRV_DECL(0);
+ return drv->inTransaction;
+}
+
+static int cpdo_sq4_driver_opt_set( cpdo_driver * self, char const * key,
va_list vargs )
+{
+ return cpdo_rc.NYIError;
+}
+static int cpdo_sq4_driver_opt_get( cpdo_driver * self, char const * key,
va_list vargs )
+{
+ return cpdo_rc.NYIError;
+}
+
+static cpdo_step_code cpdo_sq4_stmt_step( cpdo_stmt * self )
+{
+ STMT_DECL(CPDO_STEP_ERROR);
+ switch( sqlite4_step( stmt->stmt ) )
+ {
+ case SQLITE4_ROW:
+ return CPDO_STEP_OK;
+ case SQLITE4_DONE:
+ return CPDO_STEP_DONE;
+ default:
+ return CPDO_STEP_ERROR;
+ }
+}
+
+static int cpdo_sq4_stmt_reset( cpdo_stmt * self )
+{
+ STMT_DECL(cpdo_rc.ArgError);
+ return sqlite4_reset( stmt->stmt ) ? cpdo_rc.CheckDbError : 0;
+}
+
+static uint16_t cpdo_sq4_stmt_column_count( cpdo_stmt * self )
+{
+ int rc;
+ STMT_DECL(0);
+ rc = sqlite4_column_count( stmt->stmt );
+ return (rc>0) ? (uint32_t)rc : 0;
+}
+
+static char const * cpdo_sq4_stmt_column_name( cpdo_stmt * self, uint16_t
ndx )
+{
+ STMT_DECL(NULL);
+ return sqlite4_column_name( stmt->stmt, (int)ndx );
+}
+
+static uint16_t cpdo_sq4_stmt_bind_count( cpdo_stmt * self )
+{
+ int rc;
+ STMT_DECL(0);
+ rc = sqlite4_bind_parameter_count( stmt->stmt );
+ return (rc<=0) ? 0 : (uint16_t)rc;
+}
+
+
+static uint16_t cpdo_sq4_stmt_param_index( cpdo_stmt * self, char const *
name )
+{
+ int rc;
+ STMT_DECL(0);
+ if( ! name ) return 0;
+ else
+ {
+ rc = sqlite4_bind_parameter_index(stmt->stmt, name);
+ return (rc<=0) ? 0U : (uint16_t)rc;
+ }
+}
+
+static char const * cpdo_sq4_stmt_param_name( cpdo_stmt * self, uint16_t
ndx )
+{
+ STMT_DECL(NULL);
+ return sqlite4_bind_parameter_name( stmt->stmt, (int)ndx );
+}
+
+/** Converts sqlite4_bind_xxx() return value to cpdo_rc. */
+#define SQ4_TO_CPDO_BIND_RC(RC) \
+ if( 0 == (RC) ) return (RC); \
+ if( SQLITE4_NOMEM == (RC) ) return cpdo_rc.AllocError; \
+ else if( SQLITE4_RANGE == (RC) ) return cpdo_rc.RangeError; \
+ else return cpdo_rc.CheckDbError
+
+static int cpdo_sq4_stmt_bind_null( cpdo_stmt * self, uint16_t ndx )
+{
+ int rc;
+ STMT_DECL(cpdo_rc.ArgError);
+ rc = sqlite4_bind_null( stmt->stmt, (int)ndx );
+ SQ4_TO_CPDO_BIND_RC(rc);
+}
+
+static int cpdo_sq4_stmt_bind_int8( cpdo_stmt * self, uint16_t ndx, int8_t
v )
+{
+ int rc;
+ STMT_DECL(cpdo_rc.ArgError);
+ rc = sqlite4_bind_int( stmt->stmt, (int)ndx, (int)v );
+ SQ4_TO_CPDO_BIND_RC(rc);
+}
+
+static int cpdo_sq4_stmt_bind_int16( cpdo_stmt * self, uint16_t ndx,
int16_t v )
+{
+ int rc;
+ STMT_DECL(cpdo_rc.ArgError);
+ rc = sqlite4_bind_int( stmt->stmt, (int)ndx, (int)v );
+ SQ4_TO_CPDO_BIND_RC(rc);
+}
+
+static int cpdo_sq4_stmt_bind_int32( cpdo_stmt * self, uint16_t ndx,
int32_t v )
+{
+ int rc;
+ STMT_DECL(cpdo_rc.ArgError);
+ rc = sqlite4_bind_int( stmt->stmt, (int)ndx, (int)v );
+ SQ4_TO_CPDO_BIND_RC(rc);
+}
+
+static int cpdo_sq4_stmt_bind_int64( cpdo_stmt * self, uint16_t ndx,
int64_t v )
+{
+ int rc;
+ typedef
+#if SQLITE4_VERSION_NUMBER <= 3003006
+ /*FIXME: in which version did they rename sqlite_int64 to
+ sqlite4_int64?*/
+ sqlite_int64
+#else
***The diff for this file has been truncated for email.***
=======================================
--- /convert/addons/jspdo/cpdo_amalgamation.hpp Thu Apr 12 06:23:08 2012
+++ /convert/addons/jspdo/cpdo_amalgamation.hpp Sat Jun 30 09:31:23 2012
@@ -5,6 +5,9 @@
#if !defined(CPDO_ENABLE_SQLITE3)
# define CPDO_ENABLE_SQLITE3 0
#endif
+#if !defined(CPDO_ENABLE_SQLITE4)
+# define CPDO_ENABLE_SQLITE4 0
+#endif
#if !defined(CPDO_ENABLE_MYSQL5)
# define CPDO_ENABLE_MYSQL5 0
#endif
@@ -659,10 +662,12 @@
assign *dest to the string "NULL" (without any quotes) and
assign
*len to 4.
- On success, 0 is returned and *dest is pointed to the new
string,
- and *len is assigned to the length of the new string. The caller
- owns the string and must eventually free it using
- self->api->free_string(self, str).
+ On success, 0 is returned and *dest is pointed to the new
+ string, and *len is assigned to the length of the new
+ string. The caller owns the string and must eventually free
+ it using self->api->free_string(self, str). The bytes are
+ owned by the driver and must not be modified by the caller
+ (they may refer to a shared string).
On error non-0 is returned (one of the cpdo_rc values) and *len
and
*dest are unmodified. The most likely error codes are
cpdo_rc.ArgError
@@ -1102,9 +1107,10 @@
Binds the first len bytes of v as a string value to the
given 1-based index.
- Implementations MUST distinguish between (v==NULL) and
- (v!=NULL, len==0). The former must be treated as SQL NULL
- and the latter as an empty string.
+ Implementations must (as of 20120413) treat (v==NULL)
+ as an SQL NULL and (v!=NULL) with (0==len) as a
+ zero-length value (as opposed to an SQL NULL). If v is
+ NULL then len must be ignored.
Returns 0 on success.
*/
@@ -1116,8 +1122,10 @@
drivers which do not support it must return
cpdo_rc.UnsupportedError from this function.
- Implementations must treat (v==NULL) or (len==0) as SQL
- NULL.
+ Implementations must (as of 20120413) treat (v==NULL)
+ as an SQL NULL and (v!=NULL) with (0==len) as a
+ zero-length value (as opposed to an SQL NULL). If v is
+ NULL then len must be ignored.
Returns 0 on success.
*/
@@ -1309,11 +1317,15 @@
Returns 0 on success. On error, *val is not modified.
- Drivers must return SQL NULL values by setting *val to NULL
- if at all possible. It is recognized, however, that some
- underlying APIs may not allow clients to distinguish between
- an empty string and an SQL NULL, in which case
implementations
- should assign *val to an empty string.
+ Drivers must return SQL NULL values by setting *val to
+ NULL if at all possible. It is recognized, however,
+ that some underlying APIs may not allow clients to
+ distinguish between an empty string and an SQL NULL, in
+ which case implementations should assign *val to an
+ empty string. In either case they must set *len (if len
+ is not NULL) to 0.
+
+ FIXME? Use (unsigned char const **)?
*/
int (*string)( cpdo_stmt * self, uint16_t ndx, char const **
val, uint32_t * len );
@@ -1325,8 +1337,18 @@
support it must return cpdo_rc.UnsupportedError from this
function.
- Implementations must treat a zero-length BLOB as a NULL
- value.
+ Implementations must return SQL NULL values by setting
+ *val to NULL if at all possible. It is recognized,
+ however, that some underlying APIs may not allow
+ clients to distinguish between an empty blocs and an
+ SQL NULL, in which case implementations should assign
+ *val to "an empty value" (which reading from is
+ invalid). In either case they must set *len (if len is
+ not NULL) to 0. Length-zero blobs are handled by setting
+ *val to some "internal empty value" and *len to 0.
+ Drivers may, out of concern for safety, return
+ cpdo_rc.ArgError if len is NULL and the result would be
+ a zero-length blob.
The returned memory is owned by the statement handle, and
it may be invalidated the next time the handle is step()'d
@@ -1335,8 +1357,12 @@
memory.
Implementations must allow a NULL len value, under the
- assumption that the client knows that the hell he's doing
- and has some way to count the number of bytes himself.
+ assumption that the client knows that the hell he's
+ doing and has some way to count the number of bytes
+ himself. A NULL len can be especially problematic for
+ the caller if the row holds a length-zero blob because
+ the caller will get memory which is not legal to read
+ (the user gets a *len=0 in that case if len!=NULL).
Note that drivers may have "relatively small" limits on
blobs. e.g. sqlite3 uses int as the blob size, and
@@ -2834,7 +2860,29 @@
#endif /* WANDERINGHORSE_NET_CPDO_CPDO_SQLITE3_H_INCLUDED */
-#endif /*CPDO_ENABLE_SQLITE3*/
+#endif
+/*CPDO_ENABLE_SQLITE3*/
+#if CPDO_ENABLE_SQLITE4
+#if !defined(WANDERINGHORSE_NET_CPDO_CPDO_SQLITE4_H_INCLUDED)
+#define WANDERINGHORSE_NET_CPDO_CPDO_SQLITE4_H_INCLUDED
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ Registers the sqlite4 driver with the cpdo library.
+ Returns 0 on success, non-0 on error (see cpdo_driver_register() for
+ the return codes).
+*/
+int cpdo_driver_sqlite4_register();
+
+#if defined(__cplusplus)
+} /*extern "C"*/
+#endif
+
+#endif /* WANDERINGHORSE_NET_CPDO_CPDO_SQLITE4_H_INCLUDED */
+
+#endif
+/*CPDO_ENABLE_SQLITE4*/
#if CPDO_ENABLE_MYSQL5
#if !defined(WANDERINGHORSE_NET_CPDO_CPDO_MYSQL5_H_INCLUDED)
#define WANDERINGHORSE_NET_CPDO_CPDO_MYSQL5_H_INCLUDED
@@ -2853,18 +2901,17 @@
#endif
#endif /* WANDERINGHORSE_NET_CPDO_CPDO_MYSQL5_H_INCLUDED */
-#endif /*CPDO_ENABLE_MYSQL5*/
+#endif
+/*CPDO_ENABLE_MYSQL5*/
/* end of file cpdo_amalgamation.h */
/* start of file include/wh/cpdo/cpdo.hpp */
#if !defined(WANDERINGHORSE_NET_CPDO_CPDO_HPP_H_INCLUDED)
#define WANDERINGHORSE_NET_CPDO_CPDO_HPP_H_INCLUDED
-#include <cassert>
#include <stdexcept>
#include <string>
-#include <cstring>
-#include <algorithm> /* copy() */
-#include <sstream>
-
+#include <cstring> /* std::strlen() */
+#include <algorithm> /* std::copy() */
+#include <vector>
/**
The cpdo namespace houses a C++ wrapper around the cpdo C API.
The primary distinction between this API and the C API is that
@@ -2892,7 +2939,7 @@
class exception : public std::exception
{
public:
- ~exception() throw() {}
+ virtual ~exception() throw() {}
/**
Returns this exception's message string.
@@ -2959,7 +3006,13 @@
{
private:
cpdo_stmt * st;
+ /*
+ We store colCount only as an optimization.
+ */
uint16_t colCount;
+ /*
+ We store paramCount only as an optimization.
+ */
uint16_t paramCount;
friend class driver;
friend class chainer;
@@ -2978,6 +3031,7 @@
(0-based) are assumed.
*/
void assert_index( uint16_t ndx, unsigned char base );
+
/**
Closes the underlying statement handle, but does not
destroy this object. It is illegal to use any member
@@ -3508,8 +3562,8 @@
value_type operator()( StmtLike & st, uint16_t ndx ){
uint32_t len = 0;
typedef col_get<char const *> Proxy;
- char const * s = Proxy()( st, ndx );
- return s ? std::string(s, s + len) : std::string();
+ char const * s = Proxy()( st, ndx, len );
+ return s ? std::string(s, s+len) : std::string();
}
};
@@ -3647,11 +3701,15 @@
is 0, as that indicates a non-querying statement. It is
illegal to use this object after s as been finalized, which
normally means that s must outlive this object.
+
+ Note that the stmt class can be implicitly converted to a
+ statement.
*/
explicit stmt_row(statement & s);
+
/**
Redirects this object to use the given statement. It is not
- legal to use this object after s's lifetime has expired.
+ legal to use this object after s' lifetime has expired.
*/
stmt_row & operator=(statement & s);
~stmt_row();
@@ -3796,7 +3854,7 @@
Throws if st.step() or f() throws.
*/
- template <typename State, typename Func>
+ template <typename Func, typename State>
void step_each( statement & st, Func f, State & state )
THROWS_ON_ERROR {
stmt_row x(st);
while( st.step() ){
@@ -3814,19 +3872,19 @@
@code
struct MyFunctor {
- int rows;
- MyFunctor() : rows(0){}
- void operator()( cpdo::stmt_row & ){
- ++this->rows;
- }
+ int rows;
+ MyFunctor() : rows(0){}
+ void operator()( cpdo::stmt_row & ){
+ ++this->rows;
+ }
};
...
MyFunctor func;
{
- stmt st( db->prepare("SELECT * FROM foo") );
- step_each<MyFunctor &>( st, func );
+ stmt st( db->prepare("SELECT * FROM foo") );
+ step_each<MyFunctor &>( st, func );
}
assert( func.rows > 0 );
@endcode
@@ -4123,6 +4181,7 @@
*/
void exec_f( char const * fmt, ... );
};
+#undef THROWS_ON_ERROR
/**
The chainer class is a helper for working with prepared
@@ -4166,14 +4225,17 @@
private:
driver & db;
stmt sth;
+ /* Only stored as an optimization. */
uint16_t colCount;
+ /* Only stored as an optimization. */
uint16_t paramCount;
void updateCounts();
void assertPrepared();
void assertNotPrepared();
void assertHasBoundParams();
void assertHasColumns();
- void check_code( int code );
+ /** Throws if 0!=cpdoRcCode. */
+ void check_code( int cpdoRcCode );
public:
/**
@@ -4194,9 +4256,9 @@
transfers ownership of st to this object.
If this object is already prepared an exception is thrown
- (use clear() to remove a previous statement) and ownership
- of st is unchanged. An exception is also thrown if st is
- NULL.
+ and ownership of st is unchanged (use finalize() to remove
+ a previous statement). An exception is also thrown if st
+ is NULL.
Returns this object.
@@ -4351,9 +4413,20 @@
failIfIsSelectAndIsEmpty is true AND if step() returns
false then this function throws. If the query is not a
"fetching query" then the boolean parameter is ignored.
+
+
+ ACHTUNG: this method is only really intended for executing
+ INSERTs/UPDATEs and stepping over *single-value* result
+ sets, and not walking over multiple rows (because we don't
+ normally want to throw at the end of a result set).
*/
chainer & step( bool failIfIsSelectAndIsEmpty = true );
+ /**
+ Equivalent to statement::step().
+ */
+ bool stepBool();
+
/**
For fetching queries, IFF step()ing the handle fetches a
row then f(XYZ, state) is called, where XYZ is a
@@ -4402,15 +4475,15 @@
Returns this object.
- Note that State will, if not specified explicitly, be a
- const reference. If you need state to be copied by
- non-const reference then specify it explicitly, e.g.:
+ Note that Func will, if not specified explicitly, be copied
+ by value. If you need f to be copied by non-const reference
+ then specify it explicitly, e.g.:
@code
- chain.step_each<MyState &>( myFunctor, myState );
+ chain.step_each<MyFunctorT &>( myFunctor, myState );
@endcode
*/
- template <typename State, typename Func>
+ template <typename Func, typename State>
chainer & step_each( Func f, State & state ) {
this->assertHasColumns();
stmt_row x(this->sth);
@@ -4499,6 +4572,43 @@
};
+
+
+ /**
+ Escapes a BLOB value to an output iterator, using the format
+ X'DEADBEEF', which supported by sqlite3 and MySQL5 for
+ inserting/updating blob values as literals.
+
+ mem_ must be NULL or at least len bytes of valid memory.
+
+ nullStr is the string to be output if mem_ is NULL, and it may
+ be NULL (in which case no output is generated). If mem_ is
+ not NULL but len is 0 then X'' will be generated.
+
+ dest must be an output iterator, to which all output is
+ written.
+ */
+ template <typename OutIter>
+ void escape_blob( void const * mem_, uint32_t len, char const *
nullStr, OutIter dest ) {
+ static char const * hex = "0123456789ABCDEF";
+ if(!mem_) {
+ if(nullStr) std::copy(nullStr, nullStr + std::strlen(nullStr),
dest);
+ return;
+ }
+ else {
+ unsigned char const * mem = (unsigned char const *)mem_;
+ unsigned char ch;
+ *dest++ = 'X';
+ *dest++ = '\'';
+ for( uint32_t i = 0; i < len; ++i ){
+ ch = mem[i];
+ *dest++ = hex[((ch>>4)&0xf)];
+ *dest++ = hex[(ch&0xf)];
+ }
+ *dest++ = '\'';
+ return;
+ }
+ }
/**
@@ -4513,7 +4623,7 @@
- The data types CPDO_TYPE_CUSTOM and CPDO_TYPE_BLOB are
handled differently, depending on the values of the
- customTypesAs and blobsAs members, respectively.
+ customTypesAs and blobs_as members, respectively.
- When in "blob mode", binary data is emitted in the form
X'DEADBEEF'.
@@ -4538,7 +4648,7 @@
Default = "NULL"
*/
- char const * nullString;
+ char const * null_string;
/**
Tells us how to render CPDO_TYPE_CUSTOM fields. It must
@@ -4558,7 +4668,7 @@
Default = CPDO_TYPE_STRING
*/
- cpdo_data_type customTypesAs;
+ cpdo_data_type custom_types_as;
/**
Tells us how to render CPDO_TYPE_BLOB fields. It must have
@@ -4576,7 +4686,7 @@
Default = CPDO_TYPE_BLOB
*/
- cpdo_data_type blobsAs;
+ cpdo_data_type blobs_as;
/**
Equivalent to col_to_string(NULL).
@@ -4595,7 +4705,7 @@
column to the given output iterator.
How BLOB and CUSTOM types are handled are determined by the
- values of customTypesAs and blobsAs, respectively.
+ values of custom_types_as and blobs_as, respectively.
StmtLike must be a statement, stmt_row, or similar class
which offers GET access to statement data. If st is not in
@@ -4603,25 +4713,25 @@
*/
template <typename StmtLike, typename OutIter>
void operator()( StmtLike & st, uint16_t ndx, OutIter dest ) const{
- static char const * hex = "0123456789ABCDEF";
cpdo_data_type const t = st.col_type(ndx);
- char const * nullString = this->nullString;
+ char const * nullString = this->null_string;
if(! nullString ) nullString = "";
-#define DONULL std::copy( nullString, nullString+std::strlen(nullString),
dest ); return
+#define DONULL std::copy( nullString, nullString+std::strlen(nullString),
dest )
switch(t){
case CPDO_TYPE_NULL:
DONULL;
return;
#define DONUM(VT) { uint32_t len = 0; char const * s = st.get_string( ndx,
&len ); \
- if(!s || !len) { DONULL; } \
- else { std::copy( s, s+len, dest ); return; } \
+ if(!s) { /* "can't happen" for numerics. */ DONULL;
} \
+ else if(len) ; std::copy( s, s+len, dest ); \
} return
/* Reminder: the CPDO interface does not require
drivers to be able to convert non-string values
to strings, but both the sqlite3 and mysql5
drivers support it. If we ever add a driver which
doesn't then we'll have to re-implement the
- numeric handling here..
+ numeric handling here. e.g. we could use
+ std::ostringstream.
*/
case CPDO_TYPE_INT8: DONUM(int8_t);
case CPDO_TYPE_INT16: DONUM(int16_t);
@@ -4630,49 +4740,37 @@
case CPDO_TYPE_FLOAT: DONUM(float);
case CPDO_TYPE_DOUBLE: DONUM(double);
#undef DONUM
- case CPDO_TYPE_CUSTOM: // try it as a string and hope for
the best!
+ case CPDO_TYPE_CUSTOM:
case CPDO_TYPE_BLOB:
case CPDO_TYPE_STRING:
if((CPDO_TYPE_STRING==t)
- || ((CPDO_TYPE_BLOB==t) &&
(CPDO_TYPE_STRING==this->blobsAs))
- || ((CPDO_TYPE_CUSTOM==t) &&
(CPDO_TYPE_STRING==this->customTypesAs))
+ || ((CPDO_TYPE_BLOB==t) &&
(CPDO_TYPE_STRING==this->blobs_as))
+ || ((CPDO_TYPE_CUSTOM==t) &&
(CPDO_TYPE_STRING==this->custom_types_as))
)
{ /* treat it as a raw string value. */
uint32_t len = 0;
char const * mem = col_get<char const *>()( st, ndx,
len );
- if(!mem || !len) { DONULL; }
- std::copy( mem, mem + len, dest );
+ if(!mem) { DONULL; }
+ else if(len) std::copy( mem, mem + len, dest );
return;
}
else if(
- ((CPDO_TYPE_BLOB==t) &&
(CPDO_TYPE_BLOB==this->blobsAs))
- || ((CPDO_TYPE_CUSTOM==t) &&
(CPDO_TYPE_BLOB==this->customTypesAs))
+ ((CPDO_TYPE_BLOB==t) &&
(CPDO_TYPE_BLOB==this->blobs_as))
+ || ((CPDO_TYPE_CUSTOM==t) &&
(CPDO_TYPE_BLOB==this->custom_types_as))
)
{ /* write out the blob in the form X'deadbeef',
which is supported by sqlite3 and MySQL. */
uint32_t len = 0;
unsigned char const * mem = (unsigned char const
*)col_get<void const *>()( st, ndx, len );
- if(!mem || !len) { DONULL; }
- std::ostringstream os;
- *dest++ = 'X';
- *dest++ = '\'';
- for( uint32_t i = 0; i < len; ++i ){
- const unsigned short ch = mem[i];
- *dest++ = hex[((ch>>4)&0xf)];
- *dest++ = hex[(ch&0xf)];
- }
- *dest++ = '\'';
+ escape_blob( mem, len, nullString, dest );
return;
}
#undef DONULL
// else fall through to the error case...
- default: {
- std::ostringstream os;
- os << "Unhandled data type (#"<<t<<") in col_lex()
conversion.";
- throw exception(cpdo_rc.TypeError, false, os.str());
- }
- }
- assert(0 && "You shouldn't have gotten this far.");
+ default:
+ throw exception(cpdo_rc.TypeError, false,
+ "Unhandled data type in col_to_string
conversion.");
+ }
}
/**
@@ -4684,20 +4782,47 @@
};
#if 0
- /**
- Uses a default-constructed col_to_string() to convert st's ndx'th
- column to a string.
- */
- std::string col_lex( statement & st, uint16_t ndx );
-
- /**
- Uses a default-constructed col_to_string() to convert st's ndx'th
- column to a string.
- */
- std::string col_lex( stmt_row & st, uint16_t ndx );
+ class query_builder
+ {
+ protected:
+ typedef std::vector<std::string> PartList;
+ /*struct State {
+ uint16_t colCount;
+ uint16_t paramCount;
+ };*/
+ driver & db;
+ private:
+ PartList m_parts;
+ protected:
+ query_builder( driver & db );
+ void push( std::string const & );
+ public:
+ ~query_builder();
+ std::string to_string() const;
+ void raw( std::string const & );
+ };
+
+ class q_select : public query_builder
+ {
+ public:
+ q_select();
+ ~q_select();
+ };
+ class q_insert : public query_builder
+ {
+ public:
+ q_insert();
+ ~q_insert();
+ };
+
+ class q_update : public query_builder
+ {
+ public:
+ q_update();
+ ~q_update();
+ };
#endif
-#undef THROWS_ON_ERROR
-}
+} /* namespace */
#endif /* WANDERINGHORSE_NET_CPDO_CPDO_HPP_H_INCLUDED */
/* end of file include/wh/cpdo/cpdo.hpp */