static void
as_uv_connected(uv_connect_t* req, int status)
{
	if (uv_is_closing((uv_handle_t*)req->handle)) {
		return;
	}

	as_event_command* cmd = req->data;

	if (status == 0) {
		if (cmd->cluster->user) {
			as_uv_auth_write_start(cmd, req->handle);
		}
		else {
			as_uv_command_write_start(cmd, req->handle);
		}
	}
	else if (status != UV_ECANCELED) {
		as_node* node = cmd->node;
		as_address* primary = as_vector_get(&node->addresses, node->address_index);
		
		as_error err;
		as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to connect: %s %s:%d",
						node->name, primary->name, (int)cf_swap_from_be16(primary->addr.sin_port));
		as_uv_connect_error(cmd, &err);
	}
}
int
citrusleaf_info_auth(as_cluster *cluster, char *hostname, short port, char *names, char **values, int timeout_ms)
{
	int rv = -1;
	as_vector sockaddr_in_v;
	as_vector_inita(&sockaddr_in_v, sizeof(struct sockaddr_in), 5);
	
	if (! as_lookup(NULL, hostname, port, true, &sockaddr_in_v)) {
		goto Done;
	}
	
	for (uint32_t i = 0; i < sockaddr_in_v.size; i++)
	{
		struct sockaddr_in* sa_in = as_vector_get(&sockaddr_in_v, i);
		
		if (0 == citrusleaf_info_host_auth(cluster, sa_in, names, values, timeout_ms, true, /* check bounds */ true)) {
			rv = 0;
			goto Done;
		}
	}
	
Done:
	as_vector_destroy(&sockaddr_in_v);
	return(rv);
}
static as_node*
as_cluster_find_node(as_cluster* cluster, in_addr_t addr, in_port_t port)
{
	as_nodes* nodes = (as_nodes*)cluster->nodes;
	as_node* node;
	as_vector* addresses;
	as_address* address;
	struct sockaddr_in* sockaddr;
	in_port_t port_be = cf_swap_to_be16(port);
	
	for (uint32_t i = 0; i < nodes->size; i++) {
		node = nodes->array[i];
		addresses = &node->addresses;
		
		for (uint32_t j = 0; j < addresses->size; j++) {
			address = as_vector_get(addresses, j);
			sockaddr = &address->addr;
			
			if (sockaddr->sin_addr.s_addr == addr && sockaddr->sin_port == port_be) {
				return node;
			}
		}
	}
	return 0;
}
static void
as_free_users(as_vector* users, int offset)
{
	for (uint32_t i = offset; i < users->size; i++) {
		as_user_roles* item = as_vector_get(users, i);
		cf_free(item);
	}
	as_vector_destroy(users);
}
static int
as_node_create_connection(as_node* node, int* fd)
{
	// Create a non-blocking socket.
	*fd = cf_socket_create_nb();
	
	if (*fd == -1) {
		// Local problem - socket create failed.
		cf_debug("Socket create failed for %s", node->name);
		return CITRUSLEAF_FAIL_CLIENT;
	}
	
	// Try primary address.
	as_address* primary = as_vector_get(&node->addresses, node->address_index);
	
	if (cf_socket_start_connect_nb(*fd, &primary->addr) == 0) {
		// Connection started ok - we have our socket.
		return as_node_authenticate_connection(node, fd);
	}
	
	// Try other addresses.
	as_vector* addresses = &node->addresses;
	for (uint32_t i = 0; i < addresses->size; i++) {
		as_address* address = as_vector_get(addresses, i);
		
		// Address points into alias array, so pointer comparison is sufficient.
		if (address != primary) {
			if (cf_socket_start_connect_nb(*fd, &address->addr) == 0) {
				// Replace invalid primary address with valid alias.
				// Other threads may not see this change immediately.
				// It's just a hint, not a requirement to try this new address first.
				cf_debug("Change node address %s %s:%d", node->name, address->name, (int)cf_swap_from_be16(address->addr.sin_port));
				ck_pr_store_32(&node->address_index, i);
				return as_node_authenticate_connection(node, fd);
			}
		}
	}
	
	// Couldn't start a connection on any socket address - close the socket.
	cf_info("Failed to connect: %s %s:%d", node->name, primary->name, (int)cf_swap_from_be16(primary->addr.sin_port));
	cf_close(*fd);
	*fd = -1;
	return CITRUSLEAF_FAIL_UNAVAILABLE;
}
static void
as_cluster_find_nodes_to_add(as_cluster* cluster, as_vector* /* <as_host> */ friends, as_vector* /* <as_node*> */ nodes_to_add)
{
	as_error err;
	as_error_init(&err);
	as_vector addresses;
	as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5);
	
	as_node_info node_info;

	for (uint32_t i = 0; i < friends->size; i++) {
		as_host* friend = as_vector_get(friends, i);
		as_vector_clear(&addresses);
		
		as_status status = as_lookup(&err, friend->name, friend->port, &addresses);
		
		if (status != AEROSPIKE_OK) {
			as_log_warn("%s %s", as_error_string(status), err.message);
			continue;
		}
		
		for (uint32_t i = 0; i < addresses.size; i++) {
			struct sockaddr_in* addr = as_vector_get(&addresses, i);
			status = as_lookup_node(cluster, &err, addr, &node_info);
			
			if (status == AEROSPIKE_OK) {
				as_node* node = as_cluster_find_node(cluster->nodes, nodes_to_add, node_info.name);
				
				if (node) {
					// Duplicate node name found.  This usually occurs when the server
					// services list contains both internal and external IP addresses
					// for the same node.  Add new host to list of alias filters
					// and do not add new node.
					as_close(node_info.fd);
					as_address* a = as_node_get_address_full(node);
					as_log_info("Node %s:%d already exists with nodeid %s and address %s:%d", 
						friend->name, friend->port, node->name, a->name,
						(int)cf_swap_from_be16(a->addr.sin_port));
					node->friends++;
					as_node_add_address(node, friend, addr);
					continue;
				}
				
				node = as_node_create(cluster, friend, addr, &node_info);
				as_address* a = as_node_get_address_full(node);
				as_log_info("Add node %s %s:%d", node_info.name, a->name, (int)cf_swap_from_be16(a->addr.sin_port));
				as_vector_append(nodes_to_add, &node);
			}
			else {
				as_log_warn("Failed to connect to friend %s:%d. %s %s", friend->name, friend->port, as_error_string(status), err.message);
			}
		}
static void
as_uv_connect(as_event_command* cmd)
{
	int fd = as_event_create_socket(cmd);
	
	if (fd < 0) {
		return;
	}
	
	as_event_connection* conn = cmd->conn;
	uv_tcp_t* socket = &conn->socket;
	int status = uv_tcp_init(cmd->event_loop->loop, socket);
	
	if (status) {
		as_error err;
		as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "uv_tcp_init failed: %s", uv_strerror(status));
		// Call standard event connection error handler because as_uv_connect_error() requires that
		// uv_tcp_init() has already succeeded.
		as_event_connect_error(cmd, &err, fd);
		return;
	}
	
	// Define externally created fd to uv_tcp_t.
	status = uv_tcp_open(socket, fd);
	
	if (status) {
		as_error err;
		as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "uv_tcp_open failed: %s", uv_strerror(status));
		// Close fd directly because we created it outside of libuv and uv_tcp_t does not know about it here.
		close(fd);
		as_uv_connect_error(cmd, &err);
		return;
	}
	
	socket->data = conn;
	conn->req.connect.data = cmd;
	
	as_node* node = cmd->node;
	as_address* primary = as_vector_get(&node->addresses, node->address_index);
	
	status = uv_tcp_connect(&conn->req.connect, socket, (struct sockaddr*)&primary->addr, as_uv_connected);
	
	if (status) {
		as_error err;
		as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "uv_tcp_connect failed: %s", uv_strerror(status));
		as_uv_connect_error(cmd, &err);
	}
}
/**
 *	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) 
{
	as_error_reset(err);
	
	if (! policy) {
		policy = &as->config.policies.info;
	}
	
	as_vector sockaddr_in_v;
	as_vector_inita(&sockaddr_in_v, sizeof(struct sockaddr_in), 5);
	
	as_status status = as_lookup(NULL, err, (char*)addr, port, &sockaddr_in_v);
	
	if (status) {
		as_vector_destroy(&sockaddr_in_v);
		return status;
	}
	
	uint64_t deadline = as_socket_deadline(policy->timeout);
	as_cluster* cluster = as->cluster;
	status = AEROSPIKE_ERR_CLUSTER;
	bool loop = true;
	
	for (uint32_t i = 0; i < sockaddr_in_v.size && loop; i++) {
		struct sockaddr_in* sa_in = as_vector_get(&sockaddr_in_v, i);
		status = as_info_command_host(cluster, err, sa_in, (char*)req, policy->send_as_is, deadline, res);

		switch (status) {
			case AEROSPIKE_OK:
			case AEROSPIKE_ERR_TIMEOUT:
			case AEROSPIKE_ERR_INDEX_FOUND:
			case AEROSPIKE_ERR_INDEX_NOT_FOUND:
				loop = false;
				break;
				
			default:
				break;
		}
	}
	as_vector_destroy(&sockaddr_in_v);
	return status;
}
static as_status
as_cluster_seed_nodes(as_cluster* cluster, as_error* err, bool enable_warnings)
{
	// Add all nodes at once to avoid copying entire array multiple times.
	as_vector nodes_to_add;
	as_vector_inita(&nodes_to_add, sizeof(as_node*), 64);
	
	as_vector addresses;
	as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5);
	
	as_node_info node_info;
	as_error error_local;
	as_error_init(&error_local); // AEROSPIKE_ERR_TIMEOUT doesn't come with a message; make sure it's initialized.
	as_status status = AEROSPIKE_OK;
	
	as_seeds* seeds = as_seeds_reserve(cluster);

	for (uint32_t i = 0; i < seeds->size; i++) {
		as_seed* seed = &seeds->array[i];
		as_vector_clear(&addresses);
		
		status = as_lookup(&error_local, seed->name, seed->port, &addresses);
		
		if (status != AEROSPIKE_OK) {
			if (enable_warnings) {
				as_log_warn("Failed to lookup %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message);
			}
			continue;
		}

		for (uint32_t i = 0; i < addresses.size; i++) {
			struct sockaddr_in* addr = as_vector_get(&addresses, i);
			status = as_lookup_node(cluster, &error_local, addr, &node_info);
			
			if (status == AEROSPIKE_OK) {
				as_host host;
				if (as_strncpy(host.name, seed->name, sizeof(host.name))) {
					as_log_warn("Hostname has been truncated: %s", host.name);
				}
				host.port = seed->port;

				as_node* node = as_cluster_find_node_in_vector(&nodes_to_add, node_info.name);
				
				if (node) {
					as_close(node_info.fd);
					as_node_add_address(node, &host, addr);
				}
				else {
					node = as_node_create(cluster, &host, addr, &node_info);
					as_address* a = as_node_get_address_full(node);
					as_log_info("Add node %s %s:%d", node->name, a->name, (int)cf_swap_from_be16(a->addr.sin_port));
					as_vector_append(&nodes_to_add, &node);
				}
			}
			else {
				if (enable_warnings) {
					as_log_warn("Failed to connect to seed %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message);
				}
			}
		}
	}
	
	as_seeds_release(seeds);

	if (nodes_to_add.size > 0) {
		as_cluster_add_nodes(cluster, &nodes_to_add);
		status = AEROSPIKE_OK;
	}
	else {
		status = as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to seed cluster");
	}
	
	as_vector_destroy(&nodes_to_add);
	as_vector_destroy(&addresses);
	return status;
}
static as_status
as_lookup_node(as_cluster* cluster, as_error* err, struct sockaddr_in* addr, as_node_info* node_info)
{
	uint64_t deadline = as_socket_deadline(cluster->conn_timeout_ms);
	int fd;
	as_status status = as_info_create_socket(cluster, err, addr, deadline, &fd);

	if (status) {
		return status;
	}
	
	char* response = 0;
	status = as_info_command(err, fd, "node\nfeatures\n", true, deadline, 0, &response);

	if (status) {
		as_close(fd);
		return status;
	}
	
	as_vector values;
	as_vector_inita(&values, sizeof(as_name_value), 2);
	
	as_info_parse_multi_response(response, &values);
	
	if (values.size != 2) {
		goto Error;
	}
	
	as_name_value* nv = as_vector_get(&values, 0);
	char* node_name = nv->value;
	
	if (node_name == 0 || *node_name == 0) {
		goto Error;
	}
	as_strncpy(node_info->name, node_name, AS_NODE_NAME_SIZE);
	node_info->fd = fd;

	nv = as_vector_get(&values, 1);
	char* features = nv->value;
	
	if (features == 0) {
		goto Error;
	}
			
	char* begin = features;
	char* end = begin;
	uint8_t has_batch_index = 0;
	uint8_t has_replicas_all = 0;
	uint8_t has_double = 0;
	uint8_t has_geo = 0;
	
	while (*begin && ! (has_batch_index &&
						has_replicas_all &&
						has_double &&
						has_geo)) {
		while (*end) {
			if (*end == ';') {
				*end++ = 0;
				break;
			}
			end++;
		}
		
		if (strcmp(begin, "batch-index") == 0) {
			has_batch_index = 1;
		}
		
		if (strcmp(begin, "replicas-all") == 0) {
			has_replicas_all = 1;
		}
		
		if (strcmp(begin, "float") == 0) {
			has_double = 1;
		}

		if (strcmp(begin, "geo") == 0) {
			has_geo = 1;
		}

		begin = end;
	}
	node_info->has_batch_index = has_batch_index;
	node_info->has_replicas_all = has_replicas_all;
	node_info->has_double = has_double;
	node_info->has_geo = has_geo;
	cf_free(response);
	return AEROSPIKE_OK;
	
Error: {
		char addr_name[INET_ADDRSTRLEN];
		as_socket_address_name(addr, addr_name);
		as_error_update(err, status, "Invalid node info response from %s: %s", addr_name, response);
		cf_free(response);
		as_close(fd);
		return AEROSPIKE_ERR_CLIENT;
	}
}
static void
as_ev_connect(as_event_command* cmd)
{
	int fd = as_event_create_socket(cmd);
	
	if (fd < 0) {
		return;
	}
		
	// Try primary address.
	as_node* node = cmd->node;
	as_address* primary = as_vector_get(&node->addresses, node->address_index);
	
	// Attempt non-blocking connection.
	if (connect(fd, (struct sockaddr*)&primary->addr, sizeof(struct sockaddr)) == 0) {
		as_ev_watcher_init(cmd, fd);
		return;
	}
	
	// Check if connection is in progress.
	if (errno == EINPROGRESS) {
		as_ev_watcher_init(cmd, fd);
		return;
	}
	
	// Try other addresses.
	as_vector* addresses = &node->addresses;
	for (uint32_t i = 0; i < addresses->size; i++) {
		as_address* address = as_vector_get(addresses, i);
		
		// Address points into alias array, so pointer comparison is sufficient.
		if (address != primary) {
			if (connect(fd, (struct sockaddr*)&address->addr, sizeof(struct sockaddr)) == 0) {
				// Replace invalid primary address with valid alias.
				// Other threads may not see this change immediately.
				// It's just a hint, not a requirement to try this new address first.
				as_log_debug("Change node address %s %s:%d", node->name, address->name, (int)cf_swap_from_be16(address->addr.sin_port));
				ck_pr_store_32(&node->address_index, i);
				as_ev_watcher_init(cmd, fd);
				return;
			}
			
			// Check if connection is in progress.
			if (errno == EINPROGRESS) {
				// Replace invalid primary address with valid alias.
				// Other threads may not see this change immediately.
				// It's just a hint, not a requirement to try this new address first.
				as_log_debug("Change node address %s %s:%d", node->name, address->name, (int)cf_swap_from_be16(address->addr.sin_port));
				ck_pr_store_32(&node->address_index, i);
				
				// Connection hasn't finished.
				as_ev_watcher_init(cmd, fd);
				return;
			}
		}
	}
	
	// Failed to start a connection on any socket address.
	as_error err;
	as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to connect: %s %s:%d",
					node->name, primary->name, (int)cf_swap_from_be16(primary->addr.sin_port));
	as_event_connect_error(cmd, &err, fd);
}
/**
 *******************************************************************************************************
 * This function will be called with the results with aerospike_batch_read().
 *
 * @param records               A vector list of as_batch_read_record entries
 * @param py_recs               The pyobject to be filled with.
 *
 *******************************************************************************************************
 */
static void batch_select_recs(AerospikeClient *self, as_error *err, as_batch_read_records* records, PyObject **py_recs)
{
	as_vector* list = &records->list;
	for (uint32_t i = 0; i < list->size; i++) {
		as_batch_read_record* batch = as_vector_get(list, i);

		PyObject * rec = NULL;
		PyObject * py_rec = NULL;
		PyObject * p_key = NULL;
		py_rec = PyTuple_New(3);
		p_key = PyTuple_New(4);

		if ( batch->key.ns && strlen(batch->key.ns) > 0 ) {
			PyTuple_SetItem(p_key, 0, PyString_FromString(batch->key.ns));
		}

		if ( batch->key.set && strlen(batch->key.set) > 0 ) {
			PyTuple_SetItem(p_key, 1, PyString_FromString(batch->key.set));
		}

		if(batch->key.valuep) {
			switch(((as_val*)(batch->key.valuep))->type){
				case AS_INTEGER:
					PyTuple_SetItem(p_key, 2, PyInt_FromLong((long)batch->key.value.integer.value));
					break;

				case AS_STRING:
					PyTuple_SetItem(p_key, 2, PyString_FromString((const char *)batch->key.value.string.value));
					break;
				default:
					break;
			}
		} else {
			Py_INCREF(Py_None);
			PyTuple_SetItem(p_key, 2, Py_None);
		}

		if (batch->key.digest.init) {
			PyTuple_SetItem(p_key, 3, PyByteArray_FromStringAndSize((char *) batch->key.digest.value, AS_DIGEST_VALUE_SIZE));
		}
		PyTuple_SetItem(py_rec, 0, p_key);

		if ( batch->result == AEROSPIKE_OK ){
			record_to_pyobject(self, err, &batch->record, &batch->key, &rec);
			PyObject *py_obj = PyTuple_GetItem(rec, 1);
			Py_INCREF(py_obj);
			PyTuple_SetItem(py_rec, 1, py_obj);
			py_obj = PyTuple_GetItem(rec, 2);
			Py_INCREF(py_obj);
			PyTuple_SetItem(py_rec, 2, py_obj);
			PyList_SetItem( *py_recs, i, py_rec );
			Py_DECREF(rec);
		} else if (batch->result == AEROSPIKE_ERR_RECORD_NOT_FOUND) {
			Py_INCREF(Py_None);
			PyTuple_SetItem(py_rec, 1, Py_None);
			Py_INCREF(Py_None);
			PyTuple_SetItem(py_rec, 2, Py_None);
			PyList_SetItem( *py_recs, i, py_rec);
		}
	}
}
Exemple #13
0
/* {{{ proto int Aerospike::info( string request, string &response [, array host [, array options ]] )
    Sends an info command to a cluster node */
PHP_METHOD(Aerospike, info)
{
	as_error err;
	as_error_init(&err);
	reset_client_error(getThis());

	char* request = NULL;
	size_t request_len;
	zval* response_zval = NULL;
	zval* z_policy = NULL;

	as_host* host = NULL;
	char* host_str = NULL;
	uint16_t port_number;

	char* response_str = NULL;
	HashTable* destination_host = NULL;
	zval* host_zval = NULL;
	zval* port_zval = NULL;

	as_policy_info info_policy;
	as_policy_info* info_policy_p = NULL;
	AerospikeClient* php_client = NULL;
	aerospike* as_client = NULL;

	if (check_object_and_connection(getThis(), &err) != AEROSPIKE_OK) {
		update_client_error(getThis(), err.code, err.message, err.in_doubt);
		RETURN_LONG(err.code);
	}
	php_client = get_aerospike_from_zobj(Z_OBJ_P(getThis()));
	as_client = php_client->as_client;

	host = (as_host *)as_vector_get(as_client->config.hosts, 0);
	host_str = host->name;
	port_number = host->port;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|h!z",
			&request, &request_len,
			&response_zval, &destination_host,
			&z_policy) == FAILURE) {
		update_client_error(getThis(),AEROSPIKE_ERR_PARAM, "Invalid arguments to info", false);
		RETURN_LONG(AEROSPIKE_ERR_PARAM);
	}
	/* Cleanup any thing held by the return parameter */
	zval_dtor(response_zval);
	ZVAL_NULL(response_zval);

	if (z_policy){
		if (zval_to_as_policy_info(z_policy, &info_policy,
				&info_policy_p, &as_client->config.policies.info) != AEROSPIKE_OK) {
			update_client_error(getThis(), AEROSPIKE_ERR_PARAM, "Invalid policy.", false);
			RETURN_LONG(AEROSPIKE_ERR_PARAM);
		}
		info_policy_p = &info_policy;
	}

//	addr hostname or IP of the node
//	port
	/* This should be of the form ['addr'=>'hostname', 'port'=>3000] */
	if (destination_host) {
		host_zval = zend_hash_str_find(destination_host, "addr", strlen("addr"));
		if (!host_zval) {
			as_error_update(&err, AEROSPIKE_ERR_PARAM, "Host entry must contain an addr");
			goto CLEANUP;
		}
		if (Z_TYPE_P(host_zval) != IS_STRING) {
			as_error_update(&err, AEROSPIKE_ERR_PARAM, "addr entry must be a string");
			goto CLEANUP;
		}
		host_str = Z_STRVAL_P(host_zval);

		port_zval = zend_hash_str_find(destination_host, "port", strlen("port"));
		if (!port_zval) {
			as_error_update(&err, AEROSPIKE_ERR_PARAM, "Host entry must contain a port");
			goto CLEANUP;
		}
		if (Z_TYPE_P(port_zval) != IS_LONG) {
			as_error_update(&err, AEROSPIKE_ERR_PARAM, "port entry must be an integer");
			goto CLEANUP;
		}
		port_number = (uint16_t)Z_LVAL_P(port_zval);
	}

	if (aerospike_info_host(as_client, &err, info_policy_p, host_str, port_number, request, &response_str)
			!= AEROSPIKE_OK) {
		goto CLEANUP;
	}
	/* It is possible that an empty response will be returned, so only setup the return
	 * if we get one back.
	 */
	if (response_str) {
		ZVAL_STRING(response_zval, response_str);
	}

CLEANUP:
	if (err.code != AEROSPIKE_OK) {
		update_client_error(getThis(), err.code, err.message, err.in_doubt);
	}
	if (response_str) {
		free(response_str);
	}
	RETURN_LONG(err.code);
}
/**
 *******************************************************************************************************
 * This callback will be called with the results with aerospike_batch_exists().
 *
 * @param err                   Error object
 * @param records               An array of as_batch_read_record entries
 * @param py_recs               The pyobject to be filled in with the return
 *                              value
 *
 * Returns boolean value(true or false).
 *******************************************************************************************************
 */
static
void batch_exists_recs(as_error *err, as_batch_read_records* records, PyObject **py_recs)
{
	// Loop over records array
    as_vector* list = &records->list;
    for (uint32_t i = 0; i < list->size; i++) {
        as_batch_read_record* batch = as_vector_get(list, i);

		PyObject * rec = PyDict_New();
		PyObject * p_key = NULL;
		PyObject * py_rec = NULL;
        py_rec = PyTuple_New(2);
        p_key = PyTuple_New(4);

	    if ( batch->key.ns && strlen(batch->key.ns) > 0 ) {
		    PyTuple_SetItem(p_key, 0, PyStr_FromString(batch->key.ns));
	    }

	    if ( batch->key.set && strlen(batch->key.set) > 0 ) {
		    PyTuple_SetItem(p_key, 1, PyStr_FromString(batch->key.set));
	    }

		if ( batch->key.valuep ) {
			switch(((as_val*)(batch->key.valuep))->type){
				case AS_INTEGER:
					PyTuple_SetItem(p_key, 2, PyInt_FromLong((long)batch->key.value.integer.value));
					break;

				case AS_STRING:
					PyTuple_SetItem(p_key, 2, PyStr_FromString((const char *)batch->key.value.string.value));
					break;
				default:
					break;
			}
		} else {
			Py_INCREF(Py_None);
			PyTuple_SetItem(p_key, 2, Py_None);
		}

		if (batch->key.digest.init) {
            PyTuple_SetItem(p_key, 3, PyByteArray_FromStringAndSize((char *) batch->key.digest.value, AS_DIGEST_VALUE_SIZE));
        }

        PyTuple_SetItem(py_rec, 0, p_key);
		if ( batch->result == AEROSPIKE_OK ){

            PyObject *py_gen = PyInt_FromLong((long)batch->record.gen);
			PyDict_SetItemString( rec, "gen", py_gen );
            Py_DECREF(py_gen);
            PyObject *py_ttl = PyInt_FromLong((long)batch->record.ttl);
			PyDict_SetItemString( rec, "ttl", py_ttl );
            Py_DECREF(py_ttl);

            PyTuple_SetItem(py_rec, 1, rec);
			PyList_SetItem( *py_recs, i, py_rec );
		} else if (batch->result == AEROSPIKE_ERR_RECORD_NOT_FOUND){
		    Py_DECREF(rec);
			Py_INCREF(Py_None);
            PyTuple_SetItem(py_rec, 1, Py_None);
			PyList_SetItem( *py_recs, i, py_rec);
		}
	}
}
/**
 * @return AEROSPIKE_OK if successful. Otherwise an error occurred.
 */
as_status aerospike_udf_list(
	aerospike * as, as_error * err, const as_policy_info * policy, 
	as_udf_files * files)
{
	as_error_reset(err);
	
	if (! policy) {
		policy = &as->config.policies.info;
	}

	char* response = 0;
	as_status status = aerospike_info_any(as, err, policy, "udf-list", &response);
	
	if (status) {
		return status;
	}
	
	// response := udf-list\tfilename=<name>,hash=<hash>,type=<type>;[filename=<name>...]
	char* p = strchr(response, '\t');
	
	if (!p) {
		as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid udf-list response: %s", response);
		free(response);
		return AEROSPIKE_ERR_PARAM;
	}
	p++;
	
	uint32_t capacity = (files->capacity <= 0) ? 500 : files->capacity;
	
	as_vector ptrs;
	as_vector_inita(&ptrs, sizeof(as_udf_file_ptr), capacity);
	
	as_udf_file_ptr ptr = {0,0,0};
	char* token = p;
	
	while (*p) {
		switch (*p) {
			case '=':
				*p++ = 0;
				as_udf_parse_file(token, p, &ptr);
				break;
	
			case ',':
				*p++ = 0;
				token = p;
				break;
				
			case ';':
				*p++ = 0;
				token = p;
				as_vector_append(&ptrs, &ptr);
				ptr.name = 0;
				ptr.hash = 0;
				ptr.type = 0;
				break;
				
			default:
				p++;
				break;
		}
	}
	
	if (files->capacity == 0 && files->entries == NULL) {
		as_udf_files_init(files, ptrs.size);
	}

	uint32_t limit = ptrs.size < files->capacity ? ptrs.size : files->capacity;
	files->size = limit;

	for (uint32_t i = 0; i < limit; i++) {
		as_udf_file_ptr* ptr = as_vector_get(&ptrs, i);
		as_udf_file* file = &files->entries[i];
		
		if (ptr->name) {
			as_strncpy(file->name, ptr->name, AS_UDF_FILE_NAME_SIZE);
		}
		else {
			file->name[0] = 0;
		}
		
		if (ptr->hash) {
			// The hash is not null terminated, so normal strncpy is appropriate here.
			// strncpy will also pad zeroes if hash size is incorrect.
			strncpy((char*)file->hash, ptr->hash, AS_UDF_FILE_HASH_SIZE);
		}
		else {
			file->hash[0] = 0;
		}
		
		file->type = AS_UDF_TYPE_LUA;
		file->content._free = false;
		file->content.size = 0;
		file->content.capacity = 0;
		file->content.bytes = NULL;
	}

	as_vector_destroy(&ptrs);
	free(response);
	return AEROSPIKE_OK;
}
Exemple #16
0
// ----------------------------------------------------------------------------------
//
// getting batch of records in one call
// batch size is limited on aerospike server (default: 5000)
//
// def batch_get(keys, specific_bins = nil, options = {})
//
// params:
//   keys - Array of AerospikeC::Key objects
//   specific bins - Array of strings representing bin names
//   options - hash of options:
//     with_header: returns also generation and expire_in field (default: false)
//
//  ------
//  RETURN: Array of hashes where each hash represents record bins
//
// @TODO options policy
//
static VALUE batch_get(int argc, VALUE * argv, VALUE self) {
  rb_aero_TIMED(tm);

  as_error err;
  as_status status;
  aerospike * as = rb_aero_CLIENT;
  char ** bin_names;
  long n_bin_names;

  VALUE keys;
  VALUE specific_bins;
  VALUE options;

  rb_scan_args(argc, argv, "12", &keys, &specific_bins, &options);

  // default values for optional arguments
  if ( NIL_P(specific_bins) ) {
    specific_bins = Qnil;
  }
  else {
    if ( TYPE(specific_bins) != T_ARRAY ) rb_raise(rb_aero_OptionError, "[AerospikeC::Client][batch_get] specific_bins must be an Array");

    bin_names   = rb_array2bin_names(specific_bins);
    n_bin_names = rb_ary_len_long(specific_bins);
  }
  if ( NIL_P(options) ) {
    options = rb_hash_new();
    rb_hash_aset(options, with_header_sym, Qfalse);
  }

  long keys_len = rb_ary_len_long(keys);

  VALUE records_bins = rb_ary_new();

  as_batch_read_records records;
  as_batch_read_inita(&records, keys_len);

  // map array into as_batch_read_record * record
  for (int i = 0; i < keys_len; ++i) {
    VALUE element = rb_ary_entry(keys, i);
    VALUE tmp;

    tmp = rb_funcall(element, rb_intern("namespace"), 0);
    char * c_namespace = StringValueCStr( tmp );

    tmp = rb_funcall(element, rb_intern("set"), 0);
    char * c_set = StringValueCStr( tmp );

    as_batch_read_record * record = as_batch_read_reserve(&records);

    tmp = rb_funcall(element, rb_intern("key"), 0);

    if ( TYPE(tmp) != T_FIXNUM ) {
      char * c_key = StringValueCStr( tmp );
      as_key_init(&record->key, c_namespace, c_set, c_key);
    }
    else {
      as_key_init_int64(&record->key, c_namespace, c_set, FIX2LONG(tmp));
    }

    if ( specific_bins == Qnil ) {
      record->read_all_bins = true;
    }
    else {
      record->bin_names  = bin_names;
      record->n_bin_names = n_bin_names;
    }
  }

  // read here!
  if ( ( status = aerospike_batch_read(as, &err, NULL, &records) ) != AEROSPIKE_OK ) {
    if ( status == AEROSPIKE_ERR_RECORD_NOT_FOUND ) {
      rb_aero_logger(AS_LOG_LEVEL_WARN, &tm, 1, rb_str_new2("[Client][batch_get] AEROSPIKE_ERR_RECORD_NOT_FOUND"));
      return Qnil;
    }

    as_batch_read_destroy(&records);
    raise_as_error(err);
  }

  as_vector list = records.list;

  // map records into array of hashes
  for (long i = 0; i < list.size; ++i) {
    as_batch_read_record * record = as_vector_get(&list, i);
    as_record rec = record->record;

    VALUE bins = record2hash(&rec);
    bins = check_with_header(bins, options, &rec);

    rb_ary_push(records_bins, bins);
  }

  as_batch_read_destroy(&records);

  if ( specific_bins != Qnil ) bin_names_destroy(bin_names, n_bin_names);

  rb_aero_logger(AS_LOG_LEVEL_DEBUG, &tm, 1, rb_str_new2("[Client][batch_get] success"));

  return records_bins;
}