4 Copyright (C) Andrew Tridgell 2004
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * Component: ldb tdb backend - indexing
30 * Description: indexing routines for ldb tdb backend
32 * Author: Andrew Tridgell
36 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_private.h"
38 #include "ldb/ldb_tdb/ldb_tdb.h"
39 #include "ldb/include/ldb_parse.h"
49 static void dn_list_free(struct ldb_context *ldb, struct dn_list *list)
52 for (i=0;i<list->count;i++) {
53 ldb_free(ldb, list->dn[i]);
55 ldb_free(ldb, list->dn);
59 return the dn key to be used for an index
62 static char *ldb_dn_key(struct ldb_context *ldb,
63 const char *attr, const struct ldb_val *value)
67 if (ldb_should_b64_encode(value)) {
68 char *vstr = ldb_base64_encode(ldb, value->data, value->length);
69 if (!vstr) return NULL;
70 ldb_asprintf(ldb, &ret, "%s:%s::%s", LTDB_INDEX, attr, vstr);
75 ldb_asprintf(ldb, &ret, "%s:%s:%.*s", LTDB_INDEX, attr, value->length, (char *)value->data);
80 see if a attribute value is in the list of indexed attributes
82 static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr,
83 unsigned int *v_idx, const char *key)
86 for (i=0;i<msg->num_elements;i++) {
87 if (ldb_attr_cmp(msg->elements[i].name, key) == 0) {
88 const struct ldb_message_element *el =
90 for (j=0;j<el->num_values;j++) {
91 if (ldb_attr_cmp((char *)el->values[j].data, attr) == 0) {
103 /* used in sorting dn lists */
104 static int list_cmp(const char **s1, const char **s2)
106 return strcmp(*s1, *s2);
110 return a list of dn's that might match a simple indexed search or
112 static int ltdb_index_dn_simple(struct ldb_module *module,
113 struct ldb_parse_tree *tree,
114 const struct ldb_message *index_list,
115 struct dn_list *list)
117 struct ldb_context *ldb = module->ldb;
121 struct ldb_message msg;
127 if the value is a wildcard then we can't do a match via indexing
129 if (ltdb_has_wildcard(module, tree->u.simple.attr, &tree->u.simple.value)) {
133 /* if the attribute isn't in the list of indexed attributes then
134 this node needs a full search */
135 if (ldb_msg_find_idx(index_list, tree->u.simple.attr, NULL, LTDB_IDXATTR) == -1) {
139 /* the attribute is indexed. Pull the list of DNs that match the
141 dn = ldb_dn_key(ldb, tree->u.simple.attr, &tree->u.simple.value);
144 ret = ltdb_search_dn1(module, dn, &msg);
146 if (ret == 0 || ret == -1) {
150 for (i=0;i<msg.num_elements;i++) {
151 struct ldb_message_element *el;
153 if (strcmp(msg.elements[i].name, LTDB_IDX) != 0) {
157 el = &msg.elements[i];
159 list->dn = ldb_malloc_array_p(ldb, char *, el->num_values);
164 for (j=0;j<el->num_values;j++) {
165 list->dn[list->count] =
166 ldb_strdup(ldb, (char *)el->values[j].data);
167 if (!list->dn[list->count]) {
168 dn_list_free(ldb, list);
169 ltdb_search_dn1_free(module, &msg);
176 ltdb_search_dn1_free(module, &msg);
178 qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) list_cmp);
184 static int list_union(struct ldb_context *, struct dn_list *, const struct dn_list *);
187 return a list of dn's that might match a simple indexed search on
188 the special objectclass attribute
190 static int ltdb_index_dn_objectclass(struct ldb_module *module,
191 struct ldb_parse_tree *tree,
192 const struct ldb_message *index_list,
193 struct dn_list *list)
195 struct ldb_context *ldb = module->ldb;
196 struct ltdb_private *ltdb = module->private_data;
199 const char *target = tree->u.simple.value.data;
204 ret = ltdb_index_dn_simple(module, tree, index_list, list);
206 for (i=0;i<ltdb->cache.subclasses.num_elements;i++) {
207 struct ldb_message_element *el = <db->cache.subclasses.elements[i];
208 if (ldb_attr_cmp(el->name, target) == 0) {
210 for (j=0;j<el->num_values;j++) {
211 struct ldb_parse_tree tree2;
212 struct dn_list list2;
213 tree2.operation = LDB_OP_SIMPLE;
214 tree2.u.simple.attr = ldb_strdup(ldb, LTDB_OBJECTCLASS);
215 if (!tree2.u.simple.attr) {
218 tree2.u.simple.value = el->values[j];
219 if (ltdb_index_dn_objectclass(module, &tree2,
220 index_list, &list2) == 1) {
221 if (list->count == 0) {
225 list_union(ldb, list, &list2);
226 dn_list_free(ldb, &list2);
229 ldb_free(ldb, tree2.u.simple.attr);
238 return a list of dn's that might match a leaf indexed search
240 static int ltdb_index_dn_leaf(struct ldb_module *module,
241 struct ldb_parse_tree *tree,
242 const struct ldb_message *index_list,
243 struct dn_list *list)
245 if (ldb_attr_cmp(tree->u.simple.attr, LTDB_OBJECTCLASS) == 0) {
246 return ltdb_index_dn_objectclass(module, tree, index_list, list);
248 return ltdb_index_dn_simple(module, tree, index_list, list);
255 relies on the lists being sorted
257 static int list_intersect(struct ldb_context *ldb,
258 struct dn_list *list, const struct dn_list *list2)
260 struct dn_list list3;
263 if (list->count == 0 || list2->count == 0) {
265 dn_list_free(ldb, list);
269 list3.dn = ldb_malloc_array_p(ldb, char *, list->count);
271 dn_list_free(ldb, list);
276 for (i=0;i<list->count;i++) {
277 if (ldb_list_find(list->dn[i], list2->dn, list2->count,
278 sizeof(char *), (comparison_fn_t)strcmp) != -1) {
279 list3.dn[list3.count] = list->dn[i];
282 ldb_free(ldb, list->dn[i]);
286 ldb_free(ldb, list->dn);
288 list->count = list3.count;
297 relies on the lists being sorted
299 static int list_union(struct ldb_context *ldb,
300 struct dn_list *list, const struct dn_list *list2)
304 unsigned int count = list->count;
306 if (list->count == 0 && list2->count == 0) {
308 dn_list_free(ldb, list);
312 d = ldb_realloc_p(ldb, list->dn, char *, list->count + list2->count);
314 dn_list_free(ldb, list);
319 for (i=0;i<list2->count;i++) {
320 if (ldb_list_find(list2->dn[i], list->dn, count,
321 sizeof(char *), (comparison_fn_t)strcmp) == -1) {
322 list->dn[list->count] = ldb_strdup(ldb, list2->dn[i]);
323 if (!list->dn[list->count]) {
324 dn_list_free(ldb, list);
331 if (list->count != count) {
332 qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)list_cmp);
338 static int ltdb_index_dn(struct ldb_module *module,
339 struct ldb_parse_tree *tree,
340 const struct ldb_message *index_list,
341 struct dn_list *list);
347 static int ltdb_index_dn_or(struct ldb_module *module,
348 struct ldb_parse_tree *tree,
349 const struct ldb_message *index_list,
350 struct dn_list *list)
352 struct ldb_context *ldb = module->ldb;
360 for (i=0;i<tree->u.list.num_elements;i++) {
361 struct dn_list list2;
363 v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, &list2);
375 dn_list_free(ldb, list);
383 if (list_union(ldb, list, &list2) == -1) {
384 dn_list_free(ldb, &list2);
387 dn_list_free(ldb, &list2);
391 if (list->count == 0) {
392 dn_list_free(ldb, list);
403 static int ltdb_index_dn_not(struct ldb_module *module,
404 struct ldb_parse_tree *tree,
405 const struct ldb_message *index_list,
406 struct dn_list *list)
408 /* the only way to do an indexed not would be if we could
409 negate the not via another not or if we knew the total
410 number of database elements so we could know that the
411 existing expression covered the whole database.
413 instead, we just give up, and rely on a full index scan
414 (unless an outer & manages to reduce the list)
420 AND two index results
422 static int ltdb_index_dn_and(struct ldb_module *module,
423 struct ldb_parse_tree *tree,
424 const struct ldb_message *index_list,
425 struct dn_list *list)
427 struct ldb_context *ldb = module->ldb;
435 for (i=0;i<tree->u.list.num_elements;i++) {
436 struct dn_list list2;
438 v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, &list2);
442 dn_list_free(ldb, list);
454 if (list_intersect(ldb, list, &list2) == -1) {
455 dn_list_free(ldb, &list2);
458 dn_list_free(ldb, &list2);
461 if (list->count == 0) {
462 if (list->dn) ldb_free(ldb, list->dn);
471 return a list of dn's that might match a indexed search or
472 -1 if an error. return 0 for no matches, or 1 for matches
474 static int ltdb_index_dn(struct ldb_module *module,
475 struct ldb_parse_tree *tree,
476 const struct ldb_message *index_list,
477 struct dn_list *list)
481 switch (tree->operation) {
483 ret = ltdb_index_dn_leaf(module, tree, index_list, list);
487 ret = ltdb_index_dn_and(module, tree, index_list, list);
491 ret = ltdb_index_dn_or(module, tree, index_list, list);
495 ret = ltdb_index_dn_not(module, tree, index_list, list);
503 filter a candidate dn_list from an indexed search into a set of results
504 extracting just the given attributes
506 static int ldb_index_filter(struct ldb_module *module, struct ldb_parse_tree *tree,
508 enum ldb_scope scope,
509 const struct dn_list *dn_list,
510 const char * const attrs[], struct ldb_message ***res)
515 for (i=0;i<dn_list->count;i++) {
516 struct ldb_message msg;
518 ret = ltdb_search_dn1(module, dn_list->dn[i], &msg);
520 /* the record has disappeared? yes, this can happen */
525 /* an internal error */
529 if (ltdb_message_match(module, &msg, tree, base, scope) == 1) {
530 ret = ltdb_add_attr_results(module, &msg, attrs, &count, res);
532 ltdb_search_dn1_free(module, &msg);
542 search the database with a LDAP-like expression using indexes
543 returns -1 if an indexed search is not possible, in which
544 case the caller should call ltdb_search_full()
546 int ltdb_search_indexed(struct ldb_module *module,
548 enum ldb_scope scope,
549 struct ldb_parse_tree *tree,
550 const char * const attrs[], struct ldb_message ***res)
552 struct ldb_context *ldb = module->ldb;
553 struct ltdb_private *ltdb = module->private_data;
554 struct dn_list dn_list;
557 if (ltdb->cache.indexlist.num_elements == 0) {
558 /* no index list? must do full search */
562 ret = ltdb_index_dn(module, tree, <db->cache.indexlist, &dn_list);
565 /* we've got a candidate list - now filter by the full tree
566 and extract the needed attributes */
567 ret = ldb_index_filter(module, tree, base, scope, &dn_list,
569 dn_list_free(ldb, &dn_list);
576 add a index element where this is the first indexed DN for this value
578 static int ltdb_index_add1_new(struct ldb_context *ldb,
579 struct ldb_message *msg,
580 struct ldb_message_element *el,
583 struct ldb_message_element *el2;
585 /* add another entry */
586 el2 = ldb_realloc_p(ldb, msg->elements,
587 struct ldb_message_element, msg->num_elements+1);
593 msg->elements[msg->num_elements].name = ldb_strdup(ldb, LTDB_IDX);
594 if (!msg->elements[msg->num_elements].name) {
597 msg->elements[msg->num_elements].num_values = 0;
598 msg->elements[msg->num_elements].values = ldb_malloc_p(ldb, struct ldb_val);
599 if (!msg->elements[msg->num_elements].values) {
602 msg->elements[msg->num_elements].values[0].length = strlen(dn);
603 msg->elements[msg->num_elements].values[0].data = dn;
604 msg->elements[msg->num_elements].num_values = 1;
612 add a index element where this is not the first indexed DN for this
615 static int ltdb_index_add1_add(struct ldb_context *ldb,
616 struct ldb_message *msg,
617 struct ldb_message_element *el,
624 /* for multi-valued attributes we can end up with repeats */
625 for (i=0;i<msg->elements[idx].num_values;i++) {
626 if (strcmp(dn, msg->elements[idx].values[i].data) == 0) {
631 v2 = ldb_realloc_p(ldb, msg->elements[idx].values,
633 msg->elements[idx].num_values+1);
637 msg->elements[idx].values = v2;
639 msg->elements[idx].values[msg->elements[idx].num_values].length = strlen(dn);
640 msg->elements[idx].values[msg->elements[idx].num_values].data = dn;
641 msg->elements[idx].num_values++;
647 add an index entry for one message element
649 static int ltdb_index_add1(struct ldb_module *module, char *dn,
650 struct ldb_message_element *el, int v_idx)
652 struct ldb_context *ldb = module->ldb;
653 struct ldb_message msg;
655 int ret, added=0, added_dn=0;
658 dn_key = ldb_dn_key(ldb, el->name, &el->values[v_idx]);
663 ret = ltdb_search_dn1(module, dn_key, &msg);
665 ldb_free(ldb, dn_key);
671 msg.dn = ldb_strdup(ldb, dn_key);
673 ldb_free(ldb, dn_key);
677 msg.num_elements = 0;
679 msg.private_data = NULL;
682 ldb_free(ldb, dn_key);
684 for (i=0;i<msg.num_elements;i++) {
685 if (strcmp(LTDB_IDX, msg.elements[i].name) == 0) {
690 if (i == msg.num_elements) {
692 ret = ltdb_index_add1_new(ldb, &msg, el, dn);
694 ret = ltdb_index_add1_add(ldb, &msg, el, i, dn);
698 ret = ltdb_store(module, &msg, TDB_REPLACE);
702 ldb_free(ldb, msg.elements[i].name);
705 ldb_free(ldb, msg.dn);
708 ltdb_search_dn1_free(module, &msg);
714 add the index entries for a new record
717 int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg)
719 struct ltdb_private *ltdb = module->private_data;
723 if (ltdb->cache.indexlist.num_elements == 0) {
724 /* no indexed fields */
728 for (i=0;i<msg->num_elements;i++) {
729 ret = ldb_msg_find_idx(<db->cache.indexlist, msg->elements[i].name,
734 for (j=0;j<msg->elements[i].num_values;j++) {
735 ret = ltdb_index_add1(module, msg->dn, &msg->elements[i], j);
747 delete an index entry for one message element
749 int ltdb_index_del_value(struct ldb_module *module, const char *dn,
750 struct ldb_message_element *el, int v_idx)
752 struct ldb_context *ldb = module->ldb;
753 struct ldb_message msg;
758 dn_key = ldb_dn_key(ldb, el->name, &el->values[v_idx]);
763 ret = ltdb_search_dn1(module, dn_key, &msg);
765 ldb_free(ldb, dn_key);
770 /* it wasn't indexed. Did we have an earlier error? If we did then
772 ldb_free(ldb, dn_key);
776 i = ldb_msg_find_idx(&msg, dn, &j, LTDB_IDX);
778 ldb_debug(ldb, LDB_DEBUG_ERROR, "ERROR: dn %s not found in %s\n", dn, dn_key);
779 /* it ain't there. hmmm */
780 ltdb_search_dn1_free(module, &msg);
781 ldb_free(ldb, dn_key);
785 if (j != msg.elements[i].num_values - 1) {
786 memmove(&msg.elements[i].values[j],
787 &msg.elements[i].values[j+1],
788 (msg.elements[i].num_values-(j+1)) *
789 sizeof(msg.elements[i].values[0]));
791 msg.elements[i].num_values--;
793 if (msg.elements[i].num_values == 0) {
794 ret = ltdb_delete_noindex(module, dn_key);
796 ret = ltdb_store(module, &msg, TDB_REPLACE);
799 ltdb_search_dn1_free(module, &msg);
800 ldb_free(ldb, dn_key);
806 delete the index entries for a record
809 int ltdb_index_del(struct ldb_module *module, const struct ldb_message *msg)
811 struct ltdb_private *ltdb = module->private_data;
815 /* find the list of indexed fields */
816 if (ltdb->cache.indexlist.num_elements == 0) {
817 /* no indexed fields */
821 for (i=0;i<msg->num_elements;i++) {
822 ret = ldb_msg_find_idx(<db->cache.indexlist, msg->elements[i].name,
827 for (j=0;j<msg->elements[i].num_values;j++) {
828 ret = ltdb_index_del_value(module, msg->dn, &msg->elements[i], j);
840 traversal function that deletes all @INDEX records
842 static int delete_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
844 const char *dn = "DN=" LTDB_INDEX ":";
845 if (strncmp(key.dptr, dn, strlen(dn)) == 0) {
846 return tdb_delete(tdb, key);
852 traversal function that adds @INDEX records during a re index
854 static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
856 struct ldb_module *module = state;
857 struct ldb_message msg;
860 if (strncmp(key.dptr, "DN=@", 4) == 0 ||
861 strncmp(key.dptr, "DN=", 3) != 0) {
865 ret = ltdb_unpack_data(module, &data, &msg);
874 ret = ltdb_index_add(module, &msg);
876 ltdb_unpack_data_free(module, &msg);
882 force a complete reindex of the database
884 int ltdb_reindex(struct ldb_module *module)
886 struct ltdb_private *ltdb = module->private_data;
889 ltdb_cache_free(module);
891 if (ltdb_cache_load(module) != 0) {
895 /* first traverse the database deleting any @INDEX records */
896 ret = tdb_traverse(ltdb->tdb, delete_index, NULL);
902 /* now traverse adding any indexes for normal LDB records */
903 ret = tdb_traverse(ltdb->tdb, re_index, module);