0

In a PL/pgSQL function, I am creating a view using the EXECUTE statement. The where clause in the view takes as input some jenkins job names. These job names are passed to the function as a comma-separated string. They are then converted to an array so that they can be used as argument to ANY in the where clause. See basic code below:

CREATE OR REPLACE FUNCTION FETCH_ALL_TIME_AGGR_KPIS(jobs VARCHAR)
RETURNS SETOF GenericKPI AS $$
DECLARE
job_names TEXT[];
BEGIN
job_names = string_to_array(jobs,',');   

EXECUTE 'CREATE OR REPLACE TEMP VIEW dynamicView AS ' || 
'with pipeline_aggregated_kpis AS (
select
    jenkins_build_parent_id,
    sum (duration) as duration
from test_all_finished_job_builds_enhanced_view where job_name = ANY (' || array(select quote_ident(unnest(job_names))) || ') and jenkins_build_parent_id is not null 

group by jenkins_build_parent_id)
select ' || quote_ident('pipeline-job') || ' as job_name, b1.jenkins_build_id, pipeline_aggregated_kpis.status, pipeline_aggregated_kpis.duration FROM job_builds_enhanced_view b1 INNER JOIN pipeline_aggregated_kpis ON (pipeline_aggregated_kpis.jenkins_build_parent_id = b1.jenkins_build_id)';

RETURN QUERY (select
count(*) as total_executions,    
round(avg (duration) FILTER (WHERE status = 'SUCCESS')::numeric,2) as average_duration
from dynamicView);  
END
$$
LANGUAGE plpgsql;

The creation of the function is successful but an error message is returned when I try to call the function. See below:

eea_ci_db=> select * from FETCH_ALL_TIME_AGGR_KPIS('integration,test');
ERROR:  malformed array literal: ") and jenkins_build_parent_id is not null 
 group by jenkins_build_parent_id)
select "
LINE 7: ...| array(select quote_ident(unnest(job_names))) || ') and jen...
                                                         ^
DETAIL:  Array value must start with "{" or dimension information.
CONTEXT:  PL/pgSQL function fetch_all_time_aggr_kpis(character varying) line 8 at EXECUTE

It seems like there is something going wrong with quotes & the passing of an array of string. I tried all following options with the same result:

where job_name = ANY (' || array(select quote_ident(unnest(job_names))) || ') and jenkins_build_parent_id is not null

or

where job_name = ANY (' || quote_ident(job_names)) || ') and jenkins_build_parent_id is not null

or

where job_name = ANY (' || job_names || ') and jenkins_build_parent_id is not null

Any ideas? Thank you

3
  • Why are you creating a view in the first place? If you run that query directly you can pass parameter values through placeholders and don't need to bother about formatting literals correctly. Commented Jun 7, 2018 at 13:33
  • 1
    quote_ident() is for identifiers, i.e. references to tables and columns. You probably want quote_literal() instead. Commented Jun 7, 2018 at 13:36
  • quote_literal() did indeed solve the issue. Commented Jun 8, 2018 at 14:54

1 Answer 1

2

There is no need for dynamic SQL at all. There isn't even the need for PL/pgSQL to do this:

CREATE OR REPLACE FUNCTION FETCH_ALL_TIME_AGGR_KPIS(jobs VARCHAR)
  RETURNS SETOF GenericKPI 
AS 
$$
  with pipeline_aggregated_kpis AS (
    select jenkins_build_parent_id,
           sum (duration) as duration
    from test_all_finished_job_builds_enhanced_view 
    where job_name = ANY (string_to_array(jobs,',')) 
      and jenkins_build_parent_id is not null 
    group by jenkins_build_parent_id
  ), dynamic_view as (
    select "pipeline-job" as job_name, 
            b1.jenkins_build_id, 
            pipeline_aggregated_kpis.status, 
            pipeline_aggregated_kpis.duration 
    FROM job_builds_enhanced_view b1 
      JOIN pipeline_aggregated_kpis 
        ON pipeline_aggregated_kpis.jenkins_build_parent_id = b1.jenkins_build_id
  )
  select count(*) as total_executions,    
         round(avg (duration) FILTER (WHERE status = 'SUCCESS')::numeric,2) as average_duration
  from dynamic_view;  
$$
language sql;

You could do this with PL/pgSQL as well, you just need to use RETURN QUERY WITH ....

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.