/* * Internal Function: udf_aerospike_delbin * * Parameters: * r - udf_record to be manipulated * bname - name of the bin to be deleted * * Return value: * 0 on success * -1 on failure * * Description: * The function deletes the bin with the name * passed in as parameter. The as_bin_destroy function * which is called here, only frees the data and * the bin is marked as not in use. The bin can then be reused later. * * Synchronization : object lock acquired by the transaction thread executing UDF. * Partition reservation takes place just before the transaction starts executing * ( look for as_partition_reserve_udf in thr_tsvc.c ) * * Callers: * udf_aerospike__apply_update_atomic * In this function, if it fails at the time of update, the record is set * to rollback all the updates till this point. The case where it fails in * rollback is not handled. * * Side Notes: * i. write_to_device will be set to true on a successful bin destroy. * If all the updates from udf_aerospike__apply_update_atomic (including this) are * successful, the record will be written to disk and reopened so that the rest of * sets of updates can be applied. * * ii. If delete from sindex fails, we do not handle it. */ static int udf_aerospike_delbin(udf_record * urecord, const char * bname) { // Check that bname is not completely invalid if ( !bname || !bname[0] ) { cf_warning(AS_UDF, "delete bin: no bin name supplied"); return -1; } size_t blen = strlen(bname); as_storage_rd *rd = urecord->rd; as_transaction *tr = urecord->tr; // Check quality of bname -- first check that it is proper length, then // check that we're not over quota for bins, then finally make sure that // the bin exists. if (blen > (AS_ID_BIN_SZ - 1 ) || !as_bin_name_within_quota(rd->ns, (byte *)bname, blen)) { // Can't read bin if name too large or over quota cf_warning(AS_UDF, "bin name(%s) too big. Bin not added", bname); return -1; } as_bin * b = as_bin_get(rd, (byte *)bname, blen); if ( !b ) { cf_warning(AS_UDF, "as_bin_get failed: bin name(%s) not found", bname); return -1; } SINDEX_BINS_SETUP(delbin, 1); int sindex_ret = AS_SINDEX_OK; bool has_sindex = as_sindex_ns_has_sindex(rd->ns); if (has_sindex) { sindex_ret = as_sindex_sbin_from_bin(rd->ns, as_index_get_set_name(rd->r, rd->ns), b, delbin); } int32_t i = as_bin_get_index(rd, (byte *)bname, blen); if (i != -1) { if (has_sindex) { tr->flag |= AS_TRANSACTION_FLAG_SINDEX_TOUCHED; if (AS_SINDEX_OK == sindex_ret) { as_sindex_delete_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), 1, delbin, rd); //TODO: Check the error code returned through sindex_ret. (like out of sync ) } } as_bin_destroy(rd, i); } else { cf_warning(AS_UDF, "deleting non-existing bin %s ignored", bname); } if (has_sindex) { as_sindex_sbin_freeall(delbin, 1); } return 0; }
// Remove record from secondary index. Called only for data-in-memory. If // data-not-in-memory, existing record is not read, and secondary index entry is // cleaned up by background sindex defrag thread. // TODO - rename as as_record_... and move to record.c? void delete_adjust_sindex(as_storage_rd* rd) { as_namespace* ns = rd->ns; if (! record_has_sindex(rd->r, ns)) { return; } as_storage_rd_load_n_bins(rd); as_storage_rd_load_bins(rd, NULL); remove_from_sindex(ns, as_index_get_set_name(rd->r, ns), &rd->r->keyd, rd->bins, rd->n_bins); }
static const char * udf_record_setname(const as_rec * rec) { int ret = udf_record_param_check(rec, UDF_BIN_NONAME, __FILE__, __LINE__); if (ret) { return NULL; } udf_record * urecord = (udf_record *) as_rec_source(rec); if (urecord && (urecord->flag & UDF_RECORD_FLAG_STORAGE_OPEN)) { return as_index_get_set_name(urecord->r_ref->r, urecord->rd->ns); } else { cf_warning(AS_UDF, "Error in getting set name: no record found"); return NULL; } }
// If called for data-not-in-memory, this may read record from drive! // TODO - rename as as_record_... and move to record.c? void record_delete_adjust_sindex(as_record* r, as_namespace* ns) { if (! record_has_sindex(r, ns)) { return; } as_storage_rd rd; as_storage_record_open(ns, r, &rd); as_storage_rd_load_n_bins(&rd); as_bin stack_bins[ns->storage_data_in_memory ? 0 : rd.n_bins]; as_storage_rd_load_bins(&rd, stack_bins); remove_from_sindex(ns, as_index_get_set_name(r, ns), &r->keyd, rd.bins, rd.n_bins); as_storage_record_close(&rd); }
int as_msg_make_response_bufbuilder(as_record *r, as_storage_rd *rd, cf_buf_builder **bb_r, bool nobindata, char *nsname, bool use_sets, bool include_key, cf_vector *binlist) { // Sanity checks. Either rd should be there or nobindata and nsname should be present. if (!(rd || (nobindata && nsname))) { cf_detail(AS_PROTO, "Neither storage record nor nobindata is set. Skipping the record."); return 0; } // figure out the size of the entire buffer int set_name_len = 0; const char *set_name = NULL; int ns_len = rd ? strlen(rd->ns->name) : strlen(nsname); if (use_sets && as_index_get_set_id(r) != INVALID_SET_ID) { as_namespace *ns = NULL; if (rd) { ns = rd->ns; } else if (nsname) { ns = as_namespace_get_byname(nsname); } if (!ns) { cf_info(AS_PROTO, "Cannot get namespace, needed to get set information. Skipping record."); return -1; } set_name = as_index_get_set_name(r, ns); if (set_name) { set_name_len = strlen(set_name); } } uint8_t* key = NULL; uint32_t key_size = 0; if (include_key && as_index_is_flag_set(r, AS_INDEX_FLAG_KEY_STORED)) { if (! as_storage_record_get_key(rd)) { cf_info(AS_PROTO, "can't get key - skipping record"); return -1; } key = rd->key; key_size = rd->key_size; } uint16_t n_fields = 2; int msg_sz = sizeof(as_msg); msg_sz += sizeof(as_msg_field) + sizeof(cf_digest); msg_sz += sizeof(as_msg_field) + ns_len; if (set_name) { n_fields++; msg_sz += sizeof(as_msg_field) + set_name_len; } if (key) { n_fields++; msg_sz += sizeof(as_msg_field) + key_size; } int list_bins = 0; int in_use_bins = 0; if (rd) { in_use_bins = as_bin_inuse_count(rd); } if (nobindata == false) { if(binlist) { int binlist_sz = cf_vector_size(binlist); for(uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); cf_debug(AS_PROTO, " Binname projected inside is |%s| \n", binname); as_bin *p_bin = as_bin_get (rd, (uint8_t*)binname, strlen(binname)); if (!p_bin) { cf_debug(AS_PROTO, "To be projected bin |%s| not found \n", binname); continue; } cf_debug(AS_PROTO, "Adding bin |%s| to projected bins |%s| \n", binname); list_bins++; msg_sz += sizeof(as_msg_op); msg_sz += rd->ns->single_bin ? 0 : strlen(binname); uint32_t psz; if (as_bin_is_hidden(p_bin)) { psz = 0; } else { as_particle_tobuf(p_bin, 0, &psz); // get size } msg_sz += psz; } } else { msg_sz += sizeof(as_msg_op) * in_use_bins; // the bin headers for (uint16_t i = 0; i < in_use_bins; i++) { as_bin *p_bin = &rd->bins[i]; msg_sz += rd->ns->single_bin ? 0 : strlen(as_bin_get_name_from_id(rd->ns, p_bin->id)); uint32_t psz; if (as_bin_is_hidden(p_bin)) { psz = 0; } else { as_particle_tobuf(p_bin, 0, &psz); // get size } msg_sz += psz; } } } uint8_t *b; cf_buf_builder_reserve(bb_r, msg_sz, &b); // set up the header uint8_t *buf = b; as_msg *msgp = (as_msg *) buf; msgp->header_sz = sizeof(as_msg); msgp->info1 = (nobindata ? AS_MSG_INFO1_GET_NOBINDATA : 0); msgp->info2 = 0; msgp->info3 = 0; msgp->unused = 0; msgp->result_code = 0; msgp->generation = r->generation; msgp->record_ttl = r->void_time; msgp->transaction_ttl = 0; msgp->n_fields = n_fields; if (rd) { if (binlist) msgp->n_ops = list_bins; else msgp->n_ops = in_use_bins; } else { msgp->n_ops = 0; } as_msg_swap_header(msgp); buf += sizeof(as_msg); as_msg_field *mf = (as_msg_field *) buf; mf->field_sz = sizeof(cf_digest) + 1; mf->type = AS_MSG_FIELD_TYPE_DIGEST_RIPE; if (rd) { memcpy(mf->data, &rd->keyd, sizeof(cf_digest)); } else { memcpy(mf->data, &r->key, sizeof(cf_digest)); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + sizeof(cf_digest); mf = (as_msg_field *) buf; mf->field_sz = ns_len + 1; mf->type = AS_MSG_FIELD_TYPE_NAMESPACE; if (rd) { memcpy(mf->data, rd->ns->name, ns_len); } else { memcpy(mf->data, nsname, ns_len); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + ns_len; if (set_name) { mf = (as_msg_field *) buf; mf->field_sz = set_name_len + 1; mf->type = AS_MSG_FIELD_TYPE_SET; memcpy(mf->data, set_name, set_name_len); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + set_name_len; } if (key) { mf = (as_msg_field *) buf; mf->field_sz = key_size + 1; mf->type = AS_MSG_FIELD_TYPE_KEY; memcpy(mf->data, key, key_size); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + key_size; } if (nobindata) { goto Out; } if(binlist) { int binlist_sz = cf_vector_size(binlist); for(uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); cf_debug(AS_PROTO, " Binname projected inside is |%s| \n", binname); as_bin *p_bin = as_bin_get (rd, (uint8_t*)binname, strlen(binname)); if (!p_bin) // should it be checked before ??? continue; as_msg_op *op = (as_msg_op *)buf; buf += sizeof(as_msg_op); op->op = AS_MSG_OP_READ; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, p_bin); buf += op->name_sz; // Since there are two variable bits, the size is everything after // the data bytes - and this is only the head, we're patching up // the rest in a minute. op->op_sz = 4 + op->name_sz; if (as_bin_inuse(p_bin)) { op->particle_type = as_particle_type_convert(as_bin_get_particle_type(p_bin)); op->version = as_bin_get_version(p_bin, rd->ns->single_bin); uint32_t psz = msg_sz - (buf - b); // size remaining in buffer, for safety if (as_bin_is_hidden(p_bin)) { op->particle_type = AS_PARTICLE_TYPE_NULL; psz = 0; } else { if (0 != as_particle_tobuf(p_bin, buf, &psz)) { cf_warning(AS_PROTO, "particle to buf: could not copy data!"); } } buf += psz; op->op_sz += psz; } else { cf_debug(AS_PROTO, "Whoops !! bin not in use"); op->particle_type = AS_PARTICLE_TYPE_NULL; } as_msg_swap_op(op); } } else { // over all bins, copy into the buffer for (uint16_t i = 0; i < in_use_bins; i++) { as_msg_op *op = (as_msg_op *)buf; buf += sizeof(as_msg_op); op->op = AS_MSG_OP_READ; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, &rd->bins[i]); buf += op->name_sz; // Since there are two variable bits, the size is everything after // the data bytes - and this is only the head, we're patching up // the rest in a minute. op->op_sz = 4 + op->name_sz; if (as_bin_inuse(&rd->bins[i])) { op->particle_type = as_particle_type_convert(as_bin_get_particle_type(&rd->bins[i])); op->version = as_bin_get_version(&rd->bins[i], rd->ns->single_bin); uint32_t psz = msg_sz - (buf - b); // size remaining in buffer, for safety if (as_bin_is_hidden(&rd->bins[i])) { op->particle_type = AS_PARTICLE_TYPE_NULL; psz = 0; } else { if (0 != as_particle_tobuf(&rd->bins[i], buf, &psz)) { cf_warning(AS_PROTO, "particle to buf: could not copy data!"); } } buf += psz; op->op_sz += psz; } else { op->particle_type = AS_PARTICLE_TYPE_NULL; } as_msg_swap_op(op); } } Out: return(0); }
// NB: this uses the same logic as the bufbuild function // as_msg_make_response_bufbuilder() but does not build a buffer and simply // returns sizing information. This is required for query runtime memory // accounting. // returns -1 in case of error // otherwise returns resize value size_t as_msg_response_msgsize(as_record *r, as_storage_rd *rd, bool nobindata, char *nsname, bool use_sets, cf_vector *binlist) { // Sanity checks. Either rd should be there or nobindata and nsname should be present. if (!(rd || (nobindata && nsname))) { cf_detail(AS_PROTO, "Neither storage record nor nobindata is set. Skipping the record."); return -1; } // figure out the size of the entire buffer int set_name_len = 0; const char *set_name = NULL; int ns_len = rd ? strlen(rd->ns->name) : strlen(nsname); if (use_sets && as_index_get_set_id(r) != INVALID_SET_ID) { as_namespace *ns = NULL; if (rd) { ns = rd->ns; } else if (nsname) { ns = as_namespace_get_byname(nsname); } if (!ns) { cf_info(AS_PROTO, "Cannot get namespace, needed to get set information. Skipping record."); return -1; } set_name = as_index_get_set_name(r, ns); if (set_name) { set_name_len = strlen(set_name); } } int msg_sz = sizeof(as_msg); msg_sz += sizeof(as_msg_field) + sizeof(cf_digest); msg_sz += sizeof(as_msg_field) + ns_len; if (set_name) { msg_sz += sizeof(as_msg_field) + set_name_len; } int in_use_bins = as_bin_inuse_count(rd); int list_bins = 0; if (nobindata == false) { if(binlist) { int binlist_sz = cf_vector_size(binlist); for(uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); cf_debug(AS_PROTO, " Binname projected inside is |%s| \n", binname); as_bin *p_bin = as_bin_get (rd, (uint8_t*)binname, strlen(binname)); if (!p_bin) { cf_debug(AS_PROTO, "To be projected bin |%s| not found \n", binname); continue; } cf_debug(AS_PROTO, "Adding bin |%s| to projected bins |%s| \n", binname); list_bins++; msg_sz += sizeof(as_msg_op); msg_sz += rd->ns->single_bin ? 0 : strlen(binname); uint32_t psz; as_particle_tobuf(p_bin, 0, &psz); // get size msg_sz += psz; } } else { msg_sz += sizeof(as_msg_op) * in_use_bins; // the bin headers for (uint16_t i = 0; i < in_use_bins; i++) { as_bin *p_bin = &rd->bins[i]; msg_sz += rd->ns->single_bin ? 0 : strlen(as_bin_get_name_from_id(rd->ns, p_bin->id)); uint32_t psz; as_particle_tobuf(p_bin, 0, &psz); // get size msg_sz += psz; } } } return msg_sz; }
/* * Internal function: udf_aerospike_setbin * * Parameters: * r -- udf_record to be manipulated * bname -- name of the bin to be deleted * val -- value to be updated with * * Return value: * 0 on success * -1 on failure * * Description: * The function sets the bin with the name * passed in as parameter to the value, passed as the third parameter. * Before updating the bin, it is checked if the value can fit in the storage * * Synchronization : object lock acquired by the transaction thread executing UDF. * Partition reservation takes place just before the transaction starts executing * ( look for as_partition_reserve_udf in thr_tsvc.c ) * * Callers: * udf_aerospike__apply_update_atomic * In this function, if it fails at the time of update, the record is set * to rollback all the updates till this point. The case where it fails in * rollback is not handled. * * Side Notes: * i. write_to_device will be set to true on a successful bin update. * If all the updates from udf_aerospike__apply_update_atomic (including this) are * successful, the record will be written to disk and reopened so that the rest of * sets of updates can be applied. * * ii. If put in sindex fails, we do not handle it. * * TODO make sure anything goes into setbin only if the bin value is * changed */ static const int udf_aerospike_setbin(udf_record * urecord, const char * bname, const as_val * val, bool is_hidden) { if (bname == NULL || bname[0] == 0 ) { cf_warning(AS_UDF, "no bin name supplied"); return -1; } uint8_t type = as_val_type(val); if (is_hidden && ((type != AS_MAP) && (type != AS_LIST))) { cf_warning(AS_UDF, "Hidden %d Type Not allowed", type); return -3; } size_t blen = strlen(bname); as_storage_rd * rd = urecord->rd; as_transaction *tr = urecord->tr; as_index_ref * index = urecord->r_ref; as_bin * b = as_bin_get(rd, (byte *)bname, blen); if ( !b && (blen > (AS_ID_BIN_SZ - 1 ) || !as_bin_name_within_quota(rd->ns, (byte *)bname, blen)) ) { // Can't write bin cf_warning(AS_UDF, "bin name %s too big. Bin not added", bname); return -1; } if ( !b ) { // See if there's a free one, the hope is you will always find the bin because // you have already allocated bin space before calling this function. b = as_bin_create(index->r, rd, (byte *)bname, blen, 0); if (!b) { cf_warning(AS_UDF, "ERROR: udf_aerospike_setbin: as_bin_create: bin not found, something went really wrong!"); return -1; } } SINDEX_BINS_SETUP(oldbin, 1); SINDEX_BINS_SETUP(newbin, 1); bool needs_sindex_delete = false; bool needs_sindex_put = false; bool needs_sindex_update = false; bool has_sindex = as_sindex_ns_has_sindex(rd->ns); if (has_sindex && (as_sindex_sbin_from_bin(rd->ns, as_index_get_set_name(rd->r, rd->ns), b, oldbin) == AS_SINDEX_OK)) { needs_sindex_delete = true; } // we know we are doing an update now, make sure there is particle data, // set to be 1 wblock size now @TODO! uint32_t pbytes = 0; int ret = 0; if (!rd->ns->storage_data_in_memory && !urecord->particle_data) { urecord->particle_data = cf_malloc(rd->ns->storage_write_block_size); urecord->cur_particle_data = urecord->particle_data; urecord->end_particle_data = urecord->particle_data + rd->ns->storage_write_block_size; } cf_detail(AS_UDF, "udf_setbin: bin %s type %d ", bname, type ); switch(type) { case AS_STRING: { as_string * v = as_string_fromval(val); byte * s = (byte *) as_string_tostring(v); size_t l = as_string_len(v); // Save for later. // cf_detail(AS_UDF, "udf_setbin: string: binname %s value is %s",bname,s); if ( !as_storage_bin_can_fit(rd->ns, l) ) { cf_warning(AS_UDF, "string: bin size too big"); ret = -1; break; } if (rd->ns->storage_data_in_memory) { as_particle_frombuf(b, AS_PARTICLE_TYPE_STRING, s, l, NULL, true); } else { pbytes = l + as_particle_get_base_size(AS_PARTICLE_TYPE_STRING); if ((urecord->cur_particle_data + pbytes) < urecord->end_particle_data) { as_particle_frombuf(b, AS_PARTICLE_TYPE_STRING, s, l, urecord->cur_particle_data, rd->ns->storage_data_in_memory); urecord->cur_particle_data += pbytes; } else { cf_warning(AS_UDF, "string: bin data size too big: pbytes %d" " pdata %p cur_part+pbytes %p pend %p", pbytes, urecord->particle_data, urecord->cur_particle_data + pbytes, urecord->end_particle_data); ret = -1; break; } } break; } case AS_BYTES: { as_bytes * v = as_bytes_fromval(val); uint8_t * s = as_bytes_get(v); size_t l = as_bytes_size(v); if ( !as_storage_bin_can_fit(rd->ns, l) ) { cf_warning(AS_UDF, "bytes: bin size too big"); ret = -1; break; } if (rd->ns->storage_data_in_memory) { as_particle_frombuf(b, AS_PARTICLE_TYPE_BLOB, s, l, NULL, true); } else { pbytes = l + as_particle_get_base_size(AS_PARTICLE_TYPE_BLOB); if ((urecord->cur_particle_data + pbytes) < urecord->end_particle_data) { as_particle_frombuf(b, AS_PARTICLE_TYPE_BLOB, s, l, urecord->cur_particle_data, rd->ns->storage_data_in_memory); urecord->cur_particle_data += pbytes; } else { cf_warning(AS_UDF, "bytes: bin data size too big pbytes %d" " pdata %p cur_part+pbytes %p pend %p", pbytes, urecord->particle_data, urecord->cur_particle_data + pbytes, urecord->end_particle_data); ret = -1; break; } } break; } case AS_BOOLEAN: { as_boolean * v = as_boolean_fromval(val); bool d = as_boolean_get(v); int64_t i = __be64_to_cpup((void *)&d); if ( !as_storage_bin_can_fit(rd->ns, 8) ) { cf_warning(AS_UDF, "bool: bin size too big"); ret = -1; break; } if (rd->ns->storage_data_in_memory) { as_particle_frombuf(b, AS_PARTICLE_TYPE_INTEGER, (uint8_t *) &i, 8, NULL, true); } else { pbytes = 8 + as_particle_get_base_size(AS_PARTICLE_TYPE_INTEGER); if ((urecord->cur_particle_data + pbytes) < urecord->end_particle_data) { as_particle_frombuf(b, AS_PARTICLE_TYPE_INTEGER, (uint8_t *) &i, 8, urecord->cur_particle_data, rd->ns->storage_data_in_memory); urecord->cur_particle_data += pbytes; } else { cf_warning(AS_UDF, "bool: bin data size too big: pbytes %d %p %p %p", pbytes, urecord->particle_data, urecord->cur_particle_data, urecord->end_particle_data); ret = -1; break; } } break; } case AS_INTEGER: { as_integer * v = as_integer_fromval(val); int64_t i = as_integer_get(v); int64_t j = __be64_to_cpup((void *)&i); if ( !as_storage_bin_can_fit(rd->ns, 8) ) { cf_warning(AS_UDF, "int: bin size too big"); ret = -1; break; } if (rd->ns->storage_data_in_memory) { as_particle_frombuf(b, AS_PARTICLE_TYPE_INTEGER, (uint8_t *) &j, 8, NULL, true); } else { pbytes = 8 + as_particle_get_base_size(AS_PARTICLE_TYPE_INTEGER); if ((urecord->cur_particle_data + pbytes) < urecord->end_particle_data) { as_particle_frombuf(b, AS_PARTICLE_TYPE_INTEGER, (uint8_t *) &j, 8, urecord->cur_particle_data, rd->ns->storage_data_in_memory); urecord->cur_particle_data += pbytes; } else { cf_warning(AS_UDF, "int: bin data size too big: pbytes %d %p %p %p", pbytes, urecord->particle_data, urecord->cur_particle_data, urecord->end_particle_data); ret = -1; break; } } break; } // @LDT : Possibly include AS_LDT in this list. We need the LDT // bins to be updated by LDT lua calls, and that path takes us thru here. // However, we ALSO need to be able to set the particle type for the // bins -- so that requires extra processing here to take the LDT flags // and set the appropriate bin flags in the particle data. case AS_MAP: case AS_LIST: { as_buffer buf; as_buffer_init(&buf); as_serializer s; as_msgpack_init(&s); int rsp = 0; as_serializer_serialize(&s, (as_val *) val, &buf); if ( !as_storage_bin_can_fit(rd->ns, buf.size) ) { cf_warning(AS_UDF, "map-list: bin size too big"); ret = -1; // Clean Up and jump out. as_serializer_destroy(&s); as_buffer_destroy(&buf); break; // can't continue if value too big. } uint8_t ptype; if(is_hidden) { ptype = as_particle_type_convert_to_hidden(to_particle_type(type)); } else { ptype = to_particle_type(type); } if (rd->ns->storage_data_in_memory) { as_particle_frombuf(b, ptype, (uint8_t *) buf.data, buf.size, NULL, true); } else { pbytes = buf.size + as_particle_get_base_size(ptype); if ((urecord->cur_particle_data + pbytes) < urecord->end_particle_data) { as_particle_frombuf(b, ptype, (uint8_t *) buf.data, buf.size, urecord->cur_particle_data, rd->ns->storage_data_in_memory); urecord->cur_particle_data += pbytes; } else { cf_warning(AS_UDF, "map-list: bin data size too big: pbytes %d %p %p %p", pbytes, urecord->particle_data, urecord->cur_particle_data, urecord->end_particle_data); rsp = -1; } } as_serializer_destroy(&s); as_buffer_destroy(&buf); if (rsp) { ret = rsp; break; } break; } default: { cf_warning(AS_UDF, "unrecognized object type %d, skipping", as_val_type(val) ); break; } } // If something fail bailout if (ret) { as_sindex_sbin_freeall(oldbin, 1); as_sindex_sbin_freeall(newbin, 1); return ret; } // Update sindex if required if (has_sindex) { if (as_sindex_sbin_from_bin(rd->ns, as_index_get_set_name(rd->r, rd->ns), b, newbin) == AS_SINDEX_OK) { if (!as_sindex_sbin_match(newbin, oldbin)) { needs_sindex_put = true; } else { needs_sindex_update = true; } } if (needs_sindex_update) { tr->flag |= AS_TRANSACTION_FLAG_SINDEX_TOUCHED; as_sindex_delete_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), 1, oldbin, rd); as_sindex_put_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), 1, newbin, rd); } else { if (needs_sindex_delete) { tr->flag |= AS_TRANSACTION_FLAG_SINDEX_TOUCHED; as_sindex_delete_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), 1, oldbin, rd); } if (needs_sindex_put) { tr->flag |= AS_TRANSACTION_FLAG_SINDEX_TOUCHED; as_sindex_put_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), 1, newbin, rd); } } as_sindex_sbin_freeall(oldbin, 1); as_sindex_sbin_freeall(newbin, 1); } return ret; } // end udf_aerospike_setbin()
/* * Internal Function: udf_aerospike_delbin * * Parameters: * r - udf_record to be manipulated * bname - name of the bin to be deleted * * Return value: * 0 on success * -1 on failure * * Description: * The function deletes the bin with the name * passed in as parameter. The as_bin_destroy function * which is called here, only frees the data and * the bin is marked as not in use. The bin can then be reused later. * * Synchronization : object lock acquired by the transaction thread executing UDF. * Partition reservation takes place just before the transaction starts executing * ( look for as_partition_reserve_udf in thr_tsvc.c ) * * Callers: * udf_aerospike__apply_update_atomic * In this function, if it fails at the time of update, the record is set * to rollback all the updates till this point. The case where it fails in * rollback is not handled. * * Side Notes: * i. write_to_device will be set to true on a successful bin destroy. * If all the updates from udf_aerospike__apply_update_atomic (including this) are * successful, the record will be written to disk and reopened so that the rest of * sets of updates can be applied. * * ii. If delete from sindex fails, we do not handle it. */ static int udf_aerospike_delbin(udf_record * urecord, const char * bname) { // Check that bname is not completely invalid if ( !bname || !bname[0] ) { cf_warning(AS_UDF, "udf_aerospike_delbin: Invalid Parameters [No bin name supplied]... Fail"); return -1; } as_storage_rd *rd = urecord->rd; as_transaction *tr = urecord->tr; // Check quality of bname -- check that it is proper length, then make sure // that the bin exists. if (strlen(bname) >= AS_ID_BIN_SZ) { // Can't read bin if name too large. cf_warning(AS_UDF, "udf_aerospike_delbin: Invalid Parameters [bin name(%s) too big]... Fail", bname); return -1; } as_bin * b = as_bin_get(rd, bname); if ( !b ) { cf_debug(AS_UDF, "udf_aerospike_delbin: Invalid Operation [Bin name(%s) not found of delete]... Fail", bname); return -1; } const char * set_name = as_index_get_set_name(rd->r, rd->ns); bool has_sindex = as_sindex_ns_has_sindex(rd->ns); if (has_sindex) { SINDEX_GRLOCK(); } SINDEX_BINS_SETUP(sbins, rd->ns->sindex_cnt); as_sindex * si_arr[rd->ns->sindex_cnt]; int si_arr_index = 0; int sbins_populated = 0; if (has_sindex) { si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(rd->ns, set_name, b->id, &si_arr[si_arr_index]); sbins_populated += as_sindex_sbins_from_bin(rd->ns, set_name, b, sbins, AS_SINDEX_OP_DELETE); SINDEX_GUNLOCK(); } int32_t i = as_bin_get_index(rd, bname); if (i != -1) { if (has_sindex) { if (sbins_populated > 0) { tr->flags |= AS_TRANSACTION_FLAG_SINDEX_TOUCHED; as_sindex_update_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), sbins, sbins_populated, &rd->keyd); } } as_bin_destroy(rd, i); } else { cf_warning(AS_UDF, "udf_aerospike_delbin: Internal Error [Deleting non-existing bin %s]... Fail", bname); } if (has_sindex) { as_sindex_sbin_freeall(sbins, sbins_populated); as_sindex_release_arr(si_arr, si_arr_index); } return 0; }
/* * Internal function: udf_aerospike_setbin * * Parameters: * offset -- offset of udf bin in updates array * r -- udf_record to be manipulated * bname -- name of the bin to be deleted * val -- value to be updated with * * Return value: * 0 on success * -1 on failure * * Description: * The function sets the bin with the name * passed in as parameter to the value, passed as the third parameter. * Before updating the bin, it is checked if the value can fit in the storage * * Synchronization : object lock acquired by the transaction thread executing UDF. * Partition reservation takes place just before the transaction starts executing * ( look for as_partition_reserve_udf in thr_tsvc.c ) * * Callers: * udf_aerospike__apply_update_atomic * In this function, if it fails at the time of update, the record is set * to rollback all the updates till this point. The case where it fails in * rollback is not handled. * * Side Notes: * i. write_to_device will be set to true on a successful bin update. * If all the updates from udf_aerospike__apply_update_atomic (including this) are * successful, the record will be written to disk and reopened so that the rest of * sets of updates can be applied. * * ii. If put in sindex fails, we do not handle it. * * TODO make sure anything goes into setbin only if the bin value is * changed */ static int udf_aerospike_setbin(udf_record * urecord, int offset, const char * bname, const as_val * val, bool is_hidden) { if (bname == NULL || bname[0] == 0 ) { cf_warning(AS_UDF, "udf_aerospike_setbin: Invalid Parameters: [No bin name supplied]... Fail"); return -1; } if (as_particle_type_from_asval(val) == AS_PARTICLE_TYPE_NULL) { cf_warning(AS_UDF, "udf_aerospike_setbin: [%s] called with unusable as_val", bname); return -3; } uint8_t type = as_val_type(val); if (is_hidden && ((type != AS_MAP) && (type != AS_LIST))) { cf_warning(AS_UDF, "udf_aerospike_setbin: Invalid Operation [Hidden %d type Not allowed]... Fail", type); return -3; } as_storage_rd * rd = urecord->rd; as_transaction *tr = urecord->tr; as_bin * b = as_bin_get_or_create(rd, bname); if ( !b ) { cf_warning(AS_UDF, "udf_aerospike_setbin: Internal Error [Bin %s not found.. Possibly ran out of bins]... Fail", bname); return -1; } bool has_sindex = as_sindex_ns_has_sindex(rd->ns); if (has_sindex) { SINDEX_GRLOCK(); } SINDEX_BINS_SETUP(sbins, 2 * rd->ns->sindex_cnt); as_sindex * si_arr[2 * rd->ns->sindex_cnt]; int sbins_populated = 0; int si_arr_index = 0; const char * set_name = as_index_get_set_name(rd->r, rd->ns); if (has_sindex ) { si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(rd->ns, set_name, b->id, &si_arr[si_arr_index]); sbins_populated += as_sindex_sbins_from_bin(rd->ns, set_name, b, &sbins[sbins_populated], AS_SINDEX_OP_DELETE); } // we know we are doing an update now, make sure there is particle data, // set to be 1 wblock size now @TODO! int ret = 0; cf_detail(AS_UDF, "udf_setbin: bin %s type %d ", bname, type ); if (rd->ns->storage_data_in_memory) { if (as_bin_particle_replace_from_asval(b, val) != 0) { cf_warning(AS_UDF, "udf_aerospike_setbin: [%s] failed to replace particle", bname); ret = -4; } } else { uint32_t size = as_particle_size_from_asval(val); uint8_t *particle_buf = udf__aerospike_get_particle_buf(urecord, &urecord->updates[offset], size); if (particle_buf) { as_bin_particle_stack_from_asval(b, particle_buf, val); } else { cf_warning(AS_UDF, "udf_aerospike_setbin: [%s] failed to get space for particle size %u", bname, size); ret = -4; } } if (is_hidden && ret == 0) { if (type == AS_LIST) { as_bin_particle_list_set_hidden(b); } else if (type == AS_MAP) { as_bin_particle_map_set_hidden(b); } } // Update sindex if required if (has_sindex) { if (ret) { SINDEX_GUNLOCK(); if (sbins_populated > 0) { as_sindex_sbin_freeall(sbins, sbins_populated); } as_sindex_release_arr(si_arr, si_arr_index); return ret; } si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(rd->ns, set_name, b->id, &si_arr[si_arr_index]); sbins_populated += as_sindex_sbins_from_bin(rd->ns, set_name, b, &sbins[sbins_populated], AS_SINDEX_OP_INSERT); SINDEX_GUNLOCK(); if (sbins_populated > 0) { tr->flags |= AS_TRANSACTION_FLAG_SINDEX_TOUCHED; as_sindex_update_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), sbins, sbins_populated, &rd->keyd); as_sindex_sbin_freeall(sbins, sbins_populated); } as_sindex_release_arr(si_arr, si_arr_index); } return ret; } // end udf_aerospike_setbin()
// TODO - old pickle - remove in "six months". int old_record_apply_dim(as_remote_record *rr, as_storage_rd *rd, bool skip_sindex, bool *is_delete) { as_namespace* ns = rr->rsv->ns; as_record* r = rd->r; // Set rd->n_bins! as_storage_rd_load_n_bins(rd); // Set rd->bins! as_storage_rd_load_bins(rd, NULL); // For memory accounting, note current usage. uint64_t memory_bytes = as_storage_record_get_n_bytes_memory(rd); // Keep old bins intact for sindex adjustment and unwinding. uint16_t n_old_bins = rd->n_bins; as_bin* old_bins = rd->bins; uint16_t n_new_bins = cf_swap_from_be16(*(uint16_t *)rr->pickle); as_bin new_bins[n_new_bins]; memset(new_bins, 0, sizeof(new_bins)); rd->n_bins = n_new_bins; rd->bins = new_bins; // Fill the new bins and particles. int result = unpickle_bins(rr, rd, NULL); if (result != 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed unpickle bins ", ns->name); destroy_stack_bins(new_bins, n_new_bins); return result; } // Apply changes to metadata in as_index needed for and writing. index_metadata old_metadata; update_index_metadata(rr, &old_metadata, r); // Prepare to store or drop key, as determined by message. rd->key = rr->key; rd->key_size = rr->key_size; // Write the record to storage. if ((result = as_record_write_from_pickle(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed write ", ns->name); unwind_index_metadata(&old_metadata, r); destroy_stack_bins(new_bins, n_new_bins); return -result; } // Success - adjust sindex, looking at old and new bins. if (! (skip_sindex && next_generation(r->generation, (uint16_t)rr->generation, ns)) && record_has_sindex(r, ns)) { write_sindex_update(ns, as_index_get_set_name(r, ns), rr->keyd, old_bins, n_old_bins, new_bins, n_new_bins); } // Cleanup - destroy relevant bins, can't unwind after. destroy_stack_bins(old_bins, n_old_bins); // Fill out new_bin_space. as_bin_space* new_bin_space = NULL; if (n_new_bins != 0) { new_bin_space = (as_bin_space*) cf_malloc_ns(sizeof(as_bin_space) + sizeof(new_bins)); new_bin_space->n_bins = rd->n_bins; memcpy((void*)new_bin_space->bins, new_bins, sizeof(new_bins)); } // Swizzle the index element's as_bin_space pointer. as_bin_space* old_bin_space = as_index_get_bin_space(r); if (old_bin_space) { cf_free(old_bin_space); } as_index_set_bin_space(r, new_bin_space); // Now ok to store or drop key, as determined by message. as_record_finalize_key(r, ns, rd->key, rd->key_size); as_storage_record_adjust_mem_stats(rd, memory_bytes); *is_delete = n_new_bins == 0; return AS_OK; }
int record_apply_ssd(as_remote_record *rr, as_storage_rd *rd, bool skip_sindex, bool *is_delete) { // TODO - old pickle - remove in "six months". if (rr->is_old_pickle) { return old_record_apply_ssd(rr, rd, skip_sindex, is_delete); } as_namespace* ns = rr->rsv->ns; as_record* r = rd->r; bool has_sindex = ! (skip_sindex && next_generation(r->generation, (uint16_t)rr->generation, ns)) && record_has_sindex(r, ns); int result; uint16_t n_old_bins = 0; as_bin *old_bins = NULL; uint16_t n_new_bins = rr->n_bins; as_bin *new_bins = NULL; if (has_sindex) { // TODO - separate function? if ((result = as_storage_rd_load_n_bins(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed load n-bins ", ns->name); return -result; } n_old_bins = rd->n_bins; old_bins = alloca(n_old_bins * sizeof(as_bin)); if ((result = as_storage_rd_load_bins(rd, old_bins)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed load bins ", ns->name); return -result; } // Won't use to flatten. rd->bins = NULL; if (n_new_bins != 0) { new_bins = alloca(n_new_bins * sizeof(as_bin)); memset(new_bins, 0, n_new_bins * sizeof(as_bin)); if ((result = as_flat_unpack_remote_bins(rr, new_bins)) != 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed unpickle bins ", ns->name); return -result; } } } // Won't use to flatten, but needed to know if bins are in use. rd->n_bins = n_new_bins; // Apply changes to metadata in as_index needed for and writing. index_metadata old_metadata; update_index_metadata(rr, &old_metadata, r); // Write the record to storage. if ((result = as_record_write_from_pickle(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed write ", ns->name); unwind_index_metadata(&old_metadata, r); return -result; } // Success - adjust sindex, looking at old and new bins. if (has_sindex) { write_sindex_update(ns, as_index_get_set_name(r, ns), rr->keyd, old_bins, n_old_bins, new_bins, n_new_bins); } // Now ok to store or drop key, as determined by message. as_record_finalize_key(r, ns, rr->key, rr->key_size); *is_delete = n_new_bins == 0; return AS_OK; }
// TODO - old pickle - remove in "six months". int old_record_apply_ssd(as_remote_record *rr, as_storage_rd *rd, bool skip_sindex, bool *is_delete) { as_namespace* ns = rr->rsv->ns; as_record* r = rd->r; bool has_sindex = ! (skip_sindex && next_generation(r->generation, (uint16_t)rr->generation, ns)) && record_has_sindex(r, ns); uint16_t n_old_bins = 0; int result; if (has_sindex) { // Set rd->n_bins! if ((result = as_storage_rd_load_n_bins(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed load n-bins ", ns->name); return -result; } n_old_bins = rd->n_bins; } as_bin old_bins[n_old_bins]; if (has_sindex) { // Set rd->bins! if ((result = as_storage_rd_load_bins(rd, old_bins)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed load bins ", ns->name); return -result; } } // Stack space for resulting record's bins. uint16_t n_new_bins = cf_swap_from_be16(*(uint16_t *)rr->pickle); as_bin new_bins[n_new_bins]; memset(new_bins, 0, sizeof(new_bins)); rd->n_bins = n_new_bins; rd->bins = new_bins; // Fill the new bins and particles. cf_ll_buf_define(particles_llb, STACK_PARTICLES_SIZE); if ((result = unpickle_bins(rr, rd, &particles_llb)) != 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed unpickle bins ", ns->name); cf_ll_buf_free(&particles_llb); return result; } // Apply changes to metadata in as_index needed for and writing. index_metadata old_metadata; update_index_metadata(rr, &old_metadata, r); // Prepare to store or drop key, as determined by message. rd->key = rr->key; rd->key_size = rr->key_size; // Write the record to storage. if ((result = as_record_write_from_pickle(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed write ", ns->name); unwind_index_metadata(&old_metadata, r); cf_ll_buf_free(&particles_llb); return -result; } // Success - adjust sindex, looking at old and new bins. if (has_sindex) { write_sindex_update(ns, as_index_get_set_name(r, ns), rr->keyd, old_bins, n_old_bins, new_bins, n_new_bins); } // Now ok to store or drop key, as determined by message. as_record_finalize_key(r, ns, rd->key, rd->key_size); *is_delete = n_new_bins == 0; cf_ll_buf_free(&particles_llb); return AS_OK; }
int as_msg_make_response_bufbuilder(as_record *r, as_storage_rd *rd, cf_buf_builder **bb_r, bool nobindata, char *nsname, bool include_ldt_data, bool include_key, bool skip_empty_records, cf_vector *binlist) { // Sanity checks. Either rd should be there or nobindata and nsname should be present. if (!(rd || (nobindata && nsname))) { cf_detail(AS_PROTO, "Neither storage record nor nobindata is set. Skipping the record."); return 0; } // figure out the size of the entire buffer int set_name_len = 0; const char *set_name = NULL; int ns_len = rd ? strlen(rd->ns->name) : strlen(nsname); if (as_index_get_set_id(r) != INVALID_SET_ID) { as_namespace *ns = NULL; if (rd) { ns = rd->ns; } else if (nsname) { ns = as_namespace_get_byname(nsname); } if (!ns) { cf_info(AS_PROTO, "Cannot get namespace, needed to get set information. Skipping record."); return -1; } set_name = as_index_get_set_name(r, ns); if (set_name) { set_name_len = strlen(set_name); } } uint8_t* key = NULL; uint32_t key_size = 0; if (include_key && as_index_is_flag_set(r, AS_INDEX_FLAG_KEY_STORED)) { if (! as_storage_record_get_key(rd)) { cf_info(AS_PROTO, "can't get key - skipping record"); return -1; } key = rd->key; key_size = rd->key_size; } uint16_t n_fields = 2; int msg_sz = sizeof(as_msg); msg_sz += sizeof(as_msg_field) + sizeof(cf_digest); msg_sz += sizeof(as_msg_field) + ns_len; if (set_name) { n_fields++; msg_sz += sizeof(as_msg_field) + set_name_len; } if (key) { n_fields++; msg_sz += sizeof(as_msg_field) + key_size; } int list_bins = 0; int in_use_bins = rd ? (int)as_bin_inuse_count(rd) : 0; as_val *ldt_bin_vals[in_use_bins]; if (! nobindata) { if (binlist) { int binlist_sz = cf_vector_size(binlist); for (uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); as_bin *p_bin = as_bin_get(rd, binname); if (! p_bin) { continue; } msg_sz += sizeof(as_msg_op); msg_sz += rd->ns->single_bin ? 0 : strlen(binname); if (as_bin_is_hidden(p_bin)) { if (include_ldt_data) { msg_sz += (int)as_ldt_particle_client_value_size(rd, p_bin, &ldt_bin_vals[list_bins]); } else { ldt_bin_vals[list_bins] = NULL; } } else { msg_sz += (int)as_bin_particle_client_value_size(p_bin); } list_bins++; } // Don't return an empty record. if (skip_empty_records && list_bins == 0) { return 0; } } else { msg_sz += sizeof(as_msg_op) * in_use_bins; for (uint16_t i = 0; i < in_use_bins; i++) { as_bin *p_bin = &rd->bins[i]; msg_sz += rd->ns->single_bin ? 0 : strlen(as_bin_get_name_from_id(rd->ns, p_bin->id)); if (as_bin_is_hidden(p_bin)) { if (include_ldt_data) { msg_sz += (int)as_ldt_particle_client_value_size(rd, p_bin, &ldt_bin_vals[i]); } else { ldt_bin_vals[i] = NULL; } } else { msg_sz += (int)as_bin_particle_client_value_size(p_bin); } } } } uint8_t *b; cf_buf_builder_reserve(bb_r, msg_sz, &b); // set up the header uint8_t *buf = b; as_msg *msgp = (as_msg *) buf; msgp->header_sz = sizeof(as_msg); msgp->info1 = (nobindata ? AS_MSG_INFO1_GET_NOBINDATA : 0); msgp->info2 = 0; msgp->info3 = 0; msgp->unused = 0; msgp->result_code = 0; msgp->generation = r->generation; msgp->record_ttl = r->void_time; msgp->transaction_ttl = 0; msgp->n_fields = n_fields; if (rd) { if (binlist) msgp->n_ops = list_bins; else msgp->n_ops = in_use_bins; } else { msgp->n_ops = 0; } as_msg_swap_header(msgp); buf += sizeof(as_msg); as_msg_field *mf = (as_msg_field *) buf; mf->field_sz = sizeof(cf_digest) + 1; mf->type = AS_MSG_FIELD_TYPE_DIGEST_RIPE; if (rd) { memcpy(mf->data, &rd->keyd, sizeof(cf_digest)); } else { memcpy(mf->data, &r->key, sizeof(cf_digest)); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + sizeof(cf_digest); mf = (as_msg_field *) buf; mf->field_sz = ns_len + 1; mf->type = AS_MSG_FIELD_TYPE_NAMESPACE; if (rd) { memcpy(mf->data, rd->ns->name, ns_len); } else { memcpy(mf->data, nsname, ns_len); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + ns_len; if (set_name) { mf = (as_msg_field *) buf; mf->field_sz = set_name_len + 1; mf->type = AS_MSG_FIELD_TYPE_SET; memcpy(mf->data, set_name, set_name_len); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + set_name_len; } if (key) { mf = (as_msg_field *) buf; mf->field_sz = key_size + 1; mf->type = AS_MSG_FIELD_TYPE_KEY; memcpy(mf->data, key, key_size); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + key_size; } if (nobindata) { return 0; } if (binlist) { list_bins = 0; int binlist_sz = cf_vector_size(binlist); for (uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); as_bin *p_bin = as_bin_get(rd, binname); if (! p_bin) { continue; } as_msg_op *op = (as_msg_op *)buf; op->op = AS_MSG_OP_READ; op->version = 0; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, p_bin); op->op_sz = 4 + op->name_sz; buf += sizeof(as_msg_op) + op->name_sz; if (as_bin_is_hidden(p_bin)) { buf += as_ldt_particle_to_client(ldt_bin_vals[list_bins], op); } else { buf += as_bin_particle_to_client(p_bin, op); } list_bins++; as_msg_swap_op(op); } } else { for (uint16_t i = 0; i < in_use_bins; i++) { as_msg_op *op = (as_msg_op *)buf; op->op = AS_MSG_OP_READ; op->version = 0; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, &rd->bins[i]); op->op_sz = 4 + op->name_sz; buf += sizeof(as_msg_op) + op->name_sz; if (as_bin_is_hidden(&rd->bins[i])) { buf += as_ldt_particle_to_client(ldt_bin_vals[i], op); } else { buf += as_bin_particle_to_client(&rd->bins[i], op); } as_msg_swap_op(op); } } return 0; }