summaryrefslogtreecommitdiff
path: root/examples/c
diff options
context:
space:
mode:
authorMichael Cahill <michael.cahill@mongodb.com>2016-05-05 15:38:12 +1000
committerMichael Cahill <michael.cahill@mongodb.com>2016-05-05 15:38:12 +1000
commit636a7b25ef3eca6b98009330f4d35337d4f35717 (patch)
tree7cc2e03ad96e206cbe73343feef10197023a37da /examples/c
parenteaa7b5f0fcc62f356c33a2c56f45b609a73ca5dd (diff)
parent75c22bc0c662622c14e5c47d99ff262cede2c6bf (diff)
downloadmongodb-3.3.6.tar.gz
Merge branch 'develop' into mongodb-3.4mongodb-3.3.6
Diffstat (limited to 'examples/c')
-rw-r--r--examples/c/Makefile.am3
-rw-r--r--examples/c/ex_all.c7
-rw-r--r--examples/c/ex_config.c91
-rw-r--r--examples/c/ex_event_handler.c4
-rw-r--r--examples/c/ex_file_system.c721
-rw-r--r--examples/c/ex_schema.c76
-rw-r--r--examples/c/ex_scope.c6
-rw-r--r--examples/c/queue_example.h149
8 files changed, 951 insertions, 106 deletions
diff --git a/examples/c/Makefile.am b/examples/c/Makefile.am
index 72fd98aff7b..d5305eec5c8 100644
--- a/examples/c/Makefile.am
+++ b/examples/c/Makefile.am
@@ -7,7 +7,6 @@ noinst_PROGRAMS = \
ex_async \
ex_backup \
ex_call_center \
- ex_config \
ex_config_parse \
ex_cursor \
ex_data_source \
@@ -15,6 +14,7 @@ noinst_PROGRAMS = \
ex_event_handler \
ex_extending \
ex_extractor \
+ ex_file_system \
ex_hello \
ex_log \
ex_pack \
@@ -26,6 +26,7 @@ noinst_PROGRAMS = \
ex_thread
ex_encrypt_LDFLAGS = -rdynamic
+ex_file_system_LDFLAGS = -rdynamic
# The examples can be run with no arguments as simple smoke tests
TESTS = $(noinst_PROGRAMS)
diff --git a/examples/c/ex_all.c b/examples/c/ex_all.c
index 1c036b75461..ea97668c697 100644
--- a/examples/c/ex_all.c
+++ b/examples/c/ex_all.c
@@ -1037,6 +1037,13 @@ backup(WT_SESSION *session)
ret = cursor->close(cursor);
/*! [backup]*/
+ /*! [incremental backup]*/
+ /* Open the backup data source for incremental backup. */
+ ret = session->open_cursor(
+ session, "backup:", NULL, "target=(\"log:\")", &cursor);
+ /*! [incremental backup]*/
+ ret = cursor->close(cursor);
+
/*! [backup of a checkpoint]*/
ret = session->checkpoint(session, "drop=(from=June01),name=June01");
/*! [backup of a checkpoint]*/
diff --git a/examples/c/ex_config.c b/examples/c/ex_config.c
deleted file mode 100644
index 2ac8198176c..00000000000
--- a/examples/c/ex_config.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*-
- * Public Domain 2014-2016 MongoDB, Inc.
- * Public Domain 2008-2014 WiredTiger, Inc.
- *
- * This is free and unencumbered software released into the public domain.
- *
- * Anyone is free to copy, modify, publish, use, compile, sell, or
- * distribute this software, either in source code form or as a compiled
- * binary, for any purpose, commercial or non-commercial, and by any
- * means.
- *
- * In jurisdictions that recognize copyright laws, the author or authors
- * of this software dedicate any and all copyright interest in the
- * software to the public domain. We make this dedication for the benefit
- * of the public at large and to the detriment of our heirs and
- * successors. We intend this dedication to be an overt act of
- * relinquishment in perpetuity of all present and future rights to this
- * software under copyright law.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * ex_config.c
- * This is an example demonstrating how to configure various database and
- * table properties.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <wiredtiger.h>
-
-static const char *home;
-
-int
-main(void)
-{
- int ret;
- WT_CONNECTION *conn;
- WT_SESSION *session;
- WT_CURSOR *cursor;
- const char *key, *value;
-
- /*
- * Create a clean test directory for this run of the test program if the
- * environment variable isn't already set (as is done by make check).
- */
- if (getenv("WIREDTIGER_HOME") == NULL) {
- home = "WT_HOME";
- ret = system("rm -rf WT_HOME && mkdir WT_HOME");
- } else
- home = NULL;
-
- /*! [configure cache size] */
- if ((ret = wiredtiger_open(home, NULL,
- "create,cache_size=500M", &conn)) != 0)
- fprintf(stderr, "Error connecting to %s: %s\n",
- home, wiredtiger_strerror(ret));
- /*! [configure cache size] */
-
- /*! [create a table] */
- ret = conn->open_session(conn, NULL, NULL, &session);
-
- ret = session->create(session,
- "table:access", "key_format=S,value_format=S");
- /*! [create a table] */
-
- /*! [transaction] */
- ret = session->begin_transaction(session, "priority=100,name=mytxn");
-
- ret = session->open_cursor(session, "config:", NULL, NULL, &cursor);
-
- while ((ret = cursor->next(cursor)) == 0) {
- ret = cursor->get_key(cursor, &key);
- ret = cursor->get_value(cursor, &value);
- printf("configuration value: %s = %s\n", key, value);
- }
-
- ret = session->commit_transaction(session, NULL);
- /*! [transaction] */
-
- ret = conn->close(conn, NULL);
-
- return (ret);
-}
diff --git a/examples/c/ex_event_handler.c b/examples/c/ex_event_handler.c
index d1e08edb04d..ea30a5990fb 100644
--- a/examples/c/ex_event_handler.c
+++ b/examples/c/ex_event_handler.c
@@ -111,10 +111,10 @@ config_event_handler(void)
/*! [Configure event_handler] */
/* Make an invalid API call, to ensure the event handler works. */
+ printf("ex_event_handler: expect an error message to follow\n");
(void)conn->open_session(conn, NULL, "isolation=invalid", &session);
- if (ret == 0)
- ret = conn->close(conn, NULL);
+ ret = conn->close(conn, NULL);
return (ret);
}
diff --git a/examples/c/ex_file_system.c b/examples/c/ex_file_system.c
new file mode 100644
index 00000000000..18ea9b7242e
--- /dev/null
+++ b/examples/c/ex_file_system.c
@@ -0,0 +1,721 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ex_file_system.c
+ * demonstrates how to use the custom file system interface
+ */
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <wiredtiger.h>
+#include "queue_example.h"
+
+static const char *home;
+
+/*
+ * Example file system implementation. Using memory buffers to represent files.
+ *
+ * WARNING: This implementation isn't thread safe: WiredTiger performs schema
+ * and I/O operations in parallel, so all access to the handle must be thread-
+ * safe.
+ */
+typedef struct {
+ WT_FILE_SYSTEM iface;
+
+ int opened_file_count;
+ int opened_unique_file_count;
+ int closed_file_count;
+
+ /* Queue of file handles */
+ TAILQ_HEAD(demo_file_handle_qh, demo_file_handle) fileq;
+
+} DEMO_FILE_SYSTEM;
+
+typedef struct demo_file_handle {
+ WT_FILE_HANDLE iface;
+
+ /*
+ * Add custom file handle fields after the interface.
+ */
+ DEMO_FILE_SYSTEM *demo_fs;
+
+ TAILQ_ENTRY(demo_file_handle) q;
+ uint32_t ref; /* Reference count */
+
+ char *buf; /* In-memory contents */
+ size_t size;
+ size_t off; /* Read/write offset */
+} DEMO_FILE_HANDLE;
+
+/*
+ * Extension initialization function.
+ */
+#ifdef _WIN32
+/*
+ * Explicitly export this function so it is visible when loading extensions.
+ */
+__declspec(dllexport)
+#endif
+int demo_file_system_create(WT_CONNECTION *, WT_CONFIG_ARG *);
+
+/*
+ * Forward function declarations for file system API implementation
+ */
+static int demo_fs_open(WT_FILE_SYSTEM *,
+ WT_SESSION *, const char *, WT_OPEN_FILE_TYPE, uint32_t, WT_FILE_HANDLE **);
+static int demo_fs_directory_list(WT_FILE_SYSTEM *, WT_SESSION *,
+ const char *, const char *, char ***, uint32_t *);
+static int demo_fs_directory_list_free(
+ WT_FILE_SYSTEM *, WT_SESSION *, char **, uint32_t);
+static int demo_fs_directory_sync(WT_FILE_SYSTEM *file_system,
+ WT_SESSION *session, const char *directory);
+static int demo_fs_exist(WT_FILE_SYSTEM *, WT_SESSION *, const char *, bool *);
+static int demo_fs_remove(WT_FILE_SYSTEM *, WT_SESSION *, const char *);
+static int demo_fs_rename(
+ WT_FILE_SYSTEM *, WT_SESSION *, const char *, const char *);
+static int demo_fs_size(
+ WT_FILE_SYSTEM *, WT_SESSION *, const char *, wt_off_t *);
+static int demo_fs_terminate(WT_FILE_SYSTEM *, WT_SESSION *);
+
+/*
+ * Forward function declarations for file handle API implementation
+ */
+static int demo_file_close(WT_FILE_HANDLE *, WT_SESSION *);
+static int demo_file_lock(WT_FILE_HANDLE *, WT_SESSION *, bool);
+static int demo_file_read(
+ WT_FILE_HANDLE *, WT_SESSION *, wt_off_t, size_t, void *);
+static int demo_file_size(WT_FILE_HANDLE *, WT_SESSION *, wt_off_t *);
+static int demo_file_sync(WT_FILE_HANDLE *, WT_SESSION *);
+static int demo_file_sync_nowait(WT_FILE_HANDLE *, WT_SESSION *);
+static int demo_file_truncate(WT_FILE_HANDLE *, WT_SESSION *, wt_off_t);
+static int demo_file_write(
+ WT_FILE_HANDLE *, WT_SESSION *, wt_off_t, size_t, const void *);
+
+/*
+ * Forward function declarations for internal functions
+ */
+static int demo_handle_remove(WT_SESSION *, DEMO_FILE_HANDLE *);
+static DEMO_FILE_HANDLE *demo_handle_search(WT_FILE_SYSTEM *, const char *);
+
+#define DEMO_FILE_SIZE_INCREMENT 32768
+
+/*
+ * demo_file_system_create --
+ * Initialization point for demo file system
+ */
+int
+demo_file_system_create(WT_CONNECTION *conn, WT_CONFIG_ARG *config)
+{
+ WT_FILE_SYSTEM *file_system;
+ DEMO_FILE_SYSTEM *demo_fs;
+ int ret = 0;
+
+ (void)config; /* Unused */
+
+ if ((demo_fs = calloc(1, sizeof(DEMO_FILE_SYSTEM))) == NULL)
+ return (ENOMEM);
+ file_system = (WT_FILE_SYSTEM *)demo_fs;
+
+ /* Initialize the in-memory jump table. */
+ file_system->directory_list = demo_fs_directory_list;
+ file_system->directory_list_free = demo_fs_directory_list_free;
+ file_system->directory_sync = demo_fs_directory_sync;
+ file_system->exist = demo_fs_exist;
+ file_system->open_file = demo_fs_open;
+ file_system->remove = demo_fs_remove;
+ file_system->rename = demo_fs_rename;
+ file_system->size = demo_fs_size;
+ file_system->terminate = demo_fs_terminate;
+
+ if ((ret = conn->set_file_system(conn, file_system, NULL)) != 0) {
+ fprintf(stderr, "Error setting custom file system: %s\n",
+ wiredtiger_strerror(ret));
+ goto err;
+ }
+
+ return (0);
+
+err: free(demo_fs);
+ /* An error installing the file system is fatal. */
+ exit(1);
+}
+
+/*
+ * demo_fs_open --
+ * fopen for our demo file system
+ */
+static int
+demo_fs_open(WT_FILE_SYSTEM *file_system, WT_SESSION *session,
+ const char *name, WT_OPEN_FILE_TYPE file_type, uint32_t flags,
+ WT_FILE_HANDLE **file_handlep)
+{
+ WT_FILE_HANDLE *file_handle;
+ DEMO_FILE_HANDLE *demo_fh;
+ DEMO_FILE_SYSTEM *demo_fs;
+
+ (void)file_type; /* Unused */
+ (void)session; /* Unused */
+ (void)flags; /* Unused */
+
+ demo_fs = (DEMO_FILE_SYSTEM *)file_system;
+ demo_fh = NULL;
+
+ ++demo_fs->opened_file_count;
+
+ /*
+ * First search the file queue, if we find it, assert there's only a
+ * single reference, we only supports a single handle on any file.
+ */
+ demo_fh = demo_handle_search(file_system, name);
+ if (demo_fh != NULL) {
+ if (demo_fh->ref != 0) {
+ fprintf(stderr,
+ "demo_file_open of already open file %s\n",
+ name);
+ return (EBUSY);
+ }
+
+ demo_fh->ref = 1;
+ demo_fh->off = 0;
+
+ *file_handlep = (WT_FILE_HANDLE *)demo_fh;
+ return (0);
+ }
+
+ /* The file hasn't been opened before, create a new one. */
+ if ((demo_fh = calloc(1, sizeof(DEMO_FILE_HANDLE))) == NULL)
+ return (ENOMEM);
+
+ /* Initialize private information. */
+ demo_fh->ref = 1;
+ demo_fh->off = 0;
+ demo_fh->demo_fs = demo_fs;
+ if ((demo_fh->buf = calloc(1, DEMO_FILE_SIZE_INCREMENT)) == NULL)
+ goto enomem;
+ demo_fh->size = DEMO_FILE_SIZE_INCREMENT;
+
+ /* Initialize public information. */
+ file_handle = (WT_FILE_HANDLE *)demo_fh;
+ if ((file_handle->name = strdup(name)) == NULL)
+ goto enomem;
+
+ /*
+ * Setup the function call table for our custom file system. Set the
+ * function pointer to NULL where our implementation doesn't support
+ * the functionality.
+ */
+ file_handle->close = demo_file_close;
+ file_handle->fadvise = NULL;
+ file_handle->fallocate = NULL;
+ file_handle->fallocate_nolock = NULL;
+ file_handle->lock = demo_file_lock;
+ file_handle->map = NULL;
+ file_handle->map_discard = NULL;
+ file_handle->map_preload = NULL;
+ file_handle->unmap = NULL;
+ file_handle->read = demo_file_read;
+ file_handle->size = demo_file_size;
+ file_handle->sync = demo_file_sync;
+ file_handle->sync_nowait = demo_file_sync_nowait;
+ file_handle->truncate = demo_file_truncate;
+ file_handle->write = demo_file_write;
+
+ TAILQ_INSERT_HEAD(&demo_fs->fileq, demo_fh, q);
+ ++demo_fs->opened_unique_file_count;
+
+ *file_handlep = file_handle;
+ return (0);
+
+enomem: free(demo_fh->buf);
+ free(demo_fh);
+ return (ENOMEM);
+}
+
+/*
+ * demo_fs_directory_list --
+ * Return a list of files in a given sub-directory.
+ */
+static int
+demo_fs_directory_list(WT_FILE_SYSTEM *file_system,
+ WT_SESSION *session, const char *directory,
+ const char *prefix, char ***dirlistp, uint32_t *countp)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ DEMO_FILE_SYSTEM *demo_fs;
+ size_t dir_len, prefix_len;
+ char *name, **entries;
+ uint32_t allocated, count;
+
+ (void)session; /* Unused */
+
+ demo_fs = (DEMO_FILE_SYSTEM *)file_system;
+ entries = NULL;
+ allocated = count = 0;
+ dir_len = strlen(directory);
+ prefix_len = prefix == NULL ? 0 : strlen(prefix);
+
+ TAILQ_FOREACH(demo_fh, &demo_fs->fileq, q) {
+ name = demo_fh->iface.name;
+ if (strncmp(name, directory, dir_len) != 0 ||
+ (prefix != NULL && strncmp(name, prefix, prefix_len) != 0))
+ continue;
+
+ /*
+ * Increase the list size in groups of 10, it doesn't
+ * matter if the list is a bit longer than necessary.
+ */
+ if (count >= allocated) {
+ entries = realloc(
+ entries, (allocated + 10) * sizeof(char *));
+ if (entries == NULL)
+ return (ENOMEM);
+ memset(entries + allocated * sizeof(char *),
+ 0, 10 * sizeof(char *));
+ allocated += 10;
+ }
+ entries[count++] = strdup(name);
+ }
+
+ *dirlistp = entries;
+ *countp = count;
+
+ return (0);
+}
+
+/*
+ * demo_fs_directory_list_free --
+ * Free memory allocated by demo_fs_directory_list.
+ */
+static int
+demo_fs_directory_list_free(WT_FILE_SYSTEM *file_system,
+ WT_SESSION *session, char **dirlist, uint32_t count)
+{
+ (void)file_system;
+ (void)session;
+
+ if (dirlist != NULL) {
+ while (count > 0)
+ free(dirlist[--count]);
+ free(dirlist);
+ }
+ return (0);
+}
+
+/*
+ * demo_fs_directory_sync --
+ * Directory sync for our demo file system, which is a no-op.
+ */
+static int
+demo_fs_directory_sync(WT_FILE_SYSTEM *file_system,
+ WT_SESSION *session, const char *directory)
+{
+ (void)file_system; /* Unused */
+ (void)session; /* Unused */
+ (void)directory; /* Unused */
+
+ return (0);
+}
+
+/*
+ * demo_fs_exist --
+ * Return if the file exists.
+ */
+static int
+demo_fs_exist(WT_FILE_SYSTEM *file_system,
+ WT_SESSION *session, const char *name, bool *existp)
+{
+ (void)session; /* Unused */
+
+ *existp =
+ demo_handle_search(file_system, name) != NULL;
+
+ return (0);
+}
+
+/*
+ * demo_fs_remove --
+ * POSIX remove.
+ */
+static int
+demo_fs_remove(
+ WT_FILE_SYSTEM *file_system, WT_SESSION *session, const char *name)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ int ret;
+
+ ret = ENOENT;
+ if ((demo_fh = demo_handle_search(file_system, name)) != NULL)
+ ret = demo_handle_remove(session, demo_fh);
+
+ return (ret);
+}
+
+/*
+ * demo_fs_rename --
+ * POSIX rename.
+ */
+static int
+demo_fs_rename(WT_FILE_SYSTEM *file_system,
+ WT_SESSION *session, const char *from, const char *to)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ char *copy;
+
+ (void)session; /* Unused */
+
+ if ((demo_fh = demo_handle_search(file_system, from)) == NULL)
+ return (ENOENT);
+
+ if ((copy = strdup(to)) == NULL)
+ return (ENOMEM);
+
+ free(demo_fh->iface.name);
+ demo_fh->iface.name = copy;
+ return (0);
+}
+
+/*
+ * demo_fs_size --
+ * Get the size of a file in bytes, by file name.
+ */
+static int
+demo_fs_size(WT_FILE_SYSTEM *file_system,
+ WT_SESSION *session, const char *name, wt_off_t *sizep)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ int ret = 0;
+
+ ret = ENOENT;
+ if ((demo_fh = demo_handle_search(file_system, name)) != NULL)
+ ret = demo_file_size(
+ (WT_FILE_HANDLE *)demo_fh, session, sizep);
+
+ return (ret);
+}
+
+/*
+ * demo_fs_terminate --
+ * Discard any resources on termination
+ */
+static int
+demo_fs_terminate(WT_FILE_SYSTEM *file_system, WT_SESSION *session)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ DEMO_FILE_SYSTEM *demo_fs;
+ int ret = 0, tret;
+
+ (void)session; /* Unused */
+
+ demo_fs = (DEMO_FILE_SYSTEM *)file_system;
+
+ while ((demo_fh = TAILQ_FIRST(&demo_fs->fileq)) != NULL)
+ if ((tret =
+ demo_handle_remove(session, demo_fh)) != 0 && ret == 0)
+ ret = tret;
+
+ printf("Custom file system\n");
+ printf("\t%d unique file opens\n", demo_fs->opened_unique_file_count);
+ printf("\t%d opened\n", demo_fs->opened_file_count);
+ printf("\t%d closed\n", demo_fs->closed_file_count);
+ free(demo_fs);
+
+ return (ret);
+}
+
+/*
+ * demo_file_close --
+ * ANSI C close.
+ */
+static int
+demo_file_close(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+
+ (void)session; /* Unused */
+
+ demo_fh = (DEMO_FILE_HANDLE *)file_handle;
+ if (demo_fh->ref < 1) {
+ fprintf(stderr, "Closing already closed handle: %s\n",
+ demo_fh->iface.name);
+ return (EINVAL);
+ }
+ --demo_fh->ref;
+
+ if (demo_fh->ref == 0)
+ ++demo_fh->demo_fs->closed_file_count;
+
+ return (0);
+}
+
+/*
+ * demo_file_lock --
+ * Lock/unlock a file.
+ */
+static int
+demo_file_lock(WT_FILE_HANDLE *file_handle, WT_SESSION *session, bool lock)
+{
+ /* Locks are always granted. */
+ (void)file_handle; /* Unused */
+ (void)session; /* Unused */
+ (void)lock; /* Unused */
+ return (0);
+}
+
+/*
+ * demo_file_read --
+ * POSIX pread.
+ */
+static int
+demo_file_read(WT_FILE_HANDLE *file_handle,
+ WT_SESSION *session, wt_off_t offset, size_t len, void *buf)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ int ret = 0;
+ size_t off;
+
+ (void)session; /* Unused */
+ demo_fh = (DEMO_FILE_HANDLE *)file_handle;
+
+ off = (size_t)offset;
+ if (off < demo_fh->size) {
+ if (len > demo_fh->size - off)
+ len = demo_fh->size - off;
+ memcpy(buf, (uint8_t *)demo_fh->buf + off, len);
+ demo_fh->off = off + len;
+ } else
+ ret = EINVAL;
+
+ if (ret == 0)
+ return (0);
+ /*
+ * WiredTiger should never request data past the end of a file, so
+ * flag an error if it does.
+ */
+ fprintf(stderr,
+ "%s: handle-read: failed to read %zu bytes at offset %zu\n",
+ demo_fh->iface.name, len, off);
+ return (EINVAL);
+}
+
+/*
+ * demo_file_size --
+ * Get the size of a file in bytes, by file handle.
+ */
+static int
+demo_file_size(
+ WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t *sizep)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+
+ (void)session; /* Unused */
+ demo_fh = (DEMO_FILE_HANDLE *)file_handle;
+
+ assert(demo_fh->size != 0);
+ *sizep = (wt_off_t)demo_fh->size;
+ return (0);
+}
+
+/*
+ * demo_file_sync --
+ * Ensure the content of the file is stable. This is a no-op in our
+ * memory backed file system.
+ */
+static int
+demo_file_sync(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
+{
+ (void)file_handle; /* Unused */
+ (void)session; /* Unused */
+
+ return (0);
+}
+
+/*
+ * demo_file_sync_nowait --
+ * Ensure the content of the file is stable. This is a no-op in our
+ * memory backed file system.
+ */
+static int
+demo_file_sync_nowait(WT_FILE_HANDLE *file_handle, WT_SESSION *session)
+{
+ (void)file_handle; /* Unused */
+ (void)session; /* Unused */
+
+ return (0);
+}
+
+/*
+ * demo_file_truncate --
+ * POSIX ftruncate.
+ */
+static int
+demo_file_truncate(
+ WT_FILE_HANDLE *file_handle, WT_SESSION *session, wt_off_t offset)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ size_t off;
+
+ (void)session; /* Unused */
+ demo_fh = (DEMO_FILE_HANDLE *)file_handle;
+
+ /*
+ * Grow the buffer as necessary, clear any new space in the file,
+ * and reset the file's data length.
+ */
+ off = (size_t)offset;
+ demo_fh->buf = realloc(demo_fh->buf, off);
+ if (demo_fh->buf == NULL) {
+ fprintf(stderr, "Failed to resize buffer in truncate\n");
+ return (ENOSPC);
+ }
+ if (demo_fh->size < off)
+ memset((uint8_t *)demo_fh->buf + demo_fh->size,
+ 0, off - demo_fh->size);
+ demo_fh->size = off;
+
+ return (0);
+}
+
+/*
+ * demo_file_write --
+ * POSIX pwrite.
+ */
+static int
+demo_file_write(WT_FILE_HANDLE *file_handle, WT_SESSION *session,
+ wt_off_t offset, size_t len, const void *buf)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ int ret = 0;
+
+ demo_fh = (DEMO_FILE_HANDLE *)file_handle;
+
+ /* Make sure the buffer is large enough for the write */
+ if ((ret = demo_file_truncate(file_handle, session,
+ offset + (wt_off_t)(len + DEMO_FILE_SIZE_INCREMENT))) != 0)
+ return (ret);
+
+ memcpy((uint8_t *)demo_fh->buf + offset, buf, len);
+ demo_fh->off = (size_t)offset + len;
+
+ return (0);
+}
+
+/*
+ * demo_handle_remove --
+ * Destroy an in-memory file handle. Should only happen on remove or
+ * shutdown.
+ */
+static int
+demo_handle_remove(WT_SESSION *session, DEMO_FILE_HANDLE *demo_fh)
+{
+ DEMO_FILE_SYSTEM *demo_fs;
+
+ (void)session; /* Unused */
+ demo_fs = demo_fh->demo_fs;
+
+ if (demo_fh->ref != 0) {
+ fprintf(stderr,
+ "demo_handle_remove on file %s with non-zero reference "
+ "count of %u\n",
+ demo_fh->iface.name, demo_fh->ref);
+ return (EINVAL);
+ }
+
+ TAILQ_REMOVE(&demo_fs->fileq, demo_fh, q);
+
+ /* Clean up private information. */
+ free(demo_fh->buf);
+ demo_fh->buf = NULL;
+
+ /* Clean up public information. */
+ free(demo_fh->iface.name);
+
+ free(demo_fh);
+
+ return (0);
+}
+
+/*
+ * demo_handle_search --
+ * Return a matching handle, if one exists.
+ */
+static DEMO_FILE_HANDLE *
+demo_handle_search(WT_FILE_SYSTEM *file_system, const char *name)
+{
+ DEMO_FILE_HANDLE *demo_fh;
+ DEMO_FILE_SYSTEM *demo_fs;
+
+ demo_fs = (DEMO_FILE_SYSTEM *)file_system;
+
+ TAILQ_FOREACH(demo_fh, &demo_fs->fileq, q)
+ if (strcmp(demo_fh->iface.name, name) == 0)
+ break;
+ return (demo_fh);
+}
+
+int
+main(void)
+{
+ WT_CONNECTION *conn;
+ const char *open_config;
+ int ret = 0;
+
+ /*
+ * Create a clean test directory for this run of the test program if the
+ * environment variable isn't already set (as is done by make check).
+ */
+ if (getenv("WIREDTIGER_HOME") == NULL) {
+ home = "WT_HOME";
+ ret = system("rm -rf WT_HOME && mkdir WT_HOME");
+ } else
+ home = NULL;
+
+ /*! [WT_FILE_SYSTEM register] */
+ /*
+ * Setup a configuration string that will load our custom file system.
+ * Use the special local extension to indicate that the entry point is
+ * in the same executable. Also enable early load for this extension,
+ * since WiredTiger needs to be able to find it before doing any file
+ * operations.
+ */
+ open_config = "create,log=(enabled=true),extensions=(local="
+ "{entry=demo_file_system_create,early_load=true})";
+ /* Open a connection to the database, creating it if necessary. */
+ if ((ret = wiredtiger_open(home, NULL, open_config, &conn)) != 0) {
+ fprintf(stderr, "Error connecting to %s: %s\n",
+ home, wiredtiger_strerror(ret));
+ return (ret);
+ }
+ /*! [WT_FILE_SYSTEM register] */
+
+ if ((ret = conn->close(conn, NULL)) != 0)
+ fprintf(stderr, "Error closing connection to %s: %s\n",
+ home, wiredtiger_strerror(ret));
+
+ return (ret);
+}
diff --git a/examples/c/ex_schema.c b/examples/c/ex_schema.c
index 70fc7eb2e62..155b982bbbe 100644
--- a/examples/c/ex_schema.c
+++ b/examples/c/ex_schema.c
@@ -69,7 +69,8 @@ main(void)
{
POP_RECORD *p;
WT_CONNECTION *conn;
- WT_CURSOR *cursor, *cursor2, *join_cursor, *stat_cursor;
+ WT_CURSOR *country_cursor, *country_cursor2, *cursor, *join_cursor,
+ *stat_cursor, *subjoin_cursor, *year_cursor;
WT_SESSION *session;
const char *country;
uint64_t recno, population;
@@ -336,18 +337,18 @@ main(void)
ret = session->open_cursor(session,
"join:table:poptable", NULL, NULL, &join_cursor);
ret = session->open_cursor(session,
- "index:poptable:country", NULL, NULL, &cursor);
+ "index:poptable:country", NULL, NULL, &country_cursor);
ret = session->open_cursor(session,
- "index:poptable:immutable_year", NULL, NULL, &cursor2);
+ "index:poptable:immutable_year", NULL, NULL, &year_cursor);
/* select values WHERE country == "AU" AND year > 1900 */
- cursor->set_key(cursor, "AU\0\0\0");
- ret = cursor->search(cursor);
- ret = session->join(session, join_cursor, cursor,
+ country_cursor->set_key(country_cursor, "AU\0\0\0");
+ ret = country_cursor->search(country_cursor);
+ ret = session->join(session, join_cursor, country_cursor,
"compare=eq,count=10");
- cursor2->set_key(cursor2, (uint16_t)1900);
- ret = cursor2->search(cursor2);
- ret = session->join(session, join_cursor, cursor2,
+ year_cursor->set_key(year_cursor, (uint16_t)1900);
+ ret = year_cursor->search(year_cursor);
+ ret = session->join(session, join_cursor, year_cursor,
"compare=gt,count=10,strategy=bloom");
/* List the values that are joined */
@@ -370,8 +371,61 @@ main(void)
ret = stat_cursor->close(stat_cursor);
ret = join_cursor->close(join_cursor);
- ret = cursor2->close(cursor2);
- ret = cursor->close(cursor);
+ ret = year_cursor->close(year_cursor);
+ ret = country_cursor->close(country_cursor);
+
+ /*! [Complex join cursors] */
+ /* Open cursors needed by the join. */
+ ret = session->open_cursor(session,
+ "join:table:poptable", NULL, NULL, &join_cursor);
+ ret = session->open_cursor(session,
+ "join:table:poptable", NULL, NULL, &subjoin_cursor);
+ ret = session->open_cursor(session,
+ "index:poptable:country", NULL, NULL, &country_cursor);
+ ret = session->open_cursor(session,
+ "index:poptable:country", NULL, NULL, &country_cursor2);
+ ret = session->open_cursor(session,
+ "index:poptable:immutable_year", NULL, NULL, &year_cursor);
+
+ /*
+ * select values WHERE (country == "AU" OR country == "UK")
+ * AND year > 1900
+ *
+ * First, set up the join representing the country clause.
+ */
+ country_cursor->set_key(country_cursor, "AU\0\0\0");
+ ret = country_cursor->search(country_cursor);
+ ret = session->join(session, subjoin_cursor, country_cursor,
+ "operation=or,compare=eq,count=10");
+ country_cursor2->set_key(country_cursor2, "UK\0\0\0");
+ ret = country_cursor2->search(country_cursor2);
+ ret = session->join(session, subjoin_cursor, country_cursor2,
+ "operation=or,compare=eq,count=10");
+
+ /* Join that to the top join, and add the year clause */
+ ret = session->join(session, join_cursor, subjoin_cursor, NULL);
+ year_cursor->set_key(year_cursor, (uint16_t)1900);
+ ret = year_cursor->search(year_cursor);
+ ret = session->join(session, join_cursor, year_cursor,
+ "compare=gt,count=10,strategy=bloom");
+
+ /* List the values that are joined */
+ while ((ret = join_cursor->next(join_cursor)) == 0) {
+ ret = join_cursor->get_key(join_cursor, &recno);
+ ret = join_cursor->get_value(join_cursor, &country, &year,
+ &population);
+ printf("ID %" PRIu64, recno);
+ printf(
+ ": country %s, year %" PRIu16 ", population %" PRIu64 "\n",
+ country, year, population);
+ }
+ /*! [Complex join cursors] */
+
+ ret = join_cursor->close(join_cursor);
+ ret = subjoin_cursor->close(subjoin_cursor);
+ ret = country_cursor->close(country_cursor);
+ ret = country_cursor2->close(country_cursor2);
+ ret = year_cursor->close(year_cursor);
ret = conn->close(conn, NULL);
diff --git a/examples/c/ex_scope.c b/examples/c/ex_scope.c
index 93878ec7e3d..ef4d67ad722 100644
--- a/examples/c/ex_scope.c
+++ b/examples/c/ex_scope.c
@@ -106,10 +106,12 @@ cursor_scope_ops(WT_CURSOR *cursor)
* memory, but as it does not position the cursor, it
* doesn't reference memory owned by the cursor, either.
*/
+ printf("ex_scope: "
+ "expect two WiredTiger error messages:\n");
if ((ret = cursor->get_key(cursor, &key)) == 0 ||
(ret = cursor->get_value(cursor, &value)) == 0) {
fprintf(stderr,
- "%s: error in s get_key/value: %s\n",
+ "%s: error in get_key/value: %s\n",
op->op, session->strerror(session, ret));
return (ret);
}
@@ -122,6 +124,8 @@ cursor_scope_ops(WT_CURSOR *cursor)
* reference key memory owned by the cursor, but has no
* value.
*/
+ printf("ex_scope: "
+ "expect one WiredTiger error message:\n");
if ((ret = cursor->get_key(cursor, &key)) != 0 ||
(ret = cursor->get_value(cursor, &value)) == 0) {
fprintf(stderr,
diff --git a/examples/c/queue_example.h b/examples/c/queue_example.h
new file mode 100644
index 00000000000..5f6674b5d1d
--- /dev/null
+++ b/examples/c/queue_example.h
@@ -0,0 +1,149 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This is a stripped down copy of the FreeBSD queue.h include file to make
+ * TAILQ_XXX functionality available in WiredTiger example programs.
+ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: releng/10.2/sys/sys/queue.h 279633 2015-03-05 09:23:43Z hselasky $
+ */
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+} while (0)