diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 99344495c04e..81cb722bf4aa 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -357,6 +357,7 @@ bif ets:delete/1
bif ets:delete/2
bif ets:delete_object/2
bif ets:first/1
+bif ets:first_lookup/1
bif ets:is_compiled_ms/1
bif ets:lookup/2
bif ets:lookup_element/3
@@ -364,6 +365,7 @@ bif ets:lookup_element/4
bif ets:info/1
bif ets:info/2
bif ets:last/1
+bif ets:last_lookup/1
bif ets:match/1
bif ets:match/2
bif ets:match/3
@@ -372,7 +374,9 @@ bif ets:match_object/2
bif ets:match_object/3
bif ets:member/2
bif ets:next/2
+bif ets:next_lookup/2
bif ets:prev/2
+bif ets:prev_lookup/2
bif ets:insert/2
bif ets:insert_new/2
bif ets:rename/2
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 009a0d2dd45e..e5e5fcf33773 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -1115,6 +1115,29 @@ BIF_RETTYPE ets_first_1(BIF_ALIST_1)
BIF_RET(ret);
}
+/*
+** Returns the first {key, object(s)} in a table
+*/
+BIF_RETTYPE ets_first_lookup_1(BIF_ALIST_1)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_ets_first_lookup_1);
+
+ cret = tb->common.meth->db_first_lookup(BIF_P, tb, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
/*
** The next BIF, given a key, return the "next" key
*/
@@ -1138,6 +1161,30 @@ BIF_RETTYPE ets_next_2(BIF_ALIST_2)
BIF_RET(ret);
}
+
+/*
+** The next_lookup BIF, given a key, return the "next" {key, object(s)}
+*/
+BIF_RETTYPE ets_next_lookup_2(BIF_ALIST_2)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_ets_next_lookup_2);
+
+ cret = tb->common.meth->db_next_lookup(BIF_P, tb, BIF_ARG_2, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
/*
** Returns the last Key in a table
*/
@@ -1161,6 +1208,29 @@ BIF_RETTYPE ets_last_1(BIF_ALIST_1)
BIF_RET(ret);
}
+/*
+** Returns the last {key, object(s)} in a table
+*/
+BIF_RETTYPE ets_last_lookup_1(BIF_ALIST_1)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_ets_last_lookup_1);
+
+ cret = tb->common.meth->db_last_lookup(BIF_P, tb, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
/*
** The prev BIF, given a key, return the "previous" key
*/
@@ -1184,6 +1254,29 @@ BIF_RETTYPE ets_prev_2(BIF_ALIST_2)
BIF_RET(ret);
}
+/*
+** The prev_lookup BIF, given a key, return the "previous" {key, object(s)}
+*/
+BIF_RETTYPE ets_prev_lookup_2(BIF_ALIST_2)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_ets_prev_lookup_2);
+
+ cret = tb->common.meth->db_prev_lookup(BIF_P, tb, BIF_ARG_2, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
/*
** take(Tab, Key)
*/
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index 700003438c65..e441faf0bd92 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -98,13 +98,22 @@ static SWord do_delete_base_node_cont(DbTableCATree *tb,
/* Method interface functions */
static int db_first_catree(Process *p, DbTable *tbl,
Eterm *ret);
+static int db_first_lookup_catree(Process *p, DbTable *tbl,
+ Eterm *ret);
static int db_next_catree(Process *p, DbTable *tbl,
Eterm key, Eterm *ret);
+static int db_next_lookup_catree(Process *p, DbTable *tbl,
+ Eterm key, Eterm *ret);
static int db_last_catree(Process *p, DbTable *tbl,
Eterm *ret);
+static int db_last_lookup_catree(Process *p, DbTable *tbl,
+ Eterm *ret);
static int db_prev_catree(Process *p, DbTable *tbl,
Eterm key,
Eterm *ret);
+static int db_prev_lookup_catree(Process *p, DbTable *tbl,
+ Eterm key,
+ Eterm *ret);
static int db_put_catree(DbTable *tbl, Eterm obj, int key_clash_fail,
SWord *consumed_reds_p);
static int db_get_catree(Process *p, DbTable *tbl,
@@ -227,7 +236,11 @@ DbTableMethod db_catree =
db_get_dbterm_key_tree_common,
db_get_binary_info_catree,
db_first_catree, /* raw_first same as first */
- db_next_catree /* raw_next same as next */
+ db_next_catree, /* raw_next same as next */
+ db_first_lookup_catree,
+ db_next_lookup_catree,
+ db_last_lookup_catree,
+ db_prev_lookup_catree
};
/*
@@ -1567,7 +1580,7 @@ int db_create_catree(Process *p, DbTable *tbl)
return DB_ERROR_NONE;
}
-static int db_first_catree(Process *p, DbTable *tbl, Eterm *ret)
+static int db_first_catree_common(Process *p, DbTable *tbl, Eterm *ret, Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
TreeDbTerm *root;
CATreeRootIterator iter;
@@ -1580,13 +1593,23 @@ static int db_first_catree(Process *p, DbTable *tbl, Eterm *ret)
root = pp ? *pp : NULL;
}
- result = db_first_tree_common(p, tbl, root, ret, NULL);
+ result = db_first_tree_common(p, tbl, root, ret, NULL, func);
destroy_root_iterator(&iter);
return result;
}
-static int db_next_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+static int db_first_catree(Process *p, DbTable *tbl, Eterm *ret)
+{
+ return db_first_catree_common(p, tbl, ret, db_copy_key_tree);
+}
+
+static int db_first_lookup_catree(Process *p, DbTable *tbl, Eterm *ret)
+{
+ return db_first_catree_common(p, tbl, ret, db_copy_key_and_object_tree);
+}
+
+static int db_next_catree_common(Process *p, DbTable *tbl, Eterm key, Eterm *ret, Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
DbTreeStack stack;
TreeDbTerm * stack_array[STACK_NEED];
@@ -1600,7 +1623,7 @@ static int db_next_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
do {
init_tree_stack(&stack, stack_array, 0);
- result = db_next_tree_common(p, tbl, (rootp ? *rootp : NULL), key, ret, &stack);
+ result = db_next_tree_common(p, tbl, (rootp ? *rootp : NULL), key, ret, &stack, func);
if (result != DB_ERROR_NONE || *ret != am_EOT)
break;
@@ -1611,7 +1634,17 @@ static int db_next_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
return result;
}
-static int db_last_catree(Process *p, DbTable *tbl, Eterm *ret)
+static int db_next_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ return db_next_catree_common(p, tbl, key, ret, db_copy_key_tree);
+}
+
+static int db_next_lookup_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ return db_next_catree_common(p, tbl, key, ret, db_copy_key_and_object_tree);
+}
+
+static int db_last_catree_common(Process *p, DbTable *tbl, Eterm *ret, Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
TreeDbTerm *root;
CATreeRootIterator iter;
@@ -1624,13 +1657,23 @@ static int db_last_catree(Process *p, DbTable *tbl, Eterm *ret)
root = pp ? *pp : NULL;
}
- result = db_last_tree_common(p, tbl, root, ret, NULL);
+ result = db_last_tree_common(p, tbl, root, ret, NULL, func);
destroy_root_iterator(&iter);
return result;
}
-static int db_prev_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+static int db_last_catree(Process *p, DbTable *tbl, Eterm *ret)
+{
+ return db_last_catree_common(p, tbl, ret, db_copy_key_tree);
+}
+
+static int db_last_lookup_catree(Process *p, DbTable *tbl, Eterm *ret)
+{
+ return db_last_catree_common(p, tbl, ret, db_copy_key_and_object_tree);
+}
+
+static int db_prev_catree_common(Process *p, DbTable *tbl, Eterm key, Eterm *ret, Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
DbTreeStack stack;
TreeDbTerm * stack_array[STACK_NEED];
@@ -1645,7 +1688,7 @@ static int db_prev_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
do {
init_tree_stack(&stack, stack_array, 0);
result = db_prev_tree_common(p, tbl, (rootp ? *rootp : NULL), key, ret,
- &stack);
+ &stack, func);
if (result != DB_ERROR_NONE || *ret != am_EOT)
break;
rootp = catree_find_prev_root(&iter, NULL);
@@ -1655,6 +1698,16 @@ static int db_prev_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
return result;
}
+static int db_prev_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ return db_prev_catree_common(p, tbl, key, ret, db_copy_key_tree);
+}
+
+static int db_prev_lookup_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ return db_prev_catree_common(p, tbl, key, ret, db_copy_key_and_object_tree);
+}
+
static int db_put_dbterm_catree(DbTable* tbl,
void* obj,
int key_clash_fail,
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index 05ee2d1be548..df81264908cf 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -643,6 +643,8 @@ static void shrink(DbTableHash* tb, int nitems);
static void grow(DbTableHash* tb, int nitems);
static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2,
Uint sz, DbTableHash*);
+static Eterm get_term_list(Process *p, DbTableHash *tb, Eterm key, HashValue hval,
+ HashDbTerm *b1, HashDbTerm **bend);
static int analyze_pattern(DbTableHash *tb, Eterm pattern,
ExtraMatchValidatorF*, /* Optional callback */
struct mp_info *mpi);
@@ -654,11 +656,20 @@ static int db_first_hash(Process *p,
DbTable *tbl,
Eterm *ret);
+static int db_first_lookup_hash(Process *p,
+ DbTable *tbl,
+ Eterm *ret);
+
static int db_next_hash(Process *p,
DbTable *tbl,
Eterm key,
Eterm *ret);
+static int db_next_lookup_hash(Process *p,
+ DbTable *tbl,
+ Eterm key,
+ Eterm *ret);
+
static int db_member_hash(DbTable *tbl, Eterm key, Eterm *ret);
static int db_get_element_hash(Process *p, DbTable *tbl,
@@ -873,7 +884,11 @@ DbTableMethod db_hash =
db_get_dbterm_key_hash,
db_get_binary_info_hash,
db_raw_first_hash,
- db_raw_next_hash
+ db_raw_next_hash,
+ db_first_lookup_hash,
+ db_next_lookup_hash,
+ db_first_lookup_hash, /* last == first */
+ db_next_lookup_hash /* prev == next */
};
#ifdef DEBUG
@@ -1072,7 +1087,32 @@ int db_create_hash(Process *p, DbTable *tbl)
return DB_ERROR_NONE;
}
-static int db_first_hash(Process *p, DbTable *tbl, Eterm *ret)
+static ERTS_INLINE Eterm db_copy_key_hash(Process* p, DbTable* tbl, HashDbTerm* b)
+{
+ Eterm key = GETKEY(&tbl->common, b->dbterm.tpl);
+ if is_immed(key) return key;
+ else {
+ Uint size = size_object(key);
+ Eterm* hp = HAlloc(p, size);
+ Eterm res = copy_struct(key, size, &hp, &MSO(p));
+ ASSERT(EQ(res,key));
+ return res;
+ }
+}
+
+static ERTS_INLINE Eterm db_copy_key_and_objects_hash(Process* p, DbTable* tbl, HashDbTerm* b) {
+ Eterm key = db_copy_key_hash(p, tbl, b);
+ HashValue hval = MAKE_HASH(key);
+ DbTableHash *tb = &tbl->hash;
+ Eterm objects = get_term_list(p, tb, key, hval, b, NULL);
+ Eterm *hp, res;
+ hp = HAlloc(p, 3);
+ res = TUPLE2(hp, key, objects);
+
+ return res;
+}
+
+static int db_first_hash_common(Process *p, DbTable *tbl, Eterm *ret, Eterm (*func)(Process *, DbTable *, HashDbTerm *))
{
DbTableHash *tb = &tbl->hash;
Uint ix = 0;
@@ -1083,7 +1123,7 @@ static int db_first_hash(Process *p, DbTable *tbl, Eterm *ret)
list = next_live(tb, &ix, &lck, list);
if (list != NULL) {
- *ret = db_copy_key(p, tbl, &list->dbterm);
+ *ret = (*func)(p, tbl, list);
RUNLOCK_HASH(lck);
}
else {
@@ -1092,8 +1132,17 @@ static int db_first_hash(Process *p, DbTable *tbl, Eterm *ret)
return DB_ERROR_NONE;
}
+static int db_first_hash(Process *p, DbTable *tbl, Eterm *ret)
+{
+ return db_first_hash_common(p, tbl, ret, db_copy_key_hash);
+}
-static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+static int db_first_lookup_hash(Process *p, DbTable *tbl, Eterm *ret)
+{
+ return db_first_hash_common(p, tbl, ret, db_copy_key_and_objects_hash);
+}
+
+static int db_next_hash_common(Process *p, DbTable *tbl, Eterm key, Eterm *ret, Eterm (*func)(Process *, DbTable *, HashDbTerm *))
{
DbTableHash *tb = &tbl->hash;
HashValue hval;
@@ -1132,12 +1181,23 @@ static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
}
else {
ASSERT(!is_pseudo_deleted(b));
- *ret = db_copy_key(p, tbl, &b->dbterm);
+ *ret = (*func)(p, tbl, b);
RUNLOCK_HASH(lck);
}
return DB_ERROR_NONE;
}
+static int db_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ return db_next_hash_common(p, tbl, key, ret, db_copy_key_hash);
+}
+
+
+static int db_next_lookup_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ return db_next_hash_common(p, tbl, key, ret, db_copy_key_and_objects_hash);
+}
+
struct tmp_uncomp_term {
Eterm term;
ErlOffHeap oh;
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 31834d413117..66e644f6e11e 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -402,13 +402,22 @@ static BIF_RETTYPE ets_select_reverse(BIF_ALIST_3);
/* Method interface functions */
static int db_first_tree(Process *p, DbTable *tbl,
Eterm *ret);
+static int db_first_lookup_tree(Process *p, DbTable *tbl,
+ Eterm *ret);
static int db_next_tree(Process *p, DbTable *tbl,
Eterm key, Eterm *ret);
+static int db_next_lookup_tree(Process *p, DbTable *tbl,
+ Eterm key, Eterm *ret);
static int db_last_tree(Process *p, DbTable *tbl,
Eterm *ret);
+static int db_last_lookup_tree(Process *p, DbTable *tbl,
+ Eterm *ret);
static int db_prev_tree(Process *p, DbTable *tbl,
Eterm key,
Eterm *ret);
+static int db_prev_lookup_tree(Process *p, DbTable *tbl,
+ Eterm key,
+ Eterm *ret);
static int db_put_tree(DbTable *tbl, Eterm obj, int key_clash_fail, SWord *consumed_reds_p);
static int db_get_tree(Process *p, DbTable *tbl,
Eterm key, Eterm *ret);
@@ -526,7 +535,11 @@ DbTableMethod db_tree =
db_get_dbterm_key_tree_common,
db_get_binary_info_tree,
db_first_tree, /* raw_first same as first */
- db_next_tree /* raw_next same as next */
+ db_next_tree, /* raw_next same as next */
+ db_first_lookup_tree,
+ db_next_lookup_tree,
+ db_last_lookup_tree,
+ db_prev_lookup_tree
};
@@ -558,8 +571,40 @@ int db_create_tree(Process *p, DbTable *tbl)
return DB_ERROR_NONE;
}
+Eterm db_copy_key_tree(Process* p, DbTable* tbl, TreeDbTerm* node)
+{
+ Eterm key = GETKEY(&tbl->common, node->dbterm.tpl);
+ if is_immed(key) return key;
+ else {
+ Uint size = size_object(key);
+ Eterm* hp = HAlloc(p, size);
+ Eterm res = copy_struct(key, size, &hp, &MSO(p));
+ ASSERT(EQ(res,key));
+ return res;
+ }
+}
+
+Eterm db_copy_key_and_object_tree(Process* p, DbTable* tbl, TreeDbTerm* node) {
+ Eterm key = db_copy_key_tree(p, tbl, node);
+ Eterm *hp, *hend, copy, object, res;
+
+ // +2 for CONS and +3 for TUPLE2
+ int size = node->dbterm.size + 2 + 3;
+ hp = HAlloc(p, size);
+ hend = hp + size;
+ copy = db_copy_object_from_ets(&tbl->common, &node->dbterm, &hp, &MSO(p));
+ object = CONS(hp, copy, NIL);
+ hp += 2;
+ res = TUPLE2(hp, key, object);
+ hp += 3;
+ HRelease(p,hend,hp);
+
+ return res;
+}
+
int db_first_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
- Eterm *ret, DbTableTree *stack_container)
+ Eterm *ret, DbTableTree *stack_container,
+ Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
DbTreeStack* stack;
TreeDbTerm *this;
@@ -581,19 +626,26 @@ int db_first_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
stack->slot = 1;
release_stack(tbl,stack_container,stack);
}
- *ret = db_copy_key(p, tbl, &this->dbterm);
+ *ret = (*func)(p, tbl, this);
return DB_ERROR_NONE;
}
static int db_first_tree(Process *p, DbTable *tbl, Eterm *ret)
{
DbTableTree *tb = &tbl->tree;
- return db_first_tree_common(p, tbl, tb->root, ret, tb);
+ return db_first_tree_common(p, tbl, tb->root, ret, tb, db_copy_key_tree);
+}
+
+static int db_first_lookup_tree(Process *p, DbTable *tbl, Eterm *ret)
+{
+ DbTableTree *tb = &tbl->tree;
+ return db_first_tree_common(p, tbl, tb->root, ret, tb, db_copy_key_and_object_tree);
}
int db_next_tree_common(Process *p, DbTable *tbl,
TreeDbTerm *root, Eterm key,
- Eterm *ret, DbTreeStack* stack)
+ Eterm *ret, DbTreeStack* stack,
+ Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
TreeDbTerm *this;
@@ -604,7 +656,7 @@ int db_next_tree_common(Process *p, DbTable *tbl,
*ret = am_EOT;
return DB_ERROR_NONE;
}
- *ret = db_copy_key(p, tbl, &this->dbterm);
+ *ret = (*func)(p, tbl, this);
return DB_ERROR_NONE;
}
@@ -612,13 +664,23 @@ static int db_next_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
{
DbTableTree *tb = &tbl->tree;
DbTreeStack* stack = get_any_stack(tbl, tb);
- int ret_val = db_next_tree_common(p, tbl, tb->root, key, ret, stack);
+ int ret_val = db_next_tree_common(p, tbl, tb->root, key, ret, stack, db_copy_key_tree);
+ release_stack(tbl,tb,stack);
+ return ret_val;
+}
+
+static int db_next_lookup_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ DbTableTree *tb = &tbl->tree;
+ DbTreeStack* stack = get_any_stack(tbl, tb);
+ int ret_val = db_next_tree_common(p, tbl, tb->root, key, ret, stack, db_copy_key_and_object_tree);
release_stack(tbl,tb,stack);
return ret_val;
}
int db_last_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
- Eterm *ret, DbTableTree *stack_container)
+ Eterm *ret, DbTableTree *stack_container,
+ Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
TreeDbTerm *this;
DbTreeStack* stack;
@@ -641,18 +703,24 @@ int db_last_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
stack->slot = NITEMS_CENTRALIZED(tbl);
release_stack(tbl,stack_container,stack);
}
- *ret = db_copy_key(p, tbl, &this->dbterm);
+ *ret = (*func)(p, tbl, this);
return DB_ERROR_NONE;
}
static int db_last_tree(Process *p, DbTable *tbl, Eterm *ret)
{
DbTableTree *tb = &tbl->tree;
- return db_last_tree_common(p, tbl, tb->root, ret, tb);
+ return db_last_tree_common(p, tbl, tb->root, ret, tb, db_copy_key_tree);
+}
+
+static int db_last_lookup_tree(Process *p, DbTable *tbl, Eterm *ret)
+{
+ DbTableTree *tb = &tbl->tree;
+ return db_last_tree_common(p, tbl, tb->root, ret, tb, db_copy_key_and_object_tree);
}
int db_prev_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, Eterm key,
- Eterm *ret, DbTreeStack* stack)
+ Eterm *ret, DbTreeStack* stack, Eterm (*func)(Process *, DbTable *, TreeDbTerm *))
{
TreeDbTerm *this;
@@ -663,7 +731,7 @@ int db_prev_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, Eterm key,
*ret = am_EOT;
return DB_ERROR_NONE;
}
- *ret = db_copy_key(p, tbl, &this->dbterm);
+ *ret = (*func)(p, tbl, this);
return DB_ERROR_NONE;
}
@@ -671,7 +739,16 @@ static int db_prev_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
{
DbTableTree *tb = &tbl->tree;
DbTreeStack* stack = get_any_stack(tbl, tb);
- int res = db_prev_tree_common(p, tbl, tb->root, key, ret, stack);
+ int res = db_prev_tree_common(p, tbl, tb->root, key, ret, stack, db_copy_key_tree);
+ release_stack(tbl,tb,stack);
+ return res;
+}
+
+static int db_prev_lookup_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ DbTableTree *tb = &tbl->tree;
+ DbTreeStack* stack = get_any_stack(tbl, tb);
+ int res = db_prev_tree_common(p, tbl, tb->root, key, ret, stack, db_copy_key_and_object_tree);
release_stack(tbl,tb,stack);
return res;
}
diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h
index 08d55e337315..4cb238298fd6 100644
--- a/erts/emulator/beam/erl_db_tree_util.h
+++ b/erts/emulator/beam/erl_db_tree_util.h
@@ -86,14 +86,18 @@ int tree_balance_left(TreeDbTerm **this);
int tree_balance_right(TreeDbTerm **this);
int db_first_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
- Eterm *ret, DbTableTree *stack_container);
+ Eterm *ret, DbTableTree *stack_container,
+ Eterm (*func)(Process *, DbTable *, TreeDbTerm *));
int db_next_tree_common(Process *p, DbTable *tbl,
TreeDbTerm *root, Eterm key,
- Eterm *ret, DbTreeStack* stack);
+ Eterm *ret, DbTreeStack* stack,
+ Eterm (*func)(Process *, DbTable *, TreeDbTerm *));
int db_last_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
- Eterm *ret, DbTableTree *stack_container);
+ Eterm *ret, DbTableTree *stack_container,
+ Eterm (*func)(Process *, DbTable *, TreeDbTerm *));
int db_prev_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, Eterm key,
- Eterm *ret, DbTreeStack* stack);
+ Eterm *ret, DbTreeStack* stack,
+ Eterm (*func)(Process *, DbTable *, TreeDbTerm *));
int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj,
int key_clash_fail, DbTableTree *stack_container);
int db_get_tree_common(Process *p, DbTableCommon *tb, TreeDbTerm *root, Eterm key,
@@ -184,4 +188,7 @@ TreeDbTerm *db_find_tree_node_common(DbTableCommon*, TreeDbTerm *root,
Eterm key);
Eterm db_binary_info_tree_common(Process*, TreeDbTerm*);
+Eterm db_copy_key_tree(Process* p, DbTable* tbl, TreeDbTerm* node);
+Eterm db_copy_key_and_object_tree(Process* p, DbTable* tbl, TreeDbTerm* node);
+
#endif /* _DB_TREE_UTIL_H */
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 35e320482c72..9aeaeed28cc1 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -256,6 +256,21 @@ typedef struct db_table_method
Only internal use by ets:info(_,binary) */
int (*db_raw_first)(Process*, DbTable*, Eterm* ret);
int (*db_raw_next)(Process*, DbTable*, Eterm key, Eterm* ret);
+ /* Same as first/last/next/prev, but returns object(s) along with key */
+ int (*db_first_lookup)(Process* p,
+ DbTable* tb, /* [in out] */
+ Eterm* ret /* [out] */);
+ int (*db_next_lookup)(Process* p,
+ DbTable* tb, /* [in out] */
+ Eterm key, /* [in] */
+ Eterm* ret /* [out] */);
+ int (*db_last_lookup)(Process* p,
+ DbTable* tb, /* [in out] */
+ Eterm* ret /* [out] */);
+ int (*db_prev_lookup)(Process* p,
+ DbTable* tb, /* [in out] */
+ Eterm key,
+ Eterm* ret);
} DbTableMethod;
typedef struct db_fixation {
@@ -392,7 +407,7 @@ Wterm db_do_read_element(DbUpdateHandle* handle, Sint position);
ERTS_GLB_INLINE Eterm db_copy_key(Process* p, DbTable* tb, DbTerm* obj)
{
Eterm key = GETKEY(tb, obj->tpl);
- if IS_CONST(key) return key;
+ if is_immed(key) return key;
else {
Uint size = size_object(key);
Eterm* hp = HAlloc(p, size);
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 792127131d44..93ed64000be8 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -197,6 +197,13 @@
Single-step traversal one key at at time, but using
+
Search with simple match patterns, using
Similar to To find subsequent objects in the table, use
+ Similar to To find preceding objects in the table, use
+ Similar to
+ It can be interleaved with Similar to
+ It can be interleaved with