]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow removing from list by attribute and value.
authorAlan T. DeKok <aland@freeradius.org>
Fri, 29 Jul 2022 18:18:33 +0000 (14:18 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 29 Jul 2022 18:18:33 +0000 (14:18 -0400)
With documentation and tests.

doc/antora/modules/reference/pages/unlang/edit.adoc
src/lib/util/edit.c
src/lib/util/token.c
src/tests/keywords/edit-list-remove

index d385e4348001f3e6d84ab9e5af2dcbbe791725f5..0861690796d11b953217bdb9a8874f81feca894e 100644 (file)
@@ -73,10 +73,10 @@ In short, failed edit operations are effectively a "noop" operation,
 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]
@@ -117,9 +117,9 @@ the list operations are simple, and well defined.
 | :=       | 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..
 |=====
@@ -251,6 +251,17 @@ Attributes can be removed from a list using the `-=` (remove) operator.
 ----
 ====
 
+.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.
 
@@ -341,7 +352,7 @@ change the attribute _value_.
 | *=       | 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>_
 |=====
index 9d1a4bbb8b2afd828165e5247fd0b7bd3f02260b..9d9788ffdbebbe518db1de8e261dcd18b437d958 100644 (file)
@@ -794,6 +794,40 @@ int fr_edit_list_insert_list_after(fr_edit_list_t *el, fr_pair_list_t *list, fr_
        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()
@@ -1327,6 +1361,22 @@ int fr_edit_list_apply_list_assignment(fr_edit_list_t *el, fr_pair_t *dst, fr_to
                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");
index a8f30c3289d4e0f89e99a72f3b5cfacc34a0bc0c..47fc882a31933ffa00bc9ab8fd5870d91c9f00be 100644 (file)
@@ -176,6 +176,7 @@ const bool fr_assignment_op[T_TOKEN_LAST] = {
 
 const bool fr_list_assignment_op[T_TOKEN_LAST] = {
        T(ADD_EQ),              /* append */
+       T(SUB_EQ),              /* remove */
        T(AND_EQ),              /* intersection */
        T(OR_EQ),               /* union */
        T(LE),                  /* merge RHS */
index 6378ef16f992c0aff1b70aeab010dcc6b99e7bc2..a90140b72ffb76b9d1cc1639a3e20056267ef9b0 100644 (file)
@@ -66,5 +66,30 @@ if (&request.Tmp-String-0) {
        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