Skip to content

Commit

Permalink
[CLIENT-2769] Add support for persistent list indexes (#570)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliannguyen4 committed Feb 8, 2024
1 parent 8aa7e68 commit 6ae15c1
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 26 deletions.
2 changes: 1 addition & 1 deletion aerospike-client-c
Submodule aerospike-client-c updated 37 files
+1 −1 LICENSE.md
+1 −1 modules/common
+835 −347 project/doxyfile
+1 −1 src/apidocs/footer.html
+11 −11 src/include/aerospike/aerospike.h
+16 −17 src/include/aerospike/as_batch.h
+26 −26 src/include/aerospike/as_cdt_ctx.h
+2 −2 src/include/aerospike/as_cdt_order.h
+1 −1 src/include/aerospike/as_command.h
+17 −17 src/include/aerospike/as_config.h
+15 −21 src/include/aerospike/as_error.h
+5 −2 src/include/aerospike/as_exp.h
+10 −45 src/include/aerospike/as_key.h
+22 −1 src/include/aerospike/as_list_operations.h
+74 −66 src/include/aerospike/as_operations.h
+20 −14 src/include/aerospike/as_partition_tracker.h
+11 −11 src/include/aerospike/as_policy.h
+58 −48 src/include/aerospike/as_query.h
+7 −10 src/include/aerospike/as_record.h
+8 −13 src/include/aerospike/as_record_iterator.h
+57 −39 src/include/aerospike/as_scan.h
+1 −1 src/include/aerospike/version.h
+2 −2 src/main/aerospike/aerospike.c
+2 −2 src/main/aerospike/aerospike_query.c
+2 −2 src/main/aerospike/aerospike_scan.c
+1 −1 src/main/aerospike/as_command.c
+31 −1 src/main/aerospike/as_list_operations.c
+3 −3 src/main/aerospike/as_map_operations.c
+75 −34 src/main/aerospike/as_partition_tracker.c
+1 −1 src/main/aerospike/version.c
+324 −2 src/test/aerospike_list/list_basics.c
+477 −14 src/test/aerospike_map/map_basics.c
+13 −0 src/test/lua/list_unordered.lua
+2 −2 vs/aerospike-client-c-libevent.nuspec
+2 −2 vs/aerospike-client-c-libuv.nuspec
+2 −2 vs/aerospike-client-c.nuspec
+7 −3 xcode/aerospike-test.xcodeproj/project.pbxproj
1 change: 1 addition & 0 deletions aerospike-stubs/aerospike.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ OP_LIST_SET_ORDER: Literal[1030]
OP_LIST_SIZE: Literal[1014]
OP_LIST_SORT: Literal[1031]
OP_LIST_TRIM: Literal[1013]
OP_LIST_CREATE: Literal[1041]
OP_MAP_CLEAR: Literal[1107]
OP_MAP_DECREMENT: Literal[1105]
OP_MAP_GET_BY_INDEX: Literal[1122]
Expand Down
31 changes: 31 additions & 0 deletions aerospike_helpers/operations/list_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,37 @@
LIST_ORDER_KEY = "list_order"
SORT_FLAGS_KEY = "sort_flags"
CTX_KEY = "ctx"
PAD_KEY = "pad"
PERSIST_INDEX_KEY = "persist_index"


def list_create(bin_name: str, list_order: int, pad: bool, persist_index: bool, ctx: Optional[list] = None):
"""
Create list create operation.
Server creates list at given context level.
Args:
bin_name (str): Bin name.
list_order (int): See :ref:`aerospike_list_order` for possible values.
persist_index (bool): If :py:obj:`True`, persist list index. A list index improves lookup performance,
but requires more storage. A list index can be created for a top-level
ordered list only. Nested and unordered list indexes are not supported.
ctx (Optional[dict]): An optional list of nested CDT :class:`cdt_ctx <aerospike_helpers.cdt_ctx>`
specifying the path to nested list. If not defined, the top-level list is used.
"""
op_dict = {
OP_KEY: aerospike.OP_LIST_CREATE,
BIN_KEY: bin_name,
LIST_ORDER_KEY: list_order,
PAD_KEY: pad,
PERSIST_INDEX_KEY: persist_index
}

if ctx is not None:
op_dict[CTX_KEY] = ctx

return op_dict


def list_append(bin_name: str, value, policy: Optional[dict] = None, ctx: Optional[list] = None):
Expand Down
5 changes: 5 additions & 0 deletions src/include/cdt_operation_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
#define AS_PY_MAP_POLICY "map_policy"
#define AS_EXPR_KEY "expr"
#define AS_EXPR_FLAGS_KEY "expr_flags"
#define AS_PY_PAD "pad"
#define AS_PY_PERSIST_INDEX "persist_index"

as_status get_bool_from_pyargs(as_error *err, char *key, PyObject *op_dict,
bool *boolean);

as_status get_bin(as_error *err, PyObject *op_dict, as_vector *unicodeStrVector,
char **binName);
Expand Down
1 change: 1 addition & 0 deletions src/include/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ enum Aerospike_list_operations {
OP_LIST_REMOVE_BY_REL_RANK_RANGE,
OP_LIST_REMOVE_BY_INDEX_RANGE_TO_END,
OP_LIST_REMOVE_BY_RANK_RANGE_TO_END,
OP_LIST_CREATE
};

enum Aerospike_map_operations {
Expand Down
23 changes: 0 additions & 23 deletions src/main/client/bit_operate.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ static as_status get_bit_resize_flags(as_error *err, PyObject *op_dict,
static as_status get_uint8t_from_pyargs(as_error *err, char *key,
PyObject *op_dict, uint8_t **value);

static as_status get_bool_from_pyargs(as_error *err, char *key,
PyObject *op_dict, bool *boolean);

static as_status get_uint32t_from_pyargs(as_error *err, char *key,
PyObject *op_dict, uint32_t *value);

Expand Down Expand Up @@ -904,26 +901,6 @@ static as_status get_bit_policy(as_error *err, PyObject *op_dict,
return AEROSPIKE_OK;
}

static as_status get_bool_from_pyargs(as_error *err, char *key,
PyObject *op_dict, bool *boolean)
{
PyObject *py_val = PyDict_GetItemString(op_dict, key);
if (!py_val) {
// op_dict does not contain key
return as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to convert %s",
key);
}

if (!PyBool_Check(py_val)) {
return as_error_update(err, AEROSPIKE_ERR_PARAM,
"key %s does not point to a boolean in the dict",
key);
}

*boolean = (bool)PyObject_IsTrue(py_val);
return AEROSPIKE_OK;
}

static as_status get_uint8t_from_pyargs(as_error *err, char *key,
PyObject *op_dict, uint8_t **value)
{
Expand Down
58 changes: 58 additions & 0 deletions src/main/client/cdt_list_operate.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
#include "cdt_list_operations.h"
#include "cdt_operation_utils.h"

static as_status add_op_list_create(AerospikeClient *self, as_error *err,
char *bin, PyObject *op_dict,
as_operations *ops,
as_static_pool *static_pool,
int serializer_type);

static as_status add_op_list_append(AerospikeClient *self, as_error *err,
char *bin, PyObject *op_dict,
as_operations *ops,
Expand Down Expand Up @@ -406,6 +412,11 @@ as_status add_new_list_op(AerospikeClient *self, as_error *err,
self, err, bin, op_dict, ops, static_pool, serializer_type);
}

case OP_LIST_CREATE: {
return add_op_list_create(self, err, bin, op_dict, ops, static_pool,
serializer_type);
}

default:
// This should never be possible since we only get here if we know that the operation is valid.
return as_error_update(err, AEROSPIKE_ERR_PARAM, "Unknown operation");
Expand Down Expand Up @@ -1140,6 +1151,53 @@ static as_status add_op_list_sort(AerospikeClient *self, as_error *err,
return AEROSPIKE_OK;
}

static as_status add_op_list_create(AerospikeClient *self, as_error *err,
char *bin, PyObject *op_dict,
as_operations *ops,
as_static_pool *static_pool,
int serializer_type)
{
int64_t order_type_int;
as_cdt_ctx ctx;
bool ctx_in_use = false;
bool pad, persist_index;

if (get_int64_t(err, AS_PY_LIST_ORDER, op_dict, &order_type_int) !=
AEROSPIKE_OK) {
return err->code;
}

if (get_bool_from_pyargs(err, AS_PY_PAD, op_dict, &pad) != AEROSPIKE_OK) {
return err->code;
}

if (get_bool_from_pyargs(err, AS_PY_PERSIST_INDEX, op_dict,
&persist_index) != AEROSPIKE_OK) {
return err->code;
}

if (get_cdt_ctx(self, err, &ctx, op_dict, &ctx_in_use, static_pool,
serializer_type) != AEROSPIKE_OK) {
return err->code;
}

bool add_op_successful = as_operations_list_create_all(
ops, bin, (ctx_in_use ? &ctx : NULL), (as_list_order)order_type_int,
pad, persist_index);

if (ctx_in_use) {
as_cdt_ctx_destroy(&ctx);
}

if (add_op_successful) {
return AEROSPIKE_OK;
}
else {
return as_error_update(err, AEROSPIKE_ERR_CLIENT,
"Failed to add list_create operation");
}
}

static as_status add_op_list_append(AerospikeClient *self, as_error *err,
char *bin, PyObject *op_dict,
as_operations *ops,
Expand Down
20 changes: 20 additions & 0 deletions src/main/client/cdt_operation_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@
#include "policy.h"
#include "conversions.h"

as_status get_bool_from_pyargs(as_error *err, char *key, PyObject *op_dict,
bool *boolean)
{
PyObject *py_val = PyDict_GetItemString(op_dict, key);
if (!py_val) {
// op_dict does not contain key
return as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to convert %s",
key);
}

if (!PyBool_Check(py_val)) {
return as_error_update(err, AEROSPIKE_ERR_PARAM,
"key %s does not point to a boolean in the dict",
key);
}

*boolean = (bool)PyObject_IsTrue(py_val);
return AEROSPIKE_OK;
}

/*
The caller of this does not own the pointer to binName, and should not free it. It is either
held by Python, or is added to the list of chars to free later.
Expand Down
4 changes: 2 additions & 2 deletions src/main/client/operate.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ static inline bool isListOp(int op)
op == OP_LIST_REMOVE_BY_VALUE_LIST ||
op == OP_LIST_REMOVE_BY_VALUE_RANGE || op == OP_LIST_SET_ORDER ||
op == OP_LIST_SORT || op == OP_LIST_REMOVE_BY_VALUE_RANK_RANGE_REL ||
op == OP_LIST_GET_BY_VALUE_RANK_RANGE_REL);
op == OP_LIST_GET_BY_VALUE_RANK_RANGE_REL || op == OP_LIST_CREATE);
}

static inline bool isNewMapOp(int op)
Expand Down Expand Up @@ -284,7 +284,7 @@ bool opRequiresValue(int op)
op != OP_MAP_REMOVE_BY_RANK && op != OP_MAP_GET_BY_KEY &&
op != OP_MAP_GET_BY_INDEX && op != OP_MAP_GET_BY_KEY_RANGE &&
op != OP_MAP_GET_BY_RANK && op != AS_OPERATOR_DELETE &&
op != OP_MAP_CREATE);
op != OP_MAP_CREATE && op != OP_LIST_CREATE);
}

bool opRequiresRange(int op)
Expand Down
1 change: 1 addition & 0 deletions src/main/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ static AerospikeConstants aerospike_constants[] = {
"OP_LIST_REMOVE_BY_VALUE_RANK_RANGE_REL"},
{OP_LIST_GET_BY_VALUE_RANK_RANGE_REL,
"OP_LIST_GET_BY_VALUE_RANK_RANGE_REL"},
{OP_LIST_CREATE, "OP_LIST_CREATE"},

/* CDT operations for use with expressions, new in 5.0 */
{OP_MAP_REMOVE_BY_VALUE_RANK_RANGE_REL,
Expand Down
19 changes: 19 additions & 0 deletions test/new_tests/test_new_list_operation_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,22 @@ def test_list_sort(self):
self.as_connection.operate(sort_key, [operation])
_, _, bins = self.as_connection.get(sort_key)
assert bins[self.test_bin] == [2, 5]

def test_list_create_pos(self):
operation = list_operations.list_create(bin_name="new_list", list_order=aerospike.LIST_ORDERED, pad=False,
persist_index=False, ctx=None)
self.as_connection.operate(self.test_key, [operation])
_, _, bins = self.as_connection.get(self.test_key)
assert bins["new_list"] == []

@pytest.mark.parametrize("list_order, pad, persist_index", [
("string", False, False),
(aerospike.LIST_ORDERED, 1, False),
(aerospike.LIST_ORDERED, False, 1)
]
)
def test_list_create_neg(self, list_order, pad, persist_index):
operation = list_operations.list_create(bin_name="new_list", list_order=list_order, pad=pad,
persist_index=persist_index, ctx=None)
with pytest.raises(e.ParamError):
self.as_connection.operate(self.test_key, [operation])

0 comments on commit 6ae15c1

Please sign in to comment.