More tests, and new code for append/prepend.
authorBrian Aker <brian@tangent.org>
Thu, 15 Nov 2007 16:10:49 +0000 (08:10 -0800)
committerBrian Aker <brian@tangent.org>
Thu, 15 Nov 2007 16:10:49 +0000 (08:10 -0800)
Also work t make this a release.

13 files changed:
ChangeLog
docs/Makefile.am
docs/libmemcached.pod
docs/memcached_set.pod
include/memcached.h
lib/Makefile.am
lib/common.h
lib/memcached_do.c
lib/memcached_response.c
lib/memcached_storage.c
lib/memcached_version.c [new file with mode: 0644]
support/libmemcached.spec.in
tests/function.c

index 3ce19953399ccd91a1a56e0db4e20ded2947fd97..84684535d57b06d3e9a87d6c93f2c491ee3fa5f4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,4 @@
-0.9
+0.9 Thu Nov 15 07:44:00 PST 2007
   * fix for when no servers are definied.
   * different buffers are now kept for different connections to
     speed up async efforts
@@ -6,6 +6,10 @@
   * Fixed bug in cases where zero length keys were provided
   * Thread cleanup issue in memslap
   * No hostname lookup on reconnect
+  * Fix for flag settings (was doing hex by accident!)
+  * Support for 1.2.4 server additions "prepend" and "append" added.
+  * Added memcached_version()... not sure if I will make this public 
+    or not.
 
 0.8 Mon Nov  5 10:40:41 PST 2007
   * Adding support for CRC hash method 
index 5d8a812b8d5886af5d74d6ddac7e901cc98faa37..723dfe2c15d98cbaa039ce310c84da0aec17ffa1 100644 (file)
@@ -31,6 +31,7 @@ man_MANS = libmemcached.3\
        memslap.1\\r
        memstat.1\\r
        memcached_add.3\\r
+       memcached_append.3\\r
        memcached_behavior_get.3\\r
        memcached_behavior_set.3\\r
        memcached_clone.3\\r
@@ -38,10 +39,12 @@ man_MANS = libmemcached.3\
        memcached_decrement.3\\r
        memcached_delete.3\\r
        memcached_fetch.3\\r
+       memcached_fetch_result.3\\r
        memcached_free.3\\r
        memcached_get.3\\r
        memcached_increment.3\\r
        memcached_mget.3\\r
+       memcached_prepend.3\\r
        memcached_replace.3\\r
        memcached_server_add.3\\r
        memcached_server_count.3\\r
@@ -84,6 +87,12 @@ memcached_replace.3: memcached_set.pod
 memcached_add.3: memcached_set.pod\r
        pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_add.3\r
 \r
+memcached_prepend.3: memcached_set.pod\r
+       pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_prepend.3\r
+\r
+memcached_append.3: memcached_set.pod\r
+       pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_append.3\r
+\r
 memcached_delete.3: memcached_delete.pod\r
        pod2man -c "libmemcached" -r "" -s 3 memcached_delete.pod > memcached_delete.3\r
 \r
index 158af9603f234b37bb38cde5524fd6f7c033e5d7..02bd04e0d529e3f82bd91a6692cc21cb478ad028 100755 (executable)
@@ -62,6 +62,6 @@ Brian Aker, E<lt>brian@tangent.orgE<gt>
 
 =head1 SEE ALSO
 
-memcached(1) libmemcached_examples(3) libmemcached(1) memcat(1) memcp(1) memflush(1) memrm(1) memslap(1) memstat(1) memcached_fetch(3) memcached_replace(3) memcached_server_list_free(3) libmemcached_examples(3) memcached_clone(3) memcached_free(3) memcached_server_add(3) memcached_server_push(3) memcached_add(3) memcached_get(3) memcached_server_count(3) memcached_servers_parse(3) memcached_create(3) memcached_increment(3) memcached_server_list(3) memcached_set(3) memcached_decrement(3) memcached_mget(3) memcached_server_list_append(3) memcached_strerror(3) memcached_delete(3) memcached_quit(3) memcached_server_list_count(3) memcached_verbosity(3) memcached_server_add_unix_socket(3) memcahed_result_create(3)  memcached_result_free(3)  memcached_result_key_value(3)  memcached_result_key_length(3)  memcached_result_value(3)  memcached_result_length(3)  memcached_result_flags(3)  memcached_result_cas(3) memcached_result_st(3)
+memcached(1) libmemcached_examples(3) libmemcached(1) memcat(1) memcp(1) memflush(1) memrm(1) memslap(1) memstat(1) memcached_fetch(3) memcached_replace(3) memcached_server_list_free(3) libmemcached_examples(3) memcached_clone(3) memcached_free(3) memcached_server_add(3) memcached_server_push(3) memcached_add(3) memcached_get(3) memcached_server_count(3) memcached_servers_parse(3) memcached_create(3) memcached_increment(3) memcached_server_list(3) memcached_set(3) memcached_decrement(3) memcached_mget(3) memcached_server_list_append(3) memcached_strerror(3) memcached_delete(3) memcached_quit(3) memcached_server_list_count(3) memcached_verbosity(3) memcached_server_add_unix_socket(3) memcahed_result_create(3)  memcached_result_free(3)  memcached_result_key_value(3)  memcached_result_key_length(3)  memcached_result_value(3)  memcached_result_length(3)  memcached_result_flags(3)  memcached_result_cas(3) memcached_result_st(3) memcached_append(3) memcached_prepend(3) memcached_fetch_result(3)
 =cut
 
index 93037c14ce60473097dde66008c9875e0a0ff235..39dd86d2bd6e21c0d3c0dece90aefaa4d2e1e0bc 100755 (executable)
@@ -31,6 +31,20 @@ C Client Library for memcached (libmemcached, -lmemcached)
                        time_t expiration,
                        uint16_t flags);
 
+  memcached_return 
+    memcached_prepend(memcached_st *ptr, 
+                      char *key, size_t key_length,
+                      char *value, size_t value_length, 
+                      time_t expiration,
+                      uint16_t flags)
+
+  memcached_return 
+    memcached_append(memcached_st *ptr, 
+                     char *key, size_t key_length,
+                      char *value, size_t value_length, 
+                      time_t expiration,
+                      uint16_t flags)
+
 =head1 DESCRIPTION
 
 memcached_set(), memcached_add(), and memcached_replace() are all used to
@@ -51,6 +65,12 @@ found on the server an error occurs.
 memcached_add() adds an object to the server. If the object is found on the
 server an error occurs, otherwise the value is stored.
 
+memcached_prepend() places a segment of data before the last piece of data 
+stored. Currently expiration and key are not used in the server.
+
+memcached_append() places a segment of data at the end of the last piece of 
+data stored. Currently expiration and key are not used in the server.
+
 memcached_set() with non-blocking IO is the fastest way to store data on the
 server.
 
index 7cb057d2b31703dc1a18cbcd318f6aa2a6ed021f..f9ae2706ad78373666d74ca3fbbe07ad78ccaec7 100644 (file)
@@ -25,6 +25,7 @@ typedef struct memcached_result_st memcached_result_st;
 typedef struct memcached_string_st memcached_string_st;
 typedef struct memcached_server_st memcached_server_st;
 
+#define MEMCACHED_VERSION_STRING 12
 #define MEMCACHED_DEFAULT_PORT 11211
 #define MEMCACHED_DEFAULT_COMMAND_SIZE 350
 #define SMALL_STRING_LEN 1024
@@ -111,6 +112,9 @@ struct memcached_server_st {
   char *read_ptr;
   struct sockaddr_in servAddr;
   memcached_connection type;
+  uint8_t major_version;
+  uint8_t minor_version;
+  uint8_t micro_version;
 };
 
 struct memcached_stat_st {
@@ -118,7 +122,7 @@ struct memcached_stat_st {
   unsigned int uptime;
   unsigned int threads;
   time_t time;
-  char version[8];
+  char version[MEMCACHED_VERSION_STRING];
   unsigned int pointer_size;
   unsigned int rusage_user;
   unsigned int rusage_system;
@@ -215,6 +219,22 @@ memcached_return memcached_replace(memcached_st *ptr, char *key, size_t key_leng
                                    char *value, size_t value_length, 
                                    time_t expiration,
                                    uint16_t  flags);
+memcached_return memcached_append(memcached_st *ptr, 
+                                  char *key, size_t key_length,
+                                  char *value, size_t value_length, 
+                                  time_t expiration,
+                                  uint16_t flags);
+memcached_return memcached_prepend(memcached_st *ptr, 
+                                   char *key, size_t key_length,
+                                   char *value, size_t value_length, 
+                                   time_t expiration,
+                                   uint16_t flags);
+memcached_return memcached_cas(memcached_st *ptr, 
+                               char *key, size_t key_length,
+                               char *value, size_t value_length, 
+                               time_t expiration,
+                               uint16_t flags,
+                               uint64_t cas);
 
 /* Get functions */
 char *memcached_get(memcached_st *ptr, char *key, size_t key_length,
index 2908fbb45d7435f4ecb5ed2621cdf9b93cf2990e..8a96e41ef2b2d62f7d333d40ddbcd628eebe1579 100644 (file)
@@ -46,7 +46,9 @@ libmemcached_la_SOURCES = crc.c \
                          memcached_string.c \
                          memcached_stats.c \
                           memcached_strerror.c \
-                         memcached_verbosity.c 
+                         memcached_verbosity.c \
+                         memcached_version.c 
+
 libmemcached_la_LIBADD =
 libmemcached_la_LDFLAGS = -version-info $(MEMCACHED_LIBRARY_VERSION)
 
index ae531e160c0489d8f97c49c852b791d26e416c02..3b8946a13ba1dac2afce36f857f79b4386a7cbf9 100644 (file)
@@ -80,6 +80,8 @@ memcached_return memcached_string_reset(memcached_string_st *string);
 void memcached_string_free(memcached_string_st *string);
 memcached_return memcached_do(memcached_st *ptr, unsigned int server_key, char *commmand, 
                               size_t command_length, char with_flush);
+memcached_return memcached_version(memcached_st *ptr);
+
 
 
 #endif /* __COMMON_H__ */
index ea0e705e1b5a352261a98b6b90a3acf077ff5777..c6bc96c07717a75e9af853920dde6bee7c246490 100644 (file)
@@ -6,6 +6,7 @@ memcached_return memcached_do(memcached_st *ptr, unsigned int server_key, char *
   memcached_return rc;
   ssize_t sent_length;
 
+  WATCHPOINT_ASSERT(command_length);
   WATCHPOINT_ASSERT(command);
 
   if (ptr->hosts[server_key].cursor_active)
index b90d861d9f3f194c5505ecaea82bd5c0d9fc6601..0755ae0c191cdeb61f217f330433c3ac2c8728ab 100644 (file)
@@ -51,7 +51,7 @@ memcached_return memcached_response(memcached_st *ptr,
 
   switch(buffer[0])
   {
-  case 'V': /* VALUE */
+  case 'V': /* VALUE || VERSION */
     return MEMCACHED_SUCCESS;
   case 'O': /* OK */
     return MEMCACHED_SUCCESS;
index c6fa63e5ff7ad2478d77db66614d256a0b0cdde7..8d2ed8c5d4adb0b3b312964f6f47830b3588a3f3 100644 (file)
@@ -14,17 +14,40 @@ typedef enum {
   SET_OP,
   REPLACE_OP,
   ADD_OP,
+  PREPEND_OP,
+  APPEND_OP,
+  CAS_OP,
 } memcached_storage_action;
 
 /* Inline this */
-#define storage_op_string(A) A == SET_OP ? "set" : ( A == REPLACE_OP ? "replace" : "add")
-
-static memcached_return memcached_send(memcached_st *ptr, 
-                                       char *key, size_t key_length, 
-                                       char *value, size_t value_length, 
-                                       time_t expiration,
-                                       uint16_t  flags,
-                                       memcached_storage_action verb)
+char *storage_op_string(memcached_storage_action verb)
+{
+  switch (verb)
+  {
+  case SET_OP:
+    return "set";
+  case REPLACE_OP:
+    return "replace";
+  case ADD_OP:
+    return "add";
+  case PREPEND_OP:
+    return "prepend";
+  case APPEND_OP:
+    return "append";
+  case CAS_OP:
+    return "cas";
+  };
+
+  return SET_OP;
+}
+
+static inline memcached_return memcached_send(memcached_st *ptr, 
+                                              char *key, size_t key_length, 
+                                              char *value, size_t value_length, 
+                                              time_t expiration,
+                                              uint16_t flags,
+                                              uint64_t cas,
+                                              memcached_storage_action verb)
 {
   char to_write;
   size_t write_length;
@@ -55,10 +78,18 @@ static memcached_return memcached_send(memcached_st *ptr,
     return rc;
 
 
-  write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, 
-                        "%s %.*s %u %llu %zu\r\n", storage_op_string(verb),
-                        (int)key_length, key, flags, 
-                        (unsigned long long)expiration, value_length);
+  if (cas)
+    write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, 
+                           "%s %.*s %u %llu %zu\r\n", storage_op_string(verb),
+                           (int)key_length, key, flags, 
+                           (unsigned long long)expiration, value_length);
+  else
+    write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, 
+                           "%s %.*s %u %llu %zu %llu\r\n", storage_op_string(verb),
+                           (int)key_length, key, flags, 
+                           (unsigned long long)expiration, value_length, 
+                           (unsigned long long)cas);
+
   if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE)
   {
     rc= MEMCACHED_WRITE_FAILURE;
@@ -102,7 +133,6 @@ static memcached_return memcached_send(memcached_st *ptr,
   {
     rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key);
   }
-
   if (rc == MEMCACHED_STORED)
     return MEMCACHED_SUCCESS;
   else 
@@ -117,38 +147,77 @@ error:
 memcached_return memcached_set(memcached_st *ptr, char *key, size_t key_length, 
                                char *value, size_t value_length, 
                                time_t expiration,
-                               uint16_t  flags)
+                               uint16_t flags)
 {
   memcached_return rc;
   LIBMEMCACHED_MEMCACHED_SET_START();
   rc= memcached_send(ptr, key, key_length, value, value_length,
-                         expiration, flags, SET_OP);
+                         expiration, flags, 0, SET_OP);
   LIBMEMCACHED_MEMCACHED_SET_END();
   return rc;
 }
 
-memcached_return memcached_add(memcached_st *ptr, char *key, size_t key_length,
+memcached_return memcached_add(memcached_st *ptr, 
+                               char *key, size_t key_length,
                                char *value, size_t value_length, 
                                time_t expiration,
-                               uint16_t  flags)
+                               uint16_t flags)
 {
   memcached_return rc;
   LIBMEMCACHED_MEMCACHED_ADD_START();
   rc= memcached_send(ptr, key, key_length, value, value_length,
-                         expiration, flags, ADD_OP);
+                         expiration, flags, 0, ADD_OP);
   LIBMEMCACHED_MEMCACHED_ADD_END();
   return rc;
 }
 
-memcached_return memcached_replace(memcached_st *ptr, char *key, size_t key_length,
+memcached_return memcached_replace(memcached_st *ptr, 
+                                   char *key, size_t key_length,
                                    char *value, size_t value_length, 
                                    time_t expiration,
-                                   uint16_t  flags)
+                                   uint16_t flags)
 {
   memcached_return rc;
   LIBMEMCACHED_MEMCACHED_REPLACE_START();
   rc= memcached_send(ptr, key, key_length, value, value_length,
-                         expiration, flags, REPLACE_OP);
+                         expiration, flags, 0, REPLACE_OP);
   LIBMEMCACHED_MEMCACHED_REPLACE_END();
   return rc;
 }
+
+memcached_return memcached_prepend(memcached_st *ptr, 
+                                   char *key, size_t key_length,
+                                   char *value, size_t value_length, 
+                                   time_t expiration,
+                                   uint16_t flags)
+{
+  memcached_return rc;
+  rc= memcached_send(ptr, key, key_length, value, value_length,
+                     expiration, flags, 0, PREPEND_OP);
+  return rc;
+}
+
+memcached_return memcached_append(memcached_st *ptr, 
+                                  char *key, size_t key_length,
+                                  char *value, size_t value_length, 
+                                  time_t expiration,
+                                  uint16_t flags)
+{
+  memcached_return rc;
+  rc= memcached_send(ptr, key, key_length, value, value_length,
+                     expiration, flags, 0, APPEND_OP);
+  return rc;
+}
+
+memcached_return memcached_cas(memcached_st *ptr, 
+                               char *key, size_t key_length,
+                               char *value, size_t value_length, 
+                               time_t expiration,
+                               uint16_t flags,
+                               uint64_t cas)
+{
+  memcached_return rc;
+  rc= memcached_send(ptr, key, key_length, value, value_length,
+                     expiration, flags, cas, APPEND_OP);
+  return rc;
+}
diff --git a/lib/memcached_version.c b/lib/memcached_version.c
new file mode 100644 (file)
index 0000000..b811a03
--- /dev/null
@@ -0,0 +1,44 @@
+#include "common.h"
+
+memcached_return memcached_version(memcached_st *ptr)
+{
+  unsigned int x;
+  size_t send_length;
+  memcached_return rc;
+  char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
+  char *response_ptr;
+
+  send_length= strlen("version\r\n");
+  memcpy(buffer, "version\r\n", send_length);
+
+  rc= MEMCACHED_SUCCESS;
+  for (x= 0; x < ptr->number_of_hosts; x++)
+  {
+    memcached_return rrc;
+
+    rrc= memcached_do(ptr, x, buffer, send_length, 1);
+    if (rrc != MEMCACHED_SUCCESS)
+    {
+      rc= MEMCACHED_SOME_ERRORS;
+      continue;
+    }
+
+    rrc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, x);
+    if (rrc != MEMCACHED_SUCCESS)
+      rc= MEMCACHED_SOME_ERRORS;
+
+    /* Find the space, and then move one past it to copy version */
+    response_ptr= index(buffer, ' ');
+    response_ptr++;
+
+    ptr->hosts[x].major_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10);
+    response_ptr= index(response_ptr, '.');
+    response_ptr++;
+    ptr->hosts[x].minor_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10);
+    response_ptr= index(response_ptr, '.');
+    response_ptr++;
+    ptr->hosts[x].micro_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10);
+  }
+
+  return rc;
+}
index 7815df31ea4ee14ffb43103afccf15a150620a63..bf112562a7c33f8f8a0564be33c59ed1ca65798c 100644 (file)
@@ -52,7 +52,7 @@ memcp - Copy files to memcached servers.
 %{_libdir}/libmemcached.la
 %{_libdir}/libmemcached.so
 %{_libdir}/libmemcached.so.1
-%{_libdir}/libmemcached.so.1.0.0
+%{_libdir}/libmemcached.so.1.0.1
 %{_libdir}/pkgconfig/libmemcached.pc
 %{_mandir}/man1/memcat.1.gz
 %{_mandir}/man1/memcp.1.gz
@@ -63,6 +63,7 @@ memcp - Copy files to memcached servers.
 %{_mandir}/man3/libmemcached.3.gz
 %{_mandir}/man3/libmemcached_examples.3.gz
 %{_mandir}/man3/memcached_add.3.gz
+%{_mandir}/man3/memcached_append.3.gz
 %{_mandir}/man3/memcached_behavior_get.3.gz
 %{_mandir}/man3/memcached_behavior_set.3.gz
 %{_mandir}/man3/memcached_clone.3.gz
@@ -70,10 +71,12 @@ memcp - Copy files to memcached servers.
 %{_mandir}/man3/memcached_decrement.3.gz
 %{_mandir}/man3/memcached_delete.3.gz
 %{_mandir}/man3/memcached_fetch.3.gz
+%{_mandir}/man3/memcached_fetch_result.3.gz
 %{_mandir}/man3/memcached_free.3.gz
 %{_mandir}/man3/memcached_get.3.gz
 %{_mandir}/man3/memcached_increment.3.gz
 %{_mandir}/man3/memcached_mget.3.gz
+%{_mandir}/man3/memcached_prepend.3.gz
 %{_mandir}/man3/memcached_quit.3.gz
 %{_mandir}/man3/memcached_replace.3.gz
 %{_mandir}/man3/memcached_server_add.3.gz
index 0200e40c0ea3f54fba1b19dbff148f30d9e9f82f..9b3555e470622bb933f93e876aabcb3b7a298fa1 100644 (file)
@@ -117,6 +117,113 @@ uint8_t set_test(memcached_st *memc)
   return 0;
 }
 
+uint8_t append_test(memcached_st *memc)
+{
+  memcached_return rc;
+  char *key= "fig";
+  char *value= "we";
+  size_t value_length;
+  uint16_t flags;
+
+  rc= memcached_flush(memc, 0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_set(memc, key, strlen(key), 
+                    value, strlen(value),
+                    (time_t)0, (uint16_t)0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_append(memc, key, strlen(key), 
+                       " the", strlen(" the"),
+                       (time_t)0, (uint16_t)0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_append(memc, key, strlen(key), 
+                       " people", strlen(" people"),
+                       (time_t)0, (uint16_t)0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  value= memcached_get(memc, key, strlen(key),
+                       &value_length, &flags, &rc);
+  assert(!memcmp(value, "we the people", strlen("we the people")));
+  assert(strlen("we the people") == value_length);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  return 0;
+}
+
+uint8_t cas_test(memcached_st *memc)
+{
+  memcached_return rc;
+  char *key= "fun";
+  size_t key_length= strlen("fun");
+  char *value= "we the people";
+  size_t value_length= strlen("we the people");
+  memcached_result_st results_obj;
+  memcached_result_st *results;
+
+  rc= memcached_flush(memc, 0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_set(memc, key, strlen(key), 
+                    value, strlen(value),
+                    (time_t)0, (uint16_t)0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_mget(memc, &key, &key_length, 1);
+
+  results= memcached_result_create(memc, &results_obj);
+
+  results= memcached_fetch_result(memc, &results_obj, &rc);
+  assert(results);
+  assert(rc == MEMCACHED_SUCCESS);
+  WATCHPOINT_NUMBER(memcached_result_cas(results));
+  WATCHPOINT_ASSERT(memcached_result_cas(results));
+
+  assert(!memcmp(value, "we the people", strlen("we the people")));
+  assert(strlen("we the people") == value_length);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  memcached_result_free(&results_obj);
+
+  return 0;
+}
+
+uint8_t prepend_test(memcached_st *memc)
+{
+  memcached_return rc;
+  char *key= "fig";
+  char *value= "people";
+  size_t value_length;
+  uint16_t flags;
+
+  rc= memcached_flush(memc, 0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_set(memc, key, strlen(key), 
+                    value, strlen(value),
+                    (time_t)0, (uint16_t)0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_prepend(memc, key, strlen(key), 
+                       "the ", strlen("the "),
+                       (time_t)0, (uint16_t)0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  rc= memcached_prepend(memc, key, strlen(key), 
+                       "we ", strlen("we "),
+                       (time_t)0, (uint16_t)0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  value= memcached_get(memc, key, strlen(key),
+                       &value_length, &flags, &rc);
+  assert(!memcmp(value, "we the people", strlen("we the people")));
+  assert(strlen("we the people") == value_length);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  return 0;
+}
+
 uint8_t add_test(memcached_st *memc)
 {
   memcached_return rc;
@@ -1407,6 +1514,18 @@ memcached_return pre_hash_ketama(memcached_st *memc)
   return MEMCACHED_SUCCESS;
 }
 
+memcached_return check_for_1_2_3(memcached_st *memc)
+{
+  memcached_version(memc);
+
+  if (memc->hosts[0].major_version >= 1 &&
+      memc->hosts[0].minor_version >= 2 &&
+      memc->hosts[0].micro_version >= 4)
+    return MEMCACHED_SUCCESS;
+
+  return MEMCACHED_FAILURE;
+}
+
 memcached_return pre_unix_socket(memcached_st *memc)
 {
   memcached_return rc;
@@ -1496,6 +1615,13 @@ test_st result_tests[] ={
   {0, 0, 0}
 };
 
+test_st version_1_2_3[] ={
+  {"append", 0, append_test },
+  {"prepend", 0, prepend_test },
+//  {"cas", 0, cas_test },
+  {0, 0, 0}
+};
+
 test_st user_tests[] ={
   {"user_supplied_bug1", 0, user_supplied_bug1 },
   {"user_supplied_bug2", 0, user_supplied_bug2 },
@@ -1530,10 +1656,12 @@ collection_st collection[] ={
   {"unix_socket", pre_unix_socket, 0, tests},
   {"unix_socket_nodelay", pre_nodelay, 0, tests},
 //  {"udp", pre_udp, 0, tests},
+  {"version_1_2_3", check_for_1_2_3, 0, version_1_2_3},
   {"string", 0, 0, string_tests},
   {"result", 0, 0, result_tests},
   {"user", 0, 0, user_tests},
   {"generate", 0, 0, generate_tests},
+  {"generate_nonblock", pre_nonblock, 0, generate_tests},
   {0, 0, 0, 0}
 };