Last active
August 29, 2015 14:18
-
-
Save mnunberg/5e62d1a8fe103d089f60 to your computer and use it in GitHub Desktop.
N1QL read-your-writes with C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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