diff --git a/src/include/conversions.h b/src/include/conversions.h index 91504bf629..a81d6f60cb 100644 --- a/src/include/conversions.h +++ b/src/include/conversions.h @@ -222,16 +222,38 @@ PyObject *create_class_instance_from_module(as_error *error_p, // We return an unsigned long long because it should be able to fit all fixed-width int types up to uint64_t // Returns -1 on error. Error indicator can be checked to verify if error occurred // TODO: replace this with new API calls in Python 3.14 -unsigned long long -convert_pyobject_to_fixed_width_integer_type(PyObject *pyobject, - unsigned long long max_bound); -uint8_t convert_pyobject_to_uint8_t(PyObject *pyobject); +unsigned long long convert_pyobject_to_unsigned_fixed_width_integer_type( + PyObject *pyobject, unsigned long long max_bound); -uint16_t convert_pyobject_to_uint16_t(PyObject *pyobject); +uint64_t convert_unsigned_long_long_into_uint64_t(as_error *err, + PyObject *pyobject, + const char *component); -uint32_t convert_pyobject_to_uint32_t(PyObject *pyobject); +int64_t convert_long_long_into_int64_t(as_error *err, PyObject *pyobject, + const char *component); -uint64_t convert_pyobject_to_uint64_t(PyObject *pyobject); +uint32_t convert_unsigned_long_into_uint32_t(as_error *err, PyObject *pyobject, + const char *component); + +int32_t convert_long_into_int32_t(as_error *err, PyObject *pyobject, + const char *component); + +int convert_long_into_int(as_error *err, PyObject *pyobject, + const char *component); + +unsigned int convert_unsigned_long_into_enum_value(as_error *err, + PyObject *py_long, + unsigned int max_enum_value, + const char *component); + +uint16_t convert_unsigned_long_into_uint16_t(as_error *err, PyObject *pyobject, + const char *component); + +int16_t convert_long_into_int16_t(as_error *err, PyObject *pyobject, + const char *component); + +uint8_t convert_unsigned_long_into_uint8_t(as_error *err, PyObject *pyobject, + const char *component); // Returns NULL on error. const char *convert_pyobject_to_str(PyObject *py_obj); diff --git a/src/include/query.h b/src/include/query.h index a2a6c65cb8..0f8df56b38 100644 --- a/src/include/query.h +++ b/src/include/query.h @@ -146,8 +146,6 @@ PyObject *AerospikeQuery_Get_Partitions_status(AerospikeQuery *self); */ PyObject *StoreUnicodePyObject(AerospikeQuery *self, PyObject *obj); -int64_t pyobject_to_int64(PyObject *py_obj); - // We need to share this with src/main/client/query.c because this function is no longer assigned // to the query type's tp_new slot. We are trying to prevent users from using the query type's constructor directly // to create a query instance. diff --git a/src/main/client/cdt_operation_utils.c b/src/main/client/cdt_operation_utils.c index 2d7a2da6d9..5e45ed2abc 100644 --- a/src/main/client/cdt_operation_utils.c +++ b/src/main/client/cdt_operation_utils.c @@ -135,22 +135,15 @@ as_status get_optional_int64_t(as_error *err, const char *key, if (!py_val) { return AEROSPIKE_OK; } - if (!PyLong_Check(py_val)) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "%s must be an integer", key); + as_error_update(err, AEROSPIKE_ERR_PARAM, + "Unable to convert Python object %s to int64_t", key); + return err->code; } - - *i64_valptr = (int64_t)PyLong_AsLongLong(py_val); - if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, "%s too large", - key); - } - return as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to convert %s", - key); + *i64_valptr = convert_long_long_into_int64_t(err, py_val, key); + if (err->code != AEROSPIKE_OK) { + return err->code; } - *found = true; return AEROSPIKE_OK; } diff --git a/src/main/client/operate.c b/src/main/client/operate.c index b743252b83..25dd02e06b 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -323,6 +323,7 @@ as_status add_op(AerospikeClient *self, as_error *err, char *bin = NULL; char *val = NULL; long offset = 0; + long long long_offset = 0; long ttl = 0; double double_offset = 0.0; int index = 0; @@ -544,7 +545,10 @@ as_status add_op(AerospikeClient *self, as_error *err, goto CLEANUP; } if (PyLong_Check(py_index)) { - index = PyLong_AsLong(py_index); + index = convert_long_into_int(err, py_index, "py_index"); + if (err->code != AEROSPIKE_OK) { + goto CLEANUP; + } } else { as_error_update(err, AEROSPIKE_ERR_PARAM, @@ -573,10 +577,15 @@ as_status add_op(AerospikeClient *self, as_error *err, as_error_update(err, AEROSPIKE_ERR_CLIENT, "Internal error"); goto CLEANUP; } - - uint32_t flags = convert_pyobject_to_uint32_t(py_flags); + if (!PyLong_Check(py_flags)) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "CDT operation's flags argument must be a long"); + goto CLEANUP; + } + uint32_t flags = + convert_unsigned_long_into_uint32_t(err, py_flags, "path flags"); Py_DECREF(py_flags); - if (PyErr_Occurred()) { + if (err->code != AEROSPIKE_OK) { as_error_update(err, AEROSPIKE_ERR_PARAM, "CDT operation's flags argument is invalid"); goto CLEANUP; @@ -680,15 +689,12 @@ as_status add_op(AerospikeClient *self, as_error *err, break; case AS_OPERATOR_INCR: if (PyLong_Check(py_value)) { - offset = PyLong_AsLong(py_value); - if (offset == -1 && PyErr_Occurred() && self->strict_types) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - as_error_update(err, AEROSPIKE_ERR_PARAM, - "integer value exceeds sys.maxsize"); - goto CLEANUP; - } + long_offset = convert_long_long_into_int64_t(err, py_value, + "AS_OPERATOR_INCR"); + if (err->code != AEROSPIKE_OK) { + goto CLEANUP; } - as_operations_add_incr(ops, bin, offset); + as_operations_add_incr(ops, bin, long_offset); } else if (PyFloat_Check(py_value)) { double_offset = PyFloat_AsDouble(py_value); diff --git a/src/main/client/query.c b/src/main/client/query.c index 5f7f7c88eb..44cf3b3abd 100644 --- a/src/main/client/query.c +++ b/src/main/client/query.c @@ -134,8 +134,11 @@ static int query_where_add(as_query **query, as_predicate_type predicate, "Bin must be a string or unicode"); return 1; } - int64_t val = pyobject_to_int64(py_val1); - + int64_t val = convert_long_long_into_int64_t(err, py_val1, + "query numeric index"); + if (err->code != AEROSPIKE_OK) { + return 1; + } as_query_where_init(*query, 1); if (index_type == 0) { as_query_where(*query, bin, as_equals(NUMERIC, val)); @@ -193,7 +196,11 @@ static int query_where_add(as_query **query, as_predicate_type predicate, return 1; } if (PyLong_Check(py_val1)) { - min = pyobject_to_int64(py_val1); + min = convert_long_long_into_int64_t(err, py_val1, + "predicate max"); + if (err->code != AEROSPIKE_OK) { + return 1; + } } else { Py_XDECREF(py_ubin); @@ -203,7 +210,11 @@ static int query_where_add(as_query **query, as_predicate_type predicate, } if (PyLong_Check(py_val2)) { - max = pyobject_to_int64(py_val2); + max = convert_long_long_into_int64_t(err, py_val2, + "predicate max"); + if (err->code != AEROSPIKE_OK) { + return 1; + } } else { Py_XDECREF(py_ubin); @@ -405,7 +416,7 @@ static PyObject *AerospikeClient_QueryApply_Invoke( } long op = PyLong_AsLong(py_op); - if (op == -1 && PyErr_Occurred()) { + if (PyErr_Occurred()) { as_error_update(&err, AEROSPIKE_ERR_PARAM, "unknown predicate type"); goto CLEANUP; @@ -413,7 +424,7 @@ static PyObject *AerospikeClient_QueryApply_Invoke( as_index_datatype op_data = (as_index_datatype)PyLong_AsLong(py_op_data); - if (op == -1 && PyErr_Occurred()) { + if (PyErr_Occurred()) { as_error_update(&err, AEROSPIKE_ERR_PARAM, "unknown index data type"); goto CLEANUP; diff --git a/src/main/client/remove.c b/src/main/client/remove.c index 78162c6001..b54e3150f6 100644 --- a/src/main/client/remove.c +++ b/src/main/client/remove.c @@ -93,16 +93,9 @@ PyObject *AerospikeClient_Remove_Invoke(AerospikeClient *self, PyObject *py_key, if (py_gen) { if (PyLong_Check(py_gen)) { remove_policy_p->generation = - (uint16_t)PyLong_AsLong(py_gen); - } - else if (PyLong_Check(py_gen)) { - remove_policy_p->generation = - (uint16_t)PyLong_AsLongLong(py_gen); - if ((uint16_t)-1 == remove_policy_p->generation && - PyErr_Occurred()) { - as_error_update( - &err, AEROSPIKE_ERR_PARAM, - "integer value for gen exceeds sys.maxsize"); + convert_unsigned_long_into_uint16_t(&err, py_gen, + "generation"); + if (err.code != AEROSPIKE_OK) { goto CLEANUP; } } diff --git a/src/main/client/truncate.c b/src/main/client/truncate.c index 141ecdc5ac..6ec369d3cc 100644 --- a/src/main/client/truncate.c +++ b/src/main/client/truncate.c @@ -65,7 +65,6 @@ PyObject *AerospikeClient_Truncate(AerospikeClient *self, PyObject *args, PyObject *py_nanos = NULL; PyObject *py_policy = NULL; PyObject *ret_val = NULL; - long long temp_long; as_error err; uint64_t nanos = 1; // If assignment fails, this will cause an error char *namespace = NULL; @@ -126,21 +125,9 @@ PyObject *AerospikeClient_Truncate(AerospikeClient *self, PyObject *args, // Start conversion of the nanosecond parameter if (PyLong_Check(py_nanos)) { - - temp_long = PyLong_AsLongLong(py_nanos); - // There was a negative number outside of the range of - 2 ^ 63 - if (temp_long < 0 && !PyErr_Occurred()) { - as_error_update(&err, AEROSPIKE_ERR_PARAM, - "Nanoseconds must be a positive value"); - goto CLEANUP; - } - // Its possible that this is a valid uint64 between 2 ^ 63 and 2^64 -1 - PyErr_Clear(); - nanos = (uint64_t)PyLong_AsUnsignedLongLong(py_nanos); - - if (PyErr_Occurred()) { - as_error_update(&err, AEROSPIKE_ERR_PARAM, - "Nanoseconds value too large"); + nanos = convert_unsigned_long_long_into_uint64_t(&err, py_nanos, + "truncate"); + if (err.code != AEROSPIKE_OK) { goto CLEANUP; } } diff --git a/src/main/client/type.c b/src/main/client/type.c index b77e5a3290..efec045f8b 100644 --- a/src/main/client/type.c +++ b/src/main/client/type.c @@ -763,7 +763,11 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, py_port = PyTuple_GetItem(py_host, 1); if (PyLong_Check(py_port)) { - port = (uint16_t)PyLong_AsLong(py_port); + port = convert_unsigned_long_into_uint16_t( + &constructor_err, py_port, "config.port"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } else { as_error_update(&constructor_err, AEROSPIKE_ERR_PARAM, @@ -832,11 +836,19 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_shm_max_nodes = PyDict_GetItemString(py_shm, "shm_max_nodes"); if (py_shm_max_nodes && PyLong_Check(py_shm_max_nodes)) { - config.shm_max_nodes = PyLong_AsLong(py_shm_max_nodes); + config.shm_max_nodes = convert_unsigned_long_into_uint32_t( + &constructor_err, py_shm_max_nodes, "config.shm_max_nodes"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } py_shm_max_nodes = PyDict_GetItemString(py_shm, "max_nodes"); if (py_shm_max_nodes && PyLong_Check(py_shm_max_nodes)) { - config.shm_max_nodes = PyLong_AsLong(py_shm_max_nodes); + config.shm_max_nodes = convert_unsigned_long_into_uint32_t( + &constructor_err, py_shm_max_nodes, "config.max_nodes"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } // This does not match documentation (wrong name and location in dict), @@ -844,11 +856,21 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_shm_max_namespaces = PyDict_GetItemString(py_shm, "shm_max_namespaces"); if (py_shm_max_namespaces && PyLong_Check(py_shm_max_namespaces)) { - config.shm_max_namespaces = PyLong_AsLong(py_shm_max_namespaces); + config.shm_max_nodes = convert_unsigned_long_into_uint32_t( + &constructor_err, py_shm_max_namespaces, + "config.shm_max_namespaces"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } py_shm_max_namespaces = PyDict_GetItemString(py_shm, "max_namespaces"); if (py_shm_max_namespaces && PyLong_Check(py_shm_max_namespaces)) { - config.shm_max_namespaces = PyLong_AsLong(py_shm_max_namespaces); + config.shm_max_namespaces = convert_unsigned_long_into_uint32_t( + &constructor_err, py_shm_max_namespaces, + "config.max_namespaces"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } // This does not match documentation (wrong name and location in dict), @@ -858,20 +880,34 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, if (py_shm_takeover_threshold_sec && PyLong_Check(py_shm_takeover_threshold_sec)) { config.shm_takeover_threshold_sec = - PyLong_AsLong(py_shm_takeover_threshold_sec); + (uint32_t)convert_unsigned_long_into_uint32_t( + &constructor_err, py_shm_takeover_threshold_sec, + "config.shm_takeover_threshold_sec"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } py_shm_takeover_threshold_sec = PyDict_GetItemString(py_shm, "takeover_threshold_sec"); if (py_shm_takeover_threshold_sec && PyLong_Check(py_shm_takeover_threshold_sec)) { config.shm_takeover_threshold_sec = - PyLong_AsLong(py_shm_takeover_threshold_sec); + (uint32_t)convert_unsigned_long_into_uint32_t( + &constructor_err, py_shm_takeover_threshold_sec, + "config.takeover_threshold_sec"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } PyObject *py_shm_cluster_key = PyDict_GetItemString(py_shm, "shm_key"); if (py_shm_cluster_key && PyLong_Check(py_shm_cluster_key)) { user_shm_key = true; - config.shm_key = PyLong_AsLong(py_shm_cluster_key); + config.shm_key = (int)convert_unsigned_long_into_uint32_t( + &constructor_err, py_shm_cluster_key, "config.shm_key"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } } @@ -922,7 +958,13 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, //global defaults setting PyObject *py_key_policy = PyDict_GetItemString(py_policies, "key"); if (py_key_policy && PyLong_Check(py_key_policy)) { - long long_key_policy = PyLong_AsLong(py_key_policy); + as_policy_key long_key_policy = + (as_policy_key)convert_unsigned_long_into_enum_value( + &constructor_err, py_key_policy, + (unsigned int)AS_POLICY_KEY_SEND, "config.policy.key"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } config.policies.read.key = long_key_policy; config.policies.write.key = long_key_policy; config.policies.apply.key = long_key_policy; @@ -933,71 +975,122 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_sock_timeout = PyDict_GetItemString(py_policies, "socket_timeout"); if (py_sock_timeout && PyLong_Check(py_sock_timeout)) { - long long_timeout = PyLong_AsLong(py_sock_timeout); - - config.policies.write.base.socket_timeout = long_timeout; - config.policies.read.base.socket_timeout = long_timeout; - config.policies.apply.base.socket_timeout = long_timeout; - config.policies.operate.base.socket_timeout = long_timeout; - config.policies.query.base.socket_timeout = long_timeout; - config.policies.scan.base.socket_timeout = long_timeout; - config.policies.remove.base.socket_timeout = long_timeout; - config.policies.batch.base.socket_timeout = long_timeout; + uint32_t socket_timeout = convert_unsigned_long_into_uint32_t( + &constructor_err, py_sock_timeout, + "config.policy.socket_timeout"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.policies.write.base.socket_timeout = socket_timeout; + config.policies.read.base.socket_timeout = socket_timeout; + config.policies.apply.base.socket_timeout = socket_timeout; + config.policies.operate.base.socket_timeout = socket_timeout; + config.policies.query.base.socket_timeout = socket_timeout; + config.policies.scan.base.socket_timeout = socket_timeout; + config.policies.remove.base.socket_timeout = socket_timeout; + config.policies.batch.base.socket_timeout = socket_timeout; + } + + PyObject *py_timeout_delay = + PyDict_GetItemString(py_policies, "timeout_delay"); + if (py_sock_timeout && PyLong_Check(py_sock_timeout)) { + uint32_t timeout_delay = convert_unsigned_long_into_uint32_t( + &constructor_err, py_timeout_delay, + "config.policy.timeout_delay"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.policies.write.base.timeout_delay = timeout_delay; + config.policies.read.base.timeout_delay = timeout_delay; + config.policies.apply.base.timeout_delay = timeout_delay; + config.policies.operate.base.timeout_delay = timeout_delay; + config.policies.query.base.timeout_delay = timeout_delay; + config.policies.scan.base.timeout_delay = timeout_delay; + config.policies.remove.base.timeout_delay = timeout_delay; + config.policies.batch.base.timeout_delay = timeout_delay; } PyObject *py_total_timeout = PyDict_GetItemString(py_policies, "total_timeout"); if (py_total_timeout && PyLong_Check(py_total_timeout)) { - long long_total_timeout = PyLong_AsLong(py_total_timeout); - - config.policies.write.base.total_timeout = long_total_timeout; - config.policies.read.base.total_timeout = long_total_timeout; - config.policies.apply.base.total_timeout = long_total_timeout; - config.policies.operate.base.total_timeout = long_total_timeout; - config.policies.query.base.total_timeout = long_total_timeout; - config.policies.scan.base.total_timeout = long_total_timeout; - config.policies.remove.base.total_timeout = long_total_timeout; - config.policies.batch.base.total_timeout = long_total_timeout; + uint32_t total_timeout = convert_unsigned_long_into_uint32_t( + &constructor_err, py_total_timeout, + "config.policy.total_timeout"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.policies.write.base.total_timeout = total_timeout; + config.policies.read.base.total_timeout = total_timeout; + config.policies.apply.base.total_timeout = total_timeout; + config.policies.operate.base.total_timeout = total_timeout; + config.policies.query.base.total_timeout = total_timeout; + config.policies.scan.base.total_timeout = total_timeout; + config.policies.remove.base.total_timeout = total_timeout; + config.policies.batch.base.total_timeout = total_timeout; } PyObject *py_max_retry = PyDict_GetItemString(py_policies, "max_retries"); if (py_max_retry && PyLong_Check(py_max_retry)) { - long long_max_retries = PyLong_AsLong(py_max_retry); - config.policies.write.base.max_retries = long_max_retries; - config.policies.read.base.max_retries = long_max_retries; - config.policies.apply.base.max_retries = long_max_retries; - config.policies.operate.base.max_retries = long_max_retries; - config.policies.query.base.max_retries = long_max_retries; - config.policies.scan.base.max_retries = long_max_retries; - config.policies.remove.base.max_retries = long_max_retries; - config.policies.batch.base.max_retries = long_max_retries; + uint32_t max_retries = convert_unsigned_long_into_uint32_t( + &constructor_err, py_max_retry, "config.policy.max_retries"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.policies.write.base.max_retries = max_retries; + config.policies.read.base.max_retries = max_retries; + config.policies.apply.base.max_retries = max_retries; + config.policies.operate.base.max_retries = max_retries; + config.policies.query.base.max_retries = max_retries; + config.policies.scan.base.max_retries = max_retries; + config.policies.remove.base.max_retries = max_retries; + config.policies.batch.base.max_retries = max_retries; } PyObject *py_exists = PyDict_GetItemString(py_policies, "exists"); if (py_exists && PyLong_Check(py_exists)) { - long long_exists = PyLong_AsLong(py_exists); - config.policies.write.exists = long_exists; + as_policy_exists exists = + (as_policy_exists)convert_unsigned_long_into_enum_value( + &constructor_err, py_exists, + (unsigned int)AS_POLICY_EXISTS_CREATE_OR_REPLACE, + "config.policy.exists"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.policies.write.exists = exists; } PyObject *py_replica = PyDict_GetItemString(py_policies, "replica"); if (py_replica && PyLong_Check(py_replica)) { - long long_replica = PyLong_AsLong(py_replica); - config.policies.read.replica = long_replica; - config.policies.write.replica = long_replica; - config.policies.apply.replica = long_replica; - config.policies.operate.replica = long_replica; - config.policies.remove.replica = long_replica; - config.policies.batch.replica = long_replica; - config.policies.scan.replica = long_replica; - config.policies.query.replica = long_replica; + as_policy_exists replica = + (as_policy_exists)convert_unsigned_long_into_enum_value( + &constructor_err, py_replica, + (unsigned int)AS_POLICY_REPLICA_RANDOM, + "config.policy.replica"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.policies.read.replica = replica; + config.policies.write.replica = replica; + config.policies.apply.replica = replica; + config.policies.operate.replica = replica; + config.policies.remove.replica = replica; + config.policies.batch.replica = replica; + config.policies.scan.replica = replica; + config.policies.query.replica = replica; } PyObject *py_ap_read_mode = PyDict_GetItemString(py_policies, "read_mode_ap"); if (py_ap_read_mode && PyLong_Check(py_ap_read_mode)) { as_policy_read_mode_ap ap_read_mode = - (as_policy_read_mode_ap)PyLong_AsLong(py_ap_read_mode); + (as_policy_read_mode_ap)convert_unsigned_long_into_enum_value( + &constructor_err, py_ap_read_mode, + (unsigned int)AS_POLICY_READ_MODE_AP_ALL, + "config.policy.read_mode_ap"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } config.policies.read.read_mode_ap = ap_read_mode; config.policies.operate.read_mode_ap = ap_read_mode; config.policies.batch.read_mode_ap = ap_read_mode; @@ -1007,7 +1100,13 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyDict_GetItemString(py_policies, "read_mode_sc"); if (py_sc_read_mode && PyLong_Check(py_sc_read_mode)) { as_policy_read_mode_sc sc_read_mode = - (as_policy_read_mode_sc)PyLong_AsLong(py_sc_read_mode); + (as_policy_read_mode_sc)convert_unsigned_long_into_enum_value( + &constructor_err, py_sc_read_mode, + (unsigned int)AS_POLICY_READ_MODE_SC_ALLOW_UNAVAILABLE, + "config.policy.read_mode_sc"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } config.policies.read.read_mode_sc = sc_read_mode; config.policies.operate.read_mode_sc = sc_read_mode; config.policies.batch.read_mode_sc = sc_read_mode; @@ -1016,11 +1115,18 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_commit_level = PyDict_GetItemString(py_policies, "commit_level"); if (py_commit_level && PyLong_Check(py_commit_level)) { - long long_commit_level = PyLong_AsLong(py_commit_level); - config.policies.write.commit_level = long_commit_level; - config.policies.apply.commit_level = long_commit_level; - config.policies.operate.commit_level = long_commit_level; - config.policies.remove.commit_level = long_commit_level; + as_policy_commit_level commit_level = + (as_policy_commit_level)convert_unsigned_long_into_enum_value( + &constructor_err, py_commit_level, + (unsigned int)AS_POLICY_COMMIT_LEVEL_MASTER, + "config.policy.commit_level"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.policies.write.commit_level = commit_level; + config.policies.apply.commit_level = commit_level; + config.policies.operate.commit_level = commit_level; + config.policies.remove.commit_level = commit_level; } // This does not match documentation (should not be in policies), @@ -1028,7 +1134,12 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_max_threads = PyDict_GetItemString(py_policies, "max_threads"); if (py_max_threads && PyLong_Check(py_max_threads)) { - config.max_conns_per_node = PyLong_AsLong(py_max_threads); + uint32_t max_conns_per_node = convert_unsigned_long_into_uint32_t( + &constructor_err, py_max_threads, "config.policy.max_threads"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.max_conns_per_node = max_conns_per_node; } // This does not match documentation (should not be in policies), @@ -1036,7 +1147,13 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_thread_pool_size = PyDict_GetItemString(py_policies, "thread_pool_size"); if (py_thread_pool_size && PyLong_Check(py_thread_pool_size)) { - config.thread_pool_size = PyLong_AsLong(py_thread_pool_size); + uint32_t thread_pool_size = convert_unsigned_long_into_uint32_t( + &constructor_err, py_thread_pool_size, + "config.policy.thread_pool_size"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.thread_pool_size = thread_pool_size; } /* @@ -1104,7 +1221,13 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_login_timeout = PyDict_GetItemString(py_policies, "login_timeout_ms"); if (py_login_timeout && PyLong_Check(py_login_timeout)) { - config.login_timeout_ms = PyLong_AsLong(py_login_timeout); + uint32_t login_timeout_ms = convert_unsigned_long_into_uint32_t( + &constructor_err, py_login_timeout, + "config.policy.login_timeout_ms"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } + config.login_timeout_ms = login_timeout_ms; } PyObject *py_auth_mode = PyDict_GetItemString(py_policies, "auth_mode"); @@ -1134,26 +1257,34 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_thread_pool_size = PyDict_GetItemString(py_config, "thread_pool_size"); if (py_thread_pool_size && PyLong_Check(py_thread_pool_size)) { - config.thread_pool_size = PyLong_AsLong(py_thread_pool_size); - } - - // max_threads (backward compatibility) - PyObject *py_max_threads = PyDict_GetItemString(py_config, "max_threads"); - if (py_max_threads && PyLong_Check(py_max_threads)) { - config.max_conns_per_node = PyLong_AsLong(py_max_threads); + config.thread_pool_size = convert_unsigned_long_into_uint32_t( + &constructor_err, py_thread_pool_size, + "config.policy.thread_pool_size"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } PyObject *py_min_conns_per_node = PyDict_GetItemString(py_config, "min_conns_per_node"); if (py_min_conns_per_node && PyLong_Check(py_min_conns_per_node)) { - config.min_conns_per_node = PyLong_AsLong(py_min_conns_per_node); + config.min_conns_per_node = convert_unsigned_long_into_uint32_t( + &constructor_err, py_min_conns_per_node, + "config.policy.min_conns_per_node"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } // max_conns_per_node PyObject *py_max_conns = PyDict_GetItemString(py_config, "max_conns_per_node"); if (py_max_conns && PyLong_Check(py_max_conns)) { - config.max_conns_per_node = PyLong_AsLong(py_max_conns); + config.max_conns_per_node = convert_unsigned_long_into_uint32_t( + &constructor_err, py_max_conns, "config.policy.max_conns_per_node"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } // max_error_rate @@ -1161,7 +1292,12 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyDict_GetItemString(py_config, "max_error_rate"); Py_XINCREF(py_max_error_rate); if (py_max_error_rate && PyLong_Check(py_max_error_rate)) { - config.max_error_rate = PyLong_AsLong(py_max_error_rate); + config.max_error_rate = convert_unsigned_long_into_uint32_t( + &constructor_err, py_max_error_rate, + "config.policy.max_error_rate"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } Py_XDECREF(py_max_error_rate); @@ -1170,7 +1306,12 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyDict_GetItemString(py_config, "error_rate_window"); Py_XINCREF(py_error_rate_window); if (py_error_rate_window && PyLong_Check(py_error_rate_window)) { - config.error_rate_window = PyLong_AsLong(py_error_rate_window); + config.error_rate_window = convert_unsigned_long_into_uint32_t( + &constructor_err, py_error_rate_window, + "config.policy.error_rate_window"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } Py_XDECREF(py_error_rate_window); @@ -1178,7 +1319,12 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_connect_timeout = PyDict_GetItemString(py_config, "connect_timeout"); if (py_connect_timeout && PyLong_Check(py_connect_timeout)) { - config.conn_timeout_ms = PyLong_AsLong(py_connect_timeout); + config.conn_timeout_ms = convert_unsigned_long_into_uint32_t( + &constructor_err, py_connect_timeout, + "config.policy.connect_timeout"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } //Whether to utilize shared connection @@ -1190,10 +1336,11 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_send_bool_as = PyDict_GetItemString(py_config, "send_bool_as"); if (py_send_bool_as != NULL && PyLong_Check(py_send_bool_as)) { - int send_bool_as_temp = PyLong_AsLong(py_send_bool_as); - if (send_bool_as_temp >= SEND_BOOL_AS_INTEGER && - send_bool_as_temp <= SEND_BOOL_AS_AS_BOOL) { - self->send_bool_as = send_bool_as_temp; + self->send_bool_as = (uint8_t)convert_unsigned_long_into_enum_value( + &constructor_err, py_send_bool_as, + (unsigned int)SEND_BOOL_AS_AS_BOOL, "config.policy.send_bool_as"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; } } @@ -1201,20 +1348,23 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_compression_threshold = PyDict_GetItemString(py_config, "compression_threshold"); if (py_compression_threshold && PyLong_Check(py_compression_threshold)) { - int compression_value = PyLong_AsLong(py_compression_threshold); - if (compression_value >= 0) { - config.policies.write.compression_threshold = compression_value; - } - else { - error_code = INIT_COMPRESSION_ERR; - goto CONSTRUCTOR_ERROR; + config.policies.write.compression_threshold = + convert_unsigned_long_into_uint32_t( + &constructor_err, py_compression_threshold, + "config.policy.write.compression_threshold"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; } } PyObject *py_tend_interval = PyDict_GetItemString(py_config, "tend_interval"); if (py_tend_interval && PyLong_Check(py_tend_interval)) { - config.tender_interval = PyLong_AsLong(py_tend_interval); + config.tender_interval = convert_unsigned_long_into_uint32_t( + &constructor_err, py_tend_interval, "config.tend_interval"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } PyObject *py_cluster_name = PyDict_GetItemString(py_config, "cluster_name"); @@ -1262,10 +1412,16 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_max_socket_idle = NULL; py_max_socket_idle = PyDict_GetItemString(py_config, "max_socket_idle"); if (py_max_socket_idle && PyLong_Check(py_max_socket_idle)) { + long max_socket_idle = PyLong_AsLong(py_max_socket_idle); if (max_socket_idle >= 0) { config.max_socket_idle = (uint32_t)max_socket_idle; } + config.max_socket_idle = convert_unsigned_long_into_uint32_t( + &constructor_err, py_max_socket_idle, "config.max_socket_idle"); + if (constructor_err.code != AEROSPIKE_OK) { + goto RAISE_EXCEPTION_WITH_AS_ERROR; + } } bool *bool_config_refs[] = {&config.force_single_node, @@ -1389,7 +1545,10 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, static int set_rack_aware_config(as_config *conf, PyObject *config_dict) { PyObject *py_config_value; - long rack_id; + + as_error err; + as_error_init(&err); + py_config_value = PyDict_GetItemString(config_dict, "rack_aware"); if (py_config_value) { if (PyBool_Check(py_config_value)) { @@ -1403,19 +1562,15 @@ static int set_rack_aware_config(as_config *conf, PyObject *config_dict) py_config_value = PyDict_GetItemString(config_dict, "rack_id"); if (py_config_value) { if (PyLong_Check(py_config_value)) { - rack_id = PyLong_AsLong(py_config_value); + conf->rack_id = + convert_long_into_int(&err, py_config_value, "config.rack_id"); + if (err.code != AEROSPIKE_OK) { + return INIT_POLICY_PARAM_ERR; + } } else { return INIT_POLICY_PARAM_ERR; // A non integer passed in. } - if (rack_id == -1 && PyErr_Occurred()) { - return INIT_POLICY_PARAM_ERR; // We had overflow. - } - - if (rack_id > INT_MAX || rack_id < INT_MIN) { - return INIT_POLICY_PARAM_ERR; // Magnitude too great for an integer in C. - } - conf->rack_id = (int)rack_id; } PyObject *rack_ids_pylist = PyDict_GetItemString(config_dict, "rack_ids"); @@ -1443,14 +1598,15 @@ static int set_rack_aware_config(as_config *conf, PyObject *config_dict) goto PARAM_ERROR; } - long rack_id = PyLong_AsLong(rack_id_pyobj); - if (rack_id == -1) { + int rack_id = + convert_long_into_int(&err, rack_id_pyobj, "config.rack_id"); + if (err.code != AEROSPIKE_OK) { // Error occurred Py_DECREF(rack_id_pyobj); goto PARAM_ERROR; } - as_config_add_rack_id(conf, (int)rack_id); + as_config_add_rack_id(conf, rack_id); Py_DECREF(rack_id_pyobj); } diff --git a/src/main/config_provider/type.c b/src/main/config_provider/type.c index 0f8f0864fc..281e4ad97a 100644 --- a/src/main/config_provider/type.c +++ b/src/main/config_provider/type.c @@ -1,6 +1,7 @@ #include "types.h" #include "config_provider.h" #include "conversions.h" +#include "pythoncapi_compat.h" static PyObject *AerospikeConfigProvider_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -24,16 +25,18 @@ static PyObject *AerospikeConfigProvider_new(PyTypeObject *type, PyObject *args, goto error; } - uint32_t interval; + uint32_t interval = AS_CONFIG_PROVIDER_INTERVAL_DEFAULT; if (py_interval) { - interval = convert_pyobject_to_uint32_t(py_interval); + if (!PyLong_Check(py_interval)) { + PyErr_Format(PyExc_TypeError, + "interval must be an uint32_t integer", py_interval); + goto error; + } + PyLong_AsUInt32(py_interval, &interval); if (PyErr_Occurred()) { goto error; } } - else { - interval = AS_CONFIG_PROVIDER_INTERVAL_DEFAULT; - } self->path = strdup(path); self->interval = interval; diff --git a/src/main/conversions.c b/src/main/conversions.c index e869275af0..420e0d2f26 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -1283,14 +1283,12 @@ as_status as_val_new_from_pyobject(AerospikeClient *self, as_error *err, } } else if (PyLong_Check(py_obj)) { - int64_t i = (int64_t)PyLong_AsLongLong(py_obj); - if (i == -1 && PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "integer value exceeds sys.maxsize"); - } + int64_t int_val = convert_long_long_into_int64_t( + err, py_obj, "as_val_new_from_pyobject"); + if (err->code != AEROSPIKE_OK) { + return err->code; } - *val = (as_val *)as_integer_new(i); + *val = (as_val *)as_integer_new(int_val); } else if (PyUnicode_Check(py_obj)) { PyObject *py_ustr = PyUnicode_AsUTF8String(py_obj); @@ -1556,14 +1554,12 @@ as_status pyobject_to_key(as_error *err, PyObject *py_keytuple, as_key *key) Py_DECREF(py_ustr); } else if (PyLong_Check(py_key)) { - int64_t k = (int64_t)PyLong_AsLongLong(py_key); - if (-1 == k && PyErr_Occurred()) { - as_error_update(err, AEROSPIKE_ERR_PARAM, - "integer value for KEY exceeds sys.maxsize"); - } - else { - returnResult = as_key_init_int64(key, ns, set, k); + int64_t int_val = + convert_long_long_into_int64_t(err, py_key, "KEY"); + if (err->code != AEROSPIKE_OK) { + return err->code; } + returnResult = as_key_init_int64(key, ns, set, int_val); } else if (PyByteArray_Check(py_key)) { uint32_t sz = (uint32_t)PyByteArray_Size(py_key); @@ -2220,8 +2216,9 @@ void initialize_bin_for_strictypes(AerospikeClient *self, as_error *err, as_bin *binop_bin = &binop->bin; if (PyLong_Check(py_value)) { - int val = PyLong_AsLong(py_value); - as_integer_init((as_integer *)&binop_bin->value, val); + int64_t int_val = convert_long_long_into_int64_t( + err, py_value, "initialize_bin_for_strictypes"); + as_integer_init((as_integer *)&binop_bin->value, (int64_t)int_val); binop_bin->valuep = &binop_bin->value; } else if (PyUnicode_Check(py_value)) { @@ -2324,23 +2321,19 @@ as_status check_and_set_meta(PyObject *py_meta, uint32_t *ttl_ref, PyObject *py_gen = PyDict_GetItemString(py_meta, "gen"); PyObject *py_ttl = PyDict_GetItemString(py_meta, "ttl"); - uint32_t ttl = 0; - uint16_t gen = 0; if (py_ttl) { if (PyLong_Check(py_ttl)) { - ttl = (uint32_t)PyLong_AsLong(py_ttl); + *ttl_ref = + convert_unsigned_long_into_uint32_t(err, py_ttl, "Ttl"); + + if (err->code != AEROSPIKE_OK) { + return err->code; + } } else { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Ttl should be an int or long"); } - - if ((uint32_t)-1 == ttl && PyErr_Occurred()) { - return as_error_update( - err, AEROSPIKE_ERR_PARAM, - "integer value for ttl exceeds sys.maxsize"); - } - *ttl_ref = ttl; } else { // Metadata dict was present, but ttl field did not exist @@ -2349,20 +2342,16 @@ as_status check_and_set_meta(PyObject *py_meta, uint32_t *ttl_ref, if (py_gen) { if (PyLong_Check(py_gen)) { - // TODO: Needs to check value doesn't go past unsigned 16 bit limit - gen = (uint16_t)PyLong_AsLong(py_gen); + *gen_ref = convert_unsigned_long_into_uint16_t(err, py_gen, + "Generation"); + if (err->code != AEROSPIKE_OK) { + return err->code; + } } else { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Generation should be an int or long"); } - - if ((uint16_t)-1 == gen && PyErr_Occurred()) { - return as_error_update( - err, AEROSPIKE_ERR_PARAM, - "integer value for gen exceeds sys.maxsize"); - } - *gen_ref = gen; } } else if (py_meta && (py_meta != Py_None)) { @@ -2381,10 +2370,11 @@ as_status pyobject_to_index(AerospikeClient *self, as_error *err, { if (PyLong_Check(py_value)) { *long_val = PyLong_AsLong(py_value); - if (*long_val == -1 && PyErr_Occurred() && self->strict_types) { + if (PyErr_Occurred() && self->strict_types) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { return as_error_update(err, AEROSPIKE_ERR_PARAM, - "integer value exceeds sys.maxsize"); + "integer value for pyobject_to_index " + "must be between LONG_MIN and LONG_MAX"); } } } @@ -2663,25 +2653,10 @@ as_status get_int_from_py_int(as_error *err, PyObject *py_long, return as_error_update(err, AEROSPIKE_ERR_PARAM, "%s must be an integer.", py_object_name); } - - long int_to_return = PyLong_AsLong(py_long); - if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "%s too large for C long.", py_object_name); - } - - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "Failed to convert %s.", py_object_name); - } - - if (int_to_return > INT_MAX || int_to_return < INT_MIN) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "%s too large for C int.", py_object_name); + *int_pointer = convert_long_into_int(err, py_long, py_object_name); + if (err->code != AEROSPIKE_OK) { + return err->code; } - - *int_pointer = (int)int_to_return; - return AEROSPIKE_OK; } @@ -2729,11 +2704,38 @@ as_status as_batch_result_to_BatchRecord(AerospikeClient *self, as_error *err, return err->code; } +long long convert_pyobject_to_signed_fixed_width_integer_type( + PyObject *pyobject, long long min_bound, long long max_bound) +{ + if (!PyLong_Check(pyobject)) { + PyErr_Format(PyExc_TypeError, "%S must be an integer", pyobject); + goto error; + } + long long value = PyLong_AsLongLong(pyobject); + if (PyErr_Occurred()) { + goto error; + } + + if (value > max_bound) { + PyErr_Format(PyExc_ValueError, "%S exceeds %lli", pyobject, max_bound); + goto error; + } + + if (value < min_bound) { + PyErr_Format(PyExc_ValueError, "%S exceeds %lli", pyobject, max_bound); + goto error; + } + + return value; + +error: + return -1; +} + // TODO: There's a helper function in the Python client wrapper code called // get_uint32_value, but this can replace it. -unsigned long long -convert_pyobject_to_fixed_width_integer_type(PyObject *pyobject, - unsigned long long max_bound) +unsigned long long convert_pyobject_to_unsigned_fixed_width_integer_type( + PyObject *pyobject, unsigned long long max_bound) { if (!PyLong_Check(pyobject)) { PyErr_Format(PyExc_TypeError, "%S must be an integer", pyobject); @@ -2755,28 +2757,230 @@ convert_pyobject_to_fixed_width_integer_type(PyObject *pyobject, return -1; } -uint8_t convert_pyobject_to_uint8_t(PyObject *pyobject) +uint64_t convert_unsigned_long_long_into_uint64_t(as_error *err, + PyObject *py_long, + const char *component) { - return (uint8_t)convert_pyobject_to_fixed_width_integer_type(pyobject, - UINT8_MAX); + uint64_t long_value = 0; + PyLong_AsUInt64(py_long, &long_value); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between 0 and UINT64_MAX", + component); + } + else { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to uint64_t", component) + } + return 0; + } + return (uint64_t)long_value; } -uint16_t convert_pyobject_to_uint16_t(PyObject *pyobject) +int64_t convert_long_long_into_int64_t(as_error *err, PyObject *py_long, + const char *component) { - return (uint16_t)convert_pyobject_to_fixed_width_integer_type(pyobject, - UINT16_MAX); + int64_t long_value = 0; + PyLong_AsInt64(py_long, &long_value); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between INT64_MIN and INT64_MAX", + component); + } + else { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to int64_t", + component) + } + return 0; + } + return (int64_t)long_value; } -uint32_t convert_pyobject_to_uint32_t(PyObject *pyobject) +uint32_t convert_unsigned_long_into_uint32_t(as_error *err, PyObject *py_long, + const char *component) { - return (uint32_t)convert_pyobject_to_fixed_width_integer_type(pyobject, - UINT32_MAX); + uint32_t long_value = 0; + PyLong_AsUInt32(py_long, &long_value); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between 0 and UINT32_MAX", + component); + } + else { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to uint32_t", component) + } + return 0; + } + return long_value; +} + +int32_t convert_long_into_int32_t(as_error *err, PyObject *py_long, + const char *component) +{ + int32_t long_value = 0; + PyLong_AsInt32(py_long, &long_value); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between INT32_MIN and INT32_MAX", + component); + } + else { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to int32_t", + component) + } + return 0; + } + return long_value; } -uint64_t convert_pyobject_to_uint64_t(PyObject *pyobject) +uint16_t convert_unsigned_long_into_uint16_t(as_error *err, PyObject *py_long, + const char *component) { - return (uint64_t)convert_pyobject_to_fixed_width_integer_type(pyobject, - UINT64_MAX); + unsigned long long_value = PyLong_AsUnsignedLong(py_long); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between 0 and UINT16_MAX", + component); + } + else { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to uint16_t", component) + } + return 0; + } + if (long_value > (unsigned long)UINT16_MAX) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "integer value for %s exceeds UINT16_MAX", component); + return 0; + } + return (uint16_t)long_value; +} + +int16_t convert_long_into_int16_t(as_error *err, PyObject *py_long, + const char *component) +{ + long long_value = PyLong_AsLong(py_long); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between INT16_MIN and INT16_MAX", + component); + } + else { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to int16_t", + component) + } + return 0; + } + if (long_value > (long)INT16_MAX) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "integer value for %s exceeds INT16_MAX", component); + return 0; + } + else if (long_value < (long)INT16_MIN) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "integer value for %s exceeds INT16_MIN", component); + return 0; + } + return (int16_t)long_value; +} + +uint8_t convert_unsigned_long_into_uint8_t(as_error *err, PyObject *py_long, + const char *component) +{ + unsigned long long_value = PyLong_AsUnsignedLong(py_long); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between 0 and UINT8_MAX", + component); + } + else { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to uint8_t", + component) + } + return 0; + } + if (long_value > (unsigned long)UINT8_MAX) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between 0 and UINT8_MAX", + component); + return 0; + } + return (uint8_t)long_value; +} + +int convert_long_into_int(as_error *err, PyObject *py_long, + const char *component) +{ + // CHECK ALL CONVERSION TO LONG VALUE AND MAKE SURE FUNCTION SIGNATURE IS CORRECT + int long_value = PyLong_AsInt(py_long); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between INT_MIN and INT_MAX", + component); + } + else { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to int", + component) + } + return 0; + } + return long_value; +} + +unsigned int convert_unsigned_long_into_enum_value(as_error *err, + PyObject *py_long, + unsigned int max_enum_value, + const char *component) +{ + unsigned long long_value = PyLong_AsUnsignedLong(py_long); + + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s must be between LONG_MIN and LONG_MAX", + component); + } + else { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "Failed to convert integer value for %s to long", + component) + } + return 0; + } + if (long_value > (unsigned long)max_enum_value) { + as_error_update( + err, AEROSPIKE_ERR_PARAM, + "integer value for %s exceeds value for the enumeration", + component); + return 0; + } + return (unsigned int)long_value; } const char *convert_pyobject_to_str(PyObject *py_obj) diff --git a/src/main/convert_expressions.c b/src/main/convert_expressions.c index 8bf1caf07f..2b29a55b9b 100644 --- a/src/main/convert_expressions.c +++ b/src/main/convert_expressions.c @@ -489,12 +489,11 @@ get_exp_val_from_pyval(AerospikeClient *self, as_static_pool *static_pool, tmp_entry; //TODO use as_exp_val((as_val *) bytes); here, might need a cast, not blocker } else if (PyLong_Check(py_obj)) { - int64_t l = (int64_t)PyLong_AsLongLong(py_obj); - if (l == -1 && PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "integer value exceeds sys.maxsize"); - } + int64_t l = convert_long_long_into_int64_t(err, py_obj, + "get_exp_val_from_pyval"); + if (err->code == AEROSPIKE_OK) { + as_exp_entry tmp_entry = as_exp_int(l); + *new_entry = tmp_entry; } as_exp_entry tmp_entry = as_exp_int(l); diff --git a/src/main/convert_partition_filter.c b/src/main/convert_partition_filter.c index a5ff85f8f6..6850075482 100644 --- a/src/main/convert_partition_filter.c +++ b/src/main/convert_partition_filter.c @@ -92,60 +92,44 @@ as_status convert_partition_filter(AerospikeClient *self, goto ERROR_CLEANUP; } - long tmp_begin = 0; + uint16_t tmp_begin = 0; if (begin && PyLong_Check(begin)) { - tmp_begin = PyLong_AsLong(begin); + tmp_begin = (uint16_t)convert_unsigned_long_into_enum_value( + err, begin, CLUSTER_NPARTITIONS - 1, "partition_filter.begin"); + if (err->code != AEROSPIKE_OK) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "invalid partition_filter policy begin, begin must " + "be an int between 0 and %d inclusive", + CLUSTER_NPARTITIONS - 1); + goto ERROR_CLEANUP; + } } else if (begin) { as_error_update(err, AEROSPIKE_ERR_PARAM, - "invalid partition_filter policy begin, begin must \ - be an int between 0 and %d inclusive", - CLUSTER_NPARTITIONS - 1); - goto ERROR_CLEANUP; - } - - if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_OverflowError)) { - as_error_update(err, AEROSPIKE_ERR_PARAM, - "invalid begin for partition id: %d, \ - begin must fit in long", - ps->part_id); - goto ERROR_CLEANUP; - } - - if (tmp_begin >= CLUSTER_NPARTITIONS || tmp_begin < 0) { - as_error_update(err, AEROSPIKE_ERR_PARAM, - "invalid partition_filter policy begin, begin must \ - be an int between 0 and %d inclusive", + "invalid partition_filter policy begin, begin must be " + "an int between 0 and %d inclusive", CLUSTER_NPARTITIONS - 1); goto ERROR_CLEANUP; } filter->begin = tmp_begin; - long tmp_count = CLUSTER_NPARTITIONS; + uint16_t tmp_count = CLUSTER_NPARTITIONS; if (count && PyLong_Check(count)) { - tmp_count = PyLong_AsLong(count); + tmp_count = (uint16_t)convert_unsigned_long_into_enum_value( + err, count, CLUSTER_NPARTITIONS, "partition_filter.begin"); + if (err->code != AEROSPIKE_OK || tmp_count == 0) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + "invalid partition_filter policy count, count must " + "be an int between 1 and %d inclusive", + CLUSTER_NPARTITIONS); + goto ERROR_CLEANUP; + } } else if (count) { as_error_update(err, AEROSPIKE_ERR_PARAM, - "invalid partition_filter policy count, count must \ - be an int between 1 and %d inclusive", - CLUSTER_NPARTITIONS); - goto ERROR_CLEANUP; - } - - if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_OverflowError)) { - as_error_update(err, AEROSPIKE_ERR_PARAM, - "invalid count for partition id: %d, \ - count must fit in long", - ps->part_id); - goto ERROR_CLEANUP; - } - - if (tmp_count > CLUSTER_NPARTITIONS || tmp_count < 1) { - as_error_update(err, AEROSPIKE_ERR_PARAM, - "invalid partition_filter policy count, count must \ - be an int between 1 and %d inclusive", + "invalid partition_filter policy count, count must be " + "an int between 1 and %d inclusive", CLUSTER_NPARTITIONS); goto ERROR_CLEANUP; } @@ -160,13 +144,13 @@ as_status convert_partition_filter(AerospikeClient *self, goto ERROR_CLEANUP; } - filter->digest.init = 0; + filter->digest.init = false; if (digest && PyDict_Check(digest)) { // TODO check these for overflow PyObject *init = PyDict_GetItemString(digest, "init"); - if (init && PyLong_Check(init)) { - filter->digest.init = PyLong_AsLong(init); + if (init && PyBool_Check(init)) { + filter->digest.init = (init == Py_True); } PyObject *value = PyDict_GetItemString(digest, "value"); @@ -191,8 +175,8 @@ as_status convert_partition_filter(AerospikeClient *self, goto ERROR_CLEANUP; } - if (PyLong_Check(py_done)) { - parts_all->done = (bool)PyLong_AsLong(py_done); + if (PyBool_Check(py_done)) { + parts_all->done = (py_done == Py_True); } else { as_error_update(err, AEROSPIKE_ERR_PARAM, @@ -210,8 +194,8 @@ as_status convert_partition_filter(AerospikeClient *self, goto ERROR_CLEANUP; } - if (PyLong_Check(py_retry)) { - parts_all->retry = (bool)PyLong_AsLong(py_retry); + if (PyBool_Check(py_retry)) { + parts_all->retry = (py_retry == Py_True); } else { as_error_update(err, AEROSPIKE_ERR_PARAM, @@ -233,8 +217,8 @@ as_status convert_partition_filter(AerospikeClient *self, } PyObject *init = PyTuple_GetItem(status_dict, 1); - if (init && PyLong_Check(init)) { - ps->digest.init = PyLong_AsLong(init); + if (init && PyBool_Check(init)) { + ps->digest.init = (init == Py_True); } else if (init) { as_error_update(err, AEROSPIKE_ERR_PARAM, @@ -243,8 +227,8 @@ as_status convert_partition_filter(AerospikeClient *self, } PyObject *retry = PyTuple_GetItem(status_dict, 2); - if (retry && PyLong_Check(retry)) { - ps->retry = (bool)PyLong_AsLong(retry); + if (retry && PyBool_Check(retry)) { + ps->retry = (retry == Py_True); } else if (retry) { as_error_update(err, AEROSPIKE_ERR_PARAM, @@ -274,13 +258,9 @@ as_status convert_partition_filter(AerospikeClient *self, } if (py_bval && PyLong_Check(py_bval)) { - ps->bval = PyLong_AsUnsignedLongLong(py_bval); - if (PyErr_Occurred() && - PyErr_ExceptionMatches(PyExc_OverflowError)) { - as_error_update(err, AEROSPIKE_ERR_PARAM, - "invalid bval for partition id: %d, bval " - "must fit in unsigned long long", - ps->part_id); + ps->bval = convert_unsigned_long_long_into_uint64_t( + err, py_bval, "partition_status.bval"); + if (err->code != AEROSPIKE_OK) { goto ERROR_CLEANUP; } } diff --git a/src/main/log.c b/src/main/log.c index ba646cae1c..9d9e28256e 100644 --- a/src/main/log.c +++ b/src/main/log.c @@ -122,12 +122,18 @@ PyObject *Aerospike_Set_Log_Level(PyObject *parent, PyObject *args, } long log_level = PyLong_AsLong(py_log_level); - if (log_level == -1 && PyErr_Occurred()) { + if (PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + as_error_update( + &err, AEROSPIKE_ERR_PARAM, + "integer value for Aerospike_Set_Log_Level exceeds ULLONG_MAX"); + } + else { as_error_update(&err, AEROSPIKE_ERR_PARAM, - "integer value exceeds sys.maxsize"); - goto CLEANUP; + "Failed to convert integer value for " + "Aerospike_Set_Log_Level to unsigned long long") } + goto CLEANUP; } is_current_log_level_off = log_level == LOG_LEVEL_OFF; diff --git a/src/main/policy.c b/src/main/policy.c index f80cf2e9c0..60632416b9 100644 --- a/src/main/policy.c +++ b/src/main/policy.c @@ -96,6 +96,158 @@ } \ } +#define POLICY_SET_FIELD_UINT_64T(__field) \ + { \ + PyObject *py_field_name = PyUnicode_FromString(#__field); \ + if (py_field_name == NULL) { \ + PyErr_Clear(); \ + return as_error_update(err, AEROSPIKE_ERR_CLIENT, \ + "Unable to create Python unicode object"); \ + } \ + PyObject *py_field = \ + PyDict_GetItemWithError(py_policy, py_field_name); \ + if (py_field == NULL && PyErr_Occurred()) { \ + PyErr_Clear(); \ + Py_DECREF(py_field_name); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch field from policy dictionary"); \ + } \ + Py_DECREF(py_field_name); \ + \ + if (py_field) { \ + if (PyLong_Check(py_field)) { \ + uint64_t field_val = convert_unsigned_long_long_into_uint64_t( \ + err, py_field, "POLICY_SET_FIELD"); \ + if (err->code != AEROSPIKE_OK) { \ + PyErr_Clear(); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch long value from policy field"); \ + } \ + policy->__field = field_val; \ + } \ + else { \ + return as_error_update(err, AEROSPIKE_ERR_PARAM, \ + "%s is invalid", #__field); \ + } \ + } \ + } + +#define POLICY_SET_FIELD_UINT_32T(__field) \ + { \ + PyObject *py_field_name = PyUnicode_FromString(#__field); \ + if (py_field_name == NULL) { \ + PyErr_Clear(); \ + return as_error_update(err, AEROSPIKE_ERR_CLIENT, \ + "Unable to create Python unicode object"); \ + } \ + PyObject *py_field = \ + PyDict_GetItemWithError(py_policy, py_field_name); \ + if (py_field == NULL && PyErr_Occurred()) { \ + PyErr_Clear(); \ + Py_DECREF(py_field_name); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch field from policy dictionary"); \ + } \ + Py_DECREF(py_field_name); \ + \ + if (py_field) { \ + if (PyLong_Check(py_field)) { \ + uint32_t field_val = convert_unsigned_long_into_uint32_t( \ + err, py_field, "POLICY_SET_FIELD"); \ + if (err->code != AEROSPIKE_OK) { \ + PyErr_Clear(); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch long value from policy field"); \ + } \ + policy->__field = field_val; \ + } \ + else { \ + return as_error_update(err, AEROSPIKE_ERR_PARAM, \ + "%s is invalid", #__field); \ + } \ + } \ + } + +#define POLICY_SET_FIELD_UINT_16T(__field) \ + { \ + PyObject *py_field_name = PyUnicode_FromString(#__field); \ + if (py_field_name == NULL) { \ + PyErr_Clear(); \ + return as_error_update(err, AEROSPIKE_ERR_CLIENT, \ + "Unable to create Python unicode object"); \ + } \ + PyObject *py_field = \ + PyDict_GetItemWithError(py_policy, py_field_name); \ + if (py_field == NULL && PyErr_Occurred()) { \ + PyErr_Clear(); \ + Py_DECREF(py_field_name); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch field from policy dictionary"); \ + } \ + Py_DECREF(py_field_name); \ + \ + if (py_field) { \ + if (PyLong_Check(py_field)) { \ + uint16_t field_val = convert_unsigned_long_into_uint16_t( \ + err, py_field, "POLICY_SET_FIELD"); \ + if (err->code != AEROSPIKE_OK) { \ + PyErr_Clear(); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch long value from policy field"); \ + } \ + policy->__field = field_val; \ + } \ + else { \ + return as_error_update(err, AEROSPIKE_ERR_PARAM, \ + "%s is invalid", #__field); \ + } \ + } \ + } + +#define POLICY_SET_FIELD_ENUM(__field, __max) \ + { \ + PyObject *py_field_name = PyUnicode_FromString(#__field); \ + if (py_field_name == NULL) { \ + PyErr_Clear(); \ + return as_error_update(err, AEROSPIKE_ERR_CLIENT, \ + "Unable to create Python unicode object"); \ + } \ + PyObject *py_field = \ + PyDict_GetItemWithError(py_policy, py_field_name); \ + if (py_field == NULL && PyErr_Occurred()) { \ + PyErr_Clear(); \ + Py_DECREF(py_field_name); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch field from policy dictionary"); \ + } \ + Py_DECREF(py_field_name); \ + \ + if (py_field) { \ + if (PyLong_Check(py_field)) { \ + uint16_t field_val = convert_unsigned_long_into_enum_value( \ + err, py_field, __max, "POLICY_SET_FIELD"); \ + if (err->code != AEROSPIKE_OK) { \ + PyErr_Clear(); \ + return as_error_update( \ + err, AEROSPIKE_ERR_CLIENT, \ + "Unable to fetch long value from policy field"); \ + } \ + policy->__field = field_val; \ + } \ + else { \ + return as_error_update(err, AEROSPIKE_ERR_PARAM, \ + "%s is invalid", #__field); \ + } \ + } \ + } + #define POLICY_SET_EXPRESSIONS_FIELD() \ { \ PyObject *py_field_name = PyUnicode_FromString("expressions"); \ @@ -264,7 +416,7 @@ as_status pyobject_to_policy_admin(AerospikeClient *self, as_error *err, } // Set policy fields - POLICY_SET_FIELD(timeout, uint32_t); + POLICY_SET_FIELD_UINT_32T(timeout); } // Update the policy POLICY_UPDATE(); @@ -315,13 +467,13 @@ static inline as_status pyobject_to_policy_base(AerospikeClient *self, as_policy_base *policy, as_exp **exp_list_p) { - POLICY_SET_FIELD(total_timeout, uint32_t); - POLICY_SET_FIELD(socket_timeout, uint32_t); - POLICY_SET_FIELD(timeout_delay, uint32_t); - POLICY_SET_FIELD(max_retries, uint32_t); - POLICY_SET_FIELD(sleep_between_retries, uint32_t); + POLICY_SET_FIELD_UINT_32T(total_timeout); + POLICY_SET_FIELD_UINT_32T(socket_timeout); + POLICY_SET_FIELD_UINT_32T(timeout_delay); + POLICY_SET_FIELD_UINT_32T(max_retries); + POLICY_SET_FIELD_UINT_32T(sleep_between_retries); POLICY_SET_FIELD(compress, bool); - POLICY_SET_FIELD(connect_timeout, uint32_t); + POLICY_SET_FIELD_UINT_32T(connect_timeout); // Setting txn field to a non-NULL value in a query or scan policy is a no-op, // so this is safe to call for a scan/query policy's base policy @@ -375,12 +527,12 @@ as_status pyobject_to_policy_apply(AerospikeClient *self, as_error *err, return retval; } - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(replica, as_policy_replica); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); //POLICY_SET_FIELD(gen, as_policy_gen); removed - POLICY_SET_FIELD(commit_level, as_policy_commit_level); + POLICY_SET_FIELD_ENUM(commit_level, AS_POLICY_COMMIT_LEVEL_MASTER); POLICY_SET_FIELD(durable_delete, bool); - POLICY_SET_FIELD(ttl, uint32_t); + POLICY_SET_FIELD_UINT_32T(ttl); POLICY_SET_FIELD(on_locking_only, bool); } @@ -438,7 +590,7 @@ pyobject_to_policy_info(as_error *err, PyObject *py_policy, } // Set policy fields - POLICY_SET_FIELD(timeout, uint32_t); + POLICY_SET_FIELD_UINT_32T(timeout); // POLICY_SET_FIELD(timeout_delay, uint32_t); POLICY_SET_FIELD(send_as_is, bool); POLICY_SET_FIELD(check_bounds, bool); @@ -490,12 +642,13 @@ as_status pyobject_to_policy_query(AerospikeClient *self, as_error *err, return retval; } POLICY_SET_FIELD(deserialize, bool); - POLICY_SET_FIELD(replica, as_policy_replica); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); // C client 6.0.0 POLICY_SET_FIELD(short_query, bool); - POLICY_SET_FIELD(expected_duration, as_query_duration); + POLICY_SET_FIELD_ENUM(expected_duration, + AS_QUERY_DURATION_LONG_RELAX_AP); } // Update the policy @@ -546,14 +699,15 @@ as_status pyobject_to_policy_read(AerospikeClient *self, as_error *err, return retval; } - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(replica, as_policy_replica); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); POLICY_SET_FIELD(deserialize, bool); POLICY_SET_FIELD(read_touch_ttl_percent, int); // 4.0.0 new policies - POLICY_SET_FIELD(read_mode_ap, as_policy_read_mode_ap); - POLICY_SET_FIELD(read_mode_sc, as_policy_read_mode_sc); + POLICY_SET_FIELD_ENUM(read_mode_ap, AS_POLICY_READ_MODE_AP_ALL); + POLICY_SET_FIELD_ENUM(read_mode_sc, + AS_POLICY_READ_MODE_SC_ALLOW_UNAVAILABLE); } // Update the policy @@ -604,12 +758,12 @@ as_status pyobject_to_policy_remove(AerospikeClient *self, as_error *err, return retval; } - POLICY_SET_FIELD(generation, uint16_t); + POLICY_SET_FIELD_UINT_16T(generation); - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(gen, as_policy_gen); - POLICY_SET_FIELD(commit_level, as_policy_commit_level); - POLICY_SET_FIELD(replica, as_policy_replica); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(gen, AS_POLICY_GEN_GT); + POLICY_SET_FIELD_ENUM(commit_level, AS_POLICY_COMMIT_LEVEL_MASTER); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); POLICY_SET_FIELD(durable_delete, bool); } @@ -669,10 +823,10 @@ pyobject_to_policy_scan(AerospikeClient *self, as_error *err, } POLICY_SET_FIELD(durable_delete, bool); - POLICY_SET_FIELD(records_per_second, uint32_t); - POLICY_SET_FIELD(max_records, uint64_t); - POLICY_SET_FIELD(replica, as_policy_replica); - POLICY_SET_FIELD(ttl, uint32_t); + POLICY_SET_FIELD_UINT_32T(records_per_second); + POLICY_SET_FIELD_UINT_64T(max_records); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); + POLICY_SET_FIELD_UINT_32T(ttl); } // Update the policy @@ -729,14 +883,14 @@ as_status pyobject_to_policy_write( return retval; } - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(gen, as_policy_gen); - POLICY_SET_FIELD(exists, as_policy_exists); - POLICY_SET_FIELD(commit_level, as_policy_commit_level); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(gen, AS_POLICY_GEN_GT); + POLICY_SET_FIELD_ENUM(exists, AS_POLICY_EXISTS_CREATE_OR_REPLACE); + POLICY_SET_FIELD_ENUM(commit_level, AS_POLICY_COMMIT_LEVEL_MASTER); POLICY_SET_FIELD(durable_delete, bool); - POLICY_SET_FIELD(replica, as_policy_replica); - POLICY_SET_FIELD(ttl, uint32_t); - POLICY_SET_FIELD(compression_threshold, uint32_t); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); + POLICY_SET_FIELD_UINT_32T(ttl); + POLICY_SET_FIELD_UINT_32T(compression_threshold); POLICY_SET_FIELD(on_locking_only, bool); } @@ -787,20 +941,21 @@ as_status pyobject_to_policy_operate(AerospikeClient *self, as_error *err, return retval; } - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(gen, as_policy_gen); - POLICY_SET_FIELD(commit_level, as_policy_commit_level); - POLICY_SET_FIELD(replica, as_policy_replica); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(gen, AS_POLICY_GEN_GT); + POLICY_SET_FIELD_ENUM(commit_level, AS_POLICY_COMMIT_LEVEL_MASTER); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); POLICY_SET_FIELD(durable_delete, bool); POLICY_SET_FIELD(deserialize, bool); - POLICY_SET_FIELD(exists, as_policy_exists); + POLICY_SET_FIELD_ENUM(exists, AS_POLICY_EXISTS_CREATE_OR_REPLACE); POLICY_SET_FIELD(read_touch_ttl_percent, int); POLICY_SET_FIELD(on_locking_only, bool); - POLICY_SET_FIELD(ttl, uint32_t); + POLICY_SET_FIELD_UINT_32T(ttl); // 4.0.0 new policies - POLICY_SET_FIELD(read_mode_ap, as_policy_read_mode_ap); - POLICY_SET_FIELD(read_mode_sc, as_policy_read_mode_sc); + POLICY_SET_FIELD_ENUM(read_mode_ap, AS_POLICY_READ_MODE_AP_ALL); + POLICY_SET_FIELD_ENUM(read_mode_sc, + AS_POLICY_READ_MODE_SC_ALLOW_UNAVAILABLE); } // Update the policy @@ -852,12 +1007,13 @@ as_status pyobject_to_policy_batch(AerospikeClient *self, as_error *err, POLICY_SET_FIELD(concurrent, bool); POLICY_SET_FIELD(allow_inline, bool); POLICY_SET_FIELD(deserialize, bool); - POLICY_SET_FIELD(replica, as_policy_replica); + POLICY_SET_FIELD_ENUM(replica, AS_POLICY_REPLICA_RANDOM); POLICY_SET_FIELD(read_touch_ttl_percent, int); // 4.0.0 new policies - POLICY_SET_FIELD(read_mode_ap, as_policy_read_mode_ap); - POLICY_SET_FIELD(read_mode_sc, as_policy_read_mode_sc); + POLICY_SET_FIELD_ENUM(read_mode_ap, AS_POLICY_READ_MODE_AP_ALL); + POLICY_SET_FIELD_ENUM(read_mode_sc, + AS_POLICY_READ_MODE_SC_ALLOW_UNAVAILABLE); // C client 6.0.0 (batch writes) POLICY_SET_FIELD(allow_inline_ssd, bool); @@ -894,13 +1050,13 @@ as_status pyobject_to_batch_write_policy(AerospikeClient *self, as_error *err, } // Set policy fields - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(commit_level, as_policy_commit_level); - POLICY_SET_FIELD(gen, as_policy_gen); - POLICY_SET_FIELD(exists, as_policy_exists); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(commit_level, AS_POLICY_COMMIT_LEVEL_MASTER); + POLICY_SET_FIELD_ENUM(gen, AS_POLICY_GEN_GT); + POLICY_SET_FIELD_ENUM(exists, AS_POLICY_EXISTS_CREATE_OR_REPLACE); POLICY_SET_FIELD(durable_delete, bool); POLICY_SET_FIELD(on_locking_only, bool); - POLICY_SET_FIELD(ttl, uint32_t); + POLICY_SET_FIELD_UINT_32T(ttl); // C client 5.0 new expressions POLICY_SET_EXPRESSIONS_FIELD(); @@ -934,8 +1090,9 @@ as_status pyobject_to_batch_read_policy(AerospikeClient *self, as_error *err, } // Set policy fields - POLICY_SET_FIELD(read_mode_ap, as_policy_read_mode_ap); - POLICY_SET_FIELD(read_mode_sc, as_policy_read_mode_sc); + POLICY_SET_FIELD_ENUM(read_mode_ap, AS_POLICY_READ_MODE_AP_ALL); + POLICY_SET_FIELD_ENUM(read_mode_sc, + AS_POLICY_READ_MODE_SC_ALLOW_UNAVAILABLE); POLICY_SET_FIELD(read_touch_ttl_percent, int); // C client 5.0 new expressions @@ -970,9 +1127,9 @@ as_status pyobject_to_batch_apply_policy(AerospikeClient *self, as_error *err, } // Set policy fields - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(commit_level, as_policy_commit_level); - POLICY_SET_FIELD(ttl, uint32_t); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(commit_level, AS_POLICY_COMMIT_LEVEL_MASTER); + POLICY_SET_FIELD_UINT_32T(ttl); POLICY_SET_FIELD(durable_delete, bool); POLICY_SET_FIELD(on_locking_only, bool); @@ -1008,11 +1165,11 @@ as_status pyobject_to_batch_remove_policy(AerospikeClient *self, as_error *err, } // Set policy fields - POLICY_SET_FIELD(key, as_policy_key); - POLICY_SET_FIELD(commit_level, as_policy_commit_level); - POLICY_SET_FIELD(gen, as_policy_gen); + POLICY_SET_FIELD_ENUM(key, AS_POLICY_KEY_SEND); + POLICY_SET_FIELD_ENUM(commit_level, AS_POLICY_COMMIT_LEVEL_MASTER); + POLICY_SET_FIELD_ENUM(gen, AS_POLICY_GEN_GT); POLICY_SET_FIELD(durable_delete, bool); - POLICY_SET_FIELD(generation, uint16_t); + POLICY_SET_FIELD_UINT_16T(generation); // C client 5.0 new expressions POLICY_SET_EXPRESSIONS_FIELD(); @@ -1536,11 +1693,15 @@ int set_as_metrics_policy_using_pyobject(as_error *err, report_size_limit_attr_name); goto error; } - - uint64_t report_size_limit = - convert_pyobject_to_uint64_t(py_report_size_limit); + if (!PyLong_Check(py_report_size_limit)) { + as_error_update(err, AEROSPIKE_ERR_PARAM, INVALID_ATTR_TYPE_ERROR_MSG, + report_size_limit_attr_name, "unsigned 64-bit integer"); + goto error; + } + uint64_t report_size_limit = convert_unsigned_long_long_into_uint64_t( + err, py_report_size_limit, "report_size_limit"); Py_DECREF(py_report_size_limit); - if (PyErr_Occurred()) { + if (err->code != AEROSPIKE_OK) { as_error_update(err, AEROSPIKE_ERR_PARAM, INVALID_ATTR_TYPE_ERROR_MSG, report_size_limit_attr_name, "unsigned 64-bit integer"); goto error; @@ -1555,10 +1716,15 @@ int set_as_metrics_policy_using_pyobject(as_error *err, interval_field_name); goto error; } - - uint32_t interval = convert_pyobject_to_uint32_t(py_interval); + if (!PyLong_Check(py_interval)) { + as_error_update(err, AEROSPIKE_ERR_PARAM, INVALID_ATTR_TYPE_ERROR_MSG, + interval_field_name, "unsigned 32-bit integer"); + goto error; + } + uint32_t interval = + convert_unsigned_long_into_uint32_t(err, py_interval, "interval"); Py_DECREF(py_interval); - if (PyErr_Occurred()) { + if (err->code != AEROSPIKE_OK) { as_error_update(err, AEROSPIKE_ERR_PARAM, INVALID_ATTR_TYPE_ERROR_MSG, interval_field_name, "unsigned 32-bit integer"); goto error; @@ -1577,10 +1743,16 @@ int set_as_metrics_policy_using_pyobject(as_error *err, uint8_field_names[i]); goto error; } - - uint8_t attr_value = convert_pyobject_to_uint8_t(py_attr_value); + if (!PyLong_Check(py_attr_value)) { + as_error_update(err, AEROSPIKE_ERR_PARAM, + INVALID_ATTR_TYPE_ERROR_MSG, uint8_field_names[i], + "unsigned 8-bit integer"); + goto error; + } + uint8_t attr_value = + convert_unsigned_long_into_uint8_t(err, py_attr_value, "latency"); Py_DECREF(py_attr_value); - if (PyErr_Occurred()) { + if (err->code != AEROSPIKE_OK) { as_error_update(err, AEROSPIKE_ERR_PARAM, INVALID_ATTR_TYPE_ERROR_MSG, uint8_field_names[i], "unsigned 8-bit integer"); diff --git a/src/main/policy_config.c b/src/main/policy_config.c index 6a76b45818..4b9195a8f3 100644 --- a/src/main/policy_config.c +++ b/src/main/policy_config.c @@ -16,6 +16,7 @@ #include #include "policy_config.h" +#include "conversions.h" #include "types.h" #include "policy.h" @@ -961,25 +962,16 @@ as_status set_base_policy(as_policy_base *base_policy, PyObject *py_policy) as_status get_uint32_value(PyObject *py_policy_val, uint32_t *return_uint32) { - long long int uint32_max = 0xFFFFFFFF; + as_error err; + as_error_init(&err); if (!py_policy_val) { return AEROSPIKE_ERR_PARAM; } if (PyLong_Check(py_policy_val)) { - long int_value = PyLong_AsLong(py_policy_val); - - if (int_value == -1 && PyErr_Occurred()) { - PyErr_Clear(); - return AEROSPIKE_ERR_PARAM; - } - - if (int_value < 0 || int_value > uint32_max) { - return AEROSPIKE_ERR_PARAM; - } - - *return_uint32 = (uint32_t)int_value; - return AEROSPIKE_OK; + *return_uint32 = convert_unsigned_long_into_uint32_t( + &err, py_policy_val, "get_uint32_value"); + return err.code; } return AEROSPIKE_ERR_PARAM; } @@ -987,8 +979,10 @@ as_status get_uint32_value(PyObject *py_policy_val, uint32_t *return_uint32) as_status set_optional_uint32_property(uint32_t *target_ptr, PyObject *py_policy, const char *name) { + as_error err; + as_error_init(&err); + PyObject *py_policy_val = NULL; - long long int uint32_max = 0xFFFFFFFF; if (!py_policy || !PyDict_Check(py_policy)) { return AEROSPIKE_OK; } @@ -998,22 +992,9 @@ as_status set_optional_uint32_property(uint32_t *target_ptr, return AEROSPIKE_OK; } if (PyLong_Check(py_policy_val)) { - long int_value = PyLong_AsLong(py_policy_val); - - if (int_value == -1 && PyErr_Occurred()) { - // This wasn't a valid int, or was too large - // We are handling the error ourselves, so clear the overflow error - PyErr_Clear(); - return AEROSPIKE_ERR_PARAM; - - /* If the number was less than zero, or would not fit in a uint32, error */ - } - if (int_value < 0 || int_value > uint32_max) { - return AEROSPIKE_ERR_PARAM; - } - - *target_ptr = (uint32_t)int_value; - return AEROSPIKE_OK; + *target_ptr = convert_unsigned_long_into_uint32_t(&err, py_policy_val, + "get_uint32_value"); + return err.code; } return AEROSPIKE_ERR_PARAM; } @@ -1021,6 +1002,8 @@ as_status set_optional_uint32_property(uint32_t *target_ptr, as_status set_optional_uint16_property(uint16_t *target_ptr, PyObject *py_policy, const char *name) { + as_error err; + as_error_init(&err); // Assume py_policy is a Python dictionary PyObject *py_policy_val = PyDict_GetItemString(py_policy, name); if (!py_policy_val) { @@ -1033,21 +1016,9 @@ as_status set_optional_uint16_property(uint16_t *target_ptr, return AEROSPIKE_ERR_PARAM; } - long int_value = PyLong_AsLong(py_policy_val); - if (int_value == -1 && PyErr_Occurred()) { - // This wasn't a valid int, or was too large - // We are handling the error ourselves, so clear the overflow error - PyErr_Clear(); - return AEROSPIKE_ERR_PARAM; - - /* If the number was less than zero, or would not fit in a uint16, error */ - } - if (int_value < 0 || int_value > UINT16_MAX) { - return AEROSPIKE_ERR_PARAM; - } - - *target_ptr = (uint16_t)int_value; - return AEROSPIKE_OK; + *target_ptr = convert_unsigned_long_into_uint16_t(&err, py_policy_val, + "get_uint32_value"); + return err.code; } as_status set_optional_bool_property(bool *target_ptr, PyObject *py_policy, @@ -1226,10 +1197,17 @@ as_status set_optional_exists(as_policy_exists *target_ptr, PyObject *py_policy, as_status set_optional_int_property(int *property_ptr, PyObject *py_policy, const char *field_name) { + as_error err; + as_error_init(&err); + PyObject *py_field = PyDict_GetItemString(py_policy, field_name); if (py_field) { if (PyLong_Check(py_field)) { - *property_ptr = (int)PyLong_AsLong(py_field); + *property_ptr = convert_long_into_int(&err, py_field, + "set_optional_int_property"); + if (err.code != AEROSPIKE_OK) { + return err.code; + } } else { return AEROSPIKE_ERR_PARAM; diff --git a/src/main/query/where.c b/src/main/query/where.c index f9eb0e37d5..beb523cec5 100644 --- a/src/main/query/where.c +++ b/src/main/query/where.c @@ -31,17 +31,6 @@ #undef TRACE #define TRACE() -// TODO: replace with helper function from conversions.c -int64_t pyobject_to_int64(PyObject *py_obj) -{ - if (PyLong_Check(py_obj)) { - return PyLong_AsLongLong(py_obj); - } - else { - return 0; - } -} - #define CTX_PARSE_ERROR_MESSAGE "Unable to parse ctx" // py_bin, py_val1, pyval2 are guaranteed to be non-NULL @@ -158,16 +147,21 @@ static int AerospikeQuery_Where_Add(AerospikeQuery *self, PyObject *py_ctx, val1 = (void *)val1_str; } else if (in_datatype == AS_INDEX_NUMERIC) { - val1_int = pyobject_to_int64(py_val1); - if (PyErr_Occurred()) { - PyErr_Clear(); - val1_int = 0; + + if (PyLong_Check(py_val1)) { + val1_int = convert_long_long_into_int64_t( + &err, py_val1, "query where numeric val1"); + if (err.code != AEROSPIKE_OK) { + PyErr_Clear(); + val1_int = 0; + } } val1 = (void *)val1_int; if (PyLong_Check(py_val2)) { - val2_int = pyobject_to_int64(py_val2); - if (PyErr_Occurred()) { + val2_int = convert_long_long_into_int64_t( + &err, py_val2, "query where numeric val2"); + if (err.code != AEROSPIKE_OK) { PyErr_Clear(); val2_int = 0; } diff --git a/src/main/transaction/type.c b/src/main/transaction/type.c index 61f2b00c14..3098401b57 100644 --- a/src/main/transaction/type.c +++ b/src/main/transaction/type.c @@ -1,5 +1,5 @@ #include - +#include "pythoncapi_compat.h" #include "types.h" #include "conversions.h" @@ -43,26 +43,33 @@ static int AerospikeTransaction_init(AerospikeTransaction *self, PyObject *args, } as_txn *txn; - uint32_t reads_capacity, writes_capacity; + uint32_t reads_capacity = AS_TXN_READ_CAPACITY_DEFAULT; + uint32_t writes_capacity = AS_TXN_WRITE_CAPACITY_DEFAULT; if (py_reads_capacity) { - reads_capacity = convert_pyobject_to_uint32_t(py_reads_capacity); + if (!PyLong_Check(py_reads_capacity)) { + PyErr_Format(PyExc_TypeError, + "reads_capacity must be an uint32_t integer", + reads_capacity); + goto error; + } + PyLong_AsUInt32(py_reads_capacity, &reads_capacity); if (PyErr_Occurred()) { goto error; } } - else { - reads_capacity = AS_TXN_READ_CAPACITY_DEFAULT; - } if (py_writes_capacity) { - writes_capacity = convert_pyobject_to_uint32_t(py_writes_capacity); + if (!PyLong_Check(py_writes_capacity)) { + PyErr_Format(PyExc_TypeError, + "writes_capacity must be an uint32_t integer", + writes_capacity); + goto error; + } + PyLong_AsUInt32(py_writes_capacity, &writes_capacity); if (PyErr_Occurred()) { goto error; } } - else { - writes_capacity = AS_TXN_WRITE_CAPACITY_DEFAULT; - } txn = as_txn_create_capacity(reads_capacity, writes_capacity); @@ -111,8 +118,9 @@ static PyObject *AerospikeTransaction_get_timeout(AerospikeTransaction *self, static int AerospikeTransaction_set_timeout(AerospikeTransaction *self, PyObject *py_value, void *closure) { - uint32_t timeout = (uint32_t)convert_pyobject_to_fixed_width_integer_type( - py_value, UINT32_MAX); + uint32_t timeout = + (uint32_t)convert_pyobject_to_unsigned_fixed_width_integer_type( + py_value, UINT32_MAX); if (PyErr_Occurred()) { return -1; } diff --git a/test/new_tests/test_append.py b/test/new_tests/test_append.py index bf0a4cbbef..ad1eb1b153 100644 --- a/test/new_tests/test_append.py +++ b/test/new_tests/test_append.py @@ -419,7 +419,6 @@ def test_neg_append_with_low_timeout(self): """ key = ("test", "demo", 1) policy = { - "gen": 5, "total_timeout": 1, # 'retry': aerospike.POLICY_RETRY_ONCE, "commit_level": aerospike.POLICY_COMMIT_LEVEL_MASTER, diff --git a/test/new_tests/test_bool_config.py b/test/new_tests/test_bool_config.py index 855133c5bc..e74d88c1ea 100644 --- a/test/new_tests/test_bool_config.py +++ b/test/new_tests/test_bool_config.py @@ -3,6 +3,7 @@ import random from aerospike import exception as e from aerospike_helpers.operations import operations as operation +from .as_status_codes import AerospikeStatus from .test_base_class import TestBaseClass import aerospike @@ -37,8 +38,6 @@ def setup(self, request, as_connection): [ (aerospike.INTEGER, 1, 0), (aerospike.AS_BOOL, True, False), - (100, True, False), - (-1, True, False), ], ) def test_bool_read_write_pos(self, send_bool_as, expected_true, expected_false): @@ -67,3 +66,41 @@ def test_bool_read_write_pos(self, send_bool_as, expected_true, expected_false): assert bins["cfg_false"] is expected_false assert bins[self.true_bin] is True assert bins[self.false_bin] is False + + @pytest.mark.parametrize( + "send_bool_as, expected_true, expected_false", + [ + (100, True, False), + ], + ) + def test_bool_read_write_neg_fail_with_pos_integer(self, send_bool_as, expected_true, expected_false): + """ + Write Python bools with different client configurations. + """ + config = TestBaseClass.get_connection_config() + config["send_bool_as"] = send_bool_as + + with pytest.raises(e.ParamError) as err_info: + test_client = aerospike.client(config).connect(config["user"], config["password"]) + + assert err_info.value.code == AerospikeStatus.AEROSPIKE_ERR_PARAM + assert err_info.value.msg == "integer value for config.policy.send_bool_as exceeds value for the enumeration" + + @pytest.mark.parametrize( + "send_bool_as, expected_true, expected_false", + [ + (-1, True, False), + ], + ) + def test_bool_read_write_neg_fail_with_neg_integer(self, send_bool_as, expected_true, expected_false): + """ + Write Python bools with different client configurations. + """ + config = TestBaseClass.get_connection_config() + config["send_bool_as"] = send_bool_as + + with pytest.raises(e.ParamError) as err_info: + test_client = aerospike.client(config).connect(config["user"], config["password"]) + + assert err_info.value.code == AerospikeStatus.AEROSPIKE_ERR_PARAM + assert err_info.value.msg == "integer value for config.policy.send_bool_as must be between LONG_MIN and LONG_MAX" diff --git a/test/new_tests/test_get_put.py b/test/new_tests/test_get_put.py index 628078a5bd..5692903f0e 100644 --- a/test/new_tests/test_get_put.py +++ b/test/new_tests/test_get_put.py @@ -824,15 +824,12 @@ def test_edge_put_with_integer_greater_than_maxisze(self): key = ("test", "demo", 1) bins = {"no": 111111111111111111111111111111111111111111111} - - try: + + with pytest.raises(e.ParamError) as err_info: assert 0 == self.as_connection.put(key, bins) - except e.ParamError as exception: - assert exception.code == -2 - assert exception.msg == "integer value exceeds sys.maxsize" - except SystemError: - pass + assert err_info.value.code == -2 + assert err_info.value.msg == "integer value for as_val_new_from_pyobject must be between INT64_MIN and INT64_MAX" def test_edge_put_with_key_as_an_integer_greater_than_maxsize(self): """ @@ -842,9 +839,12 @@ def test_edge_put_with_key_as_an_integer_greater_than_maxsize(self): bins = {"no": 11} - with pytest.raises(e.ParamError): + with pytest.raises(e.ParamError) as err_info: assert 0 == self.as_connection.put(key, bins) + assert err_info.value.code == -2 + assert err_info.value.msg == "integer value for KEY must be between INT64_MIN and INT64_MAX" + def test_unhashable_type_with_put_get(self): """ Invoke put() with bytes which used to be read as bytearray off server diff --git a/test/new_tests/test_invalid_conf.py b/test/new_tests/test_invalid_conf.py index 1e91865460..7dd074f3ae 100644 --- a/test/new_tests/test_invalid_conf.py +++ b/test/new_tests/test_invalid_conf.py @@ -4,6 +4,8 @@ import aerospike from .test_base_class import TestBaseClass +import sys + # This is for test_validate_keys # INVALID_OPTION_KEY cannot be read for the client-config policies test case as a class variable, # so these were made global @@ -60,7 +62,13 @@ def test_non_callable_deserializer(self): def test_negative_threshold_value(self): with pytest.raises(e.ParamError) as err: aerospike.client({"hosts": [("localhost", 3000)], "compression_threshold": -1}) - assert "Compression value must not be negative" in err.value.msg + # Starting in 3.14, Python does not return Overflow error, causing a different error message to return + if sys.version_info >= (3, 14): + # expect ValueError + assert "Failed to convert integer value for config.policy.write.compression_threshold to uint32_t" in err.value.msg + else: + # expect OverflowError + assert "integer value for config.policy.write.compression_threshold must be between 0 and UINT32_MAX" in err.value.msg @pytest.mark.parametrize("policy", ["read", "write", "operate", "batch", "scan", "query", "apply", "remove"]) @pytest.mark.parametrize( diff --git a/test/new_tests/test_metrics.py b/test/new_tests/test_metrics.py index 9dd264ced9..499471ab53 100644 --- a/test/new_tests/test_metrics.py +++ b/test/new_tests/test_metrics.py @@ -349,7 +349,10 @@ def test_enable_listener_throwing_exception(self, enable_callback: callable, err def test_metrics_policy_invalid_args(self, policy, field_name, expected_field_type): with pytest.raises(e.ParamError) as excinfo: self.as_connection.enable_metrics(policy=policy) - assert excinfo.value.msg == f"MetricsPolicy.{field_name} must be a {expected_field_type} type" + if 2**8 == policy.latency_shift: + assert excinfo.value.msg == f"MetricsPolicy.latency_shift must be a unsigned 8-bit integer type -> integer value for latency must be between 0 and UINT8_MAX" + else: + assert excinfo.value.msg == f"MetricsPolicy.{field_name} must be a {expected_field_type} type" def test_metrics_policy_report_dir_too_long(self): policy = MetricsPolicy( diff --git a/test/new_tests/test_mrt_api.py b/test/new_tests/test_mrt_api.py index b53abd3d63..b1e4b313a4 100644 --- a/test/new_tests/test_mrt_api.py +++ b/test/new_tests/test_mrt_api.py @@ -51,7 +51,15 @@ def test_transaction_class(self, kwargs: dict, context): # Just use kwargs to id the test case if kwargs == {"reads_capacity": 2**32, "writes_capacity": 256} and excinfo.type == OverflowError: # Internal Python error thrown in Windows - assert str(excinfo.value) == "Python int too large to convert to C unsigned long" + assert str(excinfo.value) == "Python int too large to convert to C uint32_t" + elif kwargs == {"reads_capacity": 256, "writes_capacity": 256, "invalid_arg": 1}: + assert str(excinfo.value) == "function takes at most 2 keyword arguments (3 given)" + elif kwargs == {"reads_capacity": '256', "writes_capacity": 256}: + assert str(excinfo.value) == "reads_capacity must be an uint32_t integer" + elif kwargs == {"reads_capacity": 256, "writes_capacity": '256'}: + assert str(excinfo.value) == "writes_capacity must be an uint32_t integer" + + # Even though this is an unlikely use case, this should not cause problems. def test_transaction_reinit(self): diff --git a/test/new_tests/test_query_partition.py b/test/new_tests/test_query_partition.py index 29250a3db2..5b7348e840 100644 --- a/test/new_tests/test_query_partition.py +++ b/test/new_tests/test_query_partition.py @@ -465,7 +465,7 @@ def callback(part_id, input_tuple): query_obj.foreach(callback, policy) err_code = err_info.value.code assert err_code == AerospikeStatus.AEROSPIKE_ERR_PARAM - assert "invalid partition_filter policy begin" in err_info.value.msg + assert "begin must be an int between 0 and 4095" in err_info.value.msg @pytest.mark.parametrize( "count", @@ -487,7 +487,7 @@ def callback(part_id, input_tuple): query_obj.foreach(callback, policy) err_code = err_info.value.code assert err_code == AerospikeStatus.AEROSPIKE_ERR_PARAM - assert "invalid partition_filter policy count" in err_info.value.msg + assert "count must be an int between 1 and 4096" in err_info.value.msg @pytest.mark.parametrize( "begin, count", diff --git a/test/new_tests/test_truncate.py b/test/new_tests/test_truncate.py index 40eeb283d4..b0c1f0e66e 100644 --- a/test/new_tests/test_truncate.py +++ b/test/new_tests/test_truncate.py @@ -167,20 +167,30 @@ def test_nanos_argument_before_cf_epoch(self): self.as_connection.truncate("test", "truncate", 1) def test_nanos_argument_too_large(self): - with pytest.raises(e.ParamError): + with pytest.raises(e.ParamError) as err_info: self.as_connection.truncate("test", "truncate", 2**64) + + assert err_info.value.code == -2 + assert err_info.value.msg == "integer value for truncate must be between 0 and UINT64_MAX" + def test_nanos_argument_between_int64_and_uint64(self): # This may stop raising a client error in 2264 # as the value will no longer be in the future then - with pytest.raises(e.InvalidRequest): + with pytest.raises(e.InvalidRequest) as err_info: self.as_connection.truncate("test", "truncate", 2**63 + 1) + assert err_info.value.code == 4 + assert err_info.value.msg.startswith("would truncate in the future from") + # TODO: this server's actual error message doesn't make sense to me def test_nanos_argument_between_int32_and_uint32(self): - with pytest.raises(e.InvalidRequest): + with pytest.raises(e.InvalidRequest) as err_info: self.as_connection.truncate("test", "truncate", 2**31 + 1) + assert err_info.value.code == 4 + assert err_info.value.msg.startswith("would truncate to 0 from ") + def test_nanos_argument_negative(self): with pytest.raises(e.ParamError): self.as_connection.truncate("test", "truncate", -self.truncate_threshold)