Exemple #1
0
// ----------------------------------------------------------------------------------
//
// def touch(key, options = {})
//
// params:
//   key - AeropsikeC::Key object
//   options - hash of options:
//     ttl: time to live record (default: 0)
//     policy: AerospikeC::Policy for operate
//
//  ------
//  RETURN:
//    1. hash representing record header
//    2. nil when AEROSPIKE_ERR_RECORD_NOT_FOUND
//
// @TODO options policy
//
static VALUE touch(int argc, VALUE * argv, VALUE self) {
  rb_aero_TIMED(tm);

  as_error err;
  as_status status;
  aerospike * as = rb_aero_CLIENT;

  VALUE key;
  VALUE options;

  rb_scan_args(argc, argv, "11", &key, &options);

  // default values for optional arguments
  if ( NIL_P(options) ) {
    options = rb_hash_new();
    rb_hash_aset(options, ttl_sym, rb_zero);
  }
  else {
    if ( TYPE(rb_hash_aref(options, ttl_sym)) != T_FIXNUM ) { // check ttl option
      rb_raise(rb_aero_OptionError, "[AerospikeC::Client][put] ttl must be an integer");
    }
  }

  as_key * k = rb_aero_KEY;
  as_record * rec = NULL;

  as_operations ops;
  as_operations_inita(&ops, 1);
  as_operations_add_touch(&ops);

  ops.ttl = FIX2INT( rb_hash_aref(options, ttl_sym) );

  as_policy_operate * policy = get_policy(options);

  if ( ( status = aerospike_key_operate(as, &err, policy, k, &ops, &rec) ) != AEROSPIKE_OK ) {
    as_operations_destroy(&ops);

    if ( status == AEROSPIKE_ERR_RECORD_NOT_FOUND ) {
      rb_aero_logger(AS_LOG_LEVEL_WARN, &tm, 2, rb_str_new2("[Client][touch] AEROSPIKE_ERR_RECORD_NOT_FOUND"), rb_aero_KEY_INFO);
      return Qnil;
    }

    raise_as_error(err);
  }

  VALUE header = rb_hash_new();

  rb_hash_aset(header, rb_str_new2("gen"), INT2FIX(rec->gen));
  rb_hash_aset(header, rb_str_new2("expire_in"), INT2FIX(rec->ttl));

  as_record_destroy(rec);
  as_operations_destroy(&ops);

  rb_aero_logger(AS_LOG_LEVEL_DEBUG, &tm, 2, rb_str_new2("[Client][touch] success"), rb_aero_KEY_INFO);

  return header;
}
/**
 *******************************************************************************************************
 * This function invokes csdk's API's.
 *
 * @param self                  AerospikeClient object
 * @param err                   The as_error to be populated by the function
 *                              with the encountered error if any.
 * @param key                   The C client's as_key that identifies the record.
 * @param py_list               The list containing op, bin and value.
 * @param py_meta               The metadata for the operation.
 * @param operate_policy_p      The value for operate policy.
 *******************************************************************************************************
 */
static
PyObject *  AerospikeClient_Operate_Invoke(
	AerospikeClient * self, as_error *err,
	as_key * key, PyObject * py_list, PyObject * py_meta,
	as_policy_operate * operate_policy_p)
{
	as_val* put_val = NULL;
	char* bin = NULL;
	char* val = NULL;
	long offset = 0;
    double double_offset = 0.0;
	uint32_t ttl = 0;
	long operation = 0;
	int i = 0;
	PyObject * py_rec = NULL;
	PyObject * py_ustr = NULL;
	PyObject * py_ustr1 = NULL;
	PyObject * py_bin = NULL;
	as_record * rec = NULL;

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	as_operations ops;
	Py_ssize_t size = PyList_Size(py_list);
	as_operations_inita(&ops, size);

	if (!self || !self->as) {
		as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid aerospike object");
		goto CLEANUP;
	}

	if(py_meta) {
		AerospikeClient_CheckForMeta(py_meta, &ops, err);
	}

	if (err->code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	for ( i = 0; i < size; i++) {
		PyObject * py_val = PyList_GetItem(py_list, i);
		operation = -1;
		offset = 0;
        double_offset = 0.0;
		if ( PyDict_Check(py_val) ) {
			PyObject *key_op = NULL, *value = NULL;
			PyObject * py_value = NULL;
			Py_ssize_t pos = 0;
			while (PyDict_Next(py_val, &pos, &key_op, &value)) {
				if ( ! PyString_Check(key_op) ) {
					as_error_update(err, AEROSPIKE_ERR_CLIENT, "A operation key must be a string.");
					goto CLEANUP;
				} else {
					char * name = PyString_AsString(key_op);
					if(!strcmp(name,"op") && (PyInt_Check(value) || PyLong_Check(value))) {
						operation = PyInt_AsLong(value);
					} else if (!strcmp(name, "bin")) {
						py_bin = value;
					} else if(!strcmp(name, "val")) {
						py_value = value;
					} else {
						as_error_update(err, AEROSPIKE_ERR_PARAM, "operation can contain only op, bin and val keys");
						goto CLEANUP;
					}
				}
			}

			if (py_bin) {
				if (PyUnicode_Check(py_bin)) {
					py_ustr = PyUnicode_AsUTF8String(py_bin);
					bin = PyString_AsString(py_ustr);
				} else if (PyString_Check(py_bin)) {
					bin = PyString_AsString(py_bin);
				} else {
					as_error_update(err, AEROSPIKE_ERR_PARAM, "Bin name should be of type string");
					goto CLEANUP;
				}
			} else if (!py_bin && operation != AS_OPERATOR_TOUCH) {
				as_error_update(err, AEROSPIKE_ERR_PARAM, "Bin is not given");
				goto CLEANUP;
			}
			if (py_value) {
				if (check_type(self, py_value, operation, err)) {
                    goto CLEANUP;
				} else if (PyString_Check(py_value) && (operation == AS_OPERATOR_INCR)) {
                    char * incr_string = PyString_AsString(py_value);
                    int incr_value = 0, sign = 1;

                    if (strlen(incr_string) > 15) {
				        as_error_update(err, AEROSPIKE_ERR_PARAM, "Unsupported string length for increment operation");
                        goto CLEANUP;
                    }
                    if (*incr_string == '-') {
                        incr_string = incr_string + 1;
                        sign = -1;
                    } else if (*incr_string == '+') {
                        incr_string = incr_string + 1;
                        sign = 1;
                    }
                    while (*incr_string != '\0') {
                        if (*incr_string >= 48 && *incr_string <= 57) {
                            incr_value = (incr_value * 10) + (*incr_string ^ 0x30);
                        } else {
				            as_error_update(err, AEROSPIKE_ERR_PARAM, "Unsupported operand type(s) for +: 'int' and 'str'");
                            goto CLEANUP;
                        }
                        incr_string = incr_string + 1;
                    }
                    incr_value = incr_value * sign;
                    py_value = PyInt_FromLong(incr_value);
                }
			} else if ((!py_value) && (operation != AS_OPERATOR_READ)) {
				as_error_update(err, AEROSPIKE_ERR_PARAM, "Value should be given");
				goto CLEANUP;
			}

			switch(operation) {
				case AS_OPERATOR_APPEND:
					if (PyUnicode_Check(py_value)) {
						py_ustr1 = PyUnicode_AsUTF8String(py_value);
						val = PyString_AsString(py_ustr1);
					} else {
						val = PyString_AsString(py_value);
					}
					as_operations_add_append_str(&ops, bin, val);
					break;
				case AS_OPERATOR_PREPEND:
					if (PyUnicode_Check(py_value)) {
						py_ustr1 = PyUnicode_AsUTF8String(py_value);
						val = PyString_AsString(py_ustr1);
					} else {
						val = PyString_AsString(py_value);
					}
					as_operations_add_prepend_str(&ops, bin, val);
					break;
				case AS_OPERATOR_INCR:
					if (PyInt_Check(py_value)) {
                        offset = PyInt_AsLong(py_value);
                        as_operations_add_incr(&ops, bin, offset);
                    } else if ( PyLong_Check(py_value) ) {
                        offset = PyLong_AsLong(py_value);
                        if(-1 == offset) {
                            as_error_update(err, AEROSPIKE_ERR_PARAM, "integer value exceeds sys.maxsize");
                            goto CLEANUP;
                        }
                        as_operations_add_incr(&ops, bin, offset);
                    } else if (PyFloat_Check(py_value)) {
                        double_offset = PyFloat_AsDouble(py_value);
                        as_operations_add_incr_double(&ops, bin, double_offset);
                    }
                    break;
				case AS_OPERATOR_TOUCH:
					if (PyInt_Check(py_value)) {
                        ops.ttl = PyInt_AsLong(py_value);
                    } else if ( PyLong_Check(py_value) ) {
                        ttl = PyLong_AsLong(py_value);
                        if((uint32_t)-1 == ttl) {
                            as_error_update(err, AEROSPIKE_ERR_PARAM, "integer value for ttl exceeds sys.maxsize");
                            goto CLEANUP;
                        }
                        ops.ttl = ttl;
                    }
					as_operations_add_touch(&ops);
					break;
				case AS_OPERATOR_READ:
					as_operations_add_read(&ops, bin);
					break;
				case AS_OPERATOR_WRITE:
					pyobject_to_astype_write(self, err, bin, py_value, &put_val, &ops,
							&static_pool, SERIALIZER_PYTHON);
					if (err->code != AEROSPIKE_OK) {
						goto CLEANUP;
					}
					as_operations_add_write(&ops, bin, (as_bin_value *) put_val);
					break;
				default:
					as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid operation given");
			}
		}
	}

	// Initialize record
	as_record_init(rec, 0);

	aerospike_key_operate(self->as, err, operate_policy_p, key, &ops, &rec);
	if (err->code != AEROSPIKE_OK) {
		as_error_update(err, err->code, NULL);
		goto CLEANUP;
	}
	if(rec) {
		record_to_pyobject(err, rec, key, &py_rec);
	}

CLEANUP:
	if (py_ustr) {
		Py_DECREF(py_ustr);
	}
	if (py_ustr1) {
		Py_DECREF(py_ustr1);
	}
	if (rec) {
		as_record_destroy(rec);
	}
	if (key->valuep) {
		as_key_destroy(key);
	}
	if (put_val) {
		as_val_destroy(put_val);
	}

	if ( err->code != AEROSPIKE_OK ) {
		PyObject * py_err = NULL;
		error_to_pyobject(err, &py_err);
		PyObject *exception_type = raise_exception(err);
		PyErr_SetObject(exception_type, py_err);
		Py_DECREF(py_err);
		return NULL;
	}

	if (py_rec) {
		return py_rec;
	} else {
		return PyLong_FromLong(0);
	}
}
Exemple #3
0
int
main(int argc, char* argv[])
{
	// Parse command line arguments.
	if (! example_get_opts(argc, argv, EXAMPLE_BASIC_OPTS)) {
		exit(-1);
	}

	// Connect to the aerospike database cluster.
	aerospike as;
	example_connect_to_aerospike(&as);

	// Start clean.
	example_remove_test_record(&as);

	as_error err;

	// Create an as_operations object with a pair of bin arithmetic operations.
	// Generally, if using as_operations_inita(), we won't need to destroy the
	// object unless we call as_operations_add_write() with an externally
	// allocated as_bin_value.
	as_operations ops;
	as_operations_inita(&ops, 2);
	as_operations_add_incr(&ops, "test-bin-1", 1001);
	as_operations_add_incr(&ops, "test-bin-2", 1002);

	// Log the operations.
	LOG("as_operations object to apply to database:");
	example_dump_operations(&ops);

	// Apply the operations. Since the record does not exist, it will be created
	// and the bins initialized with the ops' integer values.
	if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, NULL) !=
			AEROSPIKE_OK) {
		LOG("aerospike_key_operate() returned %d - %s", err.code, err.message);
		example_cleanup(&as);
		exit(-1);
	}

	LOG("operations succeeded");

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Generate a different set of arithmetic operations.
	as_operations_inita(&ops, 3);
	as_operations_add_incr(&ops, "test-bin-1", 1);
	as_operations_add_incr(&ops, "test-bin-2", -2);
	as_operations_add_incr(&ops, "test-bin-3", 3);

	// Log the operations.
	LOG("as_operations object to apply to database:");
	example_dump_operations(&ops);

	// Apply the operations. The first two bins exist, so those ops' values will
	// be added to the existing values. The third (non-existent) bin will be
	// created and initialized with the op's integer value.
	if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, NULL) !=
			AEROSPIKE_OK) {
		LOG("aerospike_key_operate() returned %d - %s", err.code, err.message);
		example_cleanup(&as);
		exit(-1);
	}

	LOG("operations succeeded");

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Create an as_record object with one string value bin.
	as_record rec;
	as_record_inita(&rec, 1);
	as_record_set_str(&rec, "test-bin-1", "test-bin-1-data");

	// Log its contents.
	LOG("as_record object to write to database:");
	example_dump_record(&rec);

	// Write the record to the database, to change the value type of the bin.
	if (aerospike_key_put(&as, &err, NULL, &g_key, &rec) != AEROSPIKE_OK) {
		LOG("aerospike_key_put() returned %d - %s", err.code, err.message);
		example_cleanup(&as);
		exit(-1);
	}

	LOG("write succeeded");

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Log the operations. (Same operations as last time.)
	LOG("as_operations object to apply to database:");
	example_dump_operations(&ops);

	// Try to apply the three arithmetic operations again. This will fail, since
	// we can't increment the string value. Note that if any operation in the
	// transaction is rejected, none will be applied.
	if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, NULL) !=
			AEROSPIKE_ERR_BIN_INCOMPATIBLE_TYPE) {
		LOG("aerospike_key_operate() returned %d - %s, expected "
				"AEROSPIKE_ERR_BIN_INCOMPATIBLE_TYPE", err.code, err.message);
		example_cleanup(&as);
		exit(-1);
	}

	LOG("operations failed as expected");

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Generate a pair of operations to do an atomic increment and read.
	as_operations_inita(&ops, 2);
	as_operations_add_incr(&ops, "test-bin-3", 1);
	as_operations_add_read(&ops, "test-bin-3");

	// Log the operations.
	LOG("as_operations object to apply to database:");
	example_dump_operations(&ops);

	as_record* p_rec = NULL;

	// Apply the operations. The first will add the op's value to the existing
	// value, and the second will return the result. The pair of operations will
	// be atomic on the server.
	if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, &p_rec) !=
			AEROSPIKE_OK) {
		LOG("aerospike_key_operate() returned %d - %s", err.code, err.message);
		example_cleanup(&as);
		exit(-1);
	}

	LOG("operations succeeded");

	example_dump_record(p_rec);
	as_record_destroy(p_rec);

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Cleanup and disconnect from the database cluster.
	example_cleanup(&as);

	LOG("incr example successfully completed");

	return 0;
}
Exemple #4
0
int
main(int argc, char* argv[])
{
	// Parse command line arguments.
	if (! example_get_opts(argc, argv, EXAMPLE_BASIC_OPTS)) {
		exit(-1);
	}

	// Connect to the aerospike database cluster.
	aerospike as;
	example_connect_to_aerospike(&as);

	// Start clean.
	example_remove_test_record(&as);

	as_error err;

	// Create an as_record object with one (integer value) bin. By using
	// as_record_inita(), we won't need to destroy the record if we only set
	// bins using as_record_set_int64().
	as_record rec;
	as_record_inita(&rec, 1);
	as_record_set_int64(&rec, "test-bin", 1234);

	// Set the TTL of the record so it will last a minute.
	rec.ttl = 60;

	// Log its contents.
	LOG("as_record object to write to database:");
	example_dump_record(&rec);

	// Write the record to the database.
	if (aerospike_key_put(&as, &err, NULL, &g_key, &rec) != AEROSPIKE_OK) {
		LOG("aerospike_key_put() returned %d - %s", err.code, err.message);
		example_cleanup(&as);
		exit(-1);
	}

	LOG("write succeeded");

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Create an as_operations object with a touch operation. Generally, if
	// using as_operations_inita(), we won't need to destroy the object unless
	// we call as_operations_add_write() with an externally allocated
	// as_bin_value.
	as_operations ops;
	as_operations_inita(&ops, 1);
	as_operations_add_touch(&ops);

	// Set the TTL of the record so it will last two minutes.
	ops.ttl = 120;

	// Log the operation.
	LOG("as_operations object to apply to database:");
	example_dump_operations(&ops);

	// Apply the operation. Note that it does increment the record generation.
	if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, NULL) !=
			AEROSPIKE_OK) {
		LOG("aerospike_key_operate() returned %d - %s", err.code, err.message);
		example_cleanup(&as);
		exit(-1);
	}

	LOG("operation succeeded");

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Cleanup and disconnect from the database cluster.
	example_cleanup(&as);

	LOG("touch example successfully completed");

	return 0;
}
/**
 *******************************************************************************************************
 * This function invokes csdk's API's.
 *
 * @param self                  AerospikeClient object
 * @param err                   The as_error to be populated by the function
 *                              with the encountered error if any.
 * @param key                   The C client's as_key that identifies the record.
 * @param py_list               The list containing op, bin and value.
 * @param py_meta               The metadata for the operation.
 * @param operate_policy_p      The value for operate policy.
 *******************************************************************************************************
 */
static
PyObject *  AerospikeClient_Operate_Invoke(
	AerospikeClient * self, as_error *err,
	as_key * key, PyObject * py_list, PyObject * py_meta,
	as_policy_operate * operate_policy_p)
{
	as_val* put_val = NULL;
	char* bin = NULL;
	char* val = NULL;
	long offset = 0;
	double double_offset = 0.0;
	uint32_t ttl = 0;
	long operation = 0;
	int i = 0;
	PyObject * py_rec = NULL;
	PyObject * py_ustr = NULL;
	PyObject * py_ustr1 = NULL;
	PyObject * py_bin = NULL;
	as_record * rec = NULL;

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	as_operations ops;
	Py_ssize_t size = PyList_Size(py_list);
	as_operations_inita(&ops, size);

	if (!self || !self->as) {
		as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid aerospike object");
		goto CLEANUP;
	}

	if(py_meta) {
		AerospikeClient_CheckForMeta(py_meta, &ops, err);
	}

	if (err->code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	for ( i = 0; i < size; i++) {
		PyObject * py_val = PyList_GetItem(py_list, i);
		operation = -1;
		offset = 0;
		double_offset = 0.0;
		if ( PyDict_Check(py_val) ) {
			PyObject *key_op = NULL, *value = NULL;
			PyObject * py_value = NULL;
			Py_ssize_t pos = 0;
			while (PyDict_Next(py_val, &pos, &key_op, &value)) {
				if ( ! PyString_Check(key_op) ) {
					as_error_update(err, AEROSPIKE_ERR_CLIENT, "A operation key must be a string.");
					goto CLEANUP;
				} else {
					char * name = PyString_AsString(key_op);
					if(!strcmp(name,"op") && (PyInt_Check(value) || PyLong_Check(value))) {
						operation = PyInt_AsLong(value);
					} else if (!strcmp(name, "bin")) {
						py_bin = value;
					} else if(!strcmp(name, "val")) {
						py_value = value;
					} else {
						as_error_update(err, AEROSPIKE_ERR_PARAM, "operation can contain only op, bin and val keys");
						goto CLEANUP;
					}
				}
			}

			if (py_bin) {
				if (PyUnicode_Check(py_bin)) {
					py_ustr = PyUnicode_AsUTF8String(py_bin);
					bin = PyString_AsString(py_ustr);
				} else if (PyString_Check(py_bin)) {
					bin = PyString_AsString(py_bin);
				} else if (PyByteArray_Check(py_bin)) {
                    bin = PyByteArray_AsString(py_bin);
                } else {
                    as_error_update(err, AEROSPIKE_ERR_PARAM, "Bin name should be of type string");
					goto CLEANUP;
				}

				if (self->strict_types) {
					if (strlen(bin) > AS_BIN_NAME_MAX_LEN) {
						if (py_ustr) {
							Py_DECREF(py_ustr);
							py_ustr = NULL;
						}
						as_error_update(err, AEROSPIKE_ERR_BIN_NAME, "A bin name should not exceed 14 characters limit");
						goto CLEANUP;
					}
				}
			} else if (!py_bin && operation != AS_OPERATOR_TOUCH) {
				as_error_update(err, AEROSPIKE_ERR_PARAM, "Bin is not given");
				goto CLEANUP;
			}
			if (py_value) {
				if (self->strict_types) {
					if (check_type(self, py_value, operation, err)) {
						goto CLEANUP;
					}
				}
			} else if ((!py_value) && (operation != AS_OPERATOR_READ && operation != AS_OPERATOR_TOUCH)) {
				as_error_update(err, AEROSPIKE_ERR_PARAM, "Value should be given");
				goto CLEANUP;
			}

			switch(operation) {
				case AS_OPERATOR_APPEND:
					if (PyUnicode_Check(py_value)) {
						py_ustr1 = PyUnicode_AsUTF8String(py_value);
						val = PyString_AsString(py_ustr1);
						as_operations_add_append_str(&ops, bin, val);
					} else if (PyString_Check(py_value)) {
						val = PyString_AsString(py_value);
						as_operations_add_append_str(&ops, bin, val);
					} else if (PyByteArray_Check(py_value)) {
						as_bytes *bytes;
						GET_BYTES_POOL(bytes, &static_pool, err);
						serialize_based_on_serializer_policy(self, SERIALIZER_PYTHON, &bytes, py_value, err);
						as_operations_add_append_raw(&ops, bin, bytes->value, bytes->size);
					} else {
						if (!self->strict_types || !strcmp(py_value->ob_type->tp_name, "aerospike.null")) {
							as_operations *pointer_ops = &ops;
							as_binop *binop = &pointer_ops->binops.entries[pointer_ops->binops.size++];
							binop->op = AS_OPERATOR_APPEND;
							initialize_bin_for_strictypes(self, err, py_value, binop, bin, &static_pool);
						}
					}
					break;
				case AS_OPERATOR_PREPEND:
					if (PyUnicode_Check(py_value)) {
						py_ustr1 = PyUnicode_AsUTF8String(py_value);
						val = PyString_AsString(py_ustr1);
						as_operations_add_prepend_str(&ops, bin, val);
					} else if (PyString_Check(py_value)) {
						val = PyString_AsString(py_value);
						as_operations_add_prepend_str(&ops, bin, val);
					} else if (PyByteArray_Check(py_value)) {
						as_bytes *bytes;
						GET_BYTES_POOL(bytes, &static_pool, err);
						serialize_based_on_serializer_policy(self, SERIALIZER_PYTHON, &bytes, py_value, err);
						as_operations_add_prepend_raw(&ops, bin, bytes->value, bytes->size);
					} else {
						if (!self->strict_types || !strcmp(py_value->ob_type->tp_name, "aerospike.null")) {
							as_operations *pointer_ops = &ops;
							as_binop *binop = &pointer_ops->binops.entries[pointer_ops->binops.size++];
							binop->op = AS_OPERATOR_PREPEND;
							initialize_bin_for_strictypes(self, err, py_value, binop, bin, &static_pool);
						}
					}
					break;
				case AS_OPERATOR_INCR:
					if (PyInt_Check(py_value)) {
						offset = PyInt_AsLong(py_value);
						as_operations_add_incr(&ops, bin, offset);
					} else if ( PyLong_Check(py_value) ) {
						offset = PyLong_AsLong(py_value);
						if (offset == -1 && PyErr_Occurred()) {
							if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
								as_error_update(err, AEROSPIKE_ERR_PARAM, "integer value exceeds sys.maxsize");
								goto CLEANUP;
							}
						}
						as_operations_add_incr(&ops, bin, offset);
					} else if (PyFloat_Check(py_value)) {
						double_offset = PyFloat_AsDouble(py_value);
						as_operations_add_incr_double(&ops, bin, double_offset);
					} else {
						if (!self->strict_types || !strcmp(py_value->ob_type->tp_name, "aerospike.null")) {
							as_operations *pointer_ops = &ops;
							as_binop *binop = &pointer_ops->binops.entries[pointer_ops->binops.size++];
							binop->op = AS_OPERATOR_INCR;
							initialize_bin_for_strictypes(self, err, py_value, binop, bin, &static_pool);
						}
					}
					break;
				case AS_OPERATOR_TOUCH:
                    ops.ttl = 0;
					if (py_value && PyInt_Check(py_value)) {
                        ops.ttl = PyInt_AsLong(py_value);
                    } else if (py_value && PyLong_Check(py_value)) {
                        ttl = PyLong_AsLong(py_value);
                        if((uint32_t)-1 == ttl) {
                            as_error_update(err, AEROSPIKE_ERR_PARAM, "integer value for ttl exceeds sys.maxsize");
                            goto CLEANUP;
                        }
                        ops.ttl = ttl;
                    }
					as_operations_add_touch(&ops);
					break;
				case AS_OPERATOR_READ:
					as_operations_add_read(&ops, bin);
					break;
				case AS_OPERATOR_WRITE:
					pyobject_to_astype_write(self, err, bin, py_value, &put_val, &ops,
							&static_pool, SERIALIZER_PYTHON);
					if (err->code != AEROSPIKE_OK) {
						goto CLEANUP;
					}
					as_operations_add_write(&ops, bin, (as_bin_value *) put_val);
					break;
				default:
					if (self->strict_types) {
						as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid operation given");
						goto CLEANUP;
					}
			}
		}
	}

	// Initialize record
	as_record_init(rec, 0);

	Py_BEGIN_ALLOW_THREADS
	aerospike_key_operate(self->as, err, operate_policy_p, key, &ops, &rec);
	Py_END_ALLOW_THREADS

	if (err->code != AEROSPIKE_OK) {
		as_error_update(err, err->code, NULL);
		goto CLEANUP;
	}
	if(rec) {
		record_to_pyobject(self, err, rec, key, &py_rec);
	}

CLEANUP:
	if (py_ustr) {
		Py_DECREF(py_ustr);
	}
	if (py_ustr1) {
		Py_DECREF(py_ustr1);
	}
	if (rec) {
		as_record_destroy(rec);
	}
	if (key->valuep) {
		as_key_destroy(key);
	}
	if (put_val) {
		as_val_destroy(put_val);
	}

	if ( err->code != AEROSPIKE_OK ) {
		PyObject * py_err = NULL;
		error_to_pyobject(err, &py_err);
		PyObject *exception_type = raise_exception(err);
		PyErr_SetObject(exception_type, py_err);
		Py_DECREF(py_err);
		return NULL;
	}

	if (py_rec) {
		return py_rec;
	} else {
		return PyLong_FromLong(0);
	}
}
int
main(int argc, char* argv[])
{
	// Parse command line arguments.
	if (! example_get_opts(argc, argv, EXAMPLE_BASIC_OPTS)) {
		exit(-1);
	}

	// Connect to the aerospike database cluster.
	aerospike as;
	example_connect_to_aerospike(&as);

	// Start clean.
	example_remove_test_record(&as);

	as_error err;
	as_operations ops;

	LOG("append 10 values from 0 to 9");

	// The first append will create the record and bin.
	for (int i = 0; i < 10; i++) {
		as_operations_inita(&ops, 1);
		as_operations_add_list_append_int64(&ops, "test-bin-1", i);

		if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, NULL) !=
				AEROSPIKE_OK) {
			LOG("aerospike_key_operate() returned %d - %s", err.code,
					err.message);
			example_cleanup(&as);
			exit(-1);
		}

		as_operations_destroy(&ops);
	}

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	LOG("append operations succeeded");
	LOG("pop from the tail (index -1)");

	as_operations_inita(&ops, 1);
	as_operations_add_list_pop(&ops, "test-bin-1", -1);

	as_record *p_rec = NULL;

	if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, &p_rec) !=
			AEROSPIKE_OK) {
		LOG("aerospike_key_operate() returned %d - %s", err.code, err.message);
		as_operations_destroy(&ops);
		example_cleanup(&as);
		exit(-1);
	}

	as_operations_destroy(&ops);

	LOG("pop operation succeeded");
	LOG("insert popped value at the head (index 0)");

	as_val *val = (as_val *)as_record_get(p_rec, "test-bin-1");

	as_operations_inita(&ops, 1);
	as_operations_add_list_insert(&ops, "test-bin-1", 0, val);

	if (aerospike_key_operate(&as, &err, NULL, &g_key, &ops, NULL) !=
			AEROSPIKE_OK) {
		LOG("aerospike_key_operate() returned %d - %s", err.code, err.message);
		as_operations_destroy(&ops);
		example_cleanup(&as);
		exit(-1);
	}

	as_record_destroy(p_rec);
	as_operations_destroy(&ops);

	LOG("insert operation succeeded");

	if (! example_read_test_record(&as)) {
		example_cleanup(&as);
		exit(-1);
	}

	// Cleanup and disconnect from the database cluster.
	example_cleanup(&as);

	LOG("list example successfully completed");

	return 0;
}
/*
 *******************************************************************************************************
 * Wrapper function to perform an aerospike_key_oeprate within the C client.
 *
 * @param as_object_p           The C client's aerospike object.
 * @param as_key_p              The C client's as_key that identifies the record.
 * @param options_p             The user's optional policy options to be used if set, else defaults.
 * @param error_p               The as_error to be populated by the function
 *                              with the encountered error if any.
 * @param bin_name_p            The bin name to perform operation upon.
 * @param str                   The string to be appended in case of operation: append.
 * @param offset                The offset to be incremented by in case of operation: increment.
 * @param initial_value         The initial value to be set if record is absent
 *                              in case of operation: increment.
 * @param time_to_live          The ttl for the record in case of operation: touch.
 * @param operation             The operation type.
 *
 *******************************************************************************************************
 */
extern as_status
aerospike_record_operations_ops(aerospike* as_object_p,
                                as_key* as_key_p,
                                zval* options_p,
                                as_error* error_p,
                                int8_t* bin_name_p,
                                int8_t* str,
                                u_int64_t offset,
                                u_int64_t initial_value,
                                u_int64_t time_to_live,
                                u_int64_t operation)
{
    as_status           status = AEROSPIKE_OK;
    as_policy_operate   operate_policy;
    uint32_t            serializer_policy;
    as_record*          get_rec = NULL;
    as_operations       ops;
    as_val*             value_p = NULL;
    as_integer          initial_int_val;
    int16_t             initialize_int = 0;
    const char *select[] = {bin_name_p, NULL};

    as_operations_inita(&ops, 1);
    as_policy_operate_init(&operate_policy);

    if ((!as_object_p) || (!error_p) || (!as_key_p)) {
        status = AEROSPIKE_ERR;
        goto exit;
    }

    set_policy(NULL, NULL, &operate_policy, NULL, NULL, &serializer_policy,
            options_p, error_p);
    if (AEROSPIKE_OK != (status = (error_p->code))) {
        DEBUG_PHP_EXT_DEBUG("Unable to set policy");
        goto exit;
    }

    switch(operation) {
        case AS_OPERATOR_APPEND:
            as_operations_add_append_str(&ops, bin_name_p, str);
            break;
        case AS_OPERATOR_PREPEND:
            as_operations_add_prepend_str(&ops, bin_name_p, str);
            break;
        case AS_OPERATOR_INCR:
            if (AEROSPIKE_OK != (status = aerospike_key_select(as_object_p,
                            error_p, NULL, as_key_p, select, &get_rec))) {
                goto exit;
            } else {
                if (NULL != (value_p = (as_val *) as_record_get (get_rec, bin_name_p))) {
                   if (AS_NIL == value_p->type) {
                       as_integer_init(&initial_int_val, initial_value);
                       initialize_int = 1;
                       if (!as_operations_add_write(&ops, bin_name_p,
                                   (as_bin_value*) &initial_int_val)) {
                           status = AEROSPIKE_ERR;
                           goto exit;
                       }
                   } else {
                       as_operations_add_incr(&ops, bin_name_p, offset);
                   }
                } else {
                    status = AEROSPIKE_ERR;
                    goto exit;
                }
            }
            break;
        case AS_OPERATOR_TOUCH:
            ops.ttl = time_to_live;
            as_operations_add_touch(&ops);
            break;
        default:
            status = AEROSPIKE_ERR;
            goto exit;
            break;
    }

    if (AEROSPIKE_OK != (status = aerospike_key_operate(as_object_p, error_p,
                    &operate_policy, as_key_p, &ops, &get_rec))) {
        goto exit;
    }

exit:
     as_operations_destroy(&ops);
     if (get_rec) {
         as_record_destroy(get_rec);
     }
     if (initialize_int) { 
         as_integer_destroy(&initial_int_val);
     }
     return status;
}