/** * Execute a query and call the callback function for each result item. * * @param as - the aerospike cluster to connect to. * @param err - the error is populated if the return value is not AEROSPIKE_OK. * @param policy - the policy to use for this operation. If NULL, then the default policy will be used. * @param query - the query to execute against the cluster * @param udata - user-data to be passed to the callback * @param callback - the callback function to call for each result item. * * @return AEROSPIKE_OK on success, otherwise an error. */ as_status aerospike_query_foreach( aerospike * as, as_error * err, const as_policy_query * policy, const as_query * query, aerospike_query_foreach_callback callback, void * udata) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies // as_policy_query p; // as_policy_query_resolve(&p, &as->config.policies, policy); if ( aerospike_query_init(as, err) != AEROSPIKE_OK ) { return err->code; } cl_query * clquery = as_query_toclquery(query); clquery_bridge bridge = { .udata = udata, .callback = callback }; cl_rv rc = citrusleaf_query_foreach(as->cluster, clquery, &bridge, clquery_callback); cl_query_destroy(clquery); return as_error_fromrc(err, rc); }
/** * Send an info request to the entire cluster. * * ~~~~~~~~~~{.c} * if ( aerospike_info_foreach(&as, &err, NULL, "info", callback, NULL) != AEROSPIKE_OK ) { * // handle error * } * ~~~~~~~~~~ * * The callback takes a response string. The caller should not free this string. * * ~~~~~~~~~~{.c} * bool callback(const as_error * err, const char * node, char * res, void * udata) { * // handle response * } * ~~~~~~~~~~ * * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param req The info request to send. * @param callback The function to call when a response is received. * @param udata User-data to send to the callback. * * @return AEROSPIKE_OK on success. Otherwise an error. * * @ingroup info_operations */ as_status aerospike_info_foreach( aerospike * as, as_error * err, const as_policy_info * policy, const char * req, aerospike_info_foreach_callback callback, void * udata) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_info p; as_policy_info_resolve(&p, &as->config.policies, policy); if ( !as ) { return AEROSPIKE_ERR; } citrusleaf_info_cluster_foreach_data data = { .callback = callback, .udata = udata }; char* error = 0; int rc = citrusleaf_info_cluster_foreach( as->cluster, req, p.send_as_is, p.check_bounds, p.timeout, (void *) &data, &error, citrusleaf_info_cluster_foreach_callback); if (rc) { as_strncpy(err->message, error, sizeof(err->message)); free(error); return as_error_fromrc(err, rc); } return AEROSPIKE_OK; }
/** * Send an info request to a specific host. The response must be freed by the caller on success. * * ~~~~~~~~~~{.c} * char * res = NULL; * if ( aerospike_info_host(&as, &err, NULL, "127.0.0.1", 3000, "info", &res) != AEROSPIKE_OK ) { * // handle error * } * else { * // handle response * free(res); * res = NULL; * } * ~~~~~~~~~~ * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param addr The IP address or hostname to send the request to. * @param port The port to send the request to. * @param req The info request to send. * @param res The response from the node. The response will be a NULL terminated string, allocated by the function, and must be freed by the caller. * * @return AEROSPIKE_OK on success. Otherwise an error. * * @ingroup info_operations */ as_status aerospike_info_host( aerospike * as, as_error * err, const as_policy_info * policy, const char * addr, uint16_t port, const char * req, char ** res) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_info p; as_policy_info_resolve(&p, &as->config.policies, policy); if (! as) { return AEROSPIKE_ERR; } cl_rv rc = citrusleaf_info_auth(as->cluster, (char *) addr, port, (char *) req, res, p.timeout); if (rc) { as_strncpy(err->message, *res, sizeof(err->message)); free(*res); return as_error_fromrc(err, rc); } return AEROSPIKE_OK; }
/** * This is the main driver function which can cater to different types of * scan interfaces exposed to the outside world. This functions should not be * exposed to the outside world. This generic function exists because we dont * want to duplicate too much of code. * * @param as - the aerospike cluster to connect to. * @param err - the error is populated if the return value is not AEROSPIKE_OK. * @param policy - the policy to use for this operation. If NULL, then the default policy will be used. * @param node - the name of the node to perform the scan on. * @param scan - the scan to perform * @param callback - the function to be called for each record scanned. * @param udata - user-data to be passed to the callback * * @return AEROSPIKE_OK on success. Otherwise an error occurred. */ static as_status aerospike_scan_generic( aerospike * as, as_error * err, const as_policy_scan * policy, const char * node, const as_scan * scan, aerospike_scan_foreach_callback callback, void * udata) { cl_rv clrv; as_status rc = AEROSPIKE_OK; cl_scan clscan; as_scan_toclscan(scan, policy, &clscan, false, NULL); if ( clscan.udf.type == CL_SCAN_UDF_NONE ) { scan_bridge bridge_udata = { .udata = udata, .callback = callback }; struct cl_scan_parameters_s params = { .fail_on_cluster_change = clscan.params.fail_on_cluster_change, .priority = clscan.params.priority, .concurrent = clscan.params.concurrent, .threads_per_node = 0 }; int n_bins = scan->select.size; cl_bin * bins = NULL; if ( n_bins > 0 ) { bins = (cl_bin *) alloca(sizeof(cl_bin) * n_bins); for( int i = 0; i < n_bins; i++ ) { strcpy(bins[i].bin_name, scan->select.entries[i]); citrusleaf_object_init_null(&bins[i].object); } } // If the user want to execute only on a single node... if (node) { clrv = citrusleaf_scan_node(as->cluster, (char *) node, (char *) scan->ns, (char *) scan->set, bins, n_bins, scan->no_bins, scan->percent, simplescan_cb, &bridge_udata, ¶ms); rc = as_error_fromrc(err, clrv); } else { // We are not using the very old citrusleaf_scan() call here. First of all, its // very inefficient. It makes a single node on the cluster coordinate the job // of scan. Moreover, it does not accept params like priority etc. cf_vector *v = citrusleaf_scan_all_nodes(as->cluster, (char *) scan->ns, (char *) scan->set, bins, n_bins, scan->no_bins, scan->percent, simplescan_cb, &bridge_udata, ¶ms); rc = process_node_response(v, err); } } else { // If the user want to execute only on a single node... if (node) {
static int cl_batch_cb(char *ns, cf_digest *keyd, char *set, cl_object *key, int result, uint32_t generation, uint32_t ttl, cl_bin *bins, uint16_t n_bins, void *udata) { batch_bridge * p_bridge = (batch_bridge *) udata; aerospike * as = p_bridge->as; as_batch_read * p_r = NULL; // Find the digest. for (uint32_t i = 0; i < p_bridge->n; i++) { p_r = &p_bridge->results[i]; if (memcmp(keyd, p_r->key->digest.value, AS_DIGEST_VALUE_SIZE) == 0) { // Not bothering to check set, which is not always filled. break; } p_r = NULL; } if (! p_r) { as_err(LOGGER, "couldn't find digest"); return -1; // not that this is even checked... } // Fill out this result slot. as_error err; p_r->result = as_error_fromrc(&err, result); // If the result wasn't success, we won't have any record data or metadata. if (result != 0) { return 0; } as_record_init(&p_r->record, n_bins); // works even if n_bins is 0 // There should be record metadata. p_r->record.gen = (uint16_t)generation; p_r->record.ttl = ttl; // There may be bin data. if (n_bins != 0) { clbins_to_asrecord(bins, (uint32_t)n_bins, &p_r->record); } return 0; }
/** * Removes (drops) a secondary index. * * @param as The aerospike cluster to connect to. * @param err The error is populated if the return value is not AEROSPIKE_OK. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param ns The namespace of the index to be removed * @param name The name of the index to be removed * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_index_remove( aerospike * as, as_error * err, const as_policy_info * policy, const char * ns, const char * name) { as_error_reset(err); char * response = NULL; int rc = citrusleaf_secondary_index_drop(as->cluster, ns, name, &response); if ( response != NULL ) { free(response); } return as_error_fromrc(err, rc); }
/** * Store a record in the cluster. Note that the TTL (time to live) value * is specified inside of the rec (as_record) object. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param rec The record containing the data to be written. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_put( aerospike * as, as_error * err, const as_policy_write * policy, const as_key * key, as_record * rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_write p; as_policy_write_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; aspolicywrite_to_clwriteparameters(&p, rec, &wp); int nvalues = rec->bins.size; cl_bin * values = (cl_bin *) alloca(sizeof(cl_bin) * nvalues); asrecord_to_clbins(rec, values, nvalues); cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_put_digest_with_setname(as->cluster, key->ns, key->set, (cf_digest *) digest->value, values, nvalues, &wp); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_put(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, values, nvalues, &wp); break; } default: { // ERROR CASE break; } } // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); return as_error_fromrc(err,rc); }
/** * Execute a query and call the callback function for each result item. * * @param as - the aerospike cluster to connect to. * @param err - the error is populated if the return value is not AEROSPIKE_OK. * @param policy - the policy to use for this operation. If NULL, then the default policy will be used. * @param query - the query to execute against the cluster * @param udata - user-data to be passed to the callback * @param callback - the callback function to call for each result item. * * @return AEROSPIKE_OK on success, otherwise an error. */ as_status aerospike_query_foreach( aerospike * as, as_error * err, const as_policy_query * policy, const as_query * query, aerospike_query_foreach_callback callback, void * udata) { // we want to reset the error so, we have a clean state as_error_reset(err); as_val * err_val = NULL; // resolve policies // as_policy_query p; // as_policy_query_resolve(&p, &as->config.policies, policy); if ( aerospike_query_init(as, err) != AEROSPIKE_OK ) { return err->code; } cl_query * clquery = as_query_toclquery(query); clquery_bridge bridge = { .udata = udata, .callback = callback }; cl_rv rc = citrusleaf_query_foreach(as->cluster, clquery, &bridge, clquery_callback, &err_val); as_status ret = as_error_fromrc(err, rc); if (CITRUSLEAF_OK != rc && err_val) { char * err_str = as_val_tostring(err_val); if(err_str) { strncat(err->message," : ",sizeof(err->message) - strlen(err->message)); strncat(err->message,err_str,sizeof(err->message) - strlen(err->message)); cf_free(err_str); } as_val_destroy(err_val); } cl_query_destroy(clquery); return ret; }
/** * Create a new secondary index. * * @param as - the aerospike cluster to connect to. * @param err - the error is populated if the return value is not AEROSPIKE_OK. * @param policy - the policy to use for this operation. If NULL, then the default policy will be used. * @param ns - the namespace to be indexed * @param set - the set to be indexed * @param bin - the bin to be indexed * @param type - the type of the bin to be indexed * @param name - the name of the index * * @return AEROSPIKE_OK if successful or index already exists. Otherwise an error. */ static as_status aerospike_index_create( aerospike * as, as_error * err, const as_policy_info * policy, const char * ns, const char * set, const char * bin, const char * type, const char * name) { as_error_reset(err); char * response = NULL; int rc = citrusleaf_secondary_index_create(as->cluster, ns, set, name, bin, type, &response); switch ( rc ) { case CITRUSLEAF_OK: case CITRUSLEAF_FAIL_INDEX_FOUND: break; default: as_strncpy(err->message, response, sizeof(err->message)); as_error_fromrc(err, rc); break; } free(response); return err->code; }
/** * Remove a record from the cluster. * * ~~~~~~~~~~{.c} * as_key key; * as_key_init(&key, "ns", "set", "key"); * * if ( aerospike_key_remove(&as, &err, NULL, &key) != AEROSPIKE_OK ) { * fprintf(stderr, "error(%d) %s at [%s:%d]", err.code, err.message, err.file, err.line); * } * ~~~~~~~~~~ * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_remove( aerospike * as, as_error * err, const as_policy_remove * policy, const as_key * key) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_remove p; as_policy_remove_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; aspolicyremove_to_clwriteparameters(&p, &wp); cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_delete_digest(as->cluster, key->ns, (cf_digest *) digest->value, &wp); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_delete(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, &wp); break; } default: { // ERROR CASE break; } } return as_error_fromrc(err,rc); }
static as_status process_node_response(cf_vector *v, as_error *err) { as_status rc = AEROSPIKE_OK; // This returns a vector of return values, the size of which is the size of the cluster int sz = cf_vector_size(v); cl_node_response resp; for(int i=0; i < sz; i++) { cf_vector_get(v, i, &resp); // Even if one of the node responded with an error, set the overall status as error if (resp.node_response != CITRUSLEAF_OK) { rc = as_error_fromrc(err, resp.node_response); } // Set the resp back to zero memset(&resp, 0, sizeof(cl_node_response)); } // Free the result vector cf_vector_destroy(v); return rc; }
/** * Lookup a record by key, then perform specified operations. * * ~~~~~~~~~~{.c} * as_key key; * as_key_init(&key, "ns", "set", "key"); * * as_operations ops; * as_operations_inita(&ops,2); * as_operations_append_int64(&ops, AS_OPERATOR_INCR, "bin1", 456); * as_operations_append_str(&ops, AS_OPERATOR_APPEND, "bin1", "def"); * * if ( aerospike_key_remove(&as, &err, NULL, &key, &ops) != AEROSPIKE_OK ) { * fprintf(stderr, "error(%d) %s at [%s:%d]", err.code, err.message, err.file, err.line); * } * ~~~~~~~~~~ * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param ops The operations to perform on the record. * @param rec The record to be populated with the data from AS_OPERATOR_READ operations. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_operate( aerospike * as, as_error * err, const as_policy_operate * policy, const as_key * key, const as_operations * ops, as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_operate p; as_policy_operate_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; aspolicyoperate_to_clwriteparameters(&p, ops, &wp); uint32_t gen = 0; uint32_t ttl = 0; int n_operations = ops->binops.size; cl_operation * operations = (cl_operation *) alloca(sizeof(cl_operation) * n_operations); int n_read_ops = 0; as_bin_name * read_op_bins = alloca(sizeof(as_bin_name) * n_operations); for(int i=0; i<n_operations; i++) { cl_operation * clop = &operations[i]; as_binop * op = &ops->binops.entries[i]; // Length check already done on as_bin name length. For performance we // we'll leave out this down-size check since this is a temporary shim // and we know the CL and AS limits are the same... // if ( strlen(op->bin.name) > CL_BINNAME_SIZE - 1 ) { // return as_error_update(err, AEROSPIKE_ERR_PARAM, "bin name too long: %s", op->bin.name); // } strcpy(clop->bin.bin_name, op->bin.name); clop->op = (cl_operator)op->op; // Collect bin names that are read. if (op->op == AS_OPERATOR_READ) { strcpy(read_op_bins[n_read_ops++], op->bin.name); } asbinvalue_to_clobject(op->bin.valuep, &clop->bin.object); } cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_operate_digest(as->cluster, key->ns, key->set, (cf_digest *) digest->value, operations, n_operations, &wp, &gen, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_operate(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, operations, n_operations, &wp, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( n_read_ops != 0 && rc == CITRUSLEAF_OK && rec != NULL ) { as_record * r = *rec; if ( r == NULL ) { r = as_record_new(0); } if ( r->bins.entries == NULL ) { r->bins.capacity = n_read_ops; r->bins.size = 0; r->bins.entries = malloc(sizeof(as_bin) * n_read_ops); r->bins._free = true; } r->gen = (uint16_t) gen; r->ttl = ttl; // This works around an existing client bug where the data returned for // a read operation is stored in the first bin struct with that bin // name, not necessarily the bin struct corresponding to the read. for (int i = 0; i < n_read_ops; i++) { for (int j = 0; j < n_operations; j++) { if (strcmp(read_op_bins[i], operations[j].bin.bin_name) == 0) { clbin_to_asrecord(&operations[j].bin, r); citrusleaf_object_free(&operations[j].bin.object); break; } } } *rec = r; } return as_error_fromrc(err,rc); }
/** * Lookup a record by key, then return specified bins. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param bins The bins to select. A NULL terminated array of NULL terminated strings. * @param rec The record to be populated with the data from request. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_select( aerospike * as, as_error * err, const as_policy_read * policy, const as_key * key, const char * bins[], as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_read p; as_policy_read_resolve(&p, &as->config.policies, policy); uint32_t timeout = p.timeout == UINT32_MAX ? 0 : p.timeout; uint32_t gen = 0; uint32_t ttl = 0; int nvalues = 0; cl_bin * values = NULL; for (nvalues = 0; bins[nvalues] != NULL && bins[nvalues][0] != '\0'; nvalues++) ; values = (cl_bin *) alloca(sizeof(cl_bin) * nvalues); for ( int i = 0; i < nvalues; i++ ) { if ( strlen(bins[i]) > AS_BIN_NAME_MAX_LEN ) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "bin name too long: %s", bins[i]); } strcpy(values[i].bin_name, bins[i]); citrusleaf_object_init(&values[i].object); } cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get_digest(as->cluster, key->ns, (cf_digest *) digest->value, values, nvalues, timeout, &gen, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, values, nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( rc == CITRUSLEAF_OK && rec != NULL ) { as_record * r = *rec; if ( r == NULL ) { r = as_record_new(0); } if ( r->bins.entries == NULL ) { r->bins.capacity = nvalues; r->bins.size = 0; r->bins.entries = malloc(sizeof(as_bin) * nvalues); r->bins._free = true; } clbins_to_asrecord(values, nvalues, r); r->gen = (uint16_t) gen; r->ttl = ttl; *rec = r; } if ( values != NULL ) { // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); } return as_error_fromrc(err,rc); }
/** * Check if a record exists in the cluster via its key. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param record The record to populated with metadata if record exists, otherwise NULL * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_exists( aerospike * as, as_error * err, const as_policy_read * policy, const as_key * key, as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_read p; as_policy_read_resolve(&p, &as->config.policies, policy); uint32_t timeout = p.timeout == UINT32_MAX ? 0 : p.timeout; uint32_t gen = 0; uint32_t ttl = 0; // TODO - a version of 'exists' that returns all metadata int nvalues = 0; cl_bin * values = NULL; cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_exists_digest(as->cluster, key->ns, (cf_digest *) digest->value, values, nvalues, timeout, &gen, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_exists_key(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, values, nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( values != NULL ) { // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); free(values); } switch(rc) { case CITRUSLEAF_OK: { as_record * r = *rec; if ( r == NULL ) { r = as_record_new(0); } r->gen = (uint16_t) gen; r->ttl = ttl; *rec = r; break; } default: *rec = NULL; break; } return as_error_fromrc(err,rc); }
static as_status batch_read( aerospike * as, as_error * err, const as_policy_batch * policy, const as_batch * batch, aerospike_batch_read_callback callback, void * udata, bool get_bin_data ) { as_error_reset(err); // Lazily initialize batch machinery: cl_cluster_batch_init(as->cluster); uint32_t n = batch->keys.size; as_batch_read* results = (as_batch_read*)alloca(sizeof(as_batch_read) * n); if (! results) { return as_error_update(err, AEROSPIKE_ERR_CLIENT, "failed results array allocation"); } cf_digest* digests = (cf_digest*)alloca(sizeof(cf_digest) * n); if (! digests) { return as_error_update(err, AEROSPIKE_ERR_CLIENT, "failed digests array allocation"); } // Because we're wrapping the old functionality, we only support a batch // with all keys in the same namespace. char* ns = batch->keys.entries[0].ns; for (uint32_t i = 0; i < n; i++) { if (strcmp(ns, batch->keys.entries[i].ns) != 0) { // Don't need to destroy results' records since they won't have any // associated allocations yet. return as_error_update(err, AEROSPIKE_ERR_PARAM, "batch keys must all be in the same namespace"); } as_batch_read * p_r = &results[i]; p_r->result = -1; // TODO - make an 'undefined' error as_record_init(&p_r->record, 0); p_r->key = (const as_key*)as_batch_keyat(batch, i); memcpy(&digests[i], as_key_digest((as_key*)p_r->key)->value, AS_DIGEST_VALUE_SIZE); } batch_bridge bridge; bridge.as = as; bridge.results = results; bridge.n = n; cl_rv rc = citrusleaf_batch_read(as->cluster, ns, digests, n, NULL, 0, get_bin_data, cl_batch_cb, &bridge); callback(results, n, udata); for (uint32_t i = 0; i < n; i++) { as_record_destroy(&results[i].record); } return as_error_fromrc(err, rc); }
/** * Look up a record by key, then return all bins. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param rec The record to be populated with the data from request. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_get( aerospike * as, as_error * err, const as_policy_read * policy, const as_key * key, as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_read p; as_policy_read_resolve(&p, &as->config.policies, policy); uint32_t timeout = p.timeout == UINT32_MAX ? 0 : p.timeout; uint32_t gen = 0; uint32_t ttl = 0; int nvalues = 0; cl_bin * values = NULL; cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get_all_digest_getsetname(as->cluster, key->ns, (cf_digest *) digest->value, &values, &nvalues, timeout, &gen, NULL, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get_all(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, &values, &nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( rc == CITRUSLEAF_OK && rec != NULL ) { as_record * r = *rec; if ( r == NULL ) { r = as_record_new(0); } if ( r->bins.entries == NULL ) { r->bins.capacity = nvalues; r->bins.size = 0; r->bins.entries = malloc(sizeof(as_bin) * nvalues); r->bins._free = true; } clbins_to_asrecord(values, nvalues, r); r->gen = (uint16_t) gen; r->ttl = ttl; *rec = r; } if ( values != NULL ) { // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); free(values); } return as_error_fromrc(err,rc); }
/** * Lookup a record by key, then apply the UDF. * * ~~~~~~~~~~{.c} * as_key key; * as_key_init(&key, "ns", "set", "key"); * * as_arraylist args; * as_arraylist_init(&args, 2, 0); * as_arraylist_append_int64(&args, 1); * as_arraylist_append_int64(&args, 2); * * as_val * res = NULL; * * if ( aerospike_key_apply(&as, &err, NULL, &key, "math", "add", &args, &res) != AEROSPIKE_OK ) { * fprintf(stderr, "error(%d) %s at [%s:%d]", err.code, err.message, err.file, err.line); * } * else { * as_val_destroy(res); * } * * as_arraylist_destroy(&args); * ~~~~~~~~~~ * * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param module The module containing the function to execute. * @param function The function to execute. * @param arglist The arguments for the function. * @param result The return value from the function. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_apply( aerospike * as, as_error * err, const as_policy_apply * policy, const as_key * key, const char * module, const char * function, as_list * arglist, as_val ** result) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_apply p; as_policy_apply_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; cl_write_parameters_set_default(&wp); wp.timeout_ms = p.timeout == UINT32_MAX ? 0 : p.timeout; cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_serializer ser; as_msgpack_init(&ser); as_string file; as_string_init(&file, (char *) module, true /*ismalloc*/); as_string func; as_string_init(&func, (char *) function, true /*ismalloc*/); as_buffer args; as_buffer_init(&args); as_serializer_serialize(&ser, (as_val *) arglist, &args); as_call call = { .file = &file, .func = &func, .args = &args }; uint64_t trid = 0; cl_bin * bins = 0; int n_bins = 0; cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = do_the_full_monte( as->cluster, 0, CL_MSG_INFO2_WRITE, 0, key->ns, key->set, 0, (cf_digest *) digest->value, &bins, CL_OP_WRITE, 0, &n_bins, NULL, &wp, &trid, NULL, &call, NULL ); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = do_the_full_monte( as->cluster, 0, CL_MSG_INFO2_WRITE, 0, key->ns, key->set, &okey, (cf_digest*)digest->value, &bins, CL_OP_WRITE, 0, &n_bins, NULL, &wp, &trid, NULL, &call, NULL ); break; } default: { // ERROR CASE break; } } as_buffer_destroy(&args); if (! (rc == CITRUSLEAF_OK || rc == CITRUSLEAF_FAIL_UDF_BAD_RESPONSE)) { as_error_fromrc(err, rc); } else { // Begin processing the data returned from the server, // IFF `result` argument is not NULL. // The reason is if `result` is NULL, then it implies the end user // does not care about the data returned from the server. if ( n_bins == 1 ) { cl_bin * bin = &bins[0]; if ( strcmp(bin->bin_name,"SUCCESS") == 0 ) { if ( result ) { as_val * val = NULL; clbin_to_asval(bin, &ser, &val); *result = val; } } else if ( strcmp(bin->bin_name,"FAILURE") == 0 ) { as_val * val = NULL; clbin_to_asval(bin, &ser, &val); if ( val->type == AS_STRING ) { as_string * s = as_string_fromval(val); as_error_update(err, AEROSPIKE_ERR_UDF, as_string_tostring(s)); } else { as_error_update(err, AEROSPIKE_ERR_SERVER, "unexpected failure bin type"); } as_val_destroy(val); } else { as_error_update(err, AEROSPIKE_ERR_SERVER, "unexpected bin name"); } } else { as_error_update(err, AEROSPIKE_ERR_SERVER, "unexpected number of bins"); } } if ( bins ) { citrusleaf_bins_free(bins, n_bins); free(bins); } as_serializer_destroy(&ser); return err->code; }
/** * Check if a record exists in the cluster via its key. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param exists The variable to populate with `true` if the record exists, otherwise `false`. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_exists( aerospike * as, as_error * err, const as_policy_read * policy, const as_key * key, bool * exists) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_read p; as_policy_read_resolve(&p, &as->config.policies, policy); uint32_t timeout = p.timeout == UINT32_MAX ? 0 : p.timeout; uint32_t gen = 0; uint32_t ttl = 0; // TODO - a version of 'exists' that returns all metadata int nvalues = 0; cl_bin * values = NULL; cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_exists_digest(as->cluster, key->ns, (cf_digest *) digest->value, values, nvalues, timeout, &gen, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); rc = citrusleaf_exists_key(as->cluster, key->ns, key->set, &okey, values, nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( values != NULL ) { free(values); } switch(rc) { case CITRUSLEAF_OK: if ( exists ) { *exists = true; } return AEROSPIKE_OK; case CITRUSLEAF_FAIL_NOTFOUND: if ( exists ) { *exists = false; } return AEROSPIKE_OK; default: if ( exists ) { *exists = false; } return as_error_fromrc(err,rc); } }