From 10093b44c026129b60644cd46eaf2a57681ef230 Mon Sep 17 00:00:00 2001 From: Daniel Swarbrick Date: Sun, 14 Oct 2012 13:45:50 +0200 Subject: [PATCH] mod_cdr_mongodb: update MongoDB driver to v0.6 --- .../mod_cdr_mongodb/driver/HISTORY.md | 32 ++ .../mod_cdr_mongodb/driver/README.md | 26 +- .../mod_cdr_mongodb/driver/src/bson.c | 8 +- .../mod_cdr_mongodb/driver/src/bson.h | 4 +- .../mod_cdr_mongodb/driver/src/encoding.c | 21 +- .../mod_cdr_mongodb/driver/src/env_win32.c | 9 +- .../mod_cdr_mongodb/driver/src/gridfs.c | 16 +- .../mod_cdr_mongodb/driver/src/mongo.c | 330 +++++++++++++++--- .../mod_cdr_mongodb/driver/src/mongo.h | 219 +++++++++--- .../mod_cdr_mongodb/mod_cdr_mongodb.c | 4 +- 10 files changed, 543 insertions(+), 126 deletions(-) diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md b/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md index 1659192909..9f1e9af517 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md @@ -1,5 +1,37 @@ # 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 diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md b/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md index d8c7526683..d5ecb27161 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md @@ -9,17 +9,31 @@ by providing an interface for platform-specific modules. 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 @@ -35,14 +49,6 @@ Specific error codes and error strings are then stored in the `err` and `errstr` `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 diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c index 26d17d8696..217a412f3c 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c @@ -32,7 +32,7 @@ static const int zero = 0; /* 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 @@ -308,7 +308,7 @@ MONGO_EXPORT void bson_print_raw( const char *data , int depth ) { ------------------------------ */ 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) { @@ -973,6 +973,10 @@ MONGO_EXPORT bson_err_handler set_bson_err_handler( bson_err_handler func ) { 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 ); diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h index b83d1ca9c9..b8669482d2 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h @@ -959,13 +959,15 @@ typedef int (*bson_sprintf_func)( char *, const char *, ... ); 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. * diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c index 00ecf18c92..45d0d27526 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c @@ -101,6 +101,24 @@ static int isLegalUTF8( const unsigned char *source, int length ) { 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 ) { @@ -109,7 +127,8 @@ static int bson_validate_string( bson *b, const unsigned char *string, 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 ) { diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/env_win32.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/env_win32.c index 0b301cea61..4b38928b05 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/env_win32.c +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/env_win32.c @@ -24,8 +24,8 @@ #include // send,recv,socklen_t etc #include // addrinfo #else -#include -#include +#include // send,recv,socklen_t etc +#include typedef int socklen_t; #endif @@ -33,11 +33,6 @@ typedef int socklen_t; # 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 ); } diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c index 9b2d1455ef..db80dea23c 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c @@ -170,7 +170,7 @@ static int gridfs_insert_file( gridfs *gfs, const char *name, 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; @@ -198,7 +198,7 @@ MONGO_EXPORT int gridfs_store_buffer( gridfs *gfs, const char *data, 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; @@ -259,7 +259,7 @@ MONGO_EXPORT void gridfile_write_buffer( gridfile *gfile, const char *data, 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; @@ -272,7 +272,7 @@ MONGO_EXPORT void gridfile_write_buffer( gridfile *gfile, const char *data, 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--; @@ -302,7 +302,7 @@ MONGO_EXPORT int gridfile_writer_done( gridfile *gfile ) { 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; @@ -344,7 +344,7 @@ int gridfs_store_file( gridfs *gfs, const char *filename, 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++; @@ -390,14 +390,14 @@ MONGO_EXPORT void gridfs_remove_filename( gridfs *gfs, const char *filename ) { 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 ); } diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c index eb16392297..daf92b4c7b 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c @@ -57,6 +57,8 @@ const char* _get_host_port(mongo_host_port* hp) { 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); } @@ -122,7 +124,7 @@ MONGO_EXPORT void __mongo_set_error( mongo *conn, mongo_error_t err, const char 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'; } } @@ -134,6 +136,23 @@ MONGO_EXPORT void mongo_clear_errors( mongo *conn ) { 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; @@ -378,7 +397,6 @@ MONGO_EXPORT void mongo_init_sockets( void ) { 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; @@ -412,6 +430,8 @@ MONGO_EXPORT void mongo_replset_init( mongo *conn, const char *name ) { 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 ) { @@ -736,12 +756,109 @@ static int mongo_cursor_bson_valid( mongo_cursor *cursor, const bson *bson ) { return MONGO_OK; } -/* MongoDB CRUD API */ +static int mongo_check_last_error( mongo *conn, const char *ns, + mongo_write_concern *write_concern ) { + + bson response = {NULL, 0}; + bson fields; + bson_iterator it; + int res = 0; + char *cmd_ns = mongo_ns_to_cmd_db( ns ); + + res = mongo_find_one( conn, cmd_ns, write_concern->cmd, bson_empty( &fields ), &response ); + bson_free( cmd_ns ); + + if( res != MONGO_OK ) + return MONGO_ERROR; + else { + if( ( bson_find( &it, &response, "$err" ) == BSON_STRING ) || + ( bson_find( &it, &response, "err" ) == BSON_STRING ) ) { + + __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; + } +} + +static int mongo_choose_write_concern( mongo *conn, + mongo_write_concern *custom_write_concern, + mongo_write_concern **write_concern ) { + + if( custom_write_concern ) { + *write_concern = custom_write_concern; + } + else if( conn->write_concern ) { + *write_concern = conn->write_concern; + } + + 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; +} + + +/********************************************************************* +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; + + 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 ) + + 1 + bson_size( bson ) + , 0, 0, MONGO_OP_INSERT ); + + data = &mm->data; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, ns, strlen( ns ) + 1 ); + data = mongo_data_append( data, bson->data, bson_size( bson ) ); + + + /* 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 ) { + 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; @@ -761,51 +878,44 @@ MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns, 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; - data = mongo_data_append32( data, &ZERO ); + 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; idata, bson_size( bsons[i] ) ); } - 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; + } -MONGO_EXPORT int mongo_insert( mongo *conn , const char *ns , const bson *bson ) { - - char *data; - mongo_message *mm; - - 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; + return mongo_check_last_error( conn, ns, write_concern ); + } + else { + return mongo_message_send( conn, mm ); } - - mm = mongo_message_create( 16 /* header */ - + 4 /* ZERO */ - + strlen( ns ) - + 1 + bson_size( bson ) - , 0, 0, MONGO_OP_INSERT ); - - data = &mm->data; - data = mongo_data_append32( data, &ZERO ); - 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 ); } 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. @@ -814,6 +924,11 @@ MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond, 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 @@ -829,12 +944,26 @@ MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond, 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. @@ -843,6 +972,11 @@ MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond ) { 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 @@ -856,10 +990,99 @@ MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond ) { 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; bson empty; @@ -1008,10 +1231,12 @@ MONGO_EXPORT int mongo_find_one( mongo *conn, const char *ns, const bson *query, 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 { @@ -1171,14 +1396,14 @@ MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns, const bson *ke 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; @@ -1191,6 +1416,27 @@ bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char * 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}; @@ -1404,7 +1650,7 @@ MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db, const char *us 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 ); diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h index dd4c4b4f40..57ba9216d8 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h @@ -26,8 +26,8 @@ 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 @@ -51,10 +51,12 @@ typedef enum mongo_error_t { 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 { @@ -85,6 +87,10 @@ enum mongo_update_opts { 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. */ @@ -137,6 +143,16 @@ typedef struct mongo_host_port { 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 */ @@ -153,6 +169,7 @@ typedef struct mongo { 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(). */ @@ -176,38 +193,9 @@ typedef struct { 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. */ @@ -344,61 +332,130 @@ MONGO_EXPORT void mongo_disconnect( mongo *conn ); */ 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. @@ -527,7 +584,10 @@ MONGO_EXPORT int mongo_cursor_destroy( mongo_cursor *cursor ); 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. @@ -544,7 +604,7 @@ MONGO_EXPORT double mongo_count( mongo *conn, const char *db, const char *coll, const bson *query ); /** - * Create a compouned index. + * Create a compound index. * * @param conn a mongo object. * @param ns the namespace. @@ -556,7 +616,23 @@ MONGO_EXPORT double mongo_count( mongo *conn, const char *db, const char *coll, * * @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. @@ -569,11 +645,8 @@ MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns, const bson *ke * * @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. @@ -585,7 +658,8 @@ bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char * * * @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. @@ -614,7 +688,8 @@ MONGO_EXPORT int mongo_simple_int_command( mongo *conn, const char *db, * @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. @@ -636,7 +711,8 @@ MONGO_EXPORT int mongo_cmd_drop_db( mongo *conn, const char *db ); * * @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. @@ -648,7 +724,8 @@ MONGO_EXPORT int mongo_cmd_drop_collection( mongo *conn, const char *db, const c * * @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. @@ -660,7 +737,8 @@ MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db, const char *us * * @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. @@ -706,6 +784,41 @@ MONGO_EXPORT int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *ou 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 diff --git a/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c b/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c index fec5b516ef..56f06e45b1 100644 --- a/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c +++ b/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c @@ -295,7 +295,7 @@ static switch_status_t my_on_reporting(switch_core_session_t *session) 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"); @@ -306,7 +306,7 @@ static switch_status_t my_on_reporting(switch_core_session_t *session) 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; }