and do not result in any changes.
Multiple attributes may be grouped into a set by using the `group`
-keyword. This keyword is not usually needed, and offers no benefit
-other than grouping changes to multiple attributes. When changes are
-done in a `group`, then either all of the changes are applied, or none
-of them are applied.
+keyword. When changes are done in a `group`, then either all of the
+changes are applied, or none of them are applied. This functionality
+is best used to conditionally apply attribute changes, generally when
+retrieving data from a database.
.Grouping multiple edits
[source,unlang]
| := | Override the list to the contents with the _<rhs>_. If the list already exists, its value is over-written. If the list does not exist, it is created, and the contents set to thex value of the _<rhs>_
| += | Perform list append operation. The contents of the _<rhs>_ are appended to the _<list>_. The resulting list is _<list><rhs>_.
| ^= | Perform a list prepend operation. The contents of the _<rhs>_ are prepended to the _<list>_. The resulting list is _<rhs><list>_.
-| -= | Remove attributes from the _<list>_ which match the _<rhs>_
+| -= | Remove attributes from the _<list>_ which match the _<rhs>_ attribute or list.
| \|= | Perform a list union. The resulting list has all of the contents of the original _<list>_, and the _<rhs>_ list.
-| \&= | Perform a list intersection. The resulting list has only the attributes which are in both the origianl _<list>_, and the _<rhs>_ list.
+| &= | Perform a list intersection. The resulting list has only the attributes which are in both the origianl _<list>_, and the _<rhs>_ list.
| >= | Perform a priority merge of two lists. The resulting list has all of the contents of the original _<list>_, and of all _<rhs>_ list attributes which are not already in _<list>_.
| \<= | Perform a priority merge of two lists. The resulting list has all of the contents of the original _<rhs>_, and of all _<list>_ list attributes which are not in _<rhs>_ are left alone..
|=====
----
====
+.Remove instance of `Filter-Id` which have value `bar`
+====
+[source,unlang]
+----
+&reply -= {
+ &Filter-Id = "bar"
+}
+----
+====
+
+
This syntax is clearer and more consistent than the old `!* ANY`
hacks.
| *= | Perform multiplication. The value of the _<attribute>_ is multiplied by the contents of the _<rhs>_.
| /= | Perform subtraction. The value of the _<attribute>_ is divided by the contents of the _<rhs>_.
| \|= | Perform logical "or". The value of the _<attribute>_ is "or"ed with the contents of the _<rhs>_.
-| \&= | Perform logical "and". The value of the _<attribute>_ is "and"ed with the contents of the _<rhs>_.
+| &= | Perform logical "and". The value of the _<attribute>_ is "and"ed with the contents of the _<rhs>_.
| \<<= | Perform left shift. The value of the _<attribute>_ is shifted left by the value of _<rhs>_
| \>>= | Perform right shift. The value of the _<attribute>_ is shifted right by the value of _<rhs>_
|=====
return 0;
}
+/** Removes elements matching a list
+ *
+ * O(N^2) unfortunately.
+ */
+static int fr_edit_list_delete_list(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_list_t *to_remove)
+{
+ fr_pair_t *vp;
+
+ for (vp = fr_pair_list_head(to_remove);
+ vp != NULL;
+ vp = fr_pair_list_next(to_remove, vp)) {
+ fr_pair_t *found;
+
+ /*
+ * @todo - do this recursively.
+ */
+ if (fr_type_is_structural(vp->da->type)) continue;
+
+ for (found = fr_pair_find_by_da(list, NULL, vp->da);
+ found != NULL;
+ found = fr_pair_find_by_da(list, found, vp->da)) {
+ int rcode;
+
+ rcode = fr_value_box_cmp(&vp->data, &found->data);
+ if (rcode != 0) continue;
+
+ if (fr_edit_list_pair_delete(el, list, found) < 0) return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
/** Apply operators to pairs.
*
* := is "if found vp, call fr_edit_list_pair_replace(). Otherwise call fr_edit_list_insert_pair_tail()
COPY;
return fr_edit_list_insert_list_tail(el, &dst->children, src);
+ case T_OP_SUB_EQ:
+ /*
+ * foo -= foo --> {}
+ */
+ if (&dst->children == src) {
+ fr_pair_t *vp;
+
+ while ((vp = fr_pair_list_head(&dst->children)) != NULL) {
+ if (fr_edit_list_pair_delete(el, &dst->children, vp) < 0) return -1;
+ }
+
+ return 0;
+ }
+
+ return fr_edit_list_delete_list(el, &dst->children, src);
+
case T_OP_PREPEND:
if (&dst->children == src) {
fr_strerror_printf("Cannot prepend list to itself");
test_fail
}
+&request.Tmp-String-0 := { "foo", "bar", "baz" }
+
+#
+# Remove one by value.
+#
+# @todo - allow for == or =~ in the RHS list,
+# as a condition? For now, it's an exact match. :(
+#
+&request -= {
+ &Tmp-String-0 = "bar"
+}
+
+if (&Tmp-String-0[0] != "foo") {
+ test_fail
+}
+
+if (&Tmp-String-0[1] != "baz") {
+ test_fail
+}
+
+if (&Tmp-String-0[2]) {
+ test_fail
+}
+
+%(debug_attr:request[*])
success