Beispiel #1
0
// TODO - rename as as_record_..., move to record.c, take r instead of set_name,
// and lose keyd parameter?
void
remove_from_sindex(as_namespace* ns, const char* set_name, cf_digest* keyd,
		as_bin* bins, uint32_t n_bins)
{
	SINDEX_GRLOCK();

	SINDEX_BINS_SETUP(sbins, ns->sindex_cnt);

	as_sindex* si_arr[ns->sindex_cnt];
	int si_arr_index = 0;
	int sbins_populated = 0;

	// Reserve matching sindexes.
	for (int i = 0; i < (int)n_bins; i++) {
		si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(ns, set_name,
				bins[i].id, &si_arr[si_arr_index]);
	}

	for (int i = 0; i < (int)n_bins; i++) {
		sbins_populated += as_sindex_sbins_from_bin(ns, set_name, &bins[i],
				&sbins[sbins_populated], AS_SINDEX_OP_DELETE);
	}

	SINDEX_GRUNLOCK();

	if (sbins_populated) {
		as_sindex_update_by_sbin(ns, set_name, sbins, sbins_populated, keyd);
		as_sindex_sbin_freeall(sbins, sbins_populated);
	}

	as_sindex_release_arr(si_arr, si_arr_index);
}
/*
 * 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;
}
Beispiel #3
0
bool
write_sindex_update(as_namespace* ns, const char* set_name, cf_digest* keyd,
		as_bin* old_bins, uint32_t n_old_bins, as_bin* new_bins,
		uint32_t n_new_bins)
{
	int n_populated = 0;
	bool not_just_created[n_new_bins];

	memset(not_just_created, 0, sizeof(not_just_created));

	// Maximum number of sindexes which can be changed in one transaction is
	// 2 * ns->sindex_cnt.

	SINDEX_GRLOCK();
	SINDEX_BINS_SETUP(sbins, 2 * ns->sindex_cnt);
	as_sindex* si_arr[2 * ns->sindex_cnt];
	int si_arr_index = 0;

	// Reserve matching SIs.

	for (int i = 0; i < n_old_bins; i++) {
		si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(ns, set_name,
				old_bins[i].id, &si_arr[si_arr_index]);
	}

	for (int i = 0; i < n_new_bins; i++) {
		si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(ns, set_name,
				new_bins[i].id, &si_arr[si_arr_index]);
	}

	// For every old bin, find the corresponding new bin (if any) and adjust the
	// secondary index if the bin was modified. If no corresponding new bin is
	// found, it means the old bin was deleted - also adjust the secondary index
	// accordingly.

	for (int32_t i_old = 0; i_old < (int32_t)n_old_bins; i_old++) {
		as_bin* b_old = &old_bins[i_old];
		bool found = false;

		// Loop over new bins. Start at old bin index (if possible) and go down,
		// wrapping around to do the higher indexes last. This will find a match
		// (if any) very quickly - instantly, unless there were bins deleted.

		bool any_new = n_new_bins != 0;
		int32_t n_new_minus_1 = (int32_t)n_new_bins - 1;
		int32_t i_new = n_new_minus_1 < i_old ? n_new_minus_1 : i_old;

		while (any_new) {
			as_bin* b_new = &new_bins[i_new];

			if (b_old->id == b_new->id) {
				if (as_bin_get_particle_type(b_old) !=
						as_bin_get_particle_type(b_new) ||
								b_old->particle != b_new->particle) {
					n_populated += as_sindex_sbins_populate(
							&sbins[n_populated], ns, set_name, b_old, b_new);
				}

				found = true;
				not_just_created[i_new] = true;
				break;
			}

			if (--i_new < 0 && (i_new = n_new_minus_1) <= i_old) {
				break;
			}

			if (i_new == i_old) {
				break;
			}
		}

		if (! found) {
			n_populated += as_sindex_sbins_from_bin(ns, set_name, b_old,
					&sbins[n_populated], AS_SINDEX_OP_DELETE);
		}
	}

	// Now find the new bins that are just-created bins. We've marked the others
	// in the loop above, so any left are just-created.

	for (uint32_t i_new = 0; i_new < n_new_bins; i_new++) {
		if (not_just_created[i_new]) {
			continue;
		}

		n_populated += as_sindex_sbins_from_bin(ns, set_name, &new_bins[i_new],
				&sbins[n_populated], AS_SINDEX_OP_INSERT);
	}

	SINDEX_GRUNLOCK();

	if (n_populated != 0) {
		as_sindex_update_by_sbin(ns, set_name, sbins, n_populated, keyd);
		as_sindex_sbin_freeall(sbins, n_populated);
	}

	as_sindex_release_arr(si_arr, si_arr_index);

	return n_populated != 0;
}
/*
 * 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()