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 3 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, see <http://www.gnu.org/licenses/>.
27 * Component: ldb message component utility functions
29 * Description: functions for manipulating ldb_message structures
31 * Author: Andrew Tridgell
34 #include "ldb_private.h"
37 create a new ldb_message in a given memory context (NULL for top level)
39 struct ldb_message *ldb_msg_new(void *mem_ctx)
41 return talloc_zero(mem_ctx, struct ldb_message);
45 find an element in a message by attribute name
47 struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg,
48 const char *attr_name)
51 for (i=0;i<msg->num_elements;i++) {
52 if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
53 return &msg->elements[i];
60 see if two ldb_val structures contain exactly the same data
61 return 1 for a match, 0 for a mis-match
63 int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
65 if (v1->length != v2->length) return 0;
66 if (v1->data == v2->data) return 1;
67 if (v1->length == 0) return 1;
69 if (memcmp(v1->data, v2->data, v1->length) == 0) {
77 find a value in an element
78 assumes case sensitive comparison
80 struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el,
84 for (i=0;i<el->num_values;i++) {
85 if (ldb_val_equal_exact(val, &el->values[i])) {
86 return &el->values[i];
93 duplicate a ldb_val structure
95 struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
98 v2.length = v->length;
99 if (v->data == NULL) {
104 /* the +1 is to cope with buggy C library routines like strndup
105 that look one byte beyond */
106 v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
112 memcpy(v2.data, v->data, v->length);
113 ((char *)v2.data)[v->length] = 0;
118 * Adds new empty element to msg->elements
120 static int _ldb_msg_add_el(struct ldb_message *msg,
121 struct ldb_message_element **return_el)
123 struct ldb_message_element *els;
126 * TODO: Find out a way to assert on input parameters.
127 * msg and return_el must be valid
130 els = talloc_realloc(msg, msg->elements,
131 struct ldb_message_element, msg->num_elements + 1);
134 return LDB_ERR_OPERATIONS_ERROR;
137 ZERO_STRUCT(els[msg->num_elements]);
142 *return_el = &els[msg->num_elements-1];
148 * Add an empty element with a given name to a message
150 int ldb_msg_add_empty(struct ldb_message *msg,
151 const char *attr_name,
153 struct ldb_message_element **return_el)
156 struct ldb_message_element *el;
158 ret = _ldb_msg_add_el(msg, &el);
159 if (ret != LDB_SUCCESS) {
163 /* initialize newly added element */
165 el->name = talloc_strdup(msg->elements, attr_name);
168 return LDB_ERR_OPERATIONS_ERROR;
179 * Adds an element to a message.
181 * NOTE: Ownership of ldb_message_element fields
182 * is NOT transferred. Thus, if *el pointer
183 * is invalidated for some reason, this will
184 * corrupt *msg contents also
186 int ldb_msg_add(struct ldb_message *msg,
187 const struct ldb_message_element *el,
191 struct ldb_message_element *el_new;
192 /* We have to copy this, just in case *el is a pointer into
193 * what ldb_msg_add_empty() is about to realloc() */
194 struct ldb_message_element el_copy = *el;
196 ret = _ldb_msg_add_el(msg, &el_new);
197 if (ret != LDB_SUCCESS) {
201 el_new->flags = flags;
202 el_new->name = el_copy.name;
203 el_new->num_values = el_copy.num_values;
204 el_new->values = el_copy.values;
210 add a value to a message
212 int ldb_msg_add_value(struct ldb_message *msg,
213 const char *attr_name,
214 const struct ldb_val *val,
215 struct ldb_message_element **return_el)
217 struct ldb_message_element *el;
218 struct ldb_val *vals;
221 el = ldb_msg_find_element(msg, attr_name);
223 ret = ldb_msg_add_empty(msg, attr_name, 0, &el);
224 if (ret != LDB_SUCCESS) {
229 vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
232 return LDB_ERR_OPERATIONS_ERROR;
235 el->values[el->num_values] = *val;
247 add a value to a message, stealing it into the 'right' place
249 int ldb_msg_add_steal_value(struct ldb_message *msg,
250 const char *attr_name,
254 struct ldb_message_element *el;
256 ret = ldb_msg_add_value(msg, attr_name, val, &el);
257 if (ret == LDB_SUCCESS) {
258 talloc_steal(el->values, val->data);
265 add a string element to a message
267 int ldb_msg_add_string(struct ldb_message *msg,
268 const char *attr_name, const char *str)
272 val.data = discard_const_p(uint8_t, str);
273 val.length = strlen(str);
275 if (val.length == 0) {
276 /* allow empty strings as non-existent attributes */
280 return ldb_msg_add_value(msg, attr_name, &val, NULL);
284 add a string element to a message, stealing it into the 'right' place
286 int ldb_msg_add_steal_string(struct ldb_message *msg,
287 const char *attr_name, char *str)
291 val.data = (uint8_t *)str;
292 val.length = strlen(str);
294 if (val.length == 0) {
295 /* allow empty strings as non-existent attributes */
299 return ldb_msg_add_steal_value(msg, attr_name, &val);
303 add a DN element to a message
304 WARNING: this uses the linearized string from the dn, and does not
307 int ldb_msg_add_linearized_dn(struct ldb_message *msg, const char *attr_name,
310 return ldb_msg_add_steal_string(msg, attr_name,
311 ldb_dn_alloc_linearized(msg, dn));
315 add a printf formatted element to a message
317 int ldb_msg_add_fmt(struct ldb_message *msg,
318 const char *attr_name, const char *fmt, ...)
325 str = talloc_vasprintf(msg, fmt, ap);
328 if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
330 val.data = (uint8_t *)str;
331 val.length = strlen(str);
333 return ldb_msg_add_steal_value(msg, attr_name, &val);
337 compare two ldb_message_element structures
338 assumes case sensitive comparison
340 int ldb_msg_element_compare(struct ldb_message_element *el1,
341 struct ldb_message_element *el2)
345 if (el1->num_values != el2->num_values) {
346 return el1->num_values - el2->num_values;
349 for (i=0;i<el1->num_values;i++) {
350 if (!ldb_msg_find_val(el2, &el1->values[i])) {
359 compare two ldb_message_element structures
360 comparing by element name
362 int ldb_msg_element_compare_name(struct ldb_message_element *el1,
363 struct ldb_message_element *el2)
365 return ldb_attr_cmp(el1->name, el2->name);
369 convenience functions to return common types from a message
370 these return the first value if the attribute is multi-valued
372 const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg,
373 const char *attr_name)
375 struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
376 if (!el || el->num_values == 0) {
379 return &el->values[0];
382 int ldb_msg_find_attr_as_int(const struct ldb_message *msg,
383 const char *attr_name,
386 const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
387 if (!v || !v->data) {
388 return default_value;
390 return strtol((const char *)v->data, NULL, 0);
393 unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg,
394 const char *attr_name,
395 unsigned int default_value)
398 const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
399 if (!v || !v->data) {
400 return default_value;
403 /* in LDAP there're only int32_t values */
405 ret = strtol((const char *)v->data, NULL, 0);
410 return strtoul((const char *)v->data, NULL, 0);
413 int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg,
414 const char *attr_name,
415 int64_t default_value)
417 const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
418 if (!v || !v->data) {
419 return default_value;
421 return strtoll((const char *)v->data, NULL, 0);
424 uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg,
425 const char *attr_name,
426 uint64_t default_value)
429 const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
430 if (!v || !v->data) {
431 return default_value;
434 /* in LDAP there're only int64_t values */
436 ret = strtoll((const char *)v->data, NULL, 0);
441 return strtoull((const char *)v->data, NULL, 0);
444 double ldb_msg_find_attr_as_double(const struct ldb_message *msg,
445 const char *attr_name,
446 double default_value)
448 const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
449 if (!v || !v->data) {
450 return default_value;
452 return strtod((const char *)v->data, NULL);
455 int ldb_msg_find_attr_as_bool(const struct ldb_message *msg,
456 const char *attr_name,
459 const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
460 if (!v || !v->data) {
461 return default_value;
463 if (v->length == 5 && strncasecmp((const char *)v->data, "FALSE", 5) == 0) {
466 if (v->length == 4 && strncasecmp((const char *)v->data, "TRUE", 4) == 0) {
469 return default_value;
472 const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg,
473 const char *attr_name,
474 const char *default_value)
476 const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
477 if (!v || !v->data) {
478 return default_value;
480 return (const char *)v->data;
483 struct ldb_dn *ldb_msg_find_attr_as_dn(struct ldb_context *ldb,
485 const struct ldb_message *msg,
486 const char *attr_name)
488 struct ldb_dn *res_dn;
489 const struct ldb_val *v;
491 v = ldb_msg_find_ldb_val(msg, attr_name);
492 if (!v || !v->data) {
495 res_dn = ldb_dn_from_ldb_val(mem_ctx, ldb, v);
496 if ( ! ldb_dn_validate(res_dn)) {
504 sort the elements of a message by name
506 void ldb_msg_sort_elements(struct ldb_message *msg)
508 TYPESAFE_QSORT(msg->elements, msg->num_elements,
509 ldb_msg_element_compare_name);
513 shallow copy a message - copying only the elements array so that the caller
514 can safely add new elements without changing the message
516 struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx,
517 const struct ldb_message *msg)
519 struct ldb_message *msg2;
522 msg2 = talloc(mem_ctx, struct ldb_message);
523 if (msg2 == NULL) return NULL;
527 msg2->elements = talloc_array(msg2, struct ldb_message_element,
529 if (msg2->elements == NULL) goto failed;
531 for (i=0;i<msg2->num_elements;i++) {
532 msg2->elements[i] = msg->elements[i];
544 copy a message, allocating new memory for all parts
546 struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx,
547 const struct ldb_message *msg)
549 struct ldb_message *msg2;
552 msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
553 if (msg2 == NULL) return NULL;
555 msg2->dn = ldb_dn_copy(msg2, msg2->dn);
556 if (msg2->dn == NULL) goto failed;
558 for (i=0;i<msg2->num_elements;i++) {
559 struct ldb_message_element *el = &msg2->elements[i];
560 struct ldb_val *values = el->values;
561 el->name = talloc_strdup(msg2->elements, el->name);
562 if (el->name == NULL) goto failed;
563 el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
564 for (j=0;j<el->num_values;j++) {
565 el->values[j] = ldb_val_dup(el->values, &values[j]);
566 if (el->values[j].data == NULL && values[j].length != 0) {
581 * Canonicalize a message, merging elements of the same name
583 struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb,
584 const struct ldb_message *msg)
587 struct ldb_message *msg2;
590 * Preserve previous behavior and allocate
591 * *msg2 into *ldb context
593 ret = ldb_msg_normalize(ldb, ldb, msg, &msg2);
594 if (ret != LDB_SUCCESS) {
602 * Canonicalize a message, merging elements of the same name
604 int ldb_msg_normalize(struct ldb_context *ldb,
606 const struct ldb_message *msg,
607 struct ldb_message **_msg_out)
610 struct ldb_message *msg2;
612 msg2 = ldb_msg_copy(mem_ctx, msg);
614 return LDB_ERR_OPERATIONS_ERROR;
617 ldb_msg_sort_elements(msg2);
619 for (i=1; i < msg2->num_elements; i++) {
620 struct ldb_message_element *el1 = &msg2->elements[i-1];
621 struct ldb_message_element *el2 = &msg2->elements[i];
623 if (ldb_msg_element_compare_name(el1, el2) == 0) {
624 el1->values = talloc_realloc(msg2->elements,
625 el1->values, struct ldb_val,
626 el1->num_values + el2->num_values);
627 if (el1->num_values + el2->num_values > 0 && el1->values == NULL) {
629 return LDB_ERR_OPERATIONS_ERROR;
631 memcpy(el1->values + el1->num_values,
633 sizeof(struct ldb_val) * el2->num_values);
634 el1->num_values += el2->num_values;
635 talloc_free(discard_const_p(char, el2->name));
636 if ((i+1) < msg2->num_elements) {
637 memmove(el2, el2+1, sizeof(struct ldb_message_element) *
638 (msg2->num_elements - (i+1)));
640 msg2->num_elements--;
651 * return a ldb_message representing the differences between msg1 and msg2.
652 * If you then use this in a ldb_modify() call,
653 * it can be used to save edits to a message
655 struct ldb_message *ldb_msg_diff(struct ldb_context *ldb,
656 struct ldb_message *msg1,
657 struct ldb_message *msg2)
660 struct ldb_message *mod;
662 ldb_ret = ldb_msg_difference(ldb, ldb, msg1, msg2, &mod);
663 if (ldb_ret != LDB_SUCCESS) {
671 * return a ldb_message representing the differences between msg1 and msg2.
672 * If you then use this in a ldb_modify() call it can be used to save edits to a message
674 * Result message is constructed as follows:
675 * - LDB_FLAG_MOD_ADD - elements found only in msg2
676 * - LDB_FLAG_MOD_REPLACE - elements in msg2 that have different value in msg1
677 * Value for msg2 element is used
678 * - LDB_FLAG_MOD_DELETE - elements found only in msg2
680 * @return LDB_SUCCESS or LDB_ERR_OPERATIONS_ERROR
682 int ldb_msg_difference(struct ldb_context *ldb,
684 struct ldb_message *msg1,
685 struct ldb_message *msg2,
686 struct ldb_message **_msg_out)
690 struct ldb_message *mod;
691 struct ldb_message_element *el;
692 TALLOC_CTX *temp_ctx;
694 temp_ctx = talloc_new(mem_ctx);
696 return LDB_ERR_OPERATIONS_ERROR;
699 mod = ldb_msg_new(temp_ctx);
705 mod->num_elements = 0;
706 mod->elements = NULL;
708 /* canonicalize msg2 so we have no repeated elements */
709 msg2 = ldb_msg_canonicalize(ldb, msg2);
714 /* steal msg2 into mod context as it is allocated in ldb's context */
715 talloc_steal(mod, msg2);
717 /* look in msg2 to find elements that need to be added or modified */
718 for (i=0;i<msg2->num_elements;i++) {
719 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
721 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
725 ldb_res = ldb_msg_add(mod,
727 el ? LDB_FLAG_MOD_REPLACE : LDB_FLAG_MOD_ADD);
728 if (ldb_res != LDB_SUCCESS) {
733 /* look in msg1 to find elements that need to be deleted */
734 for (i=0;i<msg1->num_elements;i++) {
735 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
737 ldb_res = ldb_msg_add_empty(mod,
738 msg1->elements[i].name,
739 LDB_FLAG_MOD_DELETE, NULL);
740 if (ldb_res != LDB_SUCCESS) {
746 /* steal resulting message into supplied context */
747 talloc_steal(mem_ctx, mod);
750 talloc_free(temp_ctx);
754 talloc_free(temp_ctx);
755 return LDB_ERR_OPERATIONS_ERROR;
759 int ldb_msg_sanity_check(struct ldb_context *ldb,
760 const struct ldb_message *msg)
764 /* basic check on DN */
765 if (msg->dn == NULL) {
766 /* TODO: return also an error string */
767 ldb_set_errstring(ldb, "ldb message lacks a DN!");
768 return LDB_ERR_INVALID_DN_SYNTAX;
771 /* basic syntax checks */
772 for (i = 0; i < msg->num_elements; i++) {
773 for (j = 0; j < msg->elements[i].num_values; j++) {
774 if (msg->elements[i].values[j].length == 0) {
775 TALLOC_CTX *mem_ctx = talloc_new(ldb);
776 /* an attribute cannot be empty */
777 /* TODO: return also an error string */
778 ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
779 msg->elements[i].name,
780 ldb_dn_get_linearized(msg->dn));
781 talloc_free(mem_ctx);
782 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
794 copy an attribute list. This only copies the array, not the elements
795 (ie. the elements are left as the same pointers)
797 const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
802 for (i=0;attrs && attrs[i];i++) /* noop */ ;
803 ret = talloc_array(mem_ctx, const char *, i+1);
807 for (i=0;attrs && attrs[i];i++) {
816 copy an attribute list. This only copies the array, not the elements
817 (ie. the elements are left as the same pointers). The new attribute is added to the list.
819 const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
825 for (i=0;attrs && attrs[i];i++) {
826 if (ldb_attr_cmp(attrs[i], new_attr) == 0) {
831 return ldb_attr_list_copy(mem_ctx, attrs);
833 ret = talloc_array(mem_ctx, const char *, i+2);
837 for (i=0;attrs && attrs[i];i++) {
847 return 1 if an attribute is in a list of attributes, or 0 otherwise
849 int ldb_attr_in_list(const char * const *attrs, const char *attr)
852 for (i=0;attrs && attrs[i];i++) {
853 if (ldb_attr_cmp(attrs[i], attr) == 0) {
862 rename the specified attribute in a search result
864 int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
866 struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
870 el->name = talloc_strdup(msg->elements, replace);
871 if (el->name == NULL) {
872 return LDB_ERR_OPERATIONS_ERROR;
879 copy the specified attribute in a search result to a new attribute
881 int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
883 struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
887 if (ldb_msg_add(msg, el, 0) != 0) {
888 return LDB_ERR_OPERATIONS_ERROR;
890 return ldb_msg_rename_attr(msg, attr, replace);
894 remove the specified element in a search result
896 void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
898 ptrdiff_t n = (el - msg->elements);
899 if (n >= msg->num_elements) {
900 /* should we abort() here? */
903 if (n != msg->num_elements-1) {
904 memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
911 remove the specified attribute in a search result
913 void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
915 struct ldb_message_element *el;
917 while ((el = ldb_msg_find_element(msg, attr)) != NULL) {
918 ldb_msg_remove_element(msg, el);
923 return a LDAP formatted GeneralizedTime string
925 char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
927 struct tm *tm = gmtime(&t);
935 /* we now excatly how long this string will be */
936 ts = talloc_array(mem_ctx, char, 18);
938 /* formatted like: 20040408072012.0Z */
940 "%04u%02u%02u%02u%02u%02u.0Z",
941 tm->tm_year+1900, tm->tm_mon+1,
942 tm->tm_mday, tm->tm_hour, tm->tm_min,
954 convert a LDAP GeneralizedTime string to a time_t. Return 0 if unable to convert
956 time_t ldb_string_to_time(const char *s)
960 if (s == NULL) return 0;
962 memset(&tm, 0, sizeof(tm));
963 if (sscanf(s, "%04u%02u%02u%02u%02u%02u.0Z",
964 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
965 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
975 convert a LDAP GeneralizedTime string in ldb_val format to a
978 int ldb_val_to_time(const struct ldb_val *v, time_t *t)
982 if (v == NULL || !v->data || v->length < 17) {
983 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
986 memset(&tm, 0, sizeof(tm));
988 if (sscanf((char *)v->data, "%04u%02u%02u%02u%02u%02u.0Z",
989 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
990 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
991 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
1002 return a LDAP formatted UTCTime string
1004 char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t)
1006 struct tm *tm = gmtime(&t);
1014 /* we now excatly how long this string will be */
1015 ts = talloc_array(mem_ctx, char, 14);
1017 /* formatted like: 20040408072012.0Z => 040408072012Z */
1018 r = snprintf(ts, 14,
1019 "%02u%02u%02u%02u%02u%02uZ",
1020 (tm->tm_year+1900)%100, tm->tm_mon+1,
1021 tm->tm_mday, tm->tm_hour, tm->tm_min,
1033 convert a LDAP UTCTime string to a time_t. Return 0 if unable to convert
1035 time_t ldb_string_utc_to_time(const char *s)
1039 if (s == NULL) return 0;
1041 memset(&tm, 0, sizeof(tm));
1042 if (sscanf(s, "%02u%02u%02u%02u%02u%02uZ",
1043 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1044 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1047 if (tm.tm_year < 50) {
1057 dump a set of results to a file. Useful from within gdb
1059 void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
1063 for (i = 0; i < result->count; i++) {
1064 struct ldb_ldif ldif;
1065 fprintf(f, "# record %d\n", i+1);
1066 ldif.changetype = LDB_CHANGETYPE_NONE;
1067 ldif.msg = result->msgs[i];
1068 ldb_ldif_write_file(ldb, f, &ldif);
1073 checks for a string attribute. Returns "1" on match and otherwise "0".
1075 int ldb_msg_check_string_attribute(const struct ldb_message *msg,
1076 const char *name, const char *value)
1078 struct ldb_message_element *el;
1081 el = ldb_msg_find_element(msg, name);
1086 val.data = discard_const_p(uint8_t, value);
1087 val.length = strlen(value);
1089 if (ldb_msg_find_val(el, &val)) {