X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fxml-tdesc.c;h=6f108a4e3ef620361b2f611efbfd251964662519;hb=c17e02e1b55b5e9cbdc6581f05bfec96dc8436f4;hp=5eeda86a97517ebf37dc59f0c16da346eff7717f;hpb=618f726fcb851883a0094aa7fa17003889b7189f;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c index 5eeda86a97..6f108a4e3e 100644 --- a/gdb/xml-tdesc.c +++ b/gdb/xml-tdesc.c @@ -1,6 +1,6 @@ /* XML target description support for GDB. - Copyright (C) 2006-2016 Free Software Foundation, Inc. + Copyright (C) 2006-2020 Free Software Foundation, Inc. Contributed by CodeSourcery. @@ -25,8 +25,15 @@ #include "xml-support.h" #include "xml-tdesc.h" #include "osabi.h" - #include "filenames.h" +#include +#include + +/* Maximum sizes. + This is just to catch obviously wrong values. */ +#define MAX_FIELD_SIZE 65536 +#define MAX_FIELD_BITSIZE (MAX_FIELD_SIZE * TARGET_CHAR_BIT) +#define MAX_VECTOR_SIZE 65536 #if !defined(HAVE_LIBEXPAT) @@ -59,15 +66,7 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher, then we will create unnecessary duplicate gdbarches. See gdbarch_list_lookup_by_info. */ -struct tdesc_xml_cache -{ - const char *xml_document; - struct target_desc *tdesc; -}; -typedef struct tdesc_xml_cache tdesc_xml_cache_s; -DEF_VEC_O(tdesc_xml_cache_s); - -static VEC(tdesc_xml_cache_s) *xml_cache; +static std::unordered_map xml_cache; /* Callback data for target description parsing. */ @@ -84,14 +83,11 @@ struct tdesc_parsing_data int next_regnum; /* The struct or union we are currently parsing, or last parsed. */ - struct tdesc_type *current_type; + tdesc_type_with_fields *current_type; - /* The byte size of the current struct type, if specified. Zero - if not specified. */ + /* The byte size of the current struct/flags type, if specified. Zero + if not specified. Flags values must specify a size. */ int current_type_size; - - /* Whether the current type is a flags type. */ - int current_type_is_flags; }; /* Handle the end of an element and its value. */ @@ -148,9 +144,10 @@ tdesc_end_compatible (struct gdb_xml_parser *parser, static void tdesc_start_target (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) { - char *version = (char *) xml_find_attribute (attributes, "version")->value; + char *version + = (char *) xml_find_attribute (attributes, "version")->value.get (); if (strcmp (version, "1.0") != 0) gdb_xml_error (parser, @@ -163,10 +160,10 @@ tdesc_start_target (struct gdb_xml_parser *parser, static void tdesc_start_feature (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) { struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; - char *name = (char *) xml_find_attribute (attributes, "name")->value; + char *name = (char *) xml_find_attribute (attributes, "name")->value.get (); data->current_feature = tdesc_create_feature (data->tdesc, name); } @@ -177,36 +174,36 @@ tdesc_start_feature (struct gdb_xml_parser *parser, static void tdesc_start_reg (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) { struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; - struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes); - int ix = 0, length; - char *name, *group, *type; + int ix = 0; + char *name, *group; + const char *type; int bitsize, regnum, save_restore; - length = VEC_length (gdb_xml_value_s, attributes); + int length = attributes.size (); - name = (char *) attrs[ix++].value; - bitsize = * (ULONGEST *) attrs[ix++].value; + name = (char *) attributes[ix++].value.get (); + bitsize = * (ULONGEST *) attributes[ix++].value.get (); - if (ix < length && strcmp (attrs[ix].name, "regnum") == 0) - regnum = * (ULONGEST *) attrs[ix++].value; + if (ix < length && strcmp (attributes[ix].name, "regnum") == 0) + regnum = * (ULONGEST *) attributes[ix++].value.get (); else regnum = data->next_regnum; - if (ix < length && strcmp (attrs[ix].name, "type") == 0) - type = (char *) attrs[ix++].value; + if (ix < length && strcmp (attributes[ix].name, "type") == 0) + type = (char *) attributes[ix++].value.get (); else type = "int"; - if (ix < length && strcmp (attrs[ix].name, "group") == 0) - group = (char *) attrs[ix++].value; + if (ix < length && strcmp (attributes[ix].name, "group") == 0) + group = (char *) attributes[ix++].value.get (); else group = NULL; - if (ix < length && strcmp (attrs[ix].name, "save-restore") == 0) - save_restore = * (ULONGEST *) attrs[ix++].value; + if (ix < length && strcmp (attributes[ix].name, "save-restore") == 0) + save_restore = * (ULONGEST *) attributes[ix++].value.get (); else save_restore = 1; @@ -228,14 +225,13 @@ tdesc_start_reg (struct gdb_xml_parser *parser, static void tdesc_start_union (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) { struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; - char *id = (char *) xml_find_attribute (attributes, "id")->value; + char *id = (char *) xml_find_attribute (attributes, "id")->value.get (); data->current_type = tdesc_create_union (data->current_feature, id); data->current_type_size = 0; - data->current_type_is_flags = 0; } /* Handle the start of a element. Initialize the type and @@ -244,24 +240,29 @@ tdesc_start_union (struct gdb_xml_parser *parser, static void tdesc_start_struct (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) { struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; - char *id = (char *) xml_find_attribute (attributes, "id")->value; - struct tdesc_type *type; + char *id = (char *) xml_find_attribute (attributes, "id")->value.get (); struct gdb_xml_value *attr; - type = tdesc_create_struct (data->current_feature, id); - data->current_type = type; + tdesc_type_with_fields *type_with_fields + = tdesc_create_struct (data->current_feature, id); + data->current_type = type_with_fields; data->current_type_size = 0; - data->current_type_is_flags = 0; attr = xml_find_attribute (attributes, "size"); if (attr != NULL) { - int size = (int) * (ULONGEST *) attr->value; + ULONGEST size = * (ULONGEST *) attr->value.get (); - tdesc_set_struct_size (type, size); + if (size > MAX_FIELD_SIZE) + { + gdb_xml_error (parser, + _("Struct size %s is larger than maximum (%d)"), + pulongest (size), MAX_FIELD_SIZE); + } + tdesc_set_struct_size (type_with_fields, size); data->current_type_size = size; } } @@ -269,28 +270,52 @@ tdesc_start_struct (struct gdb_xml_parser *parser, static void tdesc_start_flags (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) +{ + struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; + char *id = (char *) xml_find_attribute (attributes, "id")->value.get (); + ULONGEST size = * (ULONGEST *) + xml_find_attribute (attributes, "size")->value.get (); + + if (size > MAX_FIELD_SIZE) + { + gdb_xml_error (parser, + _("Flags size %s is larger than maximum (%d)"), + pulongest (size), MAX_FIELD_SIZE); + } + + data->current_type = tdesc_create_flags (data->current_feature, id, size); + data->current_type_size = size; +} + +static void +tdesc_start_enum (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, std::vector &attributes) { struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; - char *id = (char *) xml_find_attribute (attributes, "id")->value; - int length = (int) * (ULONGEST *) - xml_find_attribute (attributes, "size")->value; - struct tdesc_type *type; + char *id = (char *) xml_find_attribute (attributes, "id")->value.get (); + int size = * (ULONGEST *) + xml_find_attribute (attributes, "size")->value.get (); - type = tdesc_create_flags (data->current_feature, id, length); + if (size > MAX_FIELD_SIZE) + { + gdb_xml_error (parser, + _("Enum size %s is larger than maximum (%d)"), + pulongest (size), MAX_FIELD_SIZE); + } - data->current_type = type; + data->current_type = tdesc_create_enum (data->current_feature, id, size); data->current_type_size = 0; - data->current_type_is_flags = 1; } /* Handle the start of a element. Attach the field to the - current struct or union. */ + current struct, union or flags. */ static void tdesc_start_field (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) { struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; struct gdb_xml_value *attr; @@ -298,38 +323,109 @@ tdesc_start_field (struct gdb_xml_parser *parser, char *field_name, *field_type_id; int start, end; - field_name = (char *) xml_find_attribute (attributes, "name")->value; + field_name = (char *) xml_find_attribute (attributes, "name")->value.get (); attr = xml_find_attribute (attributes, "type"); if (attr != NULL) - field_type_id = (char *) attr->value; + { + field_type_id = (char *) attr->value.get (); + field_type = tdesc_named_type (data->current_feature, field_type_id); + } else - field_type_id = NULL; + { + field_type_id = NULL; + field_type = NULL; + } attr = xml_find_attribute (attributes, "start"); if (attr != NULL) - start = * (ULONGEST *) attr->value; + { + ULONGEST ul_start = * (ULONGEST *) attr->value.get (); + + if (ul_start > MAX_FIELD_BITSIZE) + { + gdb_xml_error (parser, + _("Field start %s is larger than maximum (%d)"), + pulongest (ul_start), MAX_FIELD_BITSIZE); + } + start = ul_start; + } else start = -1; attr = xml_find_attribute (attributes, "end"); if (attr != NULL) - end = * (ULONGEST *) attr->value; + { + ULONGEST ul_end = * (ULONGEST *) attr->value.get (); + + if (ul_end > MAX_FIELD_BITSIZE) + { + gdb_xml_error (parser, + _("Field end %s is larger than maximum (%d)"), + pulongest (ul_end), MAX_FIELD_BITSIZE); + } + end = ul_end; + } else end = -1; - if (field_type_id != NULL) + if (start != -1) { - if (data->current_type_is_flags) - gdb_xml_error (parser, _("Cannot add typed field \"%s\" to flags"), + tdesc_type_with_fields *t = data->current_type; + + /* Older versions of gdb can't handle elided end values. + Stick with that for now, to help ensure backward compatibility. + E.g., If a newer gdbserver is talking to an older gdb. */ + if (end == -1) + gdb_xml_error (parser, _("Missing end value")); + + if (data->current_type_size == 0) + gdb_xml_error (parser, + _("Bitfields must live in explicitly sized types")); + + if (field_type_id != NULL + && strcmp (field_type_id, "bool") == 0 + && start != end) + { + gdb_xml_error (parser, + _("Boolean fields must be one bit in size")); + } + + if (end >= 64) + gdb_xml_error (parser, + _("Bitfield \"%s\" goes past " + "64 bits (unsupported)"), + field_name); + + /* Assume that the bit numbering in XML is "lsb-zero". Most + architectures other than PowerPC use this ordering. In the + future, we can add an XML tag to indicate "msb-zero" numbering. */ + if (start > end) + gdb_xml_error (parser, _("Bitfield \"%s\" has start after end"), field_name); + if (end >= data->current_type_size * TARGET_CHAR_BIT) + gdb_xml_error (parser, _("Bitfield \"%s\" does not fit in struct"), + field_name); + + if (field_type != NULL) + tdesc_add_typed_bitfield (t, field_name, start, end, field_type); + else if (start == end) + tdesc_add_flag (t, start, field_name); + else + tdesc_add_bitfield (t, field_name, start, end); + } + else if (start == -1 && end != -1) + gdb_xml_error (parser, _("End specified but not start")); + else if (field_type_id != NULL) + { + /* TDESC_TYPE_FLAGS values are explicitly sized, so the following test + catches adding non-bitfield types to flags as well. */ if (data->current_type_size != 0) gdb_xml_error (parser, - _("Explicitly sized type can not " - "contain non-bitfield \"%s\""), + _("Explicitly sized type cannot " + "contain non-bitfield \"%s\""), field_name); - field_type = tdesc_named_type (data->current_feature, field_type_id); if (field_type == NULL) gdb_xml_error (parser, _("Field \"%s\" references undefined " "type \"%s\""), @@ -337,63 +433,63 @@ tdesc_start_field (struct gdb_xml_parser *parser, tdesc_add_field (data->current_type, field_name, field_type); } - else if (start != -1 && end != -1) - { - struct tdesc_type *t = data->current_type; - - if (data->current_type_is_flags) - tdesc_add_flag (t, start, field_name); - else - { - if (data->current_type_size == 0) - gdb_xml_error (parser, - _("Implicitly sized type can " - "not contain bitfield \"%s\""), - field_name); - - if (end >= 64) - gdb_xml_error (parser, - _("Bitfield \"%s\" goes past " - "64 bits (unsupported)"), - field_name); - - /* Assume that the bit numbering in XML is "lsb-zero". Most - architectures other than PowerPC use this ordering. In - the future, we can add an XML tag to indicate "msb-zero" - numbering. */ - if (start > end) - gdb_xml_error (parser, _("Bitfield \"%s\" has start after end"), - field_name); - - if (end >= data->current_type_size * TARGET_CHAR_BIT) - gdb_xml_error (parser, - _("Bitfield \"%s\" does not fit in struct")); - - tdesc_add_bitfield (t, field_name, start, end); - } - } else gdb_xml_error (parser, _("Field \"%s\" has neither type nor bit position"), field_name); } +/* Handle the start of an element. Attach the value to the + current enum. */ + +static void +tdesc_start_enum_value (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, std::vector &attributes) +{ + struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; + struct gdb_xml_value *attr; + char *field_name; + ULONGEST ul_value; + int value; + + field_name = (char *) xml_find_attribute (attributes, "name")->value.get (); + + attr = xml_find_attribute (attributes, "value"); + ul_value = * (ULONGEST *) attr->value.get (); + if (ul_value > INT_MAX) + { + gdb_xml_error (parser, + _("Enum value %s is larger than maximum (%d)"), + pulongest (ul_value), INT_MAX); + } + value = ul_value; + + tdesc_add_enum_value (data->current_type, value, field_name); +} + /* Handle the start of a element. Initialize the type and record it with the current feature. */ static void tdesc_start_vector (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, - void *user_data, VEC(gdb_xml_value_s) *attributes) + void *user_data, std::vector &attributes) { struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data; - struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes); struct tdesc_type *field_type; char *id, *field_type_id; - int count; + ULONGEST count; - id = (char *) attrs[0].value; - field_type_id = (char *) attrs[1].value; - count = * (ULONGEST *) attrs[2].value; + id = (char *) attributes[0].value.get (); + field_type_id = (char *) attributes[1].value.get (); + count = * (ULONGEST *) attributes[2].value.get (); + + if (count > MAX_VECTOR_SIZE) + { + gdb_xml_error (parser, + _("Vector size %s is larger than maximum (%d)"), + pulongest (count), MAX_VECTOR_SIZE); + } field_type = tdesc_named_type (data->current_feature, field_type_id); if (field_type == NULL) @@ -413,12 +509,24 @@ static const struct gdb_xml_attribute field_attributes[] = { { NULL, GDB_XML_AF_NONE, NULL, NULL } }; +static const struct gdb_xml_attribute enum_value_attributes[] = { + { "name", GDB_XML_AF_NONE, NULL, NULL }, + { "value", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL }, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; + static const struct gdb_xml_element struct_union_children[] = { { "field", field_attributes, NULL, GDB_XML_EF_REPEATABLE, tdesc_start_field, NULL }, { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } }; +static const struct gdb_xml_element enum_children[] = { + { "evalue", enum_value_attributes, NULL, GDB_XML_EF_REPEATABLE, + tdesc_start_enum_value, NULL }, + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +}; + static const struct gdb_xml_attribute reg_attributes[] = { { "name", GDB_XML_AF_NONE, NULL, NULL }, { "bitsize", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, @@ -442,6 +550,12 @@ static const struct gdb_xml_attribute flags_attributes[] = { { NULL, GDB_XML_AF_NONE, NULL, NULL } }; +static const struct gdb_xml_attribute enum_attributes[] = { + { "id", GDB_XML_AF_NONE, NULL, NULL }, + { "size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL}, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; + static const struct gdb_xml_attribute vector_attributes[] = { { "id", GDB_XML_AF_NONE, NULL, NULL }, { "type", GDB_XML_AF_NONE, NULL, NULL }, @@ -467,6 +581,9 @@ static const struct gdb_xml_element feature_children[] = { { "flags", flags_attributes, struct_union_children, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, tdesc_start_flags, NULL }, + { "enum", enum_attributes, enum_children, + GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, + tdesc_start_enum, NULL }, { "vector", vector_attributes, NULL, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, tdesc_start_vector, NULL }, @@ -503,55 +620,39 @@ static struct target_desc * tdesc_parse_xml (const char *document, xml_fetch_another fetcher, void *fetcher_baton) { - struct cleanup *back_to, *result_cleanup; struct tdesc_parsing_data data; - struct tdesc_xml_cache *cache; - char *expanded_text; - int ix; /* Expand all XInclude directives. */ - expanded_text = xml_process_xincludes (_("target description"), - document, fetcher, fetcher_baton, 0); - if (expanded_text == NULL) + std::string expanded_text; + + if (!xml_process_xincludes (expanded_text, + _("target description"), + document, fetcher, fetcher_baton, 0)) { warning (_("Could not load XML target description; ignoring")); return NULL; } /* Check for an exact match in the list of descriptions we have - previously parsed. strcmp is a slightly inefficient way to - do this; an SHA-1 checksum would work as well. */ - for (ix = 0; VEC_iterate (tdesc_xml_cache_s, xml_cache, ix, cache); ix++) - if (strcmp (cache->xml_document, expanded_text) == 0) - { - xfree (expanded_text); - return cache->tdesc; - } - - back_to = make_cleanup (null_cleanup, NULL); + previously parsed. */ + const auto it = xml_cache.find (expanded_text); + if (it != xml_cache.end ()) + return it->second.get (); memset (&data, 0, sizeof (struct tdesc_parsing_data)); - data.tdesc = allocate_target_description (); - result_cleanup = make_cleanup_free_target_description (data.tdesc); - make_cleanup (xfree, expanded_text); + target_desc_up description (allocate_target_description ()); + data.tdesc = description.get (); if (gdb_xml_parse_quick (_("target description"), "gdb-target.dtd", - tdesc_elements, expanded_text, &data) == 0) + tdesc_elements, expanded_text.c_str (), &data) == 0) { /* Parsed successfully. */ - struct tdesc_xml_cache new_cache; - - new_cache.xml_document = expanded_text; - new_cache.tdesc = data.tdesc; - VEC_safe_push (tdesc_xml_cache_s, xml_cache, &new_cache); - discard_cleanups (result_cleanup); - do_cleanups (back_to); + xml_cache.emplace (std::move (expanded_text), std::move (description)); return data.tdesc; } else { warning (_("Could not load XML target description; ignoring")); - do_cleanups (back_to); return NULL; } } @@ -564,28 +665,16 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher, const struct target_desc * file_read_description_xml (const char *filename) { - struct target_desc *tdesc; - char *tdesc_str; - struct cleanup *back_to; - char *dirname; - - tdesc_str = xml_fetch_content_from_file (filename, NULL); - if (tdesc_str == NULL) + gdb::optional tdesc_str + = xml_fetch_content_from_file (filename, NULL); + if (!tdesc_str) { warning (_("Could not open \"%s\""), filename); return NULL; } - back_to = make_cleanup (xfree, tdesc_str); - - dirname = ldirname (filename); - if (dirname != NULL) - make_cleanup (xfree, dirname); - - tdesc = tdesc_parse_xml (tdesc_str, xml_fetch_content_from_file, dirname); - do_cleanups (back_to); - - return tdesc; + return tdesc_parse_xml (tdesc_str->data (), xml_fetch_content_from_file, + (void *) ldirname (filename).c_str ()); } /* Read a string representation of available features from the target, @@ -595,7 +684,7 @@ file_read_description_xml (const char *filename) is "target.xml". Other calls may be performed for the DTD or for . */ -static char * +static gdb::optional fetch_available_features_from_target (const char *name, void *baton_) { struct target_ops *ops = (struct target_ops *) baton_; @@ -614,19 +703,61 @@ fetch_available_features_from_target (const char *name, void *baton_) const struct target_desc * target_read_description_xml (struct target_ops *ops) { - struct target_desc *tdesc; - char *tdesc_str; - struct cleanup *back_to; - - tdesc_str = fetch_available_features_from_target ("target.xml", ops); - if (tdesc_str == NULL) + gdb::optional tdesc_str + = fetch_available_features_from_target ("target.xml", ops); + if (!tdesc_str) return NULL; - back_to = make_cleanup (xfree, tdesc_str); - tdesc = tdesc_parse_xml (tdesc_str, - fetch_available_features_from_target, - ops); - do_cleanups (back_to); + return tdesc_parse_xml (tdesc_str->data (), + fetch_available_features_from_target, + ops); +} + +/* Fetches an XML target description using OPS, processing + includes, but not parsing it. Used to dump whole tdesc + as a single XML file. */ - return tdesc; +gdb::optional +target_fetch_description_xml (struct target_ops *ops) +{ +#if !defined(HAVE_LIBEXPAT) + static int have_warned; + + if (!have_warned) + { + have_warned = 1; + warning (_("Can not fetch XML target description; XML support was " + "disabled at compile time")); + } + + return {}; +#else + gdb::optional + tdesc_str = fetch_available_features_from_target ("target.xml", ops); + if (!tdesc_str) + return {}; + + std::string output; + if (!xml_process_xincludes (output, + _("target description"), + tdesc_str->data (), + fetch_available_features_from_target, ops, 0)) + { + warning (_("Could not load XML target description; ignoring")); + return {}; + } + return output; +#endif +} + +/* See xml-tdesc.h. */ + +const struct target_desc * +string_read_description_xml (const char *xml) +{ + return tdesc_parse_xml (xml, [] (const char *href, void *baton) + { + error (_("xincludes are unsupported with this method")); + return gdb::optional (); + }, nullptr); }