]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add %sql.fetch() and %sql.modify() xlats
authorNick Porter <nick@portercomputing.co.uk>
Tue, 12 Nov 2024 08:29:13 +0000 (08:29 +0000)
committerNick Porter <nick@portercomputing.co.uk>
Tue, 12 Nov 2024 08:29:13 +0000 (08:29 +0000)
The generic %sql() xlat attempts to determine whether a query is
expected to return data based on the initial key word in the statement.

That is not guarunteed to be correct in all situations, e.g. `UPDATE ...
RETURNING ...` does return data unlike other queries starting `UPDATE`

%sql.fetch() is expected to return data.
%sql.modify() is expected to return the number of rows affected.

src/modules/rlm_sql/rlm_sql.c

index f4dd4cc9d9fd355571215ba29cc00c4836dc23b2..fdb56de378bb316c6b21c80f3cc1b813a99ef1ec 100644 (file)
@@ -612,6 +612,79 @@ static xlat_action_t sql_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_PUSH_UNLANG;
 }
 
+/** Execute an arbitrary SQL query, expecting results to be returned
+ *
+@verbatim
+%sql.fetch(<sql statement>)
+@endverbatim
+ *
+ * @ingroup xlat_functions
+ */
+static xlat_action_t sql_fetch_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx,
+                                   request_t *request, fr_value_box_list_t *in)
+{
+       sql_xlat_call_env_t     *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
+       rlm_sql_handle_t        *handle = NULL;
+       rlm_sql_t const         *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
+       rlm_sql_thread_t        *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
+       fr_value_box_t          *arg = fr_value_box_list_head(in);
+       fr_sql_query_t          *query_ctx = NULL;
+
+       if (!inst->driver->uses_trunks) {
+               handle = fr_pool_connection_get(inst->pool, request);   /* connection pool should produce error */
+               if (!handle) return XLAT_ACTION_FAIL;
+       }
+
+       if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
+               rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
+       }
+
+       MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request, handle,
+                                          thread->trunk, arg->vb_strvalue, SQL_QUERY_SELECT));
+
+       unlang_xlat_yield(request, sql_xlat_select_resume, NULL, 0, query_ctx);
+       if (unlang_function_push(request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, query_ctx) != UNLANG_ACTION_PUSHED_CHILD) return XLAT_ACTION_FAIL;
+
+       return XLAT_ACTION_PUSH_UNLANG;
+}
+
+/** Execute an arbitrary SQL query, returning the number of rows affected
+ *
+@verbatim
+%sql.modify(<sql statement>)
+@endverbatim
+ *
+ * @ingroup xlat_functions
+ */
+static xlat_action_t sql_modify_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
+                                    request_t *request, fr_value_box_list_t *in)
+{
+       sql_xlat_call_env_t     *call_env = talloc_get_type_abort(xctx->env_data, sql_xlat_call_env_t);
+       rlm_sql_handle_t        *handle = NULL;
+       rlm_sql_t const         *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_sql_t);
+       rlm_sql_thread_t        *thread = talloc_get_type_abort(xctx->mctx->thread, rlm_sql_thread_t);
+       fr_value_box_t          *arg = fr_value_box_list_head(in);
+       fr_sql_query_t          *query_ctx = NULL;
+       rlm_rcode_t             p_result;
+
+       if (!inst->driver->uses_trunks) {
+               handle = fr_pool_connection_get(inst->pool, request);   /* connection pool should produce error */
+               if (!handle) return XLAT_ACTION_FAIL;
+       }
+
+       if (call_env->filename.type == FR_TYPE_STRING && call_env->filename.vb_length > 0) {
+               rlm_sql_query_log(inst, call_env->filename.vb_strvalue, arg->vb_strvalue);
+       }
+
+       MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request, handle,
+                                          thread->trunk, arg->vb_strvalue, SQL_QUERY_OTHER));
+
+       unlang_xlat_yield(request, sql_xlat_query_resume, NULL, 0, query_ctx);
+       if (inst->query(&p_result, NULL, request, query_ctx) == UNLANG_ACTION_PUSHED_CHILD) return XLAT_ACTION_PUSH_UNLANG;
+
+       return sql_xlat_query_resume(ctx, out, &(xlat_ctx_t){.rctx = query_ctx, .inst = inst}, request, in);
+}
+
 /** Converts a string value into a #fr_pair_t
  *
  * @param[in,out] ctx to allocate #fr_pair_t (s).
@@ -2278,9 +2351,11 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
 
        /*
         *      The xlat escape function needs access to inst - so
-        *      argument parser details need to be defined here
+        *      argument parser details need to be defined here.
+        *      Parented off the module instance "boot" so it can be shared
+        *      between three xlats.
         */
-       MEM(sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2));
+       MEM(sql_xlat_arg = talloc_zero_array(boot, xlat_arg_parser_t, 2));
        MEM(uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t));
        *uctx = (rlm_sql_escape_uctx_t){ .sql = inst, .handle = NULL };
        sql_xlat_arg[0] = (xlat_arg_parser_t){
@@ -2295,6 +2370,17 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
 
        xlat_func_args_set(xlat, sql_xlat_arg);
 
+       /*
+        *      Register instances of the SQL xlat with pre-determined output types
+        */
+       if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "fetch", sql_fetch_xlat, FR_TYPE_VOID)))) return -1;
+       xlat_func_call_env_set(xlat, &xlat_method_env);
+       xlat_func_args_set(xlat, sql_xlat_arg);
+
+       if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "modify", sql_modify_xlat, FR_TYPE_UINT32)))) return -1;
+       xlat_func_call_env_set(xlat, &xlat_method_env);
+       xlat_func_args_set(xlat, sql_xlat_arg);
+
        if (unlikely(!(xlat = module_rlm_xlat_register(boot, mctx, "escape", sql_escape_xlat, FR_TYPE_STRING)))) return -1;
        sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
        sql_xlat_arg[0] = (xlat_arg_parser_t){