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/ldb_tdb/ldb_tdb.h"
46 static void dn_list_free(struct dn_list *list)
49 for (i=0;i<list->count;i++) {
52 if (list->dn) free(list->dn);
56 return the dn key to be used for an index
59 static char *ldb_dn_key(const char *attr, const struct ldb_val *value)
63 if (ldb_should_b64_encode(value)) {
64 char *vstr = ldb_base64_encode(value->data, value->length);
65 if (!vstr) return NULL;
66 asprintf(&ret, "@INDEX:%s::%s", attr, vstr);
71 asprintf(&ret, "@INDEX:%s:%s", attr, (char *)value->data);
76 see if a attribute value is in the list of indexed attributes
78 static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr,
82 for (i=0;i<msg->num_elements;i++) {
83 if (strcmp(msg->elements[i].name, "@IDXATTR") == 0) {
84 const struct ldb_message_element *el =
86 for (j=0;j<el->num_values;j++) {
87 if (strcmp((char *)el->values[j].data, attr) == 0) {
100 return a list of dn's that might match a simple indexed search or
102 static int ltdb_index_dn_simple(struct ldb_context *ldb,
103 struct ldb_parse_tree *tree,
104 const struct ldb_message *index_list,
105 struct dn_list *list)
109 struct ldb_message msg;
115 if the value is a wildcard then we can't do a match via indexing
117 if (ltdb_has_wildcard(&tree->u.simple.value)) {
121 /* if the attribute isn't in the list of indexed attributes then
122 this node needs a full search */
123 if (ldb_msg_find_idx(index_list, tree->u.simple.attr, NULL) == -1) {
127 /* the attribute is indexed. Pull the list of DNs that match the
129 dn = ldb_dn_key(tree->u.simple.attr, &tree->u.simple.value);
132 ret = ltdb_search_dn1(ldb, dn, &msg);
134 if (ret == 0 || ret == -1) {
138 for (i=0;i<msg.num_elements;i++) {
139 struct ldb_message_element *el;
141 if (strcmp(msg.elements[i].name, "@IDX") != 0) {
145 el = &msg.elements[i];
147 list->dn = malloc_array_p(char *, el->num_values);
152 for (j=0;j<el->num_values;j++) {
153 list->dn[list->count] =
154 strdup((char *)el->values[j].data);
155 if (!list->dn[list->count]) {
157 ltdb_search_dn1_free(ldb, &msg);
164 ltdb_search_dn1_free(ldb, &msg);
166 qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) strcmp);
174 relies on the lists being sorted
176 static int list_intersect(struct dn_list *list, const struct dn_list *list2)
178 struct dn_list list3;
181 if (list->count == 0 || list2->count == 0) {
187 list3.dn = malloc_array_p(char *, list->count);
194 for (i=0;i<list->count;i++) {
195 if (list_find(list->dn[i], list2->dn, list2->count,
196 sizeof(char *), (comparison_fn_t)strcmp) != -1) {
197 list3.dn[list3.count] = list->dn[i];
206 list->count = list3.count;
215 relies on the lists being sorted
217 static int list_union(struct dn_list *list, const struct dn_list *list2)
221 unsigned int count = list->count;
223 if (list->count == 0 && list2->count == 0) {
229 d = realloc_p(list->dn, char *, list->count + list2->count);
236 for (i=0;i<list2->count;i++) {
237 if (list_find(list2->dn[i], list->dn, count,
238 sizeof(char *), (comparison_fn_t)strcmp) == -1) {
239 list->dn[list->count] = strdup(list2->dn[i]);
240 if (!list->dn[list->count]) {
248 if (list->count != count) {
249 qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)strcmp);
255 static int ltdb_index_dn(struct ldb_context *ldb,
256 struct ldb_parse_tree *tree,
257 const struct ldb_message *index_list,
258 struct dn_list *list);
264 static int ltdb_index_dn_or(struct ldb_context *ldb,
265 struct ldb_parse_tree *tree,
266 const struct ldb_message *index_list,
267 struct dn_list *list)
275 for (i=0;i<tree->u.list.num_elements;i++) {
276 struct dn_list list2;
278 v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2);
298 if (list_union(list, &list2) == -1) {
299 dn_list_free(&list2);
302 dn_list_free(&list2);
306 if (list->count == 0) {
318 static int ltdb_index_dn_not(struct ldb_context *ldb,
319 struct ldb_parse_tree *tree,
320 const struct ldb_message *index_list,
321 struct dn_list *list)
323 /* the only way to do an indexed not would be if we could
324 negate the not via another not or if we knew the total
325 number of database elements so we could know that the
326 existing expression covered the whole database.
328 instead, we just give up, and rely on a full index scan
329 (unless an outer & manages to reduce the list)
335 AND two index results
337 static int ltdb_index_dn_and(struct ldb_context *ldb,
338 struct ldb_parse_tree *tree,
339 const struct ldb_message *index_list,
340 struct dn_list *list)
348 for (i=0;i<tree->u.list.num_elements;i++) {
349 struct dn_list list2;
351 v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2);
367 if (list_intersect(list, &list2) == -1) {
368 dn_list_free(&list2);
371 dn_list_free(&list2);
374 if (list->count == 0) {
375 if (list->dn) free(list->dn);
384 return a list of dn's that might match a indexed search or
385 -1 if an error. return 0 for no matches, or 1 for matches
387 static int ltdb_index_dn(struct ldb_context *ldb,
388 struct ldb_parse_tree *tree,
389 const struct ldb_message *index_list,
390 struct dn_list *list)
394 switch (tree->operation) {
396 ret = ltdb_index_dn_simple(ldb, tree, index_list, list);
400 ret = ltdb_index_dn_and(ldb, tree, index_list, list);
404 ret = ltdb_index_dn_or(ldb, tree, index_list, list);
408 ret = ltdb_index_dn_not(ldb, tree, index_list, list);
416 filter a candidate dn_list from an indexed search into a set of results
417 extracting just the given attributes
419 static int ldb_index_filter(struct ldb_context *ldb, struct ldb_parse_tree *tree,
421 enum ldb_scope scope,
422 const struct dn_list *dn_list,
423 const char *attrs[], struct ldb_message ***res)
426 unsigned int count = 0;
428 for (i=0;i<dn_list->count;i++) {
429 struct ldb_message msg;
431 ret = ltdb_search_dn1(ldb, dn_list->dn[i], &msg);
433 /* the record has disappeared? yes, this can happen */
438 /* an internal error */
442 if (ldb_message_match(ldb, &msg, tree, base, scope) == 1) {
443 ret = ltdb_add_attr_results(ldb, &msg, attrs, &count, res);
445 ltdb_search_dn1_free(ldb, &msg);
455 search the database with a LDAP-like expression using indexes
456 returns -1 if an indexed search is not possible, in which
457 case the caller should call ltdb_search_full()
459 int ltdb_search_indexed(struct ldb_context *ldb,
461 enum ldb_scope scope,
462 struct ldb_parse_tree *tree,
463 const char *attrs[], struct ldb_message ***res)
465 struct ldb_message index_list;
466 struct dn_list dn_list;
469 /* find the list of indexed fields */
470 ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
472 /* no index list? must do full search */
476 ret = ltdb_index_dn(ldb, tree, &index_list, &dn_list);
477 ltdb_search_dn1_free(ldb, &index_list);
480 /* we've got a candidate list - now filter by the full tree
481 and extract the needed attributes */
482 ret = ldb_index_filter(ldb, tree, base, scope, &dn_list,
484 dn_list_free(&dn_list);
491 add a index element where this is the first indexed DN for this value
493 static int ltdb_index_add1_new(struct ldb_context *ldb,
494 struct ldb_message *msg,
495 struct ldb_message_element *el,
498 struct ldb_message_element *el2;
500 /* add another entry */
501 el2 = realloc_p(msg->elements, struct ldb_message_element, msg->num_elements+1);
507 msg->elements[msg->num_elements].name = "@IDX";
508 msg->elements[msg->num_elements].num_values = 0;
509 msg->elements[msg->num_elements].values = malloc_p(struct ldb_val);
510 if (!msg->elements[msg->num_elements].values) {
513 msg->elements[msg->num_elements].values[0].length = strlen(dn);
514 msg->elements[msg->num_elements].values[0].data = dn;
515 msg->elements[msg->num_elements].num_values = 1;
523 add a index element where this is not the first indexed DN for this
526 static int ltdb_index_add1_add(struct ldb_context *ldb,
527 struct ldb_message *msg,
528 struct ldb_message_element *el,
534 v2 = realloc_p(msg->elements[idx].values,
536 msg->elements[idx].num_values+1);
540 msg->elements[idx].values = v2;
542 msg->elements[idx].values[msg->elements[idx].num_values].length = strlen(dn);
543 msg->elements[idx].values[msg->elements[idx].num_values].data = dn;
544 msg->elements[idx].num_values++;
550 add an index entry for one message element
552 static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
553 struct ldb_message_element *el, int v_idx)
555 struct ldb_message msg;
559 dn_key = ldb_dn_key(el->name, &el->values[v_idx]);
564 ret = ltdb_search_dn1(ldb, dn_key, &msg);
571 msg.dn = strdup(dn_key);
577 msg.num_elements = 0;
584 for (i=0;i<msg.num_elements;i++) {
585 if (strcmp("@IDX", msg.elements[i].name) == 0) {
590 if (i == msg.num_elements) {
591 ret = ltdb_index_add1_new(ldb, &msg, el, dn);
593 ret = ltdb_index_add1_add(ldb, &msg, el, i, dn);
597 ret = ltdb_store(ldb, &msg, TDB_REPLACE);
600 ltdb_search_dn1_free(ldb, &msg);
606 add the index entries for a new record
609 int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg)
612 struct ldb_message index_list;
614 /* find the list of indexed fields */
615 ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
617 /* no indexed fields or an error */
621 for (i=0;i<msg->num_elements;i++) {
622 ret = ldb_msg_find_idx(&index_list, msg->elements[i].name, NULL);
626 for (j=0;j<msg->elements[i].num_values;j++) {
627 ret = ltdb_index_add1(ldb, msg->dn, &msg->elements[i], j);
629 ltdb_search_dn1_free(ldb, &index_list);
635 ltdb_search_dn1_free(ldb, &index_list);
642 delete an index entry for one message element
644 static int ltdb_index_del1(struct ldb_context *ldb, const char *dn,
645 struct ldb_message_element *el, int v_idx)
647 struct ldb_message msg;
651 dn_key = ldb_dn_key(el->name, &el->values[v_idx]);
656 ret = ltdb_search_dn1(ldb, dn_key, &msg);
663 /* it wasn't indexed. Did we have an earlier error? If we did then
665 ltdb_search_dn1_free(ldb, &msg);
669 i = ldb_msg_find_idx(&msg, dn, &j);
671 /* it ain't there. hmmm */
672 ltdb_search_dn1_free(ldb, &msg);
676 if (j != msg.elements[i].num_values - 1) {
677 memmove(&msg.elements[i].values[j],
678 &msg.elements[i].values[j+1],
679 (msg.elements[i].num_values-1) *
680 sizeof(msg.elements[i].values[0]));
682 msg.elements[i].num_values--;
684 if (msg.elements[i].num_values == 0) {
685 ret = ltdb_delete_noindex(ldb, dn_key);
687 ret = ltdb_store(ldb, &msg, TDB_REPLACE);
690 ltdb_search_dn1_free(ldb, &msg);
696 delete the index entries for a record
699 int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg)
702 struct ldb_message index_list;
704 /* find the list of indexed fields */
705 ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
707 /* no indexed fields or an error */
711 for (i=0;i<msg->num_elements;i++) {
712 ret = ldb_msg_find_idx(&index_list, msg->elements[i].name, NULL);
716 for (j=0;j<msg->elements[i].num_values;j++) {
717 ret = ltdb_index_del1(ldb, msg->dn, &msg->elements[i], j);
719 ltdb_search_dn1_free(ldb, &index_list);
730 traversal function that deletes all @INDEX records
732 static int delete_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
734 if (strncmp(key.dptr, "DN=@INDEX:", 10) == 0) {
735 return tdb_delete(tdb, key);
741 traversal function that adds @INDEX records during a re index
743 static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
745 struct ldb_context *ldb = state;
746 struct ldb_message msg;
749 if (strncmp(key.dptr, "DN=@", 4) == 0 ||
750 strncmp(key.dptr, "DN=", 3) != 0) {
754 ret = ltdb_unpack_data(ldb, &data, &msg);
761 ret = ltdb_index_add(ldb, &msg);
763 ltdb_unpack_data_free(&msg);
769 force a complete reindex of the database
771 int ltdb_reindex(struct ldb_context *ldb)
773 struct ltdb_private *ltdb = ldb->private;
776 /* first traverse the database deleting any @INDEX records */
777 ret = tdb_traverse(ltdb->tdb, delete_index, NULL);
783 /* now traverse adding any indexes for normal LDB records */
784 ret = tdb_traverse(ltdb->tdb, re_index, ldb);