will simply not work with them. We recommend switching to the new
edit syntax, which is more powerful, and less verbose.
+Unlike version 3, attribute editing can result in a `fail` return
+code. That is, edits either return `noop` on success, or `fail` on failure.
+
+In most cases, an edit failure will result in the processing section
+exiting, and returning `fail`. This behavior can be overriden with a xref:unlang/transaction.adoc[transaction], or a xref:unlang/try.adoc[try] / xref:unlang/catch.adoc[catch] block.
+
An edit statement has the following standard syntax:
.Syntax
=== Grouping Edits
-Multiple attributes may be grouped into a set by using the `group`
-keyword. When changes are done in a `group`, then either all of the
+Multiple attributes may be grouped into a set by using the xref:unlang/transaction.adoc[transaction]
+keyword. When changes are done in a `transaction`, 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]
-----
-group {
- &reply.Reply-Message += %sql("SELECT ...")
- &reply.Filter-Id := "foo"
-}
-----
-
-If the above SQL `select` statement fails, then then
-`&reply.Filter-Id` attribute will not exist.
-
== List Editing
.Syntax
#include "timeout_priv.h"
#include "limit_priv.h"
#include "transaction_priv.h"
+#include "try_priv.h"
+#include "catch_priv.h"
#define UNLANG_IGNORE ((unlang_t *) -1)
unlang_ctx2.actions.actions[RLM_MODULE_INVALID] = MOD_ACTION_RETURN;
unlang_ctx2.actions.actions[RLM_MODULE_DISALLOW] = MOD_ACTION_RETURN;
- /*
- * We always create a group, even if the section is empty.
- */
g = group_allocate(parent, cs, &transaction);
if (!g) return NULL;
return c;
}
+static unlang_t *compile_try(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
+{
+ unlang_group_t *g;
+ unlang_t *c;
+ CONF_SECTION *next;
+
+ static unlang_ext_t const ext = {
+ .type = UNLANG_TYPE_TRY,
+ .len = sizeof(unlang_try_t),
+ .type_name = "unlang_try_t",
+ };
+
+ /*
+ * The transaction is empty, ignore it.
+ */
+ if (!cf_item_next(cs, NULL)) {
+ cf_log_err(cs, "'try' sections cannot be empty");
+ return NULL;
+ }
+
+ next = cf_section_next(cf_item_to_section(cf_parent(cs)), cs);
+ if (!next || (strcmp(cf_section_name1(next), "catch") != 0)) {
+ cf_log_err(cs, "'try' sections must be followed by a 'catch'");
+ return NULL;
+ }
+
+ g = group_allocate(parent, cs, &ext);
+ if (!g) return NULL;
+
+ c = unlang_group_to_generic(g);
+ c->debug_name = c->name = cf_section_name1(cs);
+
+ return compile_children(g, unlang_ctx, true);
+}
+
+static unlang_t *compile_catch(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
+{
+ unlang_group_t *g;
+ unlang_t *c;
+
+ static unlang_ext_t const ext = {
+ .type = UNLANG_TYPE_CATCH,
+ .len = sizeof(unlang_catch_t),
+ .type_name = "unlang_catch_t",
+ };
+
+ g = group_allocate(parent, cs, &ext);
+ if (!g) return NULL;
+
+ c = unlang_group_to_generic(g);
+ c->debug_name = c->name = cf_section_name1(cs);
+
+ /*
+ * No arg2: catch all errors
+ */
+ if (!cf_section_name2(cs)) {
+ unlang_catch_t *ca = unlang_group_to_catch(g);
+
+ ca->catching[RLM_MODULE_REJECT] = true;
+ ca->catching[RLM_MODULE_FAIL] = true;
+ ca->catching[RLM_MODULE_INVALID] = true;
+ ca->catching[RLM_MODULE_DISALLOW] = true;
+ }
+
+ /*
+ * @todo - Else parse and limit the things we catch
+ */
+
+ return compile_children(g, unlang_ctx, true);
+}
+
static int8_t case_cmp(void const *one, void const *two)
{
{ L("call"), (void *) compile_call },
{ L("caller"), (void *) compile_caller },
{ L("case"), (void *) compile_case },
+ { L("catch"), (void *) compile_catch },
{ L("else"), (void *) compile_else },
{ L("elsif"), (void *) compile_elsif },
{ L("foreach"), (void *) compile_foreach },
{ L("switch"), (void *) compile_switch },
{ L("timeout"), (void *) compile_timeout },
{ L("transaction"), (void *) compile_transaction },
+ { L("try"), (void *) compile_try },
{ L("update"), (void *) compile_update },
};
static int unlang_section_keywords_len = NUM_ELEMENTS(unlang_section_keywords);