Skip to content

Instantly share code, notes, and snippets.

@mnunberg
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mnunberg/5e62d1a8fe103d089f60 to your computer and use it in GitHub Desktop.
Save mnunberg/5e62d1a8fe103d089f60 to your computer and use it in GitHub Desktop.
N1QL read-your-writes with C
/*
* Simple C "script" showing how to connect to Couchbase 4.0 cluster and
* execute a N1QL query. This also creates the primary index which should
* be executed only once.
*
* If you try to run this against an older cluster (e.g. 3.0.x) you will get
* an LCB_NOT_SUPPORTED error (Operation not supported).
*
* Compile and link with couchbase (-lcouchbase). This requires libcouchbase
* version 2.4.8 or greater
*
* This file uses the "V3" API (<libcouchbase/api3.h>)
*
* First argument is a _connection string_, e.g. couchbase://localhost/default
*/
#include <libcouchbase/couchbase.h> // Core library
#include <libcouchbase/api3.h> // lcb_CMDSTORE, lcb_store3
#include <libcouchbase/n1ql.h> // lcb_n1p_*, lcb_n1ql_*
#include <string.h> // strlen
#include <stdlib.h> // exit
#include <stdio.h> // fprintf
/* Handy function which does poor man's error checking (prints the error and
* crashes the program). This is the actual implementation. It is wrapped by
* a macro which takes care of displaying the statement which caused the failure
*/
static void do_or_die_real(const char *msg, lcb_error_t err)
{
if (err != LCB_SUCCESS) {
fprintf(stderr, "`%s` failed: 0x%x (%s)\n",
msg, err, lcb_strerror(NULL, err));
exit(EXIT_FAILURE);
}
}
#define do_or_die(e) do_or_die_real(#e, e)
static void cb_n1qlrow(lcb_t instance, int cbtype, const lcb_RESPBASE *rb)
{
const lcb_RESPN1QL *resp = (const lcb_RESPN1QL *)rb;
if (resp->rc != LCB_SUCCESS) {
fprintf(stderr, "N1QL request failed! (0x%x): %s\n", rb->rc, lcb_strerror(instance, rb->rc));
}
if (resp->rflags & LCB_RESP_F_FINAL) {
fprintf(stderr, "==== N1QL metadata:\n%.*s\n", (int)resp->nrow, resp->row);
} else {
fprintf(stderr, "==== Got row:\n%.*s\n", (int)resp->nrow, resp->row);
}
}
static void cb_store(lcb_t instance, int cbtype, const lcb_RESPBASE *rb)
{
// Ensure nothing went wrong, and the item was actually stored..
do_or_die_real("store callback", rb->rc);
lcb_N1QLPARAMS *params = lcb_n1p_new(); // Create params object
lcb_n1p_setstmtz(params,
"SELECT * FROM `travel-sample` WHERE type=$1 AND id=$2"); // Set the static query
lcb_n1p_posparam(params, "\"airline\"", -1); // Push first parameter
lcb_n1p_posparam(params, "0", -1); // Push second parameter
// Ensure the request is consistent with the latest data from the server
do_or_die(lcb_n1p_setconsistency(params, LCB_N1P_CONSISTENCY_REQUEST));
// Allocate the N1QLCMD structure on the stack.
// The lcb_n1p_mkcmd() function populates the command with its current
// state. You can inspect the command structure's `query` field if you
// wish.
lcb_CMDN1QL cmd = { 0 };
do_or_die(lcb_n1p_mkcmd(params, &cmd));
// Callback to receive each row
cmd.callback = (lcb_N1QLCALLBACK)cb_n1qlrow;
// Schedule the query. Note that lcb_wait() is not required since we are
// already in the event loop (this is executed from within the callback).
do_or_die(lcb_n1ql_query(instance, NULL, &cmd));
// Free the parameters object.
lcb_n1p_free(params);
}
static void create_index(lcb_t instance)
{
lcb_N1QLPARAMS *n1p = lcb_n1p_new();
lcb_CMDN1QL cmd = { 0 };
cmd.callback = (lcb_N1QLCALLBACK)cb_n1qlrow;
do_or_die(lcb_n1p_setstmtz(n1p, "CREATE PRIMARY INDEX ON `travel-sample`"));
do_or_die(lcb_n1p_mkcmd(n1p, &cmd));
do_or_die(lcb_n1ql_query(instance, NULL, &cmd));
lcb_n1p_free(n1p);
fprintf(stderr, "Creating index (may fail if already exists)..\n");
lcb_wait(instance);
fprintf(stderr, "DONE\n");
}
int main(int argc, char **argv)
{
lcb_t instance;
struct lcb_create_st cropts = { 0 };
cropts.version = 3;
// Use the connection string, assumed to be the first argument on the
// commandline. If absent, just assume localhost/default
cropts.v.v3.connstr = argc > 1 ? argv[1] : "couchbase://localhost/travel-sample";
do_or_die(lcb_create(&instance, &cropts));
do_or_die(lcb_connect(instance));
lcb_wait(instance);
do_or_die(lcb_get_bootstrap_status(instance));
// Create the index first. This might fail if the index already exists..
create_index(instance);
lcb_install_callback3(instance, LCB_CALLBACK_STORE, cb_store);
// Schedule the store operation. This is using the "V3" API (<libcouchbase/api3.h>).
// you can accomplish the same using the lcb_store_cmd and lcb_store(), but it's
// lengthier.
lcb_CMDSTORE scmd = { 0 };
const char key[] = "airline_000";
// Some macros to make the JSON more readable..
#define JSON_FIELD(k, v) "\""k"\":" "\"" v "\""
#define JSON_FIELD_(k, v) JSON_FIELD(k, v) ","
const char value[] = "{"
"\"id\":0,"
JSON_FIELD_("type", "airline")
JSON_FIELD_("name", "Couchbase Airways")
JSON_FIELD_("iata", "CBX")
JSON_FIELD_("icao", "CBX")
JSON_FIELD_("callsign", "COUCH-BASE")
JSON_FIELD("country", "United States")
"}";
scmd.operation = LCB_SET;
LCB_CMD_SET_KEY(&scmd, key, strlen(key));
LCB_CMD_SET_VALUE(&scmd, value, strlen(value));
lcb_sched_enter(instance);
do_or_die(lcb_store3(instance, NULL, &scmd));
lcb_sched_leave(instance);
lcb_wait(instance);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment