Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
51d2911
query.results(): reuse code from query.foreach()
juliannguyen4 Aug 26, 2025
03709fa
finish cleanup
juliannguyen4 Aug 26, 2025
3911257
Merge remote-tracking branch 'origin/dev' into CLIENT-2417-refactor-q…
juliannguyen4 Sep 25, 2025
791aa28
Finish query.results() merge
juliannguyen4 Sep 25, 2025
6aae294
not used
juliannguyen4 Sep 25, 2025
45534da
rm unused var
juliannguyen4 Sep 25, 2025
0d357c5
Fix
juliannguyen4 Sep 25, 2025
45e62c2
mv
juliannguyen4 Sep 25, 2025
445d3e1
Merge remote-tracking branch 'origin/dev' into CLIENT-2417-refactor-q…
juliannguyen4 Dec 12, 2025
83d4773
Implement and test aerospikeERROR_DETAIL_EXP_TRACE.
juliannguyen4 Aug 6, 2026
575e6c6
Pull Andrei's latest changes
juliannguyen4 Aug 17, 2026
bbb69e7
Pull C client for more changes
juliannguyen4 Aug 19, 2026
c985114
Merge remote-tracking branch 'origin/stage' into CLIENT-5054-add-thir…
juliannguyen4 Aug 19, 2026
40a6425
Implement BatchRecord.subcode and BatchRecord.message. TODO - tests.
juliannguyen4 Aug 19, 2026
9bc2519
Address improper indentation causing doc build to fail.
juliannguyen4 Aug 19, 2026
3ee483d
Add additional module constants for server subcodes for parity with s…
juliannguyen4 Aug 19, 2026
5cad3fe
Address redundant description since these new constants are already u…
juliannguyen4 Aug 19, 2026
f9d960a
Address compiler error on macOS
juliannguyen4 Aug 19, 2026
8af57ba
Fix e2e test for expression tracing
juliannguyen4 Aug 19, 2026
d0d7217
Address spellcheck error.
juliannguyen4 Aug 19, 2026
53a0928
Address test regression due to lack of server check.
juliannguyen4 Aug 19, 2026
5f82d78
Add e2e test that reads a batch row's subcode and error message detail.
juliannguyen4 Aug 20, 2026
b592ce1
Make tests be compatible with server < 8.1.3
juliannguyen4 Aug 20, 2026
a70aa70
Merge remote-tracking branch 'origin/stage' into CLIENT-5054-add-thir…
juliannguyen4 Aug 21, 2026
4c1174f
Add comment for clarity
juliannguyen4 Aug 21, 2026
4a39f20
Address inconsistent naming between C client and Python client error …
juliannguyen4 Aug 21, 2026
b721e71
Address additional inconsistent naming for subcode attribute
juliannguyen4 Aug 21, 2026
fa217e1
Add test case that tests as_batch_result_to_BatchRecord -> set_error_…
juliannguyen4 Aug 21, 2026
a005abe
Merge remote-tracking branch 'origin/stage' into CLIENT-2417-refactor…
juliannguyen4 Aug 21, 2026
5518260
Merge remote-tracking branch 'origin/stage' into CLIENT-5054-add-thir…
juliannguyen4 Aug 21, 2026
15c98cd
Update C client to stage with expression tracing support
juliannguyen4 Aug 24, 2026
da71888
Align documentation with C client
juliannguyen4 Aug 24, 2026
ab900a5
fix: address segv when as_batch_result/as_batch_base_record.message i…
juliannguyen4 Aug 24, 2026
20f440b
Address test regression now that as_error messages include the node a…
juliannguyen4 Aug 24, 2026
a3f1123
fix: address test regression where subcode is no longer included in e…
juliannguyen4 Aug 24, 2026
f9496b7
Merge remote-tracking branch 'origin/stage' into CLIENT-5054-add-thir…
juliannguyen4 Aug 25, 2026
96571c2
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Aug 25, 2026
f79ea1d
refactor: remove redundant bool flag in udata passed to C client's fo…
juliannguyen4 Aug 25, 2026
e103f9b
fix: address segv because of borrowed reference being free'd.
juliannguyen4 Aug 25, 2026
0d38e1d
fix: address faulty boolean logic; query.foreach() returns None and q…
juliannguyen4 Aug 25, 2026
e444870
fix: refactor scan.foreach() to use each_result in order to have each…
juliannguyen4 Aug 25, 2026
31d4363
fix: refactor scan.results() to use the same implementation as scan.f…
juliannguyen4 Aug 25, 2026
c607078
fix: add required missing header file
juliannguyen4 Aug 25, 2026
1d7f1af
fix: address scan.results() returning the wrong value
juliannguyen4 Aug 25, 2026
3e911e7
add additional test cases for batch_write and batch_operate that test…
juliannguyen4 Aug 26, 2026
6c96b75
fix: pull C client to address test failures where the regular batch m…
juliannguyen4 Aug 26, 2026
728bfe6
test: make final revisions to tests. batch_write should take Read Bat…
juliannguyen4 Aug 26, 2026
5886e2f
docs(doctest): address expected output regression due to error messag…
juliannguyen4 Aug 26, 2026
9bb46c2
Revert "docs(doctest): address expected output regression due to erro…
juliannguyen4 Aug 26, 2026
01576c8
docs(doctest): address expected output regression due to error messag…
juliannguyen4 Aug 26, 2026
5d21517
refactor: add comment to clarify why there is set_error_details_in_py…
juliannguyen4 Aug 26, 2026
313378c
test: verify that BatchRecord.message is None and BatchRecord.subcode…
juliannguyen4 Aug 26, 2026
00eddf7
test: setup fixture required for this e2e test
juliannguyen4 Aug 26, 2026
1d4a2e5
test: dynamic config error_detail_verbosity on both reads and writes
juliannguyen4 Aug 26, 2026
f559ffa
refactor: minimize overhead for including partition_query flag in uda…
juliannguyen4 Aug 26, 2026
f4a5d0b
refactor: make code easier to read whether it is called by query.resu…
juliannguyen4 Aug 26, 2026
4b775b4
refactor: also use is_scan_results flag similar to foreground query i…
juliannguyen4 Aug 26, 2026
bcf2a2f
refactor: scan.foreach() - move error check after convert_partition_f…
juliannguyen4 Aug 26, 2026
0373fd3
fix: for scan.foreach(), add missing cleanup code for callback-level …
juliannguyen4 Aug 26, 2026
22d1731
fix: for query,scan.results(), if an item fails to be appended to the…
juliannguyen4 Aug 26, 2026
56b703d
fix: prevent SystemError exception when PyList_New() fails to be crea…
juliannguyen4 Aug 26, 2026
3bf3543
fix: prevent reading an undeclared boolean var
juliannguyen4 Aug 26, 2026
cc9c2e1
fix: address regression/crash with foreground queries
juliannguyen4 Aug 26, 2026
cdde334
fix: update c client to support error_detail_verbosity in dynamic config
juliannguyen4 Aug 26, 2026
d6446e5
tests: increase code coverage as much as possible
juliannguyen4 Aug 26, 2026
707566e
test: properly skip dynamic config test case for server versions that…
juliannguyen4 Aug 26, 2026
9d72bd9
feat: implement part of list join operation and expression. TODO - ne…
juliannguyen4 Aug 26, 2026
4cf77d2
doc: clarify that when using NumericType.FLOAT, the input string must…
juliannguyen4 Aug 26, 2026
22e5894
fix: update C client to pull latest PRD changes and address string op…
juliannguyen4 Aug 27, 2026
c5c1889
Merge remote-tracking branch 'origin/stage' into CLIENT-5338-string-l…
juliannguyen4 Aug 27, 2026
9de3705
refactor: point c client to stage
juliannguyen4 Aug 27, 2026
2b1ea09
Merge branch 'CLIENT-5275-doc-clarify-NumericType-FLOAT' into CLIENT-…
juliannguyen4 Aug 27, 2026
71e8cb2
feat: implement list_join and bit_b64_encode expression and operations
juliannguyen4 Aug 27, 2026
42ff0b0
feat: make snip op and expr's end parameter optional
juliannguyen4 Aug 27, 2026
3014dfc
feat: expose CREATE_ONLY and UPDATE_ONLY string write flags
juliannguyen4 Aug 27, 2026
2cbb571
feat: expose additional server subcodes SUB_OPNOT_STRING_REGEX_LIMIT_…
juliannguyen4 Aug 27, 2026
2639189
fix: address test runtime error due to incorrect constant naming in a…
juliannguyen4 Aug 27, 2026
e56df0e
fix: address stubtest errors by adding missing stubs
juliannguyen4 Aug 27, 2026
817d823
fix: move Bit64Encode expression's bin parameter to the end to be con…
juliannguyen4 Aug 27, 2026
f641b90
fix: move ListJoin expression's bin parameter to the end to be consis…
juliannguyen4 Aug 27, 2026
ac40213
docs: fix ListJoin docstring for bin parameter. Map bins are not valid
juliannguyen4 Aug 27, 2026
ff0e970
doc: align Python client with all string ops changes from C client la…
juliannguyen4 Aug 27, 2026
6cfd199
doc: address doc build errors
juliannguyen4 Aug 27, 2026
0047f8a
fix: SplitSeparator expression should fail if separator isn't present
juliannguyen4 Aug 27, 2026
71a1413
docs: address invalid formatting for cross ref
juliannguyen4 Aug 27, 2026
d6ee937
docs: address invalid formatting for cross ref
juliannguyen4 Aug 27, 2026
4afc35a
docs: add period at end of sentence..
juliannguyen4 Aug 27, 2026
a0bf5a9
docs: address invalid formatting for wire protocol bytes description
juliannguyen4 Aug 27, 2026
96c62fe
fix: make sure as_exp_join_separator has enough memory
juliannguyen4 Aug 27, 2026
45b57ba
tests: enable error detail expression tracing by default for the clie…
juliannguyen4 Aug 27, 2026
6a95108
fix: only allocate bare minimum memory for as_exp_bit_b64_encode. Als…
juliannguyen4 Aug 27, 2026
7eb3902
test: list_join expr and ops. fix bugs along the way
juliannguyen4 Aug 27, 2026
2c96ed0
test: remove invalid test that passes in non-str bin name to expressi…
juliannguyen4 Aug 27, 2026
670f100
test: snip expression with end set to none, and b64encode op. fix iss…
juliannguyen4 Aug 27, 2026
fd6caef
fix: b64encode failing due to byte_offset not being parsed properly
juliannguyen4 Aug 27, 2026
28195bb
fix: list_join op separator param not being honored
juliannguyen4 Aug 27, 2026
03efbfc
refactor: use BYTE_OFFSET_KEY instead of hardcoded string for bit_b64…
juliannguyen4 Aug 27, 2026
d81924f
tests: gate 8.1.3 tests properly to avoid failing on older server ver…
juliannguyen4 Aug 28, 2026
4239f8c
feat: add invert_size parameter to BitB64Encode expression
juliannguyen4 Aug 28, 2026
440c856
tests: address missing import
juliannguyen4 Aug 28, 2026
52d6ed7
test: refactor test_string_operations.py by applying indirect fixture…
juliannguyen4 Aug 28, 2026
1a6cc02
fix: add test cases for CREATE_ONLY and UPDATE_ONLY
juliannguyen4 Aug 28, 2026
e7db0bc
test: list join test case passed indirect params to fixture that it d…
juliannguyen4 Aug 28, 2026
ece9dd9
test: fix list_join test cases failing on server < 8.1.3
juliannguyen4 Aug 28, 2026
297b84c
test: change test_string_policy_update_only to only expect write op t…
juliannguyen4 Aug 28, 2026
23bbdfe
fix: remove debug print
juliannguyen4 Aug 28, 2026
364dbb6
refactor: reduce payload size for ListJoin if separator is None
juliannguyen4 Aug 28, 2026
2bf860a
style: format get_enum_from_py_dict and get_int_from_py_dict to make …
juliannguyen4 Aug 28, 2026
8b684c9
refactor: clear up why there are separate op codes LIST_JOIN and LIST…
juliannguyen4 Aug 28, 2026
21ee478
test: fix invalid test syntax
juliannguyen4 Aug 28, 2026
c0e0f5b
test: make test compatible with server < 8.1.3
juliannguyen4 Aug 28, 2026
babe80f
Merge branch 'CLIENT-5338-string-list-join-op-and-expr' into CLIENT-5…
juliannguyen4 Aug 28, 2026
9ed86c7
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Sep 1, 2026
47d2547
Have query.results() back up the query's partitions status before per…
juliannguyen4 Sep 2, 2026
86bf05e
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Sep 3, 2026
338bc33
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Sep 3, 2026
6c49f69
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Sep 10, 2026
c25e139
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Sep 14, 2026
9f6fdd9
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Sep 14, 2026
c6d1abe
fix: handle case where paginated query is not currently tracking the …
juliannguyen4 Sep 14, 2026
84f808c
fix: address compiler error due to var typo
juliannguyen4 Sep 14, 2026
211dbbc
fix: issue where a new paginated query that resumes from a partitions…
juliannguyen4 Sep 17, 2026
273b620
fix: issue where query can fail before a backup of the internal parti…
juliannguyen4 Sep 17, 2026
81dc932
perf: prevent repeated heap allocations for every query.results() cal…
juliannguyen4 Sep 18, 2026
95d9de7
refactor: clear up why we reset the partitions status buffer
juliannguyen4 Sep 18, 2026
5c229f1
refactor: clear up why partitions_status_backup_buffer and partitions…
juliannguyen4 Sep 18, 2026
7609636
refactor: clear up why we have logic to malloc for self->partitions_s…
juliannguyen4 Sep 18, 2026
f7083ae
refactor: clear up why boolean flag exists in case reader finds it re…
juliannguyen4 Sep 18, 2026
4ee89d1
refactor: use Cursor to minimize line diffs for this file. Github doe…
juliannguyen4 Sep 18, 2026
319886e
Merge remote-tracking branch 'origin/stage' into CLIENT-5298-address-…
juliannguyen4 Sep 25, 2026
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
11 changes: 11 additions & 0 deletions src/include/foreach.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <Python.h>
#include "client.h"

// Struct for Python User-Data for the Callback
typedef struct {
PyObject *py_obj;
AerospikeClient *client;
as_vector thread_errors;
pthread_mutex_t thread_errors_mutex;
bool partition_query;
} LocalData;
5 changes: 5 additions & 0 deletions src/include/query.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ PyObject *StoreUnicodePyObject(AerospikeQuery *self, PyObject *obj);

int64_t pyobject_to_int64(PyObject *py_obj);

PyObject *AerospikeQuery_Foreach_Invoke(AerospikeQuery *self,
PyObject *py_callback,
PyObject *py_policy,
PyObject *py_options);

// 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.
Expand Down
6 changes: 6 additions & 0 deletions src/include/scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,9 @@ PyObject *AerospikeScan_Get_Partitions_status(AerospikeScan *self);

AerospikeScan *AerospikeScan_Type_New(PyTypeObject *type,
AerospikeClient *client);

PyObject *AerospikeScan_Foreach_Invoke(AerospikeScan *self,
PyObject *py_callback,
PyObject *py_policy,
PyObject *py_options,
PyObject *py_nodename);
7 changes: 7 additions & 0 deletions src/include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ typedef struct {
typedef struct {
PyObject_HEAD AerospikeClient *client;
as_query query;

// This is for query.results() to restore its original partitions status before being called
// We don't want to heap allocate a new partitions status every time we call query.results().
// And the stack size may be too small to hold the partitions status.
as_partitions_status *partitions_status_backup_buffer;
size_t partitions_status_backup_buffer_capacity;

UnicodePyObjects u_objs;
as_vector *unicodeStrVector;
as_static_pool *static_pool;
Expand Down
2 changes: 1 addition & 1 deletion src/main/convert_partition_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ as_status convert_partition_filter(AerospikeClient *self,
parts_setup(filter->begin, filter->count, //cluster->n_partitions,
&filter->digest);

if (parts_stat && PyDict_Check(parts_stat)) {
if (parts_stat && PyDict_Check(parts_stat) && PyDict_Size(parts_stat) > 0) {

PyObject *py_done =
PyDict_GetItemString(parts_stat, PARTITIONS_STATUS_KEY_DONE);
Expand Down
230 changes: 164 additions & 66 deletions src/main/query/foreach.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,9 @@
#include "exceptions.h"
#include "query.h"
#include "policy.h"
#include "foreach.h"

// Struct for Python User-Data for the Callback
typedef struct {
PyObject *callback;
AerospikeClient *client;
int partition_query;
as_vector thread_errors;
pthread_mutex_t thread_errors_mutex;
} LocalData;

static bool each_result(const as_val *val, void *udata)
bool each_result(const as_val *val, void *udata)
{
bool retval = true;

Expand All @@ -50,7 +42,7 @@ static bool each_result(const as_val *val, void *udata)

// Extract callback user-data
LocalData *data = (LocalData *)udata;
PyObject *py_callback = data->callback;
PyObject *py_callback_or_list_of_results = data->py_obj;

// Python Function Arguments and Result Value
PyObject *py_arglist = NULL;
Expand All @@ -72,45 +64,61 @@ static bool each_result(const as_val *val, void *udata)
goto EXIT_CALLBACK;
}

// Build Python Function Arguments
if (data->partition_query) {
if (PyList_Check(py_callback_or_list_of_results)) {
// query.results()
if (py_result) {
int retval =
PyList_Append(py_callback_or_list_of_results, py_result);
Py_DECREF(py_result);
if (retval == -1) {
as_error_update(&thread_err_local, AEROSPIKE_ERR_CLIENT,
"Failed to append item to results list");
goto EXIT_CALLBACK;
}
}
}
else {
// Build Python Function Arguments
if (data->partition_query) {

uint32_t part_id = 0;
uint32_t part_id = 0;

as_record *rec = as_record_fromval(val);
as_record *rec = as_record_fromval(val);

if (rec->key.digest.init) {
part_id =
as_partition_getid(rec->key.digest.value, CLUSTER_NPARTITIONS);
}
if (rec->key.digest.init) {
part_id = as_partition_getid(rec->key.digest.value,
CLUSTER_NPARTITIONS);
}

py_arglist = PyTuple_New(2);
py_arglist = PyTuple_New(2);

PyTuple_SetItem(py_arglist, 0, PyLong_FromUnsignedLong(part_id));
PyTuple_SetItem(py_arglist, 1, py_result);
}
else {
py_arglist = PyTuple_New(1);
PyTuple_SetItem(py_arglist, 0, py_result);
}
PyTuple_SetItem(py_arglist, 0, PyLong_FromUnsignedLong(part_id));
PyTuple_SetItem(py_arglist, 1, py_result);
}
else {
py_arglist = PyTuple_New(1);
PyTuple_SetItem(py_arglist, 0, py_result);
}

// Invoke Python Callback
py_return = PyObject_Call(py_callback, py_arglist, NULL);

// Release Python Function Arguments
Py_DECREF(py_arglist);
// handle return value
if (!py_return) {
// an exception was raised, handle it (someday)
// for now, we bail from the loop
as_error_update(&thread_err_local, AEROSPIKE_ERR_CLIENT,
"Callback function contains an error");
retval = false;
}
else if (py_return == Py_False) {
retval = false;
// Invoke Python Callback
py_return =
PyObject_Call(py_callback_or_list_of_results, py_arglist, NULL);

// Release Python Function Arguments
Py_DECREF(py_arglist);
// handle return value
if (!py_return) {
// an exception was raised, handle it (someday)
// for now, we bail from the loop
as_error_update(&thread_err_local, AEROSPIKE_ERR_CLIENT,
"Callback function contains an error");
retval = false;
}
else if (py_return == Py_False) {
retval = false;
}
Py_XDECREF(py_return);
}
Py_XDECREF(py_return);

EXIT_CALLBACK:
if (thread_err_local.code != AEROSPIKE_OK) {
Expand All @@ -129,27 +137,13 @@ static bool each_result(const as_val *val, void *udata)
return retval;
}

PyObject *AerospikeQuery_Foreach(AerospikeQuery *self, PyObject *args,
PyObject *kwds)
PyObject *AerospikeQuery_Foreach_Invoke(AerospikeQuery *self,
PyObject *py_callback,
PyObject *py_policy,
PyObject *py_options)
{
// Python Function Arguments
PyObject *py_callback = NULL;
PyObject *py_policy = NULL;
PyObject *py_options = NULL;
// Python Function Keyword Arguments
static char *kwlist[] = {"callback", "policy", "options", NULL};

// Python Function Argument Parsing
if (PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:foreach", kwlist,
&py_callback, &py_policy,
&py_options) == false) {
as_query_destroy(&self->query);
return NULL;
}

// Initialize callback user data
LocalData data;
data.callback = py_callback;
LocalData data = {0};
data.client = self->client;
data.partition_query = 0;

Expand All @@ -171,7 +165,12 @@ PyObject *AerospikeQuery_Foreach(AerospikeQuery *self, PyObject *args,
as_partition_filter *partition_filter_p = NULL;
as_partitions_status *ps = NULL;

// Initialize error
bool is_query_results = py_callback == NULL;

as_partitions_status *backed_up_part_status = NULL;
// query.results() may raise an exception before the backup is made
// so we don't want to reset the query's current partitions status in this case
bool is_query_state_backed_up = false;

if (!self || !self->client->as) {
as_error_update(&err, AEROSPIKE_ERR_PARAM, "Invalid aerospike object");
Expand All @@ -184,6 +183,18 @@ PyObject *AerospikeQuery_Foreach(AerospikeQuery *self, PyObject *args,
goto CLEANUP;
}

if (is_query_results) {
data.py_obj = PyList_New(0);
if (data.py_obj == NULL) {
as_error_update(&err, AEROSPIKE_ERR_CLIENT,
"Was unable to construct results list");
goto CLEANUP;
}
}
else {
data.py_obj = py_callback;
}

// Convert python policy object to as_policy_exists
pyobject_to_policy_query(
self->client, &err, py_policy, &query_policy, &query_policy_p,
Expand Down Expand Up @@ -212,6 +223,50 @@ PyObject *AerospikeQuery_Foreach(AerospikeQuery *self, PyObject *args,
goto CLEANUP;
}

if (is_query_results) {
as_partitions_status *backup_source = NULL;
if (self->query.parts_all) {
// This query is resuming.
// If there is a user-provided partitions status, the C client will still ignore it in this case.
backup_source = self->query.parts_all;
}
else if (ps) {
// This is a new query object
backup_source = ps;
}

if (backup_source) {
size_t backup_size_bytes =
sizeof(as_partitions_status) +
sizeof(as_partition_status) * backup_source->part_count;

// The number of partitions is always 4096, but we have this code in case
// self->partitions_status_backup_buffer was never initialized before
if (self->partitions_status_backup_buffer_capacity <
backup_size_bytes) {
as_partitions_status *new_buffer = cf_malloc(backup_size_bytes);
if (!new_buffer) {
as_error_update(&err, AEROSPIKE_ERR_CLIENT,
"Failed to back up partitions status");
goto CLEANUP;
}

if (self->partitions_status_backup_buffer) {
as_partitions_status_release(
self->partitions_status_backup_buffer);
}
self->partitions_status_backup_buffer = new_buffer;
self->partitions_status_backup_buffer_capacity =
backup_size_bytes;
}

backed_up_part_status = self->partitions_status_backup_buffer;
memcpy(backed_up_part_status, backup_source, backup_size_bytes);
backed_up_part_status->ref_count = 1;
}
is_query_state_backed_up = true;
}

Py_BEGIN_ALLOW_THREADS

// Invoke operation
Expand Down Expand Up @@ -260,10 +315,53 @@ PyObject *AerospikeQuery_Foreach(AerospikeQuery *self, PyObject *args,
pthread_mutex_destroy(&data.thread_errors_mutex);

if (err.code != AEROSPIKE_OK) {
raise_exception_base(&err, Py_None, Py_None, Py_None, Py_None, Py_None);
if (is_query_results) {
if (is_query_state_backed_up) {
if (self->query.parts_all) {
as_partitions_status_release(self->query.parts_all);
}
self->query.parts_all = backed_up_part_status;
if (backed_up_part_status) {
// query.parts_all is now the owner of the backup, not the query's backup buffer ptr
// If the query object is destroyed, we don't want to double free the partitions status
self->partitions_status_backup_buffer = NULL;
self->partitions_status_backup_buffer_capacity = 0;
}
}

Py_XDECREF(data.py_obj);
}

raise_exception(&err);
return NULL;
}

if (is_query_results) {
return data.py_obj;
}
else {
Py_RETURN_NONE;
}
}

PyObject *AerospikeQuery_Foreach(AerospikeQuery *self, PyObject *args,
PyObject *kwds)
{
// Python Function Arguments
PyObject *py_callback = NULL;
PyObject *py_policy = NULL;
PyObject *py_options = NULL;
// Python Function Keyword Arguments
static char *kwlist[] = {"callback", "policy", "options", NULL};

// Python Function Argument Parsing
if (PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:foreach", kwlist,
&py_callback, &py_policy,
&py_options) == false) {
as_query_destroy(&self->query);
return NULL;
}

Py_INCREF(Py_None);
return Py_None;
return AerospikeQuery_Foreach_Invoke(self, py_callback, py_policy,
py_options);
}
Loading
Loading