]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
mod_cdr_mongodb: update MongoDB driver to v0.6
authorDaniel Swarbrick <daniel.swarbrick@gmail.com>
Sun, 14 Oct 2012 11:45:50 +0000 (13:45 +0200)
committerDaniel Swarbrick <daniel.swarbrick@gmail.com>
Sun, 14 Oct 2012 11:45:50 +0000 (13:45 +0200)
src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md
src/mod/event_handlers/mod_cdr_mongodb/driver/README.md
src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c
src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h
src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c
src/mod/event_handlers/mod_cdr_mongodb/driver/src/env_win32.c
src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c
src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c
src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h
src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c

index 1659192909292b7197b0a0de6a842d13867ec5c4..9f1e9af517d8031d47e1f8f71f27a3e0e38b63c7 100644 (file)
@@ -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
 
index d8c752668360bcc48364fbd59f02e8c63d25f58e..d5ecb271610fdd946b1eb1080236642eed00cf19 100644 (file)
@@ -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
index 26d17d86965ee970ece4b96a4f2b437c45b7060a..217a412f3ca0d3a3626bc74ca980b4ad752ba35a 100644 (file)
@@ -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 );
index b83d1ca9c9066397f51d5b3f71f3f8e009a6a72c..b8669482d273855aa5f1325fb22f0f63a55080a4 100644 (file)
@@ -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.
  *
index 00ecf18c92e6b1dfd6415e1c22ee31a36b095629..45d0d275268b38d4aacbf98345f07f3350e3a09f 100644 (file)
@@ -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 ) {
index 0b301cea6123796365387b9980bb3ba3b37cf6a4..4b38928b05965f4f06876f464981681fab42d488 100644 (file)
@@ -24,8 +24,8 @@
 #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
 
@@ -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 );
 }
index 9b2d1455ef669ec4cbaa99b740273e92256af74d..db80dea23c986c5413a8f79334e05e5fbd0dcbe2 100644 (file)
@@ -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 );
     }
 
index eb16392297db3efcf490ab036ddd34814b1aad08..daf92b4c7be2a8d66adbbd8ee67788db2bad4085 100644 (file)
@@ -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,57 +756,77 @@ 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 ) {
 
-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 )
@@ -798,14 +838,84 @@ MONGO_EXPORT int mongo_insert( mongo *conn , const char *ns , const bson *bson )
     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.
@@ -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,9 +990,98 @@ 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;
@@ -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 );
index dd4c4b4f403b571c7ab6f9cfa37b2c45dff348cc..57ba9216d8c0f70202006e32d5a0a1ffbb2400ee 100644 (file)
@@ -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
index fec5b516efdc1abb0f9b8b91c698b4db3f97794d..56f06e45b12754e34dea785de3cd3c6fdb0cd1dd 100644 (file)
@@ -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;
                                }