refers to the first attributes, `[1]` refers to the second attribute,
etc.
-The `<index>` can be an integer (0..1000), as in the example below.
+The `<index>` can be an integer (0..1000).
The indexes are limited to 1000, because there are essentially no
protocols which have more than 1000 attributes.
&reply.NAS-IP-Address[2]
----
-The `<index>` can also be a reference to a numerical attribute, as in the example below.
+The `<index>` can also be a special value `n`, which means "the last attribute in the list.
+
+.Last attribute in a list
+[source,unlang]
+----
+&EAP-Message[n]
+----
+
+The `<index>` can also be a reference to a numerical attribute.
The reference *must* be to an attribute of numerical data type. Structural data types and `string` or `octets` types are not allowed. If the index is out of bounds (e.g. negative), then the reference fails.
&EAP-Message[&foo]
----
-The `<index>` can also be a special value `n`, which means "the last attribute in the list.
+The `<index>` can also be an expression which is calculated at run time. The expression _must_ not cause the server to call an external database, script, etc. The main purpose of these expressions is to calculated an index without first placing it into another attribute.
-.Last attribute in a list
+If the index must be calculated from an external database call or script, simply place that value into an attribute first, and then use that attribute as in index.
+
+The expression _must_ be in an expansion block: `%{...}`.
+
+.Expression as an Array index
[source,unlang]
----
-&EAP-Message[n]
+&index = 0
+
+&EAP-Message[%{&index + 1}]
----
=== Array References in lists
if (ar_filter_is_none(ar)) {
num = 0;
+ } else if (ar_filter_is_expr(ar)) {
+ fr_value_box_t box;
+ request_t *request = cc->request;
+
+ if (unlang_xlat_eval_type(request, &box, FR_TYPE_UINT8, NULL, request, ar->ar_expr) < 0) {
+ RPEDEBUG("Failed evaluating expression");
+ vp = NULL;
+ pop = true;
+ goto done;
+ }
+
+ num = box.vb_uint8;
+
} else if (!ar_filter_is_num(ar)) {
request_t *request = cc->request;
--- /dev/null
+#
+# PRE: attr-index
+#
+# Tests for using attribute references as array index
+#
+uint32 index
+
+&request += {
+ &Class = 0x01020304,
+ &Class = 0x05060708,
+ &Class = 0x090a0b0c,
+}
+
+#
+# Computed indexes, with some limitations
+#
+&index := 1
+
+if (&Class[%{&index - 1}] != 0x01020304) {
+ test_fail
+}
+
+&index := 4
+
+if (&Class[%{&index - 2}] != 0x090a0b0c) {
+ test_fail
+}
+
+success