fr_sbuff_t our_in = FR_SBUFF(in);
fr_sbuff_marker_t m_lhs, m_rhs, m_op;
fr_sbuff_term_t const *tt = p_rules ? p_rules->terminals : NULL;
- map_t *parent, *new_parent;
+ map_t *parent;
if (parent_p) {
- new_parent = parent = *parent_p;
+ parent = *parent_p;
} else {
- new_parent = parent = NULL;
+ parent = NULL;
}
*out = NULL;
*/
while (fr_sbuff_next_if_char(&our_in, '.')) {
if (!parent) goto no_parent;
- new_parent = parent = parent->parent;
+ parent = parent->parent;
}
/*
* attribute MUST come from the
* root of the dictionary tree.
*/
- new_parent = parent = NULL;
+ parent = NULL;
}
slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &our_in,
map_list_insert_tail(&parent->child, map);
}
- if (parent_p) *parent_p = new_parent;
+ if (parent_p) *parent_p = parent;
MAP_VERIFY(map);
*out = map;
*
* @param[in] ctx where to allocate the map.
* @param[out] out Where to write the new map (must be freed with talloc_free()).
+ * @param[in,out] parent_p the parent map, updated for relative maps
* @param[in] request the request
* @param[in] lhs of map
- * @param[in] op of map
+ * @param[in] op_str operator for map
* @param[in] rhs of map
* @param[in] lhs_rules for parsing the LHS
* @param[in] rhs_rules for parsing the RHS
* - 0 on success.
* - -1 on failure.
*/
-int map_afrom_fields(TALLOC_CTX *ctx, map_t **out, request_t *request, char const *lhs, char const *op, char const *rhs,
+int map_afrom_fields(TALLOC_CTX *ctx, map_t **out, map_t **parent_p, request_t *request,
+ char const *lhs, char const *op_str, char const *rhs,
tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules)
{
- ssize_t slen;
- fr_token_t quote;
- map_t *map;
- tmpl_rules_t my_rules;
+ ssize_t slen;
+ fr_token_t quote, op;
+ map_t *map;
+ map_t *parent = *parent_p;
+ tmpl_rules_t my_rules;
- map = map_alloc(ctx, NULL);
- if (!map) {
- fr_strerror_const("Out of memory");
+ op = fr_table_value_by_str(fr_tokens_table, op_str, T_INVALID);
+ if ((op == T_INVALID) || (!fr_assignment_op[op] && !fr_comparison_op[op])) {
+ fr_strerror_printf("Invalid operator '%s'", op_str);
return -1;
}
- map->op = fr_table_value_by_str(fr_tokens_table, op, T_INVALID);
- if ((map->op == T_INVALID) || (!fr_assignment_op[map->op] && !fr_comparison_op[map->op])) {
- fr_strerror_printf("Invalid operator '%s'", op);
- goto error;
- }
-
my_rules = *lhs_rules;
lhs_rules = &my_rules;
* comparisons. We rewrite assignments to use the control list.
*
* @todo - as we expand the use of this function, perhaps add another argument which controls
- * this flag.
+ * this flag. But this function already has parameter overload :(
*/
- if (fr_assignment_op[map->op] && (lhs_rules->attr.list_def == request_attr_request)) {
+ if (fr_assignment_op[op] && (lhs_rules->attr.list_def == request_attr_request)) {
my_rules.attr.list_def = request_attr_control;
}
- if (lhs[0] == '.') {
- fr_strerror_const("Nested is not supported!");
- return -1;
+ /*
+ * One '.' means "the current parent".
+ */
+ if (*lhs == '.') {
+ if (!parent) {
+ no_parent:
+ fr_strerror_const("Unexpected location for relative attribute - no parent attribute exists");
+ return -1;
+ }
+ lhs++;
+
+ /*
+ * Multiple '.' means "go to our parents parent".
+ */
+ while (*lhs == '.') {
+ if (!parent) goto no_parent;
+
+ parent = parent->parent;
+ lhs++;
+ }
+
+ /*
+ * Child elements can only be "=".
+ */
+ if (parent) {
+ if (fr_comparison_op[op]) {
+ fr_strerror_const("Comparison operators cannot be used inside of structural data types");
+ return -1;
+ }
+
+ if (op != T_OP_EQ) {
+ fr_strerror_const("Invalid operator inside of structural data type - must be '='");
+ return -1;
+ }
+ }
}
+ MEM(map = map_alloc(ctx, parent));
+ map->op = op;
+
/*
- * Allocate the LHS, which must be an attribute.
- *
- * @todo - track relative attributes, which begin with a '.'
+ * Start looking in the correct parent, not in whatever we were handed.
*/
- slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, lhs, lhs_rules);
+ if (parent) {
+ fr_assert(tmpl_is_attr(parent->lhs));
+ my_rules.attr.namespace = tmpl_attr_tail_da(parent->lhs);
+
+ slen = tmpl_afrom_attr_substr(map, NULL, &map->lhs, &FR_SBUFF_IN(lhs, strlen(lhs)),
+ &map_parse_rules_bareword_quoted, lhs_rules);
+ } else {
+ /*
+ * There's no '.', so this
+ * attribute MUST come from the
+ * root of the dictionary tree.
+ */
+ parent = NULL;
+
+ /*
+ * Allocate the LHS, which must be an attribute.
+ *
+ * @todo - track relative attributes, which begin with a '.'
+ */
+ slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, lhs, lhs_rules);
+ }
if (slen <= 0) {
error:
talloc_free(map);
/*
* @todo - check that the entire string was parsed.
*/
-
*out = map;
+ *parent_p = parent;
return 0;
}
rlm_sql_row_t row;
int rows = 0;
sql_rcode_t rcode;
-// fr_pair_t *relative_vp = NULL;
+ map_t *parent = NULL;
tmpl_rules_t lhs_rules = (tmpl_rules_t) {
.attr = {
.dict_def = request->dict,
while (rlm_sql_fetch_row(&row, inst, request, handle) == RLM_SQL_OK) {
map_t *map;
- if (map_afrom_fields(ctx, &map, request, row[2], row[4], row[3], &lhs_rules, &rhs_rules) < 0) {
+ if (map_afrom_fields(ctx, &map, &parent, request, row[2], row[4], row[3], &lhs_rules, &rhs_rules) < 0) {
RPEDEBUG("Error parsing user data from database result");
(inst->driver->sql_finish_select_query)(*handle, &inst->config);
return -1;
}
- map_list_insert_tail(out, map);
+ if (!map->parent) map_list_insert_tail(out, map);
rows++;
}