int test_operate(cl_cluster *clc) { cl_object key; cl_operation ops[3]; cl_rv rv; citrusleaf_object_init_str(&key, myKey); strcpy(&ops[0].bin.bin_name[0],bin1); strcpy(&ops[1].bin.bin_name[0],bin2); strcpy(&ops[2].bin.bin_name[0],bin3); citrusleaf_object_init(&ops[0].bin.object); citrusleaf_object_init_int(&ops[1].bin.object, 2); citrusleaf_object_init_blob(&ops[2].bin.object, blobData2, strlen(blobData2)+1); ops[0].op = CL_OP_READ; ops[1].op = CL_OP_INCR; ops[2].op = CL_OP_WRITE; rv = citrusleaf_operate(clc, ns, myset, &key, &ops[0], 3, NULL, false, NULL); if( rv != CITRUSLEAF_OK ){ printf(" TEST FAILED - go-right case of Operate is failing with %d\n", rv); return -1; } // and look at the value we read... if( strcmp(ops[0].bin.object.u.str, strData) ){ printf( "TEST FAILED - Operate did not read back correct data! %s, %s\n", ops[0].bin.object.u.str, strData); return -1; } // and release that value... citrusleaf_object_free(&ops[0].bin.object); // now read the values back. ops[0].op = CL_OP_READ; ops[1].op = CL_OP_READ; ops[2].op = CL_OP_READ; citrusleaf_object_init(&ops[0].bin.object); citrusleaf_object_init(&ops[1].bin.object); citrusleaf_object_init(&ops[2].bin.object); rv = citrusleaf_operate(clc, ns, myset, &key, &ops[0], 3, NULL ,false, NULL); if( rv != CITRUSLEAF_OK ){ printf(" TEST FAILED - go-right case of Operate is failing with %d\n", rv); return -1; } // check the values... if( strcmp(ops[0].bin.object.u.str, strData) ){ printf(" TEST FAILED - did not read back the same string\n"); return -1; } if( ops[1].bin.object.u.i64 != intData+2 ){ printf(" TEST FAILED - did not read back correct int %lu %lu\n", ops[1].bin.object.u.i64, intData+2); return -1; } if( strcmp(ops[2].bin.object.u.blob, blobData2 ) ){ printf(" TEST FAILED - did not read back blob correctly %s, %s\n", (char *)ops[2].bin.object.u.blob, blobData2); return -1; } // and free them all... citrusleaf_object_free(&ops[0].bin.object); citrusleaf_object_free(&ops[1].bin.object); citrusleaf_object_free(&ops[2].bin.object); // what happens if I request something that doesn't exist? // XXX - should do this elsewhere... /* strcpy(&ops[0].bin.bin_name[0], "doesnt exist"); rv = citrusleaf_operate(clc, mySet, myKey, &ops[3], 3, NULL ); */ return 0; }
/** * 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); }