# MongoDB C Driver History
+## 0.6
+2012-6-3
+** API CHANGE **
+
+Version 0.6 supports write concern. This involves a backward-breaking
+API change, as the write functions now take an optional write_concern
+object.
+
+The driver now also supports the MONGO_CONTINUE_ON_ERROR flag for
+batch inserts.
+
+The new function prototypes are as follows:
+
+* int mongo_insert( mongo *conn, const char *ns, const bson *data,
+ mongo_write_concern *custom_write_concern );
+
+* int mongo_insert_batch( mongo *conn, const char *ns,
+ const bson **data, int num, mongo_write_concern *custom_write_concern );
+
+* int mongo_update( mongo *conn, const char *ns, const bson *cond,
+ const bson *op, int flags, mongo_write_concern *custom_write_concern,
+ int flags );
+
+* int mongo_remove( mongo *conn, const char *ns, const bson *cond,
+ mongo_write_concern *custom_write_concern );
+
+* Allow DBRefs (i.e., allows keys $ref, $id, and $db)
+* Added mongo_create_capped_collection().
+* Fixed some bugs in the SCons and Makefile build scripts.
+* Fixes for SCons and Makefile shared library install targets.
+* Other minor bug fixes.
+
## 0.5.2
2012-5-4
Until the 1.0 release, this driver should be considered alpha. Keep in mind that the API will be in flux until then.
+# Documentation
+
+Documentation exists in the project's `docs` folder. You can read the latest
+docs online at (http://api.mongodb.org/c/current/).
+
+The docs are built using Sphinx and Doxygen. If you have these tools installed, then
+you can build the docs with scons:
+
+ scons docs
+
+The html docs will appear in docs/html.
+
# Building
First check out the version you want to build. *Always build from a particular tag, since HEAD may be
-a work in progress.* For example, to build version 0.5.2, run:
+a work in progress.* For example, to build version 0.6, run:
- git checkout v0.5.2
+ git checkout v0.6
You can then build the driver with scons:
scons
+For more build options, see the docs.
+
## Running the tests
Make sure that you're running mongod on 127.0.0.1 on the default port (27017). The replica set
test assumes a replica set with at least three nodes running at 127.0.0.1 and starting at port
`mongo` and `bson` objects. It is the client's responsibility to check for errors and handle
them appropriately.
-# Docs
-The docs are built using Sphinx and Doxygen. If you have these tools installed, then
-you can build the docs with scons:
-
- scons docs
-
-The html docs will appear in docs/html.
-
# ISSUES
You can report bugs, request new features, and view this driver's roadmap
/* Custom standard function pointers. */
void *( *bson_malloc_func )( size_t ) = malloc;
void *( *bson_realloc_func )( void *, size_t ) = realloc;
-void ( *bson_free )( void * ) = free;
+void ( *bson_free_func )( void * ) = free;
#ifdef R_SAFETY_NET
bson_printf_func bson_printf;
#else
------------------------------ */
MONGO_EXPORT bson_iterator* bson_iterator_create() {
- return (bson_iterator*)malloc(sizeof(bson_iterator*));
+ return ( bson_iterator* )malloc( sizeof( bson_iterator ) );
}
MONGO_EXPORT void bson_iterator_dispose(bson_iterator* i) {
return old;
}
+MONGO_EXPORT void bson_free( void *ptr ) {
+ bson_free_func( ptr );
+}
+
MONGO_EXPORT void *bson_malloc( int size ) {
void *p;
p = bson_malloc_func( size );
extern void *( *bson_malloc_func )( size_t );
extern void *( *bson_realloc_func )( void *, size_t );
-extern void ( *bson_free )( void * );
+extern void ( *bson_free_func )( void * );
extern bson_printf_func bson_printf;
extern bson_fprintf_func bson_fprintf;
extern bson_sprintf_func bson_sprintf;
extern bson_printf_func bson_errprintf;
+MONGO_EXPORT void bson_free( void *ptr );
+
/**
* Allocates memory and checks return value, exiting fatally if malloc() fails.
*
return 1;
}
+/* If the name is part of a db ref ($ref, $db, or $id), then return true. */
+static int bson_string_is_db_ref( const unsigned char *string, const int length ) {
+ int result = 0;
+
+ if( length >= 4 ) {
+ if( string[1] == 'r' && string[2] == 'e' && string[3] == 'f' )
+ result = 1;
+ }
+ else if( length >= 3 ) {
+ if( string[1] == 'i' && string[2] == 'd' )
+ result = 1;
+ else if( string[1] == 'd' && string[2] == 'b' )
+ result = 1;
+ }
+
+ return result;
+}
+
static int bson_validate_string( bson *b, const unsigned char *string,
const int length, const char check_utf8, const char check_dot,
const char check_dollar ) {
int sequence_length = 1;
if( check_dollar && string[0] == '$' ) {
- b->err |= BSON_FIELD_INIT_DOLLAR;
+ if( !bson_string_is_db_ref( string, length ) )
+ b->err |= BSON_FIELD_INIT_DOLLAR;
}
while ( position < length ) {
#include <ws2tcpip.h> // send,recv,socklen_t etc
#include <wspiapi.h> // addrinfo
#else
-#include <windows.h>
-#include <winsock.h>
+#include <ws2tcpip.h> // send,recv,socklen_t etc
+#include <winsock2.h>
typedef int socklen_t;
#endif
# define NI_MAXSERV 32
#endif
-static void mongo_clear_errors( mongo *conn ) {
- conn->err = 0;
- memset( conn->errstr, 0, MONGO_ERR_LEN );
-}
-
int mongo_env_close_socket( int socket ) {
return closesocket( socket );
}
bson_append_string( &ret, "contentType", contenttype );
}
bson_finish( &ret );
- result = mongo_insert( gfs->client, gfs->files_ns, &ret );
+ result = mongo_insert( gfs->client, gfs->files_ns, &ret, NULL );
bson_destroy( &ret );
return result;
chunkLen = DEFAULT_CHUNK_SIZE < ( unsigned int )( end - data_ptr ) ?
DEFAULT_CHUNK_SIZE : ( unsigned int )( end - data_ptr );
oChunk = chunk_new( id, chunkNumber, data_ptr, chunkLen );
- mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
+ mongo_insert( gfs->client, gfs->chunks_ns, oChunk, NULL );
chunk_free( oChunk );
chunkNumber++;
data_ptr += chunkLen;
memcpy( buffer + gfile->pending_len, data, data_partial_len );
oChunk = chunk_new( gfile->id, gfile->chunk_num, buffer, DEFAULT_CHUNK_SIZE );
- mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
+ mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk, NULL );
chunk_free( oChunk );
gfile->chunk_num++;
gfile->length += DEFAULT_CHUNK_SIZE;
while( chunks_to_write > 0 ) {
oChunk = chunk_new( gfile->id, gfile->chunk_num, data, DEFAULT_CHUNK_SIZE );
- mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
+ mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk, NULL );
chunk_free( oChunk );
gfile->chunk_num++;
chunks_to_write--;
int response;
if( gfile->pending_data ) {
oChunk = chunk_new( gfile->id, gfile->chunk_num, gfile->pending_data, gfile->pending_len );
- mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
+ mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk, NULL );
chunk_free( oChunk );
bson_free( gfile->pending_data );
gfile->length += gfile->pending_len;
chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd );
do {
oChunk = chunk_new( id, chunkNumber, buffer, chunkLen );
- mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
+ mongo_insert( gfs->client, gfs->chunks_ns, oChunk, NULL );
chunk_free( oChunk );
length += chunkLen;
chunkNumber++;
bson_init( &b );
bson_append_oid( &b, "_id", &id );
bson_finish( &b );
- mongo_remove( gfs->client, gfs->files_ns, &b );
+ mongo_remove( gfs->client, gfs->files_ns, &b, NULL );
bson_destroy( &b );
/* Remove all chunks from the file with the specified id */
bson_init( &b );
bson_append_oid( &b, "files_id", &id );
bson_finish( &b );
- mongo_remove( gfs->client, gfs->chunks_ns, &b );
+ mongo_remove( gfs->client, gfs->chunks_ns, &b, NULL );
bson_destroy( &b );
}
MONGO_EXPORT const char* mongo_get_primary(mongo* conn) {
mongo* conn_ = (mongo*)conn;
+ if( !(conn_->connected) || (conn_->primary->host == '\0') )
+ return NULL;
return _get_host_port(conn_->primary);
}
str_size = strlen( str ) + 1;
errstr_size = str_size > MONGO_ERR_LEN ? MONGO_ERR_LEN : str_size;
memcpy( conn->errstr, str, errstr_size );
- conn->errstr[errstr_size] = '\0';
+ conn->errstr[errstr_size-1] = '\0';
}
}
memset( conn->lasterrstr, 0, MONGO_ERR_LEN );
}
+/* Note: this function returns a char* which must be freed. */
+static char *mongo_ns_to_cmd_db( const char *ns ) {
+ char *current = NULL;
+ char *cmd_db_name = NULL;
+ int len = 0;
+
+ for( current = (char *)ns; *current != '.'; current++ ) {
+ len++;
+ }
+
+ cmd_db_name = (char *)bson_malloc( len + 6 );
+ strncpy( cmd_db_name, ns, len );
+ strncpy( cmd_db_name + len, ".$cmd", 6 );
+
+ return cmd_db_name;
+}
+
MONGO_EXPORT int mongo_validate_ns( mongo *conn, const char *ns ) {
char *last = NULL;
char *current = NULL;
mongo_env_sock_init();
}
-
MONGO_EXPORT void mongo_init( mongo *conn ) {
memset( conn, 0, sizeof( mongo ) );
conn->max_bson_size = MONGO_DEFAULT_MAX_BSON_SIZE;
memcpy( conn->replset->name, name, strlen( name ) + 1 );
conn->primary = bson_malloc( sizeof( mongo_host_port ) );
+ conn->primary->host[0] = '\0';
+ conn->primary->next = NULL;
}
static void mongo_replset_add_node( mongo_host_port **list, const char *host, int port ) {
return MONGO_OK;
}
-/* MongoDB CRUD API */
+static int mongo_check_last_error( mongo *conn, const char *ns,
+ mongo_write_concern *write_concern ) {
-MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns,
- const bson **bsons, int count ) {
+ bson response = {NULL, 0};
+ bson fields;
+ bson_iterator it;
+ int res = 0;
+ char *cmd_ns = mongo_ns_to_cmd_db( ns );
- mongo_message *mm;
- int i;
- char *data;
- int overhead = 16 + 4 + strlen( ns ) + 1;
- int size = overhead;
+ res = mongo_find_one( conn, cmd_ns, write_concern->cmd, bson_empty( &fields ), &response );
+ bson_free( cmd_ns );
- if( mongo_validate_ns( conn, ns ) != MONGO_OK )
+ if( res != MONGO_OK )
return MONGO_ERROR;
+ else {
+ if( ( bson_find( &it, &response, "$err" ) == BSON_STRING ) ||
+ ( bson_find( &it, &response, "err" ) == BSON_STRING ) ) {
- for( i=0; i<count; i++ ) {
- size += bson_size( bsons[i] );
- if( mongo_bson_valid( conn, bsons[i], 1 ) != MONGO_OK )
+ __mongo_set_error( conn, MONGO_WRITE_ERROR,
+ "See conn->lasterrstr for details.", 0 );
+ mongo_set_last_error( conn, &it, &response );
return MONGO_ERROR;
+ } else
+ return MONGO_OK;
}
+}
- if( ( size - overhead ) > conn->max_bson_size ) {
- conn->err = MONGO_BSON_TOO_LARGE;
- return MONGO_ERROR;
- }
-
- mm = mongo_message_create( size , 0 , 0 , MONGO_OP_INSERT );
-
- data = &mm->data;
- data = mongo_data_append32( data, &ZERO );
- data = mongo_data_append( data, ns, strlen( ns ) + 1 );
+static int mongo_choose_write_concern( mongo *conn,
+ mongo_write_concern *custom_write_concern,
+ mongo_write_concern **write_concern ) {
- for( i=0; i<count; i++ ) {
- data = mongo_data_append( data, bsons[i]->data, bson_size( bsons[i] ) );
+ if( custom_write_concern ) {
+ *write_concern = custom_write_concern;
+ }
+ else if( conn->write_concern ) {
+ *write_concern = conn->write_concern;
}
- return mongo_message_send( conn, mm );
+ if( *write_concern && !((*write_concern)->cmd) ) {
+ __mongo_set_error( conn, MONGO_WRITE_CONCERN_INVALID,
+ "Must call mongo_write_concern_finish() before using *write_concern.", 0 );
+ return MONGO_ERROR;
+ }
+ else
+ return MONGO_OK;
}
-MONGO_EXPORT int mongo_insert( mongo *conn , const char *ns , const bson *bson ) {
+
+/*********************************************************************
+CRUD API
+**********************************************************************/
+
+MONGO_EXPORT int mongo_insert( mongo *conn, const char *ns,
+ const bson *bson, mongo_write_concern *custom_write_concern ) {
char *data;
mongo_message *mm;
+ mongo_write_concern *write_concern = NULL;
if( mongo_validate_ns( conn, ns ) != MONGO_OK )
return MONGO_ERROR;
- /* Make sure that BSON is valid for insert. */
if( mongo_bson_valid( conn, bson, 1 ) != MONGO_OK ) {
return MONGO_ERROR;
}
+ if( mongo_choose_write_concern( conn, custom_write_concern,
+ &write_concern ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
mm = mongo_message_create( 16 /* header */
+ 4 /* ZERO */
+ strlen( ns )
data = mongo_data_append( data, ns, strlen( ns ) + 1 );
data = mongo_data_append( data, bson->data, bson_size( bson ) );
- return mongo_message_send( conn, mm );
+
+ /* TODO: refactor so that we can send the insert message
+ and the getlasterror messages together. */
+ if( write_concern ) {
+ if( mongo_message_send( conn, mm ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
+ return mongo_check_last_error( conn, ns, write_concern );
+ }
+ else {
+ return mongo_message_send( conn, mm );
+ }
+}
+
+MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns,
+ const bson **bsons, int count, mongo_write_concern *custom_write_concern,
+ int flags ) {
+
+ mongo_message *mm;
+ mongo_write_concern *write_concern = NULL;
+ int i;
+ char *data;
+ int overhead = 16 + 4 + strlen( ns ) + 1;
+ int size = overhead;
+
+ if( mongo_validate_ns( conn, ns ) != MONGO_OK )
+ return MONGO_ERROR;
+
+ for( i=0; i<count; i++ ) {
+ size += bson_size( bsons[i] );
+ if( mongo_bson_valid( conn, bsons[i], 1 ) != MONGO_OK )
+ return MONGO_ERROR;
+ }
+
+ if( ( size - overhead ) > conn->max_bson_size ) {
+ conn->err = MONGO_BSON_TOO_LARGE;
+ return MONGO_ERROR;
+ }
+
+ if( mongo_choose_write_concern( conn, custom_write_concern,
+ &write_concern ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
+ mm = mongo_message_create( size , 0 , 0 , MONGO_OP_INSERT );
+
+ data = &mm->data;
+ if( flags & MONGO_CONTINUE_ON_ERROR )
+ data = mongo_data_append32( data, &ONE );
+ else
+ data = mongo_data_append32( data, &ZERO );
+ data = mongo_data_append( data, ns, strlen( ns ) + 1 );
+
+ for( i=0; i<count; i++ ) {
+ data = mongo_data_append( data, bsons[i]->data, bson_size( bsons[i] ) );
+ }
+
+ /* TODO: refactor so that we can send the insert message
+ * and the getlasterror messages together. */
+ if( write_concern ) {
+ if( mongo_message_send( conn, mm ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
+ return mongo_check_last_error( conn, ns, write_concern );
+ }
+ else {
+ return mongo_message_send( conn, mm );
+ }
}
MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond,
- const bson *op, int flags ) {
+ const bson *op, int flags, mongo_write_concern *custom_write_concern ) {
char *data;
mongo_message *mm;
+ mongo_write_concern *write_concern = NULL;
/* Make sure that the op BSON is valid UTF-8.
* TODO: decide whether to check cond as well.
return MONGO_ERROR;
}
+ if( mongo_choose_write_concern( conn, custom_write_concern,
+ &write_concern ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
mm = mongo_message_create( 16 /* header */
+ 4 /* ZERO */
+ strlen( ns ) + 1
data = mongo_data_append( data, cond->data, bson_size( cond ) );
data = mongo_data_append( data, op->data, bson_size( op ) );
- return mongo_message_send( conn, mm );
+ /* TODO: refactor so that we can send the insert message
+ * and the getlasterror messages together. */
+ if( write_concern ) {
+ if( mongo_message_send( conn, mm ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
+ return mongo_check_last_error( conn, ns, write_concern );
+ }
+ else {
+ return mongo_message_send( conn, mm );
+ }
}
-MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond ) {
+MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond,
+ mongo_write_concern *custom_write_concern ) {
+
char *data;
mongo_message *mm;
+ mongo_write_concern *write_concern = NULL;
/* Make sure that the BSON is valid UTF-8.
* TODO: decide whether to check cond as well.
return MONGO_ERROR;
}
+ if( mongo_choose_write_concern( conn, custom_write_concern,
+ &write_concern ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
mm = mongo_message_create( 16 /* header */
+ 4 /* ZERO */
+ strlen( ns ) + 1
data = mongo_data_append32( data, &ZERO );
data = mongo_data_append( data, cond->data, bson_size( cond ) );
- return mongo_message_send( conn, mm );
+ /* TODO: refactor so that we can send the insert message
+ * and the getlasterror messages together. */
+ if( write_concern ) {
+ if( mongo_message_send( conn, mm ) == MONGO_ERROR ) {
+ return MONGO_ERROR;
+ }
+
+ return mongo_check_last_error( conn, ns, write_concern );
+ }
+ else {
+ return mongo_message_send( conn, mm );
+ }
+}
+
+
+/*********************************************************************
+Write Concern API
+**********************************************************************/
+
+MONGO_EXPORT void mongo_write_concern_init( mongo_write_concern *write_concern ) {
+ memset( write_concern, 0, sizeof( mongo_write_concern ) );
+}
+
+MONGO_EXPORT int mongo_write_concern_finish( mongo_write_concern *write_concern ) {
+ bson *command;
+
+ /* Destory any existing serialized write concern object and reuse it. */
+ if( write_concern->cmd ) {
+ bson_destroy( write_concern->cmd );
+ command = write_concern->cmd;
+ }
+ else
+ command = (bson *)bson_malloc( sizeof( bson ) );
+
+ if( !command ) {
+ return MONGO_ERROR;
+ }
+
+ bson_init( command );
+
+ bson_append_int( command, "getlasterror", 1 );
+
+ if( write_concern->mode ) {
+ bson_append_string( command, "w", write_concern->mode );
+ }
+
+ else if( write_concern->w ) {
+ bson_append_int( command, "w", write_concern->w );
+ }
+
+ if( write_concern->wtimeout ) {
+ bson_append_int( command, "wtimeout", write_concern->wtimeout );
+ }
+
+ if( write_concern->j ) {
+ bson_append_int( command, "j", write_concern->j );
+ }
+
+ if( write_concern->fsync ) {
+ bson_append_int( command, "fsync", write_concern->fsync );
+ }
+
+ bson_finish( command );
+
+ /* write_concern now owns the BSON command object.
+ * This is freed in mongo_write_concern_destroy(). */
+ write_concern->cmd = command;
+
+ return MONGO_OK;
}
+MONGO_EXPORT void mongo_write_concern_destroy( mongo_write_concern *write_concern ) {
+ if( !write_concern )
+ return;
+
+ if( write_concern->cmd )
+ bson_destroy( write_concern->cmd );
+
+ bson_free( write_concern->cmd );
+}
+
+MONGO_EXPORT void mongo_set_write_concern( mongo *conn,
+ mongo_write_concern *write_concern ) {
+
+ conn->write_concern = write_concern;
+}
+
+/**
+ * Free the write_concern object (specifically, the BSON object that it holds).
+ */
+MONGO_EXPORT void mongo_write_concern_destroy( mongo_write_concern *write_concern );
+
static int mongo_cursor_op_query( mongo_cursor *cursor ) {
int res;
mongo_cursor_set_limit( cursor, 1 );
if ( mongo_cursor_next( cursor ) == MONGO_OK ) {
- bson_init_size( out, bson_size( (bson *)&cursor->current ) );
- memcpy( out->data, cursor->current.data,
- bson_size( (bson *)&cursor->current ) );
- out->finished = 1;
+ if( out ) {
+ bson_init_size( out, bson_size( (bson *)&cursor->current ) );
+ memcpy( out->data, cursor->current.data,
+ bson_size( (bson *)&cursor->current ) );
+ out->finished = 1;
+ }
mongo_cursor_destroy( cursor );
return MONGO_OK;
} else {
strncpy( idxns, ns, 1024-16 );
strcpy( strchr( idxns, '.' ), ".system.indexes" );
- mongo_insert( conn, idxns, &b );
+ mongo_insert( conn, idxns, &b, NULL );
bson_destroy( &b );
*strchr( idxns, '.' ) = '\0'; /* just db not ns */
return mongo_cmd_get_last_error( conn, idxns, out );
}
-bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out ) {
+MONGO_EXPORT bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out ) {
bson b;
bson_bool_t success;
return success;
}
+MONGO_EXPORT int mongo_create_capped_collection( mongo *conn, const char *db,
+ const char *collection, int size, int max, bson *out ) {
+
+ bson b;
+ int result;
+
+ bson_init( &b );
+ bson_append_string( &b, "create", collection );
+ bson_append_bool( &b, "capped", 1 );
+ bson_append_int( &b, "size", size );
+ if( max > 0 )
+ bson_append_int( &b, "max", size );
+ bson_finish( &b );
+
+ result = mongo_run_command( conn, db, &b, out );
+
+ bson_destroy( &b );
+
+ return result;
+}
+
MONGO_EXPORT double mongo_count( mongo *conn, const char *db, const char *ns, const bson *query ) {
bson cmd;
bson out = {NULL, 0};
bson_append_finish_object( &pass_obj );
bson_finish( &pass_obj );
- res = mongo_update( conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT );
+ res = mongo_update( conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT, NULL );
bson_free( ns );
bson_destroy( &user_obj );
MONGO_EXTERN_C_START
#define MONGO_MAJOR 0
-#define MONGO_MINOR 5
-#define MONGO_PATCH 2
+#define MONGO_MINOR 6
+#define MONGO_PATCH 0
#define MONGO_OK 0
#define MONGO_ERROR -1
MONGO_SOCKET_ERROR, /**< Other socket error. */
MONGO_READ_SIZE_ERROR, /**< The response is not the expected length. */
MONGO_COMMAND_FAILED, /**< The command returned with 'ok' value of 0. */
+ MONGO_WRITE_ERROR, /**< Write with given write_concern returned an error. */
MONGO_NS_INVALID, /**< The name for the ns (database or collection) is invalid. */
MONGO_BSON_INVALID, /**< BSON not valid for the specified op. */
MONGO_BSON_NOT_FINISHED, /**< BSON object has not been finished. */
- MONGO_BSON_TOO_LARGE /**< BSON object exceeds max BSON size. */
+ MONGO_BSON_TOO_LARGE, /**< BSON object exceeds max BSON size. */
+ MONGO_WRITE_CONCERN_INVALID /**< Supplied write concern object is invalid. */
} mongo_error_t;
typedef enum mongo_cursor_error_t {
MONGO_UPDATE_BASIC = 0x4
};
+enum mongo_insert_opts {
+ MONGO_CONTINUE_ON_ERROR = 0x1
+};
+
enum mongo_cursor_opts {
MONGO_TAILABLE = ( 1<<1 ), /**< Create a tailable cursor. */
MONGO_SLAVE_OK = ( 1<<2 ), /**< Allow queries on a non-primary node. */
struct mongo_host_port *next;
} mongo_host_port;
+typedef struct mongo_write_concern {
+ int w; /**< Number of nodes this write should be replicated to. */
+ int wtimeout; /**< Number of milliseconds before replication timeout. */
+ int j; /**< If non-zero, block until the journal sync. */
+ int fsync; /**< Same a j with journaling enabled; otherwise, call fsync. */
+ const char *mode; /**< Either "majority" or a getlasterrormode. Overrides w value. */
+
+ bson *cmd; /**< The BSON object representing the getlasterror command. */
+} mongo_write_concern;
+
typedef struct {
mongo_host_port *seeds; /**< List of seeds provided by the user. */
mongo_host_port *hosts; /**< List of host/ports given by the replica set */
int op_timeout_ms; /**< Read and write timeout in milliseconds. */
int max_bson_size; /**< Largest BSON object allowed on this connection. */
bson_bool_t connected; /**< Connection status. */
+ mongo_write_concern *write_concern; /**< The default write concern. */
mongo_error_t err; /**< Most recent driver error code. */
int errcode; /**< Most recent errno or WSAGetLastError(). */
int skip; /**< Bitfield containing cursor options. */
} mongo_cursor;
-/* Connection API */
-
-MONGO_EXPORT mongo* mongo_create();
-MONGO_EXPORT void mongo_dispose(mongo* conn);
-MONGO_EXPORT int mongo_get_err(mongo* conn);
-MONGO_EXPORT int mongo_is_connected(mongo* conn);
-MONGO_EXPORT int mongo_get_op_timeout(mongo* conn);
-MONGO_EXPORT const char* mongo_get_primary(mongo* conn);
-MONGO_EXPORT int mongo_get_socket(mongo* conn) ;
-MONGO_EXPORT int mongo_get_host_count(mongo* conn);
-MONGO_EXPORT const char* mongo_get_host(mongo* conn, int i);
-MONGO_EXPORT mongo_cursor* mongo_cursor_create();
-MONGO_EXPORT void mongo_cursor_dispose(mongo_cursor* cursor);
-MONGO_EXPORT int mongo_get_server_err(mongo* conn);
-MONGO_EXPORT const char* mongo_get_server_err_string(mongo* conn);
-
-/**
- * Set an error this mongo connection object. Mostly for internal use.
- *
- * @param conn a mongo connection object.
- * @param err a driver error code of mongo_error_t.
- * @param errstr a string version of the error.
- * @param errorcode Currently errno or WSAGetLastError().
- */
-MONGO_EXPORT void __mongo_set_error( mongo *conn, mongo_error_t err,
- const char *errstr, int errorcode );
-/**
- * Clear all errors stored on this mongo connection object.
- *
- * @param conn a mongo connection object.
- */
-MONGO_EXPORT void mongo_clear_errors( mongo *conn );
+/*********************************************************************
+Connection API
+**********************************************************************/
/** Initialize sockets for Windows.
*/
*/
MONGO_EXPORT void mongo_destroy( mongo *conn );
+/**
+ * Specify the write concern object that this connection should use
+ * by default for all writes (inserts, updates, and deletes). This value
+ * can be overridden by passing a write_concern object to any write function.
+ *
+ * @param conn a mongo object.
+ * @param write_concern pointer to a write concern object.
+ *
+ */
+MONGO_EXPORT void mongo_set_write_concern( mongo *conn,
+ mongo_write_concern *write_concern );
+
+
+/*********************************************************************
+CRUD API
+**********************************************************************/
+
/**
* Insert a BSON document into a MongoDB server. This function
* will fail if the supplied BSON struct is not UTF-8 or if
* the keys are invalid for insert (contain '.' or start with '$').
*
+ * The default write concern set on the conn object will be used.
+ *
* @param conn a mongo object.
* @param ns the namespace.
* @param data the bson data.
+ * @param custom_write_concern a write concern object that will
+ * override any write concern set on the conn object.
*
* @return MONGO_OK or MONGO_ERROR. If the conn->err
* field is MONGO_BSON_INVALID, check the err field
* on the bson struct for the reason.
*/
-MONGO_EXPORT int mongo_insert( mongo *conn, const char *ns, const bson *data );
+MONGO_EXPORT int mongo_insert( mongo *conn, const char *ns, const bson *data,
+ mongo_write_concern *custom_write_concern );
/**
* Insert a batch of BSON documents into a MongoDB server. This function
* will fail if any of the documents to be inserted is invalid.
*
+ * The default write concern set on the conn object will be used.
+ *
* @param conn a mongo object.
* @param ns the namespace.
* @param data the bson data.
* @param num the number of documents in data.
+ * @param custom_write_concern a write concern object that will
+ * override any write concern set on the conn object.
+ * @param flags flags on this batch insert. Currently, this value
+ * may be 0 or MONGO_CONTINUE_ON_ERROR, which will cause the
+ * batch insert to continue even if a given insert in the batch fails.
*
* @return MONGO_OK or MONGO_ERROR.
*
*/
-MONGO_EXPORT int mongo_insert_batch( mongo *conn , const char *ns ,
- const bson **data , int num );
+MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns,
+ const bson **data, int num, mongo_write_concern *custom_write_concern,
+ int flags );
/**
* Update a document in a MongoDB server.
*
+ * The default write concern set on the conn object will be used.
+ *
* @param conn a mongo object.
* @param ns the namespace.
* @param cond the bson update query.
* @param op the bson update data.
* @param flags flags for the update.
+ * @param custom_write_concern a write concern object that will
+ * override any write concern set on the conn object.
*
* @return MONGO_OK or MONGO_ERROR with error stored in conn object.
*
*/
MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond,
- const bson *op, int flags );
+ const bson *op, int flags, mongo_write_concern *custom_write_concern );
/**
* Remove a document from a MongoDB server.
*
+ * The default write concern set on the conn object will be used.
+ *
* @param conn a mongo object.
* @param ns the namespace.
* @param cond the bson query.
+ * @param custom_write_concern a write concern object that will
+ * override any write concern set on the conn object.
*
* @return MONGO_OK or MONGO_ERROR with error stored in conn object.
*/
-MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond );
+MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond,
+ mongo_write_concern *custom_write_concern );
+
+
+/*********************************************************************
+Write Concern API
+**********************************************************************/
+
+/**
+ * Initialize a mongo_write_concern object. Effectively zeroes out the struct.
+ *
+ */
+MONGO_EXPORT void mongo_write_concern_init( mongo_write_concern *write_concern );
+
+/**
+ * Finish this write concern object by serializing the literal getlasterror
+ * command that will be sent to the server.
+ *
+ * You must call mongo_write_concern_destroy() to free the serialized BSON.
+ *
+ */
+MONGO_EXPORT int mongo_write_concern_finish( mongo_write_concern *write_concern );
+
+/**
+ * Free the write_concern object (specifically, the BSON that it owns).
+ *
+ */
+MONGO_EXPORT void mongo_write_concern_destroy( mongo_write_concern *write_concern );
+
+/*********************************************************************
+Cursor API
+**********************************************************************/
/**
* Find documents in a MongoDB server.
MONGO_EXPORT int mongo_find_one( mongo *conn, const char *ns, const bson *query,
const bson *fields, bson *out );
-/* MongoDB Helper Functions */
+
+/*********************************************************************
+Command API and Helpers
+**********************************************************************/
/**
* Count the number of documents in a collection matching a query.
const bson *query );
/**
- * Create a compouned index.
+ * Create a compound index.
*
* @param conn a mongo object.
* @param ns the namespace.
*
* @return MONGO_OK if index is created successfully; otherwise, MONGO_ERROR.
*/
-MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns, const bson *key, int options, bson *out );
+MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns,
+ const bson *key, int options, bson *out );
+
+/**
+ * Create a capped collection.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace (e.g., "dbname.collectioname")
+ * @param size the size of the capped collection in bytes.
+ * @param max the max number of documents this collection is
+ * allowed to contain. If zero, this argument will be ignored
+ * and the server will use the collection's size to age document out.
+ * If using this option, ensure that the total size can contain this
+ * number of documents.
+ */
+MONGO_EXPORT int mongo_create_capped_collection( mongo *conn, const char *db,
+ const char *collection, int size, int max, bson *out );
/**
* Create an index with a single key.
*
* @return true if the index was created.
*/
-bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out );
-
-/* ----------------------------
- COMMANDS
- ------------------------------ */
+MONGO_EXPORT bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns,
+ const char *field, int options, bson *out );
/**
* Run a command on a MongoDB server.
*
* @return MONGO_OK if the command ran without error.
*/
-MONGO_EXPORT int mongo_run_command( mongo *conn, const char *db, const bson *command, bson *out );
+MONGO_EXPORT int mongo_run_command( mongo *conn, const char *db,
+ const bson *command, bson *out );
/**
* Run a command that accepts a simple string key and integer value.
* @return true if the command ran without error.
*
*/
-MONGO_EXPORT int mongo_simple_str_command( mongo *conn, const char *db, const char *cmd, const char *arg, bson *out );
+MONGO_EXPORT int mongo_simple_str_command( mongo *conn, const char *db,
+ const char *cmd, const char *arg, bson *out );
/**
* Drop a database.
*
* @return true if the collection drop was successful.
*/
-MONGO_EXPORT int mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out );
+MONGO_EXPORT int mongo_cmd_drop_collection( mongo *conn, const char *db,
+ const char *collection, bson *out );
/**
* Add a database user.
*
* @return MONGO_OK or MONGO_ERROR.
*/
-MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass );
+MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db,
+ const char *user, const char *pass );
/**
* Authenticate a user.
*
* @return MONGO_OK on sucess and MONGO_ERROR on failure.
*/
-MONGO_EXPORT int mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass );
+MONGO_EXPORT int mongo_cmd_authenticate( mongo *conn, const char *db,
+ const char *user, const char *pass );
/**
* Check if the current server is a master.
MONGO_EXPORT void mongo_cmd_reset_error( mongo *conn, const char *db );
+/*********************************************************************
+Utility API
+**********************************************************************/
+
+MONGO_EXPORT mongo* mongo_create();
+MONGO_EXPORT void mongo_dispose(mongo* conn);
+MONGO_EXPORT int mongo_get_err(mongo* conn);
+MONGO_EXPORT int mongo_is_connected(mongo* conn);
+MONGO_EXPORT int mongo_get_op_timeout(mongo* conn);
+MONGO_EXPORT const char* mongo_get_primary(mongo* conn);
+MONGO_EXPORT int mongo_get_socket(mongo* conn) ;
+MONGO_EXPORT int mongo_get_host_count(mongo* conn);
+MONGO_EXPORT const char* mongo_get_host(mongo* conn, int i);
+MONGO_EXPORT mongo_cursor* mongo_cursor_create();
+MONGO_EXPORT void mongo_cursor_dispose(mongo_cursor* cursor);
+MONGO_EXPORT int mongo_get_server_err(mongo* conn);
+MONGO_EXPORT const char* mongo_get_server_err_string(mongo* conn);
+
+/**
+ * Set an error on a mongo connection object. Mostly for internal use.
+ *
+ * @param conn a mongo connection object.
+ * @param err a driver error code of mongo_error_t.
+ * @param errstr a string version of the error.
+ * @param errorcode Currently errno or WSAGetLastError().
+ */
+MONGO_EXPORT void __mongo_set_error( mongo *conn, mongo_error_t err,
+ const char *errstr, int errorcode );
+/**
+ * Clear all errors stored on a mongo connection object.
+ *
+ * @param conn a mongo connection object.
+ */
+MONGO_EXPORT void mongo_clear_errors( mongo *conn );
+
MONGO_EXTERN_C_END
#endif
switch_mutex_lock(globals.mongo_mutex);
- if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
+ if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr, NULL) != MONGO_OK) {
if (globals.mongo_conn->err == MONGO_IO_ERROR) {
mongo_error_t db_status;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MongoDB connection failed; attempting reconnect...\n");
status = SWITCH_STATUS_FALSE;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MongoDB connection re-established.\n");
- if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
+ if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr, NULL) != MONGO_OK) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err);
status = SWITCH_STATUS_FALSE;
}