You can force a new plan on each execution if you make the query dynamic:
create or replace function test_function_2(
startdate timestamp, enddate timestamp
) returns void as $function$
begin
delete from cl_final_report;
execute $$
INSERT INTO cl_final_report
SELECT
b.batchkey AS batchnumber,
pv.productkey,
p.name AS productname,
avg(r.value) AS avgchemlean,
sum(r.auxvalue) AS totalweight,
max(o.time) AS timecompleted
FROM result r
LEFT JOIN physicalvalue pv ON r.physicalvaluekey = pv.physicalvaluekey
LEFT JOIN product p ON pv.productkey = p.productkey
LEFT JOIN object o ON r.objectkey = o.objectkey
LEFT JOIN batch b ON o.batchkey = b.batchkey
WHERE pv.name = 'CL'::text AND
and o.time between $1 and $2
GROUP BY b.batchkey, pv.productkey, p.name
$$ using startdate, enddate;
end;
$function$ language plpgsql;
For it to work in 8.3 without using
do string concatenation:
create or replace function test_function_2(
startdate timestamp, enddate timestamp
) returns void as $function$
begin
delete from cl_final_report;
execute $$
INSERT INTO cl_final_report
SELECT
b.batchkey AS batchnumber,
pv.productkey,
p.name AS productname,
avg(r.value) AS avgchemlean,
sum(r.auxvalue) AS totalweight,
max(o.time) AS timecompleted
FROM result r
LEFT JOIN physicalvalue pv ON r.physicalvaluekey = pv.physicalvaluekey
LEFT JOIN product p ON pv.productkey = p.productkey
LEFT JOIN object o ON r.objectkey = o.objectkey
LEFT JOIN batch b ON o.batchkey = b.batchkey
WHERE pv.name = 'CL'::text AND
and o.time between '$$ || startdate || $$' and '$$ || enddate || $$'
GROUP BY b.batchkey, pv.productkey, p.name
$$;
end;
$function$ language plpgsql;