]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow edits to be grouped in a transaction via group { ... }
authorAlan T. DeKok <aland@freeradius.org>
Fri, 29 Jul 2022 16:30:52 +0000 (12:30 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 29 Jul 2022 16:30:52 +0000 (12:30 -0400)
src/lib/unlang/compile.c
src/tests/keywords/edit-group [new file with mode: 0644]

index 87b7968339820074e1eefaa1b5619a4e64d64152..da5cb9655e01a7b320f4c90479ad9b06e640586f 100644 (file)
@@ -85,6 +85,7 @@ typedef struct {
        rlm_components_t        component;
        char const              *section_name1;
        char const              *section_name2;
+       bool                    all_edits;
        unlang_actions_t        actions;
        tmpl_rules_t const      *rules;
 } unlang_compile_t;
@@ -2161,7 +2162,10 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct
                                goto add_child;
                        }
 
-                       edit = NULL; /* no longer doing implicit merging of edits */
+                       /*
+                        *      Only merge edits if they're all in a group { ... }
+                        */
+                       if (!unlang_ctx->all_edits) edit = NULL;
 
                        if (strcmp(name, "actions") == 0) {
                                if (!compile_action_subsection(c, g->cs, subcs)) {
@@ -2396,6 +2400,10 @@ static unlang_t *compile_section(unlang_t *parent, unlang_compile_t *unlang_ctx,
 
 static unlang_t *compile_group(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
 {
+       CONF_ITEM *ci = NULL;
+       bool all_edits = true;
+       unlang_compile_t unlang_ctx2;
+
        static unlang_ext_t const group = {
                .type = UNLANG_TYPE_GROUP,
                .len = sizeof(unlang_group_t),
@@ -2404,6 +2412,44 @@ static unlang_t *compile_group(unlang_t *parent, unlang_compile_t *unlang_ctx, C
 
        if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
 
+       if (!unlang_ctx->all_edits) {
+               while ((ci = cf_item_next(cs, ci)) != NULL) {
+                       char const *name;
+
+                       if (cf_item_is_section(ci)) {
+                               CONF_SECTION *subcs;
+
+                               subcs = cf_item_to_section(ci);
+                               name = cf_section_name1(subcs);
+
+                       } else if (cf_item_is_pair(ci)) {
+                               CONF_PAIR *cp;
+
+                               cp = cf_item_to_pair(ci);
+                               name = cf_pair_attr(cp);
+
+                       } else {
+                               continue;
+                       }
+
+                       if (*name != '&') {
+                               all_edits = false;
+                               break;
+                       }
+               }
+
+               /*
+                *      The parent wasn't all edits, but we are.  Set
+                *      the "all edits" flag, and recurse.
+                */
+               if (all_edits) {
+                       compile_copy_context(&unlang_ctx2, unlang_ctx, unlang_ctx->component);
+                       unlang_ctx2.all_edits = true;
+
+                       return compile_section(parent, &unlang_ctx2, cs, &group);
+               }
+       }
+
        return compile_section(parent, unlang_ctx, cs, &group);
 }
 
diff --git a/src/tests/keywords/edit-group b/src/tests/keywords/edit-group
new file mode 100644 (file)
index 0000000..0690da1
--- /dev/null
@@ -0,0 +1,48 @@
+#
+#  PRE: edit
+#
+
+#
+#  ALl of these edits are grouped.  So if one of them fails, all of
+#  them are rolled back.
+#
+group {
+       &request.Tmp-String-0 := "foo"
+       &request.Tmp-String-1 := %{request.Tmp-Integer-0}  # doesn't exist
+       &request.Tmp-String-2 := "bar"
+}
+
+if (&request.Tmp-String-0) {
+       test_fail
+}
+
+if (&request.Tmp-String-1) {
+       test_fail
+}
+
+if (&request.Tmp-String-2) {
+       test_fail
+}
+
+#
+#  All of these succeed individually, so all of them should succeed.
+#
+group {
+       &request.Tmp-String-0 := "foo"
+       &request.Tmp-String-1 := "yup"
+       &request.Tmp-String-2 := "bar"
+}
+
+if (&request.Tmp-String-0 != "foo") {
+       test_fail
+}
+
+if (&request.Tmp-String-1 != "yup") {
+       test_fail
+}
+
+if (&request.Tmp-String-2 != "bar") {
+       test_fail
+}
+
+success