Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
881ef67
Move into inline function
juliannguyen4 Oct 14, 2025
8dc1e17
reuse check_and_set_meta
juliannguyen4 Oct 14, 2025
2267d46
Validate metadata dictionary keys
juliannguyen4 Oct 14, 2025
24603b0
Also validate keys for remove_bin's metadata dictionary parameter
juliannguyen4 Oct 14, 2025
9ced0c4
Calling PyDict_* API on a non dictionary obj probably causing this se…
juliannguyen4 Oct 14, 2025
9cac6fe
Update docs
juliannguyen4 Oct 14, 2025
79d48b2
fix a few tests. some tests are returning a result with error indicat…
juliannguyen4 Oct 14, 2025
a1a01c3
HLL policy shouldn't be passed to operate as a policy
juliannguyen4 Oct 15, 2025
9fed163
Fix bug where an invalid bin value doesn't raise an error
juliannguyen4 Oct 15, 2025
f2acbe6
Rm comments since var names are self explanatory
juliannguyen4 Oct 15, 2025
a56d8d7
Reduce nested if statements
juliannguyen4 Oct 15, 2025
9477c89
check_and_set_meta is resetting the err
juliannguyen4 Oct 15, 2025
4304eda
Change bool param to const char* so we can set any adjective
juliannguyen4 Oct 15, 2025
1ef4e7d
fix tests
juliannguyen4 Oct 15, 2025
f5ae549
explain why check_and_set_meta params are defined this way
juliannguyen4 Oct 22, 2025
98d2215
refactor. also document inconsistent behavior
juliannguyen4 Oct 22, 2025
cb6aef7
fix bad comment
juliannguyen4 Oct 22, 2025
bc2ba3a
note
juliannguyen4 Oct 22, 2025
cbb629b
Revert since refactoring changes make it hard to see actual related c…
juliannguyen4 Oct 22, 2025
7c5069c
Reuse helper function but dont refactor helper function so code revie…
juliannguyen4 Oct 22, 2025
e267012
fix
juliannguyen4 Oct 22, 2025
f9b45e8
Have remove_bin reuse check_and_set_meta. Metadata should not be igno…
juliannguyen4 Oct 22, 2025
9a004fe
fix potential memory leaks with put(). TODO test
juliannguyen4 Oct 23, 2025
2344481
fix bug where as_record_destroy() is called even if as_record_init() …
juliannguyen4 Oct 23, 2025
438c0ab
Add test cases for bug fixes
juliannguyen4 Oct 23, 2025
9766311
Fix test so remove_bin works
juliannguyen4 Oct 23, 2025
91b6572
as_record_init_from_pyobject not used in remove_bin.c
juliannguyen4 Oct 23, 2025
6a82c85
fix test...
juliannguyen4 Oct 24, 2025
ac4ff3d
fix naming
juliannguyen4 Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion doc/aerospike.rst
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,10 @@ Only the `hosts` key is required; the rest of the keys are optional.
:columns: 1

* **validate_keys** (:class:`bool`)
(Optional) Validate keys passed into this config dictionary as well as any policy dictionaries.
(Optional) Validate keys passed into this config dictionary as well as any:

- :ref:`aerospike_policies`
- :ref:`metadata_dict`

If a key that is undefined in this documentation gets passed to a config or policy dictionary:

Expand Down
7 changes: 5 additions & 2 deletions src/include/conversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,11 @@ void initialize_bin_for_strictypes(AerospikeClient *self, as_error *err,
as_status bin_strict_type_checking(AerospikeClient *self, as_error *err,
PyObject *py_bin, char **bin);

as_status check_and_set_meta(PyObject *py_meta, as_operations *ops,
as_error *err);
// Both as_operations and as_record have ttl and gen fields,
// so we have ttl and gen as separate parameters instead of accepting either as_operations or as_record
as_status check_and_set_meta(PyObject *py_meta, uint32_t *ttl_ref,
uint16_t *gen_ref, as_error *err,
bool validate_keys);

as_status as_batch_read_results_to_pyobject(as_error *err,
AerospikeClient *client,
Expand Down
2 changes: 2 additions & 0 deletions src/include/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,5 @@ typedef struct {
} PyListenerData;

void free_py_listener_data(PyListenerData *py_listener_data);

#define POLICY_DICTIONARY_ADJECTIVE_FOR_ERROR_MESSAGE "policy"
6 changes: 3 additions & 3 deletions src/include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ extern PyObject *py_hll_policy_valid_keys;
extern PyObject *py_info_and_write_policy_valid_keys;
// scan.apply() takes in one policy parameter that accepts both write and info policy options
extern PyObject *py_info_and_scan_policy_valid_keys;
extern PyObject *py_record_metadata_valid_keys;

#define INVALID_DICTIONARY_KEY_ERROR_PART1 "is an invalid"
#define INVALID_DICTIONARY_KEY_ERROR_PART2 "dictionary key"
Expand All @@ -159,8 +160,7 @@ extern PyObject *py_info_and_scan_policy_valid_keys;
// Return 0 and set err if dictionary has invalid keys
// Return 1 if dictionary's keys are all valid
//
// is_py_dict_a_policy is for error reporting only;
// If is_py_dict_a_policy is false, we are validating a client config dictionary
// adjective is for error reporting only
extern int does_py_dict_contain_valid_keys(as_error *err, PyObject *py_dict,
PyObject *py_set_of_valid_keys,
bool is_py_dict_a_policy);
const char *adjective);
6 changes: 5 additions & 1 deletion src/main/aerospike.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,8 @@ DEFINE_SET_OF_VALID_KEYS(hll_policy, "flags", NULL)

DEFINE_SET_OF_VALID_KEYS(admin_policy, "timeout", NULL)

DEFINE_SET_OF_VALID_KEYS(record_metadata, "gen", "ttl", NULL)

// Use a struct to create pairs of pyobjects and list of strings defined above
// When we initialize the module, we create sets for the valid keys that the client can use later

Expand Down Expand Up @@ -750,7 +752,9 @@ static struct py_set_name_to_str_list py_set_name_to_str_lists[] = {
PY_SET_NAME_TO_STR_LIST(list_policy_valid_keys),
PY_SET_NAME_TO_STR_LIST(hll_policy_valid_keys),
PY_SET_NAME_TO_STR_LIST(info_and_write_policy_valid_keys),
PY_SET_NAME_TO_STR_LIST(info_and_scan_policy_valid_keys)};
PY_SET_NAME_TO_STR_LIST(info_and_scan_policy_valid_keys),
PY_SET_NAME_TO_STR_LIST(record_metadata_valid_keys),
};

// Return NULL if an exception is raised
// Returns strong reference to new Python dictionary
Expand Down
3 changes: 2 additions & 1 deletion src/main/client/batch_write.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ static PyObject *AerospikeClient_BatchWriteInvoke(AerospikeClient *self,
ops = as_operations_new(py_ops_size);
garb->ops_to_free = ops;

if (check_and_set_meta(py_meta, ops, err) != AEROSPIKE_OK) {
if (check_and_set_meta(py_meta, &ops->ttl, &ops->gen, err,
self->validate_keys) != AEROSPIKE_OK) {
goto CLEANUP_ON_ERROR;
}

Expand Down
6 changes: 4 additions & 2 deletions src/main/client/operate.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,8 @@ static PyObject *AerospikeClient_Operate_Invoke(AerospikeClient *self,
memset(&static_pool, 0, sizeof(static_pool));
CHECK_CONNECTED(err);

if (check_and_set_meta(py_meta, &ops, err) != AEROSPIKE_OK) {
if (check_and_set_meta(py_meta, &ops.ttl, &ops.gen, err,
self->validate_keys) != AEROSPIKE_OK) {
goto CLEANUP;
}

Expand Down Expand Up @@ -1036,7 +1037,8 @@ AerospikeClient_OperateOrdered_Invoke(AerospikeClient *self, as_error *err,
}
}

if (check_and_set_meta(py_meta, &ops, err) != AEROSPIKE_OK) {
if (check_and_set_meta(py_meta, &ops.ttl, &ops.gen, err,
self->validate_keys) != AEROSPIKE_OK) {
goto CLEANUP;
}

Expand Down
8 changes: 2 additions & 6 deletions src/main/client/put.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ PyObject *AerospikeClient_Put_Invoke(AerospikeClient *self, PyObject *py_key,
bool key_initialised = false;
bool record_initialised = false;

// Initialize record
as_record_init(&rec, 0);
record_initialised = true;

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

Expand Down Expand Up @@ -98,6 +94,8 @@ PyObject *AerospikeClient_Put_Invoke(AerospikeClient *self, PyObject *py_key,
goto CLEANUP;
}

record_initialised = true;

// Convert python policy object to as_policy_write
pyobject_to_policy_write(self, &err, py_policy, &write_policy,
&write_policy_p, &self->as->config.policies.write,
Expand All @@ -120,11 +118,9 @@ PyObject *AerospikeClient_Put_Invoke(AerospikeClient *self, PyObject *py_key,
}

if (key_initialised == true) {
// Destroy the key if it is initialised.
as_key_destroy(&key);
}
if (record_initialised == true) {
// Destroy the record if it is initialised.
as_record_destroy(&rec);
}

Expand Down
40 changes: 3 additions & 37 deletions src/main/client/remove_bin.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,43 +104,9 @@ AerospikeClient_RemoveBin_Invoke(AerospikeClient *self, PyObject *py_key,
}
}

if (py_meta && PyDict_Check(py_meta)) {
PyObject *py_gen = PyDict_GetItemString(py_meta, "gen");
PyObject *py_ttl = PyDict_GetItemString(py_meta, "ttl");

if (py_ttl) {
if (PyLong_Check(py_ttl)) {
rec.ttl = (uint32_t)PyLong_AsLong(py_ttl);
if ((uint32_t)-1 == rec.ttl && PyErr_Occurred()) {
as_error_update(
err, AEROSPIKE_ERR_PARAM,
"integer value for ttl exceeds sys.maxsize");
goto CLEANUP;
}
}
else {
as_error_update(err, AEROSPIKE_ERR_PARAM,
"Ttl should be an int or long");
goto CLEANUP;
}
}

if (py_gen) {
if (PyLong_Check(py_gen)) {
rec.gen = (uint16_t)PyLong_AsLongLong(py_gen);
if ((uint16_t)-1 == rec.gen && PyErr_Occurred()) {
as_error_update(
err, AEROSPIKE_ERR_PARAM,
"integer value for gen exceeds sys.maxsize");
goto CLEANUP;
}
}
else {
as_error_update(err, AEROSPIKE_ERR_PARAM,
"Generation should be an int or long");
goto CLEANUP;
}
}
check_and_set_meta(py_meta, &rec.ttl, &rec.gen, err, self->validate_keys);
if (err->code != AEROSPIKE_OK) {
goto CLEANUP;
}

Py_BEGIN_ALLOW_THREADS
Expand Down
18 changes: 10 additions & 8 deletions src/main/client/type.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ static PyObject *AerospikeClient_Type_New(PyTypeObject *type, PyObject *args,

int does_py_dict_contain_valid_keys(as_error *err, PyObject *py_dict,
PyObject *py_set_of_valid_keys,
bool is_py_dict_a_policy)
const char *adjective)
{
Py_ssize_t pos = 0;
PyObject *py_key = NULL;
Expand All @@ -540,8 +540,6 @@ int does_py_dict_contain_valid_keys(as_error *err, PyObject *py_dict,
}
else if (res == 0) {
// Key is invalid
const char *adjective =
is_py_dict_a_policy ? "policy" : "client config";
// py_key may not be a string
PyObject *py_error_msg = PyUnicode_FromFormat(
INVALID_DICTIONARY_KEY_ERROR, py_key, adjective);
Expand All @@ -567,6 +565,8 @@ int does_py_dict_contain_valid_keys(as_error *err, PyObject *py_dict,
return -1;
}

#define CLIENT_CONFIG_DICTIONARY_ADJECTIVE_FOR_ERROR_MESSAGE "client config"

static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args,
PyObject *kwds)
{
Expand Down Expand Up @@ -624,7 +624,8 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args,

if (validate_keys) {
int retval = does_py_dict_contain_valid_keys(
&constructor_err, py_config, py_client_config_valid_keys, false);
&constructor_err, py_config, py_client_config_valid_keys,
CLIENT_CONFIG_DICTIONARY_ADJECTIVE_FOR_ERROR_MESSAGE);
if (retval == -1) {
goto RAISE_EXCEPTION_WITHOUT_AS_ERROR;
}
Expand Down Expand Up @@ -681,7 +682,7 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args,
if (validate_keys) {
int retval = does_py_dict_contain_valid_keys(
&constructor_err, py_lua, py_client_config_lua_valid_keys,
false);
CLIENT_CONFIG_DICTIONARY_ADJECTIVE_FOR_ERROR_MESSAGE);
if (retval == -1) {
goto RAISE_EXCEPTION_WITHOUT_AS_ERROR;
}
Expand Down Expand Up @@ -719,7 +720,7 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args,
if (validate_keys) {
int retval = does_py_dict_contain_valid_keys(
&constructor_err, py_tls, py_client_config_tls_valid_keys,
false);
CLIENT_CONFIG_DICTIONARY_ADJECTIVE_FOR_ERROR_MESSAGE);
if (retval == -1) {
goto RAISE_EXCEPTION_WITHOUT_AS_ERROR;
}
Expand Down Expand Up @@ -801,7 +802,7 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args,
if (validate_keys) {
int retval = does_py_dict_contain_valid_keys(
&constructor_err, py_shm, py_client_config_shm_valid_keys,
false);
CLIENT_CONFIG_DICTIONARY_ADJECTIVE_FOR_ERROR_MESSAGE);
if (retval == -1) {
goto RAISE_EXCEPTION_WITHOUT_AS_ERROR;
}
Expand Down Expand Up @@ -895,7 +896,8 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args,
if (validate_keys) {
int retval = does_py_dict_contain_valid_keys(
&constructor_err, py_policies,
py_client_config_policies_valid_keys, false);
py_client_config_policies_valid_keys,
CLIENT_CONFIG_DICTIONARY_ADJECTIVE_FOR_ERROR_MESSAGE);
if (retval == -1) {
goto RAISE_EXCEPTION_WITHOUT_AS_ERROR;
}
Expand Down
Loading
Loading