2016-08-09 58 views
-1

使用PostgreSQL 9.5。來自plpgsql函數的OUT參數在函數中爲NULL,但在單元測試時顯示

這裏是有問題的代碼,從job_parameters():

 SELECT 
      j.controller_id 
      , j.model_id 
      , (
       SELECT cp.parameters 
       FROM commit_schema.controller_parameters cp 
       WHERE cp.parameters_id = j.controller_parameters_id 
       AND cp.controller_id = j.controller_id 
      ) AS _ctrl_par 
      , (
       SELECT mp.parameters 
       FROM commit_schema.model_parameters mp 
       WHERE mp.parameters_id = j.model_parameters_id 
       AND mp.model_id = j.model_id 
      ) AS _mod_par 
      , j.initial_glucose_id 
     INTO 
      controller_id 
      , model_id 
      , controller_parameters 
      , model_parameters 
      , initial_glucose_id 
     FROM 
       commit_schema.job j 
--   INNER JOIN 
--     commit_schema.model_parameters mp 
--     ON j.model_parameters_id = mp.parameters_id 
--   INNER JOIN 
--     commit_schema.controller_parameters cp 
--     ON j.controller_parameters_id = cp.parameters_id 
     WHERE j.sim_id = sim_id; 

當運行這個查詢在一個單獨的窗口固定SIM_ID(最後WHERE子句中使用),我得到的所有行的我希望被退回。 SELECT INTO將此查詢的結果直接移動到此plpgsql函數的OUT參數中,但運行此函數時,除sim_id之外的所有列均爲NULL,這是由於在函數的前面選擇了sim_id這一事實。

起初我以爲這是由於我用於Inner Join的鍵導致行顯示爲NULL,所以我選擇了子查詢。這在單獨運行此查詢時起作用,但將其整合到函數的其餘部分會導致其失敗。

我嘗試使用RAISE NOTICE '%'進行疑難解答,然後是單個參數,但似乎無法打印到控制檯。

所有相關功能完整的代碼是在這裏:

DROP DOMAIN IF EXISTS computer_name CASCADE; 
CREATE DOMAIN computer_name AS varchar(50); 

DROP TYPE IF EXISTS error_cluster CASCADE; 
CREATE TYPE error_cluster AS (
    error_code integer 
    , error_msg varchar(100) 
); 

DROP FUNCTION IF EXISTS select_client_id(computer_name); 
CREATE FUNCTION select_client_id (
    IN clean_name computer_name DEFAULT 'none' 
    , OUT client_id integer 
    ) 
    AS $$ 
    BEGIN 
     SELECT c.client_id INTO client_id 
     FROM commit_schema.client c 
     WHERE c.client_name = $1 
     LIMIT 1; 
    END; 
    $$ LANGUAGE plpgsql; 

DROP FUNCTION IF EXISTS get_client_id(computer_name, error_cluster); 
CREATE FUNCTION get_client_id (
    IN clean_name computer_name DEFAULT 'none' 
    , IN error_in error_cluster DEFAULT (0,'') 
    , OUT _client_id integer 
    , OUT error_out error_cluster) 
    AS $$ 
    BEGIN 
     SELECT client_id INTO _client_id 
     FROM select_client_id(clean_name); 
     IF _client_id IS NULL THEN 
      INSERT INTO commit_schema.client (client_name) VALUES (clean_name); 
      SELECT client_id INTO _client_id 
      FROM select_client_id(clean_name); 
     END IF; 
     IF _client_id IS NULL THEN 
      SELECT 
       -10000 
       , 'No Client ID found after Client Name insert.' 
      INTO 
       error_out.error_code 
       , error_out.error_msg; 
     ELSE 
      SELECT 
       error_in.error_code 
       , error_in.error_msg 
      INTO 
       error_out.error_code 
       , error_out.error_msg; 
     END IF; 
    END; 
    $$ LANGUAGE plpgsql; 

/* 
    Get a simulation ID 
*/ 

DROP FUNCTION IF EXISTS get_sim_id(error_cluster); 
CREATE FUNCTION get_sim_id(
    IN error_in error_cluster 
    , OUT _sim_id integer 
    , OUT error_out error_cluster) 
    AS $$ 
    BEGIN 
     -- SELECT 
--    MIN(j.sim_id) 
--   INTO 
--    _sim_id 
--   FROM commit_schema.job j 
--   WHERE j.job_status_id = 0; 
     UPDATE commit_schema.job j 
     SET job_status_id = 1 
     FROM (
       SELECT sim_id 
       FROM commit_schema.job 
       WHERE job_status_id = 0 
       ORDER BY sim_id 
       LIMIT 1 
       FOR UPDATE SKIP LOCKED 
     ) sub 
     WHERE j.sim_id = sub.sim_id 
     RETURNING j.sim_id INTO _sim_id; 
     IF NOT FOUND THEN 
      SELECT 
       -10000 
       , 'No more jobs left to run!' 
       , -1 
      INTO 
       error_out.error_code 
       , error_out.error_msg 
       , _sim_id; 
     ELSE 
      SELECT 
       error_in.error_code 
       , error_in.error_msg 
      INTO 
       error_out.error_code 
       , error_out.error_msg; 
     END IF; 
    END; 
    $$ LANGUAGE plpgsql; 

DROP FUNCTION IF EXISTS set_job_status(integer, smallint, error_cluster); 
CREATE FUNCTION set_job_status(
    IN sim_id integer 
    , IN status smallint 
    , IN error_in error_cluster 
    , OUT error_out error_cluster) 
    AS $$ 
    BEGIN 
     UPDATE commit_schema.job j 
     SET j.job_status_id = $2 
     WHERE j.sim_id = $1; 
     IF NOT FOUND THEN 
      SELECT 
       -10000 
       , 'No job with sim_id='||$1 
      INTO 
       error_out.error_code 
       , error_out.error_msg; 
     ELSE 
      SELECT 
       error_in.error_code 
       , error_in.error_msg 
      INTO 
       error_out.error_code 
       , error_out.error_msg; 
     END IF; 
    END; 
    $$ LANGUAGE plpgsql; 

DROP FUNCTION IF EXISTS set_job_status_to_pending(integer, error_cluster); 
CREATE FUNCTION set_job_status_to_pending(
    IN sim_id integer 
    , IN error_in error_cluster 
    , OUT error_out error_cluster) 
    AS $$ 
    BEGIN 
     SELECT 
      sj.error_out.error_code 
      , sj.error_out.error_msg 
     INTO 
      error_out.error_code 
      , error_out.error_msg 
     FROM set_job_status($1, 1, error_in.*) sj; 
    END; 
    $$ LANGUAGE plpgsql; 

DROP FUNCTION IF EXISTS set_job_status_to_running(integer, error_cluster); 
CREATE FUNCTION set_job_status_to_running(
    IN sim_id integer 
    , IN error_in error_cluster 
    , OUT error_out error_cluster) 
    AS $$ 
    BEGIN 
     SELECT 
      sj.error_out.error_code 
      , sj.error_out.error_msg 
     INTO 
      error_out.error_code 
      , error_out.error_msg 
     FROM set_job_status($1, 2, error_in.*) sj; 
    END; 
    $$ LANGUAGE plpgsql; 

DROP FUNCTION IF EXISTS reset_job_status(integer, error_cluster); 
CREATE FUNCTION reset_job_status(
    IN sim_id integer 
    , IN error_in error_cluster 
    , OUT error_out error_cluster) 
    AS $$ 
    BEGIN 
     SELECT 
      sj.error_out.error_code 
      , sj.error_out.error_msg 
     INTO 
      error_out.error_code 
      , error_out.error_msg 
     FROM set_job_status($1, 0, error_in.*) sj; 
    END; 
    $$ LANGUAGE plpgsql; 

DROP FUNCTION IF EXISTS job_parameters(computer_name); 
CREATE FUNCTION job_parameters (
    IN computer_id computer_name DEFAULT 'none' 
    , OUT controller_id integer 
    , OUT sim_id integer 
    , OUT controller_parameters integer ARRAY 
    , OUT model_id integer 
    , OUT model_parameters integer ARRAY 
    , OUT initial_glucose_id integer 
    , OUT final_error_code integer 
    , OUT final_error_msg varchar(100)) 
    AS $$ 
    DECLARE 
     -- get rid of garbage in the name we receive 
     clean_name computer_name := lower(regexp_replace(computer_id, '\W+', '', 'g')); 
     current_error error_cluster; 
     error_out error_cluster; 
    BEGIN 
     SELECT 
      0 
      , '' 
     INTO 
      current_error.error_code 
      , current_error.error_msg; 
     -- -- See if the incoming client already has a client_id 
     -- -- If it does, use that. Else give it a new one. 
     SELECT 
      error_out.error_msg 
      , error_out.error_code 
     INTO 
      current_error.error_msg 
      , current_error.error_code 
     FROM get_client_id(clean_name, current_error.*); 

     -- Get a simulation ID 
     SELECT 
      _sim_id 
      , error_out.error_msg 
      , error_out.error_code 
     INTO 
      sim_id 
      , current_error.error_msg 
      , current_error.error_code 
     FROM get_sim_id(current_error.*); 

     IF current_error <> (0,'') THEN 
      SELECT 
       current_error.error_code 
       , current_error.error_msg 
      INTO 
       final_error_code 
       , final_error_msg; 
      RETURN; 
     END IF; 

--   -- Set the job to pending 
--   SELECT 
--    error_out.error_code 
--    , error_out.error_msg 
--   INTO 
--    current_error.error_code 
--    , current_error.error_msg 
--   FROM set_job_status_to_pending(sim_id, current_error.*); 

--   IF current_error <> (0,'') THEN 
--    SELECT 
--     current_error.error_code 
--     , current_error.error_msg 
--    INTO 
--     final_error_code 
--     , final_error_msg; 
--    RETURN; 
--   END IF; 

     -- Get the parameters 
     SELECT 
      j.controller_id 
      , j.model_id 
      , (
       SELECT cp.parameters 
       FROM commit_schema.controller_parameters cp 
       WHERE cp.parameters_id = j.controller_parameters_id 
       AND cp.controller_id = j.controller_id 
      ) AS _ctrl_par 
      , (
       SELECT mp.parameters 
       FROM commit_schema.model_parameters mp 
       WHERE mp.parameters_id = j.model_parameters_id 
       AND mp.model_id = j.model_id 
      ) AS _mod_par 
      , j.initial_glucose_id 
     INTO 
      controller_id 
      , model_id 
      , controller_parameters 
      , model_parameters 
      , initial_glucose_id 
     FROM 
       commit_schema.job j 
--   INNER JOIN 
--     commit_schema.model_parameters mp 
--     ON j.model_parameters_id = mp.parameters_id 
--   INNER JOIN 
--     commit_schema.controller_parameters cp 
--     ON j.controller_parameters_id = cp.parameters_id 
     WHERE j.sim_id = sim_id; 
     RAISE NOTICE 'mp: % cp: % simid: %', model_parameters, controller_parameters, sim_id; 

     IF NOT FOUND THEN 
      SELECT 
       current_error.error_msg || 'No row found for sim_id ' || sim_id ||'. ' 
       , -10000 
      INTO 
       final_error_msg 
       , final_error_code; 
      RETURN; 
     END IF; 

     IF controller_id IS NULL THEN 
      SELECT 
       current_error.error_msg || 'No Controller ID found for this sim_id. ' 
       , -10000 
      INTO 
       current_error.error_msg 
       , current_error.error_code; 
     END IF; 

     IF model_id IS NULL THEN 
      SELECT 
       current_error.error_msg || 'No Model ID found for this sim_id. ' 
       , -10000 
      INTO 
       current_error.error_msg 
       , current_error.error_code; 
     END IF; 

     IF controller_parameters IS NULL THEN 
      SELECT 
       current_error.error_msg || 'No Controller Parameters found for this sim_id. ' 
       , -10000 
      INTO 
       current_error.error_msg 
       , current_error.error_code; 
     END IF; 

     IF model_parameters IS NULL THEN 
      SELECT 
       current_error.error_msg || 'No Model Parameters found for this sim_id. ' 
       , -10000 
      INTO 
       current_error.error_msg 
       , current_error.error_code; 
     END IF; 

     IF current_error = (0,'') THEN 
     -- If everything went well, set job to running 
      SELECT 
       error_out.error_code 
       , error_out.error_msg 
      INTO 
       current_error.error_code 
       , current_error.error_msg 
      FROM set_job_status_to_running(sim_id, current_error.*); 
     ELSE 
     -- Otherwise we reset the simid 
      SELECT 
       error_out.error_code 
      INTO current_error 
      FROM reset_job_status(sim_id, current_error.*); 
     END IF; 

     SELECT 
      current_error.error_msg 
      , current_error.error_code 
     INTO 
      final_error_msg 
      , final_error_code; 

    END; 
    $$ LANGUAGE plpgsql; 
+0

你應該真正發佈整個功能,讓任何人都能正確理解問題和解決方案。 – Patrick

+0

用全功能創建腳本更新了OP。警告:它不是很漂亮。 – ijustlovemath

+0

問題中有太多(可能不相關的)代碼。 SO不適用於代碼審查,它是一個Q/A網站。 –

回答

1

功能set_job_status(...)有語法錯誤:

CREATE FUNCTION set_job_status(
    IN sim_id integer 
    , IN status smallint 
    , IN error_in error_cluster 
    , OUT error_out error_cluster) 
    AS $$ 
    BEGIN 
     UPDATE commit_schema.job j 
     SET j.job_status_id = $2 -- invalid!! 
     WHERE j.sim_id = $1; 

UPDATESET子句中不能表限定列名。必須是:

 SET job_status_id = $2 

不知道是否這一切,我停止看那裏。