]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Rework SQL xlat to push SQL query function on the stack
authorNick Porter <nick@portercomputing.co.uk>
Mon, 20 May 2024 10:35:37 +0000 (11:35 +0100)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 7 Jun 2024 02:26:58 +0000 (22:26 -0400)
src/modules/rlm_sql/rlm_sql.c

index 5da2d0f8135a6612239216c3299d70da5728dc45..c918b16d8a78411e94683fb86fca4fe9639cda46 100644 (file)
@@ -386,6 +386,103 @@ static int sql_box_escape(fr_value_box_t *vb, void *uctx)
        return sql_xlat_escape(NULL, vb, uctx);
 }
 
+static xlat_action_t sql_xlat_query_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
+                                          request_t *request, UNUSED fr_value_box_list_t *in)
+{
+       fr_sql_query_t          *query_ctx = talloc_get_type_abort(xctx->rctx, fr_sql_query_t);
+       rlm_sql_t const         *inst = query_ctx->inst;
+       fr_value_box_t          *vb;
+       xlat_action_t           ret = XLAT_ACTION_DONE;
+       rlm_sql_handle_t        *handle = query_ctx->handle;
+       int                     numaffected;
+
+       fr_assert(query_ctx->type == SQL_QUERY_OTHER);
+
+       if (query_ctx->rcode != RLM_SQL_OK) {
+               RERROR("SQL query failed: %s", fr_table_str_by_value(sql_rcode_description_table,
+                                                                    query_ctx->rcode, "<INVALID>"));
+               rlm_sql_print_error(inst, request, query_ctx, false);
+               ret = XLAT_ACTION_FAIL;
+               goto finish;
+       }
+
+       numaffected = (inst->driver->sql_affected_rows)(query_ctx, &inst->config);
+       if (numaffected < 1) {
+               RDEBUG2("SQL query affected no rows");
+               goto finish;
+       }
+
+       MEM(vb = fr_value_box_alloc_null(ctx));
+       fr_value_box_uint32(vb, NULL, (uint32_t)numaffected, false);
+       fr_dcursor_append(out, vb);
+
+finish:
+       talloc_free(query_ctx);
+       if (!inst->driver->uses_trunks) fr_pool_connection_release(inst->pool, request, handle);
+
+       return ret;
+}
+
+static xlat_action_t sql_xlat_select_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
+                                           request_t *request, UNUSED fr_value_box_list_t *in)
+{
+       fr_sql_query_t          *query_ctx = talloc_get_type_abort(xctx->rctx, fr_sql_query_t);
+       rlm_sql_t const         *inst = query_ctx->inst;
+       fr_value_box_t          *vb;
+       xlat_action_t           ret = XLAT_ACTION_DONE;
+       rlm_sql_handle_t        *handle = query_ctx->handle;
+       rlm_rcode_t             p_result;
+       rlm_sql_row_t           row;
+       bool                    fetched = false;
+
+       fr_assert(query_ctx->type == SQL_QUERY_SELECT);
+
+       if (query_ctx->rcode != RLM_SQL_OK) {
+       query_error:
+               RERROR("SQL query failed: %s", fr_table_str_by_value(sql_rcode_description_table,
+                                                                    query_ctx->rcode, "<INVALID>"));
+               rlm_sql_print_error(inst, request, query_ctx, false);
+               ret = XLAT_ACTION_FAIL;
+               goto finish;
+       }
+
+       do {
+               inst->fetch_row(&p_result, NULL, request, query_ctx);
+               row = query_ctx->row;
+               switch (query_ctx->rcode) {
+               case RLM_SQL_OK:
+                       if (row[0]) break;
+
+                       RDEBUG2("NULL value in first column of result");
+                       ret = XLAT_ACTION_FAIL;
+                       goto finish;
+
+               case RLM_SQL_NO_MORE_ROWS:
+                       if (!fetched) {
+                               RDEBUG2("SQL query returned no results");
+                               ret = XLAT_ACTION_FAIL;
+                       }
+                       goto finish;
+
+               default:
+                       goto query_error;
+               }
+
+               fetched = true;
+
+               MEM(vb = fr_value_box_alloc_null(ctx));
+               fr_value_box_strdup(vb, vb, NULL, row[0], false);
+               fr_dcursor_append(out, vb);
+
+       } while (1);
+
+finish:
+       talloc_free(query_ctx);
+       if (!inst->driver->uses_trunks) fr_pool_connection_release(inst->pool, request, handle);
+
+       return ret;
+}
+
 /** Execute an arbitrary SQL query
  *
  * For SELECTs, the values of the first column will be returned.
@@ -404,19 +501,18 @@ static xlat_action_t sql_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
 {
        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_row_t           row;
        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);
-       xlat_action_t           ret = XLAT_ACTION_DONE;
        char const              *p;
        fr_value_box_t          *arg = fr_value_box_list_head(in);
-       fr_value_box_t          *vb = NULL;
-       bool                    fetched = false;
        fr_sql_query_t          *query_ctx = NULL;
        rlm_rcode_t             p_result;
+       unlang_action_t         query_ret = UNLANG_ACTION_CALCULATE_RESULT;
 
-       handle = fr_pool_connection_get(inst->pool, request);   /* connection pool should produce error */
-       if (!handle) return XLAT_ACTION_FAIL;
+       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);
@@ -436,77 +532,23 @@ static xlat_action_t sql_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        if ((strncasecmp(p, "insert", 6) == 0) ||
            (strncasecmp(p, "update", 6) == 0) ||
            (strncasecmp(p, "delete", 6) == 0)) {
-               int numaffected;
-
                MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request, handle,
                                                   thread->trunk, arg->vb_strvalue, SQL_QUERY_OTHER));
-               inst->query(&p_result, NULL, request, query_ctx);
-               if (query_ctx->rcode != RLM_SQL_OK) {
-               query_error:
-                       RERROR("SQL query failed: %s", fr_table_str_by_value(sql_rcode_description_table,
-                                                                            query_ctx->rcode, "<INVALID>"));
-
-                       ret = XLAT_ACTION_FAIL;
-                       goto finish;
-               }
 
-               numaffected = (inst->driver->sql_affected_rows)(query_ctx, &inst->config);
-               if (numaffected < 1) {
-                       RDEBUG2("SQL query affected no rows");
+               unlang_xlat_yield(request, sql_xlat_query_resume, NULL, 0, query_ctx);
+               query_ret = inst->query(&p_result, NULL, request, query_ctx);
+               if (query_ret == UNLANG_ACTION_PUSHED_CHILD) return XLAT_ACTION_PUSH_UNLANG;
 
-                       goto finish;
-               }
-
-               MEM(vb = fr_value_box_alloc_null(ctx));
-               fr_value_box_uint32(vb, NULL, (uint32_t)numaffected, false);
-               fr_dcursor_append(out, vb);
-
-               goto finish;
+               return sql_xlat_query_resume(ctx, out, &(xlat_ctx_t){.rctx = query_ctx, .inst = inst}, request, in);
        } /* else it's a SELECT statement */
 
        MEM(query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request, handle,
                                           thread->trunk, arg->vb_strvalue, SQL_QUERY_SELECT));
-       inst->select(&p_result, NULL, request, query_ctx);
-       if (query_ctx->rcode != RLM_SQL_OK) goto query_error;
 
-       do {
-               inst->fetch_row(&p_result, NULL, request, query_ctx);
-               row = query_ctx->row;
-               switch (query_ctx->rcode) {
-               case RLM_SQL_OK:
-                       if (row[0]) break;
+       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;
 
-                       RDEBUG2("NULL value in first column of result");
-                       ret = XLAT_ACTION_FAIL;
-
-                       goto finish;
-
-               case RLM_SQL_NO_MORE_ROWS:
-                       if (!fetched) {
-                               RDEBUG2("SQL query returned no results");
-                               ret = XLAT_ACTION_FAIL;
-                       }
-
-                       goto finish;
-
-               default:
-                       goto query_error;
-               }
-
-               fetched = true;
-
-               MEM(vb = fr_value_box_alloc_null(ctx));
-               fr_value_box_strdup(vb, vb, NULL, row[0], false);
-               fr_dcursor_append(out, vb);
-
-       } while (1);
-
-finish:
-       handle = query_ctx->handle;
-       talloc_free(query_ctx);
-       fr_pool_connection_release(inst->pool, request, handle);
-
-       return ret;
+       return XLAT_ACTION_PUSH_UNLANG;
 }
 
 /** Converts a string value into a #fr_pair_t