Files
PgTidy/testdata/corpus/test_mm_proc.pgsql
T
Hein 5cdec88299
CI / Test (push) Successful in 37s
CI / Build (push) Successful in 52s
feat(format): expand the runtime safety gate
Replace the bare SemanticallyEqual call in every frontend (CLI fmt, LSP
formatting + rangeFormatting) with format.VerifySafe, which runs four
checks before any formatted output is emitted:

  - semantic equivalence  - the code token stream is unchanged
  - comment preservation  - no -- or /* */ comment is dropped, merged,
    split, reordered, or reworded (line endings / indentation normalised
    away; recurses into dollar-quoted bodies)
  - structural balance    - the () [] and BEGIN/CASE/IF/LOOP...END nesting
    profile matches, ignoring anything inside a comment or a string
  - idempotence           - a second format pass would not change it

On failure the CLI now prints the specific reason and keeps the original.

The comment check surfaced two real formatter bugs, both fixed in
formatBodyStatements:

  - multi-line /* */ comments inside a PL/pgSQL body had their interior
    lines re-split and reindented as if they were statements; they are
    now tracked and carried verbatim with the opening line
  - a column-0 -- line was glued onto the preceding line by the
    split-line-join, which merged consecutive comment lines into one

Regenerate testdata/corpus/test_mm_proc.pgsql (was carrying the mangled
output). TestCorpusIdempotentAndSafe now runs the full VerifySafe bundle;
add safety_test.go with targeted cases.
2026-09-10 15:17:07 +02:00

1576 lines
55 KiB
PL/PgSQL

CREATE OR REPLACE FUNCTION mm_proc(
p_doctype text --File Types: html, docx, text, allfieldvalues
, p_commtype text --Merge Data Type: e.g. SMS,Email,All (Will be passed through to menu/taglist funct)
, p_template bytea --Source/Template file to be merge
, p_data_prefix text --Table prefix for data source. e.g. tcli (client) , sta (standard letters)
, p_data_rid integer --rid for given table prefix
, p_filterdata json = NULL --Advanced filter data.
, OUT p_doc bytea --Merged document output
, OUT p_docguid text --optional guid to store document guid from tempfile table. (For disk reading)
, OUT p_retval integer
, OUT p_errmsg text
)
LANGUAGE plpgsql
VOLATILE
AS
$$
DECLARE
--Error Handling--
m_funcname text = 'mm_proc';
m_errmsg text;
m_errcontext text;
m_errdetail text;
m_errhint text;
m_errstate text;
m_retval integer;
--Error Handling--
m_blankexec text;
m_comma text;
m_debug_exestr text;
m_exec_orderstr text;
m_execfilter text;
m_execstr text;
m_fields json;
m_guid citext;
m_json json;
m_json_full json;
m_json_full_complex jsonb;
m_result text;
m_tablefilter text;
m_temppath citext;
m_user citext;
m_exttype citext;
m_returnvalues boolean = false;
m_data_prefix citext;
m_data_rid integer;
m_rid_comm integer;
m_rid_commattachment integer;
m_rid_obl integer;
m_doctype citext;
m_ltime timestamp;
m_hasfilestream boolean;
m_rid_obligation integer;
g_debug boolean;
g_tag_s text;
g_tag_e text;
g_tag_split text;
g_tag_oper text;
g_mtype_root integer = 0;
g_mtype_field integer = 1;
g_mtype_tblfield integer = 2;
g_mtype_tblroot integer = 3;
g_mtype_aggfield integer = 4;
g_mtype_picture integer = 5;
g_mtype_special integer = 6;
g_mtype_filter integer = 7;
g_mtype_condfield integer = 8;
g_mtype_docreplace integer = 9;
g_mtype_html integer = 10;
g_benchmark integer;
a_tblroot integer[];
a_types citext[];
a_inner_selected citext[];
r_template record;
r_doc record;
r_lp record;
r_lp_t record;
r_lp_c record;
r_retval record;
r_tmp record;
--r_lp_prev record;
m_start timestamp = clock_timestamp();
BEGIN
p_retval = 0;
p_errmsg = '';
G_DEBUG = True;
G_BENCHMARK = 0;
G_TAG_S = '[*'; --chr(171);
G_TAG_E = '*]';--chr(187);
G_TAG_SPLIT = '|';
G_TAG_OPER = ':'; --operator
m_exttype = 'txt';
a_types = array['html','docx','txt','plainhtml','allfieldvalues']; --
m_hasfilestream = false;
a_inner_selected = array[]::citext[];
m_ltime = clock_timestamp();
m_guid = newid();
m_user = f_getuser();
-- if p_doctype ilike '%plain%' and f_iscompressed(p_template) in ('none')
-- and byteatotext(p_template) not ilike '%'|| G_TAG_S || '%' || G_TAG_E || '%'
-- then
-- raise warning 'No tags to merge in plain template.';
-- p_doc = p_template;
-- return;
-- end if;
-- perform log_event(m_funcname,format('INIT _ type=%s commtype=%s p_data_prefix=%s p_data_rid=%s Template:%s'
-- ,p_doctype, p_commtype,p_data_prefix,p_data_rid
-- ,case when octet_length(p_template) < 100 then byteatotext(p_template) else octet_length(p_template)::text end
-- ),bt_enum('eventlog','local notice'));
if p_data_prefix::citext = 'com'
then
select c.rid_communication
, c.rid_parent
, c.tableprefix
from communication c
where c.rid_communication = p_data_rid into m_rid_comm, m_data_rid, m_data_prefix;
elsif p_data_prefix::citext = 'cra'
then
m_rid_commattachment = p_data_rid;
select c.rid_communication
, c.rid_parent
, c.tableprefix
, ca.rid_obligation
, dt.typex
from commattachment ca
inner join communication c on c.rid_communication = ca.rid_parent
and ca.tableprefix = 'com'
left outer join document d on d.rid_document = ca.rid_document
left outer join documenttype dt on dt.rid_documenttype = d.rid_documenttype
where ca.rid_commattachment = p_data_rid into m_rid_comm, m_data_rid, m_data_prefix, m_rid_obl, m_doctype;
elsif p_data_prefix::citext = 'ctl'
then
select ctl.rid_parent as rid_parent
, ctl.tableprefix as parent_prefix
from tasklist ctl
--inner join t_adproclient cli on cli.rid_adproclient = ctl.rid_parent
-- and ctl.tableprefix = 'tcli'
where ctl.rid_tasklist = p_data_rid into m_data_rid, m_data_prefix;
elsif p_data_prefix::citext = 'ehr'
then
raise exception 'Merging email headers are not supported at this time.';
else
m_data_prefix = p_data_prefix;
m_data_rid = p_data_rid;
end if;
---init: start---
select format('tmpl_%s_%s_%s.%s', m_data_prefix, m_data_rid, m_guid, p_doctype)::citext as filename
,''::citext as filepath
,''::citext as debugsql_filename
,''::citext as debug_filename
,''::citext as debug_peekfilename
,''::citext as magictype
,'T' || m_guid::citext as guid
,p_template::bytea as blob
into r_template;
select format('doc_%s_%s_%s.%s', m_data_prefix, m_data_rid, m_guid, p_doctype)::citext as filename
,''::citext as filepath
,'D' || m_guid::citext as guid
,null::bytea as blob
into r_doc;
-- perform log_event(m_funcname,format('init = p_doctype = %s p_data_prefix = %s p_data_rid = %s p_template[size] = %s'
-- ,p_doctype,p_data_prefix,p_data_rid,octet_length(p_template)),bt_enum('eventlog','local notice'));
if p_doctype = 'allfieldvalues'
then
m_returnvalues = true;
elsif p_doctype::citext <> all(a_types)
then
raise exception 'This type [%] is not yet supported. Allowed types: %', p_doctype, a_types;
end if;
if p_template is null and not m_returnvalues
then
p_retval = 1;
p_errmsg = 'No template';
return;
end if;
if p_commtype ilike '%sms%' and (p_doctype ilike '%text%' or p_doctype ilike '%html%' or p_doctype ilike '%all%')
then
p_doctype = 'plainhtml';
end if;
select f_normpath(b.programpath || '/temp/mailmerge')::citext
,b.sqldebuglevel > 99
from f_bus() b limit 1
into m_temppath, G_DEBUG;
if m_hasfilestream or G_DEBUG
then
perform pl_mkdir(m_temppath);
end if;
r_template.magictype = f_normpath(f_iscompressed(p_template))::citext;
r_template.filepath = f_normpath(format('%s/%s',m_temppath, r_template.filename))::citext;
r_template.debugsql_filename = f_normpath(format('%s/%s%s/%s.pgsql',m_temppath, p_data_prefix, p_data_rid, r_template.filename))::citext;
r_template.debug_filename = f_normpath(format('%s/%s%s/%s.debug.json',m_temppath, p_data_prefix, p_data_rid, r_template.filename))::citext;
r_template.debug_peekfilename = f_normpath(format('%s/%s%s/%s.peek.json',m_temppath, p_data_prefix, p_data_rid, r_template.filename))::citext;
if g_debug
then
perform pl_mkdir(f_normpath(format('%s/%s%s',m_temppath, p_data_prefix, p_data_rid)));
perform pl_writefile(f_normpath(format('%s/%s%s/%s.template.docx',m_temppath, p_data_prefix, p_data_rid,r_template.filename))
,p_template);
end if;
r_doc.filepath = f_normpath(format('%s/%s',m_temppath, r_doc.filename))::citext;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Before MergeMenu SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
drop table if exists tmp_merge_init_src;
create temp table tmp_merge_init_src
--on commit drop
as
with msrc as (
select *
from crm_get_merge_menu(p_commtype::citext) p
), src as (
select c.rid_tree_mergefeld::integer as rid
,nv(c.rid_parent)::integer as rid_parent
,btrim(nv(c.mergetag),' ')::citext as mergetag
,nv(c.mergetype) as merge_type
,btrim(nv(c.tablename),' ')::citext as table_name
,btrim(nv(c.fieldname),' ')::citext as field_name
,btrim(nv(c.filterstring),' ')::citext as filter_string
,btrim(nv(g.orderstring),' ')::citext as order_string
--parent
,nv(p.rid_tree_mergefeld)::integer as parent_rid
,nv(p.rid_parent)::integer as parent_rid_parent
,btrim(p.mergetag,' ')::citext as parent_mergetag
,nv(p.mergetype)::integer as parent_merge_type
,ifblnk(btrim(p.tablename,' '), btrim(nv(g.tablename),' '))::citext as parent_table_name
,btrim(nv(p.fieldname),' ')::citext as parent_field_name
,ifblnk(btrim(p.filterstring,' '),btrim(nv(g.filterstring),' '))::citext as parent_filter_string
,ifblnk(btrim(p.orderstring,' '),btrim(nv(g.orderstring),' '))::citext as parent_order_string
--grand parent
,nv(g.rid_tree_mergefeld)::integer as grand_rid
,btrim(g.mergetag,' ')::citext as grand_mergetag
,nv(g.mergetype)::integer as grand_merge_type
,ifblnk(g.tablename, nv(g.tablename))::citext as grand_table_name
,btrim(nv(g.fieldname),' ')::citext as grand_field_name
,btrim(nv(g.filterstring),' ')::citext as grand_filter_string
from msrc p
left outer join msrc c on c.rid_parent = p.rid_tree_mergefeld --child
and nv(c.mergetype) > 0
left outer join msrc g on g.rid_tree_mergefeld = p.rid_parent --grand parent
where nv(p.mergetype) = 0
or (p.mergetype > 0 and c.rid_tree_mergefeld > 0)
)
select d.rid
, d.rid_parent
, case
when nv(d.merge_type) = G_MTYPE_SPECIAL and strpos(d.mergetag, G_TAG_SPLIT) > 0
then substr(d.mergetag, 0, strpos(d.mergetag, G_TAG_SPLIT)) ||
substr(d.mergetag, length(d.mergetag) - length(G_TAG_E) + 1, length(d.mergetag))
else d.mergetag
end
::citext as mergetag
,d.merge_type
, d.table_name
, d.field_name
, d.filter_string
, d.order_string
, d.parent_rid
, d.parent_rid_parent
, d.parent_mergetag
, d.parent_merge_type
, d.parent_table_name
, d.parent_field_name
, d.parent_filter_string
, d.parent_order_string
, d.grand_rid
, d.grand_mergetag
, d.grand_merge_type
, d.grand_table_name
, d.grand_field_name
, d.grand_filter_string
from src d ;
--CREATE INDEX "idx_tmp_merge_init_src_type" ON tmp_merge_init_src USING btree (merge_type);
--CREATE INDEX "idx_tmp_merge_init_src_tag" ON tmp_merge_init_src USING btree (mergetag);
--HTML field types has a special meaning in the frontend. We treat them as normal fields.
update tmp_merge_init_src u
set merge_type = G_MTYPE_FIELD
where u.merge_type = G_MTYPE_HTML;
--raise notice 'After MergeMenu: %', (clock_timestamp() - m_ltime);
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf After MergeMenu SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
--Do a peek on the template and get fields we need to fetch.
if not m_returnvalues
then
if nv(octet_length(p_template)) < 2
then
raise exception E'Template document not found or blank. \r\n% File: %',octet_length(p_template), r_template.filepath;
end if;
if m_hasfilestream
then
--filesystem
select r.p_retval, r.p_errmsg
from pl_writefile(r_template.filepath, p_template) r into m_retval, m_errmsg;
if m_retval > 0
then
raise exception 'Failed to save template file to temp directory. %',m_errmsg using hint = 'in pl_writefile, while peaking template';
end if;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Before Peek SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
select r.p_retval, r.p_errmsg, r.p_result
from pl_mailmerge(format('peek_%s', p_doctype), r_template.filepath, '', ''
, 1 /*New mode, new tags*/) r into m_retval, m_errmsg, m_result;
if m_retval > 0
then
raise exception '%', m_errmsg using hint = 'in pl_mailmerge, while peaking template';
end if;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf After Peek SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
if nv(m_result) = '' --or m_result not ilike '%{%}%'
then
raise exception 'No data returned from peak.' using hint = 'in pl_mailmerge, while peaking template';
end if;
if G_DEBUG
then
perform pl_writefile(r_template.debug_peekfilename, convert_to(m_result,'utf8'));
end if;
select r.p_retval, r.p_errmsg
from f_tempfile_add(r_template.filename, p_doctype, r_template.guid, 600, m_data_rid,
m_data_prefix) r into m_retval, m_errmsg;
else
--stream
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Before Peek SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
select r.p_retval, r.p_errmsg, r.p_result, r.p_file
from pl_mailmerge(format('peek_%s', p_doctype), null, null, '', 1, p_template) r into m_retval, m_errmsg, m_result;
if m_retval > 0
then
raise exception '%', m_errmsg using hint = 'in pl_mailmerge, while peaking template';
end if;
if G_DEBUG
then
perform pl_writefile(r_template.debug_peekfilename, convert_to(m_result,'utf8'));
end if;
--perform log_event(m_funcname,format('Peek Result: %s', m_result),bt_enum('eventlog','local notice'));
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf After Peek SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
end if;
m_fields = m_result::json;
drop table if exists tmp_merge_init_fields;
create temp table tmp_merge_init_fields
--on commit drop
as
with docfields as (
select 'document'::citext as source
,btrim(btrim(r::text,' '),'"')::citext as mergetag
,1 as taglocation
from json_array_elements(m_fields->'doctags') r
union all
select 'header'::citext as source
,btrim(btrim(r::text,' '),'"')::citext as mergetag
,1 as taglocation
from json_array_elements(m_fields->'headertags') r
union all
select 'footer'::citext as source
,btrim(btrim(r::text,' '),'"')::citext as mergetag
,1 as taglocation
from json_array_elements(m_fields->'footertags') r
union all
--also the table tags in all the tables.
select r.key::citext as source
,replace(l.value::text,'"','')::citext as mergetag
,2 as taglocation
from json_each(m_fields->'tabletags') r (key,value)
left outer join json_array_elements(r.value) l (value) on json_typeof(r.value) = 'array'
where nv(l.value::text) <> ''
union all
select 'raw'::citext as source
,btrim(btrim(r::text,' '),'"')::citext as mergetag
,1 as taglocation
from json_array_elements(m_fields->'raw') r
)
select distinct
on (d.source, d.taglocation,d.mergetag)
d.source::citext as source
,(case when d.taglocation = 2 then split_part(d.source, ',', 1) else null end)::citext as tblid
,(case when d.taglocation = 2 then split_part(d.source, ',', 2) else null end)::citext as tblparent
,format('%s%s%s',G_TAG_S, replace(replace(split_part(split_part(d.mergetag, G_TAG_SPLIT,1) , G_TAG_OPER,1),G_TAG_E,''),G_TAG_S,''),G_TAG_E)::citext as mergetag
,replace(case when strpos(d.mergetag,G_TAG_SPLIT) > 0 and d.taglocation <> 5 and strpos(d.mergetag,G_TAG_E)- strpos(d.mergetag,G_TAG_SPLIT)-1 > 0
then substr(d.mergetag,strpos(d.mergetag,G_TAG_SPLIT)+1, strpos(d.mergetag,G_TAG_E)- strpos(d.mergetag,G_TAG_SPLIT)-1)
when strpos(d.mergetag,G_TAG_OPER) > 0 and strpos(d.mergetag,G_TAG_E)- strpos(d.mergetag,G_TAG_OPER)-1 > 0
then substr(d.mergetag,strpos(d.mergetag,G_TAG_OPER)+1, strpos(d.mergetag,G_TAG_E)- strpos(d.mergetag,G_TAG_OPER)-1)
else ''
end,'''','')::citext as tagvalue
,(regexp_split_to_array(replace(replace(d.mergetag,G_TAG_E,''),G_TAG_S,''),'(\'|| G_TAG_OPER || '|\' || G_TAG_SPLIT || ')' ))[2:] as operators
,d.taglocation::citext as taglocation
,d.mergetag as originalmergetag
,0:: integer as table_level
,row_number() over(order by d.source, d.mergetag) as rowid
from docfields d ;
---special update for tables linking
update tmp_merge_init_fields u
set tblparent = r.p_tblparent
, source = r.source
, tblid = r.p_tblid from (
select distinct f.tblid
, f.tblparent
, f2.tblparent as p_tblparent
, f2.tblid as p_tblid
, f.mergetag
, f2.source
from tmp_merge_init_fields f
inner join tmp_merge_init_fields f2 on f2.tblid = f.tblparent
where f.mergetag ilike '%free!tbl%'
) r
where u.tblid = r.tblid;
update tmp_merge_init_fields u
set table_level = r.table_level from (
select f1.rowid
,case when nv(f4.tblid) <> '' then 4
when nv(f3.tblid) <> '' then 3
when nv(f2.tblid) <> '' then 2
when nv(f1.tblid) <> '' then 1
else 0
end as table_level
from tmp_merge_init_fields f1
left outer join tmp_merge_init_fields f2 on f1.tblparent = f2.tblid
left outer join tmp_merge_init_fields f3 on f2.tblparent = f3.tblid
left outer join tmp_merge_init_fields f4 on f3.tblparent = f4.tblid
) r
where u.rowid = r.rowid ;
else
drop table if exists tmp_merge_init_fields;
create temp table tmp_merge_init_fields
--on commit drop
as
select distinct
on (s.mergetag)
''::citext as source
,''::citext as tblid
,''::citext as tblparent
,s.mergetag::citext as mergetag
,''::citext as tagvalue
,array[]::citext[] as operators
,1::citext as taglocation
,s.mergetag::citext as originalmergetag
from tmp_merge_init_src s
where s.merge_type in (G_MTYPE_ROOT, G_MTYPE_FIELD, G_MTYPE_AGGFIELD);
end if; --End of do a peek
--raise notice 'After Merge: %', (clock_timestamp() - m_ltime);
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf After MergeMenu SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
if not exists (
select 1
from tmp_merge_init_fields m
where nv(m.mergetag) <> ''
)
then
-- perform log_event(m_funcname,format('NO TAGS p_doctype=%s p_commtype=%s p_data_prefix=%s p_data_rid=%s'
-- ,p_doctype, p_commtype,p_data_prefix,p_data_rid),bt_enum('eventlog','local notice'));
--
p_doc = p_template;
return;
end if;
--Update [extra_filter]
/*
update tmp_merge_init_src u
set table_name = replace(u.table_name, '[extra_filter]', ifblnk(r.extra_filter, 'True'))
, parent_table_name = replace(u.parent_table_name, '[extra_filter]', ifblnk(r.extra_filter, 'True'))
, field_name = replace(u.field_name, '[extra_filter]', ifblnk(r.extra_filter, 'True'))
, parent_field_name = replace(u.parent_field_name, '[extra_filter]', ifblnk(r.extra_filter, 'True'))
, filter_string = replace(u.filter_string, '[extra_filter]', ifblnk(r.extra_filter, 'True'))
, parent_filter_string = replace(u.parent_filter_string, '[extra_filter]', ifblnk(r.extra_filter, 'True'))
from (
select f.tblid
, f.mergetag
, ltrim(ltrim(string_agg(ifblnk(s2.filter_string,'True'), ' and '),' '), 'and') as extra_filter
from tmp_merge_init_fields f
inner join tmp_merge_init_fields f2 on f2.tblid = f.tblid
inner join tmp_merge_init_src s2 on s2.mergetag = f2.mergetag
and s2.merge_type = G_MTYPE_FILTER
group by f.tblid,f.mergetag
) r
where u.mergetag = r.mergetag
;
*/
/*
---Use this code to test
select pl_writefile('/mnt/t/temp/t.docx',r.p_doc)
,r.*
from mm_proc('docx','allfieldvalues', (
select tat.maindoc
from templateattachment tat
--cross join view_blob(tat.rid_templateattachment,'templateattachment','maindoc','rid_templateattachment','utf8') v
where tat.description ilike 'Form 16 (%'
), 'TCLI',1000016) r
*/
/*
drop table if exists debug_merge_init_src;
create table debug_merge_init_src as
select * from tmp_merge_init_src
;
drop table if exists debug_merge_init_fields;
create table debug_merge_init_fields as
select * from tmp_merge_init_fields
;
*/
--------Keywords For Client,Creditor,Debt Counsellor,Trader---------------------------------------------------------
---ID replace process
if m_data_prefix::citext in ('tclc','clc')
then
m_rid_obligation = m_data_rid;
m_rid_obl = m_data_rid;
select clc.rid_adproclient
from t_adproclientcreditorl1 clc
where clc.rid_adproclientcreditorl1 = m_data_rid into m_data_rid;
m_data_prefix = 'TCLI';
end if;
if m_data_prefix::citext in ('tcli','tclc','usr','sclc')
then
if m_data_prefix::citext in ('tcli')
and not exists (
select 1
from t_adproclient cli
where cli.rid_adproclient = m_data_rid
)
then
raise exception 'Client % does not exists.', m_data_rid;
end if;
-----------------------------------------------------------[CHANGE ID: 29078] 14/07/2020 14:54 Begin
if m_data_prefix::citext in ('usr')
then
select usr.login
from users usr
where usr.rid_user = m_data_rid into m_user;
m_data_rid = 0;
end if;
if m_rid_comm > 0 and nv(m_rid_obligation) = 0
then
select ct.rid_obligation
from commattachment ct
where ct.tableprefix = 'COM'
and ct.rid_parent = m_rid_comm
and ct.doclink = 1
and ct.rid_obligation > 0
order by ct.rid_templateattachment desc limit 1
into m_rid_obligation ;
end if;
/*if r_comm.data_prefix = 'RPC'
then
select usr.login
from remotepc rpc
inner join users usr on usr.rid_user = rpc.rid_user
where rpc.rid_remotepc = r_comm.data_rid
into m_user
;
m_data_rid = 0;
end if;*/
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Merge Replace SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
for r_lp in (
select '[rid_obligation]'::citext as key
,ifblnk(m_rid_obligation,0)::citext as value
)
loop
update tmp_merge_init_src u
set table_name = replace(u.table_name, r_lp.key, r_lp.value)
, parent_table_name = replace(u.parent_table_name, r_lp.key, r_lp.value)
, field_name = replace(u.field_name, r_lp.key, r_lp.value)
, parent_field_name = replace(u.parent_field_name, r_lp.key, r_lp.value)
, filter_string = replace(u.filter_string, r_lp.key, r_lp.value)
, parent_filter_string = replace(u.parent_filter_string, r_lp.key, r_lp.value)
;
end loop;
-----------------------------------------------------------[CHANGE ID: 29078] 14/07/2020 14:54 End
for r_lp in (
with keywords as (
select distinct r.v[1]::citext as colname
from tmp_merge_init_src f
cross join regexp_matches(format('%s %s %s %s %s %s',f.table_name,f.parent_table_name,f.field_name,f.parent_field_name,f.parent_filter_string,f.filter_string)
, '\[(.*?)\]', 'ig') r(v)
where m_data_prefix = 'tcli'
and length(nv(r.v[1])) > 2
)
,keyworddata as (
select r.j
from exec(format('select (''{ "debugflag": "%s"'' %s || ''}'')::json from t_adproclient cli where cli.rid_adproclient = %s'
,G_DEBUG
,(select string_agg(format('|| '',"%s":"'' || nv(%s)::text || ''"''',format('[%s]',k.colname), k.colname), E'\r\n') from keywords k
where nv(k.colname) not in (
'rid_communication'
,'rid_commattachment'
,'rid_obl_filter'
,'exec_user'
,'extra_filter'
,'rid_user'
,'p_doctype'
,'rid_obligation'
----!!!!!Remeber to add the records here to exclude if the already have a value below
)
)
,m_data_rid
)
) r(j json)
)
select j.key::citext as key
, btrim(j.value::text,'"')::text::citext as value
from keyworddata d
cross join json_each(d.j) j(key,value)
where j.key <> ''
-----Some extra generic keywords
union all
select '[rid_communication]'::citext as key
,ifblnk(m_rid_comm,0)::citext as value
union all
select '[rid_obl_filter]'::citext as key
,ifblnk(m_rid_obl,0)::citext as value
union all
select '[exec_user]'::citext as key
,nv(m_user)::citext as value
union all
select '[extra_filter]'::citext as key
,'True'::citext as value
union all
select '[rid_commattachment]'::citext as key
,ifblnk(m_rid_commattachment,0)::citext as value --[CHANGE ID: 28878] 23/06/2020 11:33
/* union all
select '[rid_remotepc]'::citext as key
,nv(r_comm.data_rid)::citext as value --[CHANGE ID: 29078] 15/07/2020 9:36*/
union all
select '[p_doctype]'::citext as key
,ifblnk(p_doctype,'docx')::citext as value
)
loop
update tmp_merge_init_src u
set table_name = replace(u.table_name, r_lp.key, r_lp.value)
, parent_table_name = replace(u.parent_table_name, r_lp.key, r_lp.value)
, field_name = replace(u.field_name, r_lp.key, r_lp.value)
, parent_field_name = replace(u.parent_field_name, r_lp.key, r_lp.value)
, filter_string = replace(u.filter_string, r_lp.key, r_lp.value)
, parent_filter_string = replace(u.parent_filter_string, r_lp.key, r_lp.value)
;
end loop;
else
select string_agg(distinct format('[%s]', r.v[1]::text), ',')
, count(1)
from tmp_merge_init_src f
cross join regexp_matches(
format('%s %s %s %s %s %s', f.table_name, f.parent_table_name, f.field_name, f.parent_field_name,
f.parent_filter_string, f.filter_string)
, '\[(.*?)\]', 'ig') r(v) into m_errmsg,m_retval;
if m_retval > 0
then
raise exception E'The following data fields could not be found for prefix % \r\n%', ifblnk(m_data_prefix, 'Null'), m_errmsg using hint = 'in ID replace process';
end if;
end if;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Merge Filter SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
--------Basic Fields---------------------------------------------------------------------------------
m_execstr = '';
m_execfilter = '';
m_blankexec = '';
m_comma = '';
for r_lp in (
with fields as (
select f.mergetag
,f.merge_type
,f.table_name
,f.field_name
,f.filter_string
,f.parent_mergetag
,f.parent_merge_type
,f.parent_table_name
,f.parent_field_name
,f.parent_filter_string
from tmp_merge_init_src f
)
,filtereddocfields as (
select distinct on (d.originalmergetag, d.taglocation)
d.source
,d.tblid
,d.tblparent
,d.mergetag as joinmergetag --- because opertators are filtered out already, we join without operators to be able to find the tag.
,(case when strpos(d.originalmergetag, G_TAG_OPER) > 0 then d.originalmergetag
else d.mergetag
end)::citext as mergetag
,replace(d.tagvalue,'''','') as tagvalue
,d.taglocation
,d.originalmergetag
,d.operators
from tmp_merge_init_fields d
)
select
f.merge_type
,f.table_name
,f.field_name
,f.filter_string
,f.parent_mergetag
,f.parent_merge_type
,f.parent_table_name
,f.parent_field_name
,f.parent_filter_string
,d.mergetag
,replace(d.tagvalue,'''','') as tagvalue
,d.source
,d.tblid
,d.operators
,row_number() over(partition by (case
when f.merge_type IN (G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD,G_MTYPE_PICTURE)
then G_MTYPE_FIELD
else f.merge_type
end)
, f.parent_table_name
, f.table_name
, f.parent_filter_string
, f.filter_string
order by (case
when f.merge_type IN (G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD,G_MTYPE_PICTURE)
then G_MTYPE_FIELD
else f.merge_type
end) asc
, f.parent_table_name
, f.table_name
, f.field_name
) as rn
---Additional fields
,''::citext as ops_string
from fields f
inner join filtereddocfields d on d.joinmergetag = f.mergetag
where f.merge_type in (G_MTYPE_ROOT,G_MTYPE_FIELD,G_MTYPE_AGGFIELD,G_MTYPE_PICTURE, G_MTYPE_CONDFIELD) --all except tables
and (f.merge_type <> G_MTYPE_AGGFIELD
or f.merge_type = G_MTYPE_AGGFIELD
and nv(d.source) = ''
)
order by (case when f.merge_type IN (G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD,G_MTYPE_PICTURE)
then G_MTYPE_FIELD
else f.merge_type
end) asc
, f.parent_table_name
, f.table_name
, rn desc
)
loop
--raise notice 'Fields: %', r_lp;
if nv(r_lp.field_name) = '' and r_lp.merge_type not in (G_MTYPE_TBLROOT,G_MTYPE_SPECIAL)
then
if G_DEBUG
then
perform log_event(m_funcname,format('Blank field name for p_doctype=%s, p_commtype=%s, p_data_prefix=%s, p_data_rid=%s
field_name=%s, merge_type=%s, table_name=%s
' ,p_doctype, p_commtype,p_data_prefix,p_data_rid
,r_lp.field_name,r_lp.merge_type,r_lp.table_name
--,(select jsonb_agg(row_to_json(f)::jsonb) from tmp_merge_init_fields f)::text
),bt_enum('eventlog','local notice'));
end if;
--raise exception 'Blank field name for record %', r_lp;
continue ;
end if;
if nv(r_lp.ops_string) = ''
then
r_lp.ops_string = r_lp.field_name;
end if;
for r_tmp in (
select o.r as op
,split_part(o.r, '=', 1) as opname
,split_part(o.r, '=', 2) as opval
from unnest(r_lp.operators) with ordinality o(r,i)
order by o.i asc
)
loop
r_lp.ops_string = mailmerge_rule(r_lp.ops_string::citext, r_tmp.opname::citext, r_tmp.opval::citext, r_lp.mergetag::citext);
end loop;
--perform log_event(m_funcname,format('mailmerge_rules %s',r_lp.ops_string),bt_enum('eventlog','local notice'));
if r_lp.merge_type = G_MTYPE_AGGFIELD
then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',%s::text, 'type', '%s')::text %s$S$
,m_execstr,m_comma,r_lp.mergetag
,r_lp.ops_string, r_lp.merge_type, E'\r\n');
elseif r_lp.merge_type = G_MTYPE_PICTURE
then
m_execstr = format($S$%s|| '%s"%s":'
|| json_build_object('value',%s::text, 'type', '%s'
, 'w', mailmerge_specialfield('width', '%s', %s) ,'h', mailmerge_specialfield('height', '%s', %s))::text %s$S$
,m_execstr,m_comma,r_lp.mergetag,r_lp.field_name, r_lp.merge_type
,r_lp.mergetag,quote_nullable(m_data_rid),r_lp.mergetag,quote_nullable(m_data_rid), E'\r\n');
elseif r_lp.merge_type = G_MTYPE_TBLFIELD
then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',json_agg(%s::text), 'type', '%s')::text %s$S$
,m_execstr,m_comma,r_lp.mergetag
,r_lp.ops_string
,r_lp.merge_type, E'\r\n');
elseif r_lp.merge_type = G_MTYPE_SPECIAL
then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',%s, 'type', '%s')::text %s$S$,m_execstr,m_comma,r_lp.mergetag,quote_literal(r_lp.tagvalue), r_lp.merge_type, E'\r\n');
else
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',%s::text, 'type', '%s')::text %s$S$
,m_execstr,m_comma,r_lp.mergetag
,r_lp.ops_string
,r_lp.merge_type, E'\r\n');
end if;
m_execfilter = format('%s %s',nv(m_execfilter), r_lp.filter_string);
--We use this to fill in blanks.
m_blankexec = format($S$%s|| '%s"%s":' || json_build_object('value','', 'type', '%s')::text %s$S$,m_blankexec,m_comma,r_lp.mergetag, r_lp.merge_type, E'\r\n');
if r_lp.rn = 1
then
if ifblnk(r_lp.parent_table_name,'') = ''
then
m_execstr = format(E'select (''{'' %s \r\n || ''}'')::json ;',m_execstr );
else
select string_agg(s.filter_string, ' ')
from tmp_merge_init_fields f
inner join tmp_merge_init_src s on s.mergetag = f.mergetag
and s.merge_type = G_MTYPE_FILTER
where f.source = r_lp.source
and exists ( --make sure there is a parent, else this breaks. It means there will be not parent table.
select 1
from tmp_merge_init_src s2
where s2.rid = s.rid_parent)
into m_tablefilter ;
m_execstr = format(E'select (''{'' %s \r\n || ''}'')::json \r\nfrom %s \r\n%s;'
,m_execstr, r_lp.parent_table_name,ifblnk(r_lp.parent_filter_string, ' where 1=1 ') || nv(m_execfilter) || nv(m_tablefilter) );
end if;
m_blankexec = format(E'select (''{'' %s \r\n || ''}'')::json \r\n;',m_blankexec );
m_debug_exestr = nv(m_debug_exestr) || E'\r\n----'|| r_lp.parent_table_name ||E'\r\n' || nv(m_execstr) || E'\r\n';
select r.str::json
from exec(m_execstr) r(str json) into m_json;
if m_json is null
then
select r.str::json
from exec(m_blankexec) r(str json) into m_json;
end if;
if nv(m_json_full::text) <> ''
then
m_json_full = (m_json_full::jsonb || m_json::jsonb)::json;
else
m_json_full = m_json;
end if;
m_tablefilter = '';
m_execfilter = '';
m_execstr = '';
m_blankexec = '';
m_comma = '';
end if;
if nv(m_comma) = '' and length(m_execstr) > 2
then
m_comma = ',';
end if;
end loop;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Complex Fields SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
--------Complex Fields---------------------------------------------------------------------------------
m_execfilter = '';
m_tablefilter = '';
m_execstr = '';
m_blankexec = '';
m_comma = '';
m_exec_orderstr = '';
-----@2023-10-24 -
---This code must move the tag of a parent col in the child table back to the parent table in order to complete the join.
/*
with a as (
select d.mergetag as mt
,d.source
,d.tblparent
,d.tblid
,d.table_level
,d.rowid
,d.taglocation
,f.*
from tmp_merge_init_src f
inner join tmp_merge_init_fields d on d.mergetag = f.mergetag
where f.merge_type = 2
), aa as (
select
(select count(1) from a b where b.rid_parent = a.rid_parent and b.tblid = a.tblid) as cnt
,*
from a
), b as (
select aa.rid
,aa.rowid
,aa.rid_parent
,aa.tblid
,aa.cnt < (select b.cnt from aa b where b.rid_parent = aa.rid_parent order by b.cnt desc limit 1) as oddone
,(select b.tblid from aa b where b.rid_parent = aa.rid_parent order by b.cnt desc limit 1) as newtblid
,(select b.tblparent from aa b where b.rid_parent = aa.rid_parent order by b.cnt desc limit 1) as newtblparent
,(select b.source from aa b where b.rid_parent = aa.rid_parent order by b.cnt desc limit 1) as newsource
,(select b.table_level from aa b where b.rid_parent = aa.rid_parent order by b.cnt desc limit 1) as new_table_level
,aa.mt
from aa
)
update tmp_merge_init_fields f
set tblid = b.newtblid
,tblparent = b.newtblparent
,source = b.newsource
,table_level = b.new_table_level
from b
where f.rowid = b.rowid
;
*/
select array_agg(f.rid)
from tmp_merge_init_src f
where f.grand_merge_type = G_MTYPE_TBLROOT
and f.merge_type = G_MTYPE_TBLROOT into a_tblroot;
if a_tblroot is null then a_tblroot = array[-1];
end if;
m_json_full_complex = jsonb_build_object();
--a_tblroot = array[-1];
BEGIN
for r_lp_t in (
with fields as (
select f.rid
,f.mergetag
,f.merge_type
,f.table_name
,f.field_name
,f.filter_string
,f.parent_rid
,f.parent_mergetag
,f.parent_merge_type
,f.parent_table_name
,f.parent_field_name
,f.parent_filter_string
,f.grand_rid
,f.grand_mergetag
,f.grand_merge_type
,f.grand_table_name
,f.grand_field_name
,f.grand_filter_string
,f.order_string
,f.parent_order_string
,newid()::citext as guid
from tmp_merge_init_src f
where f.rid <> any(a_tblroot)
and f.parent_rid <> any(a_tblroot)
and f.grand_rid <> any(a_tblroot)
)
,joined as (
select f.rid
,f.merge_type
,f.table_name
,f.field_name
,f.filter_string
,f.parent_rid
,f.parent_mergetag
,f.parent_merge_type
,f.parent_table_name
,f.parent_field_name
,f.parent_filter_string
,f.grand_rid
,f.grand_mergetag
,f.grand_merge_type
,f.grand_table_name
,f.grand_field_name
,f.grand_filter_string
,f.order_string
,f.parent_order_string
,replace(nv(d.tagvalue),'''','')::citext as tagvalue
,nv(d.source)::citext as source
,d.tblid::citext as tblid
,d.tblparent::citext as tblparent
,d.mergetag as joinmergetag --- because opertators are filtered out already, we join without operators to be able to find the tag.
,(case when strpos(d.originalmergetag, G_TAG_OPER) > 0 then d.originalmergetag
else d.mergetag
end)::citext as mergetag
,''::citext as ops_string
,d.operators
,d.table_level
,f.guid
from fields f
inner join tmp_merge_init_fields d on d.mergetag = f.mergetag
where f.merge_type in (G_MTYPE_ROOT,G_MTYPE_TBLFIELD,G_MTYPE_TBLROOT,G_MTYPE_AGGFIELD ) --all tables
)
select e.*
,row_number() over(partition by
e.source
, case when e.merge_type in (G_MTYPE_SPECIAL, G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD )
then G_MTYPE_TBLFIELD
else e.merge_type
end
, e.parent_table_name
, e.table_name
, e.parent_filter_string
, e.filter_string
order by
e.source
, case
when e.merge_type in (G_MTYPE_SPECIAL, G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD )
then G_MTYPE_TBLFIELD
when e.merge_type IN (G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD,G_MTYPE_PICTURE)
then G_MTYPE_FIELD
else e.merge_type
end
, e.parent_table_name, e.table_name, e.field_name) as rn
from joined e
where nv(e.tblparent) in ('0','')
order by
e.source
,case when e.merge_type in (G_MTYPE_SPECIAL, G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD )
then G_MTYPE_TBLFIELD
when e.merge_type IN (G_MTYPE_CONDFIELD,G_MTYPE_AGGFIELD,G_MTYPE_PICTURE)
then G_MTYPE_FIELD
else e.merge_type
end
,e.parent_table_name
,e.table_name
,rn desc
)
loop
-- perform log_event(m_funcname,format(' Tables %s=%s src=%s rn=%s' ,p_data_prefix,p_data_rid
-- ,r_lp_t.source,r_lp_t.rn
-- ),bt_enum('eventlog','local notice')
-- --,(select jsonb_agg(row_to_json(f)::jsonb) from tmp_merge_init_fields f)::text
-- );
if r_lp_t.table_name = any(a_inner_selected) and nv(r_lp_t.table_name ) <> ''
then
raise notice 'Table used as inner table: %', r_lp_t.table_name;
continue;
end if;
--raise notice 'Field:% Table:% Tag: % Type: %', r_lp_t.field_name, r_lp_t.table_name, r_lp_t.mergetag, r_lp_t.merge_type;
if nv(r_lp_t.field_name) = '' and r_lp_t.merge_type not in (G_MTYPE_TBLROOT,G_MTYPE_SPECIAL)
then
if G_DEBUG
then
perform log_event(m_funcname,format('Blank field name on Complex merge for p_doctype=%s, p_commtype=%s, p_data_prefix=%s, p_data_rid=%s
field_name=%s, merge_type=%s, table_name=%s
' ,p_doctype, p_commtype,p_data_prefix,p_data_rid
,r_lp.field_name,r_lp.merge_type,r_lp.table_name),bt_enum('eventlog','local notice')
--,(select jsonb_agg(row_to_json(f)::jsonb) from tmp_merge_init_fields f)::text
);
end if;
--raise exception 'Blank field name for record %', r_lp;
continue;
end if;
if nv(m_exec_orderstr) = '' and nv(r_lp_t.parent_order_string) <> ''
then
m_exec_orderstr = r_lp_t.parent_order_string;
--raise notice 'Applying order % by for % %.', r_lp_t.parent_order_string, r_lp_t.parent_table_name,r_lp_t.field_name;
/*
select s.parent_order_string
from tmp_merge_init_fields f
inner join tmp_merge_init_src s on s.parent_rid = r_lp_t.rid
and s.merge_type = G_MTYPE_TBLROOT
and nv(s.parent_order_string) <> ''
where f.source = r_lp_t.source
limit 1
into m_exec_orderstr;
*/
end if;
if nv(r_lp_t.ops_string) = ''
then
r_lp_t.ops_string = r_lp_t.field_name;
end if;
for r_tmp in (
select o.r as op
,split_part(o.r, '=', 1) as opname
,split_part(o.r, '=', 2) as opval
from unnest(r_lp_t.operators) with ordinality o(r,i)
order by o.i asc
)
loop
r_lp_t.ops_string = mailmerge_rule(r_lp_t.ops_string::citext, r_tmp.opname::citext, r_tmp.opval::citext, r_lp_t.mergetag::citext);
end loop;
if r_lp_t.merge_type in (G_MTYPE_TBLFIELD, G_MTYPE_CONDFIELD)
then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',json_agg(%s::text %s), 'type', '%s')::text %s$S$
,m_execstr,m_comma,r_lp_t.mergetag
,r_lp_t.ops_string
, m_exec_orderstr, r_lp_t.merge_type, E'\r\n');
elseif r_lp_t.merge_type = G_MTYPE_SPECIAL--special fields
then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',%s, 'type', '%s')::text %s$S$
,m_execstr,m_comma,r_lp_t.mergetag,quote_literal(r_lp_t.tagvalue), r_lp_t.merge_type, E'\r\n');
--raise notice 'Special Field: %s',r_lp_t;
elseif r_lp.merge_type = G_MTYPE_PICTURE
then
m_execstr = format($S$%s|| '%s"%s":'
|| json_build_object('value',%s::text, 'type', '%s'
, 'w', mailmerge_specialfield('width', '%s', %s) ,'h', mailmerge_specialfield('height', '%s', %s))::text %s$S$
,m_execstr,m_comma,r_lp.mergetag,r_lp.field_name, r_lp.merge_type
,r_lp.mergetag,quote_nullable(m_data_rid),r_lp.mergetag,quote_nullable(m_data_rid), E'\r\n');
elseif nv(r_lp_t.field_name) <> ''
then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',%s::text, 'type', '%s')::text %s$S$
,m_execstr,m_comma,r_lp_t.mergetag
, r_lp_t.ops_string
, r_lp_t.merge_type, E'\r\n');
end if;
m_execfilter = format('%s %s',nv(m_execfilter), r_lp_t.filter_string);
--We use this to fill in blanks.
m_blankexec = format($S$%s|| '%s"%s":' || json_build_object('value','', 'type', '%s')::text %s$S$,m_blankexec,m_comma,r_lp_t.mergetag, r_lp_t.merge_type, E'\r\n');
if r_lp_t.rn = 1
then
--Inner level tables (2)
--raise notice 'Begin: parent: %', r_lp_t;
for r_lp_c in (
select f.rid
,max(f.merge_type) as merge_type
,format('%s', max(ifblnk(d.tblid,f.table_name)))::citext as subname
,($S
$(select ('{'|| $S$ || string_agg(format($SS$'"%1$s": ' || json_build_object('value', json_agg(%2$s::text),
'type',
'%3$s'
)
:
:
text
$SS$,
c
.
mergetag,
c
.
field_name,
c
.
merge_type
),
'|| '',''||'
)
||
$S$
||
'}'
)
:
:
json
from $S$ || max (
f
.
table_name
)
||
' '
||
ifblnk (
max
(
f
.
filter_string
),
'where 1=1'
)
||
')'
)
:
:
citext as qry , (
$S
$(select ('{'|| $S$ || string_agg(format($SS$'"%1$s": ' || json_build_object('value', '[]', 'type','%3$s')
:
:
text
$SS$,
c
.
mergetag,
c
.
field_name,
c
.
merge_type
),
'|| '',''||'
)
||
$S$
||
'}'
)
:
:
json $S$ || ')' ) : : citext as qryblnk , nv (
max
(
f
.
parent_order_string
)
)
:
:
citext as parent_order_string
from tmp_merge_init_src f
inner
join tmp_merge_init_src c
on c . rid_parent = f . rid and c . merge_type in (
G_MTYPE_TBLFIELD
)
inner
join tmp_merge_init_fields d
on d . mergetag = c . mergetag and d . tblparent = r_lp_t . tblid
where f . grand_rid = r_lp_t . parent_rid and f . merge_type = G_MTYPE_TBLROOT
--and f.parent_rid = any(a_tblroot)
--and d.table_level > 0
group by f . rid ) loop
raise notice 'Inner Loop: %', r_lp_c . qry;
a_inner_selected = array_append(a_inner_selected, r_lp_t.table_name);
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',json_agg(%s::json %s)::json, 'type', '%s')::text %s$S$
,m_execstr,',',r_lp_c.subname,r_lp_c.qry, r_lp_c.parent_order_string, r_lp_c.merge_type, E'\r\n');
m_blankexec = format($S$%s|| '%s"%s":' || json_build_object('value',json_agg(%s::json %s)::json, 'type', '%s')::text %s$S$
,m_blankexec,',',r_lp_c.subname,r_lp_c.qryblnk, r_lp_c.parent_order_string, r_lp_c.merge_type, E'\r\n');
end loop;
if ifblnk(r_lp_t.parent_table_name,'') = ''
then
m_execstr = format(E'select (''{'' %s \r\n || ''}'')::json ;',m_execstr );
else
select string_agg(s.filter_string, ' ')
from tmp_merge_init_fields f
inner join tmp_merge_init_src s on s.mergetag = f.mergetag
and s.merge_type = G_MTYPE_FILTER
where f.source = r_lp_t.source into m_tablefilter ;
m_execstr = format(E'select (''{'' %s \r\n || ''}'')::json \r\nfrom %s \r\n%s;'
,m_execstr, r_lp_t.parent_table_name,ifblnk(r_lp_t.parent_filter_string, ' where 1=1 ') || nv(m_execfilter) || nv(m_tablefilter) );
end if;
m_blankexec = format(E'select (''{'' %s \r\n || ''}'')::json \r\n;',m_blankexec );
select r.p_retval, r.p_errmsg, r.p_json - > 'str'
from exec_json(m_execstr, 'str json') r into m_retval,m_errmsg, m_json;
if m_json is null
then
select r.p_retval, r.p_errmsg, r.p_json - > 'str'
from exec_json(m_execstr, 'str json') r into m_retval,m_errmsg, m_json;
end if;
m_debug_exestr = nv(m_debug_exestr) || E'\r\n/*'|| nv(r_lp_t.parent_table_name) || ' len:' || nv(length(m_json::text)) ||E'*/ \r\n' || nv(m_execstr) || E'\r\n ';
if m_json_full_complex is null
then
m_json_full_complex = jsonb_build_object(r_lp_t.tblid::text,m_json);
end if;
if (m_json_full_complex->r_lp_t.tblid::text) is null
then
m_json_full_complex = jsonb_set(m_json_full_complex, format('{%s}',r_lp_t.tblid)::text[], m_json::jsonb,true);
else
m_json_full_complex = jsonb_set(m_json_full_complex, format('{%s}',r_lp_t.tblid)::text[], _jsonb_object_cat(m_json_full_complex->r_lp_t.tblid,m_json::jsonb),true);
end if;
-- perform log_event(m_funcname,format('Complex Tables %s=%s m_json_full_complex=%s' ,p_data_prefix,p_data_rid
-- ,m_json_full_complex::text
-- ),bt_enum('eventlog','local notice')
-- --,(select jsonb_agg(row_to_json(f)::jsonb) from tmp_merge_init_fields f)::text
-- );
m_execfilter = '';
m_execstr = '';
m_blankexec = '';
m_exec_orderstr = '';
m_comma = '';
m_tablefilter = '';
end if;
if nv(m_comma) = '' and length(m_execstr) > 2
then
m_comma = ',';
end if;
end loop;
if G_DEBUG
then
perform pl_writefile(r_template.debugsql_filename, convert_to(m_debug_exestr,'utf8'));
end if;
EXCEPTION
WHEN others THEN
GET STACKED DIAGNOSTICS
m_errmsg = MESSAGE_TEXT
,m_errcontext = PG_EXCEPTION_CONTEXT
,m_errdetail = PG_EXCEPTION_DETAIL
,m_errhint = PG_EXCEPTION_HINT
,m_errstate = RETURNED_SQLSTATE;
m_errmsg = format(E'Merge failed to complete. Merge fields are not setup correctly. \r\nPlease check the template. \r\nThere could be table merge tags outside of a table. \r\nDetail Error: \r\n%s',m_errmsg);
m_errmsg = nv(m_errmsg) || format(E'\r\nExecString: %s ', ifblnk(m_execstr,m_debug_exestr));
m_errmsg = nv(m_errmsg) || format(E'\r\nError Detail: %s , %s, %s, %s', m_errdetail,m_errcontext,m_errhint,m_errstate);
if G_DEBUG
then
m_errmsg = format(E'%s \r\nDebug file: %s',m_errmsg, r_template.debugsql_filename);
perform pl_writefile(r_template.debugsql_filename, convert_to(m_debug_exestr,'utf8'));
end if;
p_retval = 1;
p_errmsg = m_errmsg;
m_json_full_complex = _jsonb_object_cat(m_json_full_complex, jsonb_build_object('p_retval',p_retval,'p_errmsg',p_errmsg));
return;
--raise exception '%', m_errmsg using hint = 'in merge jsonbuild process';
END;
--------------------------------------------------------------------------------------------------------
m_json_full = json_build_object('fields',m_json_full, 'complexfields',m_json_full_complex);
if G_DEBUG
then
perform pl_writefile(r_template.debug_filename, convert_to(m_json_full::text,'utf8'));
end if;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Complex Fields 2SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
if m_returnvalues
then
p_doc = convert_to(m_json_full::text, 'utf8');
p_docguid = 'json:see->p_doc';
else
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf Before pl_mailmerge SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
if m_hasfilestream
then
--filesystem
select r.p_retval
, r.p_errmsg
, r.p_result
from pl_mailmerge(format('merge_%s', p_doctype), r_template.filepath, r_doc.filepath, m_json_full::text,
1 /*New mode, new tags*/) r into r_retval;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf After pl_mailmerge SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
if r_retval.p_retval = 1
then
raise '%',r_retval.p_errmsg;
elseif r_retval.p_retval = 2
then
p_retval = 2;
p_errmsg = r_retval.p_errmsg;
end if;
select r.p_retval, r.p_errmsg
from f_tempfile_add(r_doc.filepath, p_doctype, r_doc.guid, 600, m_data_rid, m_data_prefix) r into m_retval, m_errmsg;
p_docguid = r_doc.guid;
select r.p_outfile, r.p_retval, r.p_errmsg
from pl_readfile(r_doc.filepath) r into p_doc, m_retval, m_errmsg;
if m_retval = 0
then
perform pl_deletefile(r_doc.filepath);
perform pl_deletefile(r_template.filepath);
end if;
else
m_ltime = clock_timestamp();
--
-- select r.p_retval, r.p_errmsg
-- from f_tempfile_add(null, 'template', r_template.guid, 600,m_data_rid, m_data_prefix,r_template.blob) r
-- into m_retval, m_errmsg;
--
-- perform log_event(m_funcname,format('Dbg D:%s J:%s template:%s', p_doctype, m_json_full, r_template.guid)
-- ,bt_enum('eventlog','local notice'));
-- --stream
select r.p_retval
, r.p_errmsg
, r.p_result
, r.p_file
from pl_mailmerge(format('merge_%s', p_doctype), null, null, m_json_full::text
, 1 /*New mode, new tags*/, r_template.blob) r into r_retval;
if G_BENCHMARK = 1
then
perform log_event(m_funcname,format('Perf After pl_mailmerge Stream SinceStart: %s Duration: %s', clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
r_doc.blob = r_retval.p_file;
p_doc = r_retval.p_file;
if r_retval.p_retval = 1
then
raise '%',r_retval.p_errmsg;
elseif r_retval.p_retval = 2
then
p_retval = 2;
p_errmsg = r_retval.p_errmsg;
end if;
end if;
end if;
if G_BENCHMARK in (1,2)
then
perform log_event(m_funcname,format('Perf Merge End (%s,%s) SinceStart: %s Duration: %s',p_doctype,p_data_rid, clock_timestamp() - m_start, clock_timestamp() - m_ltime),bt_enum('eventlog','local notice'));
m_ltime = clock_timestamp();
end if;
EXCEPTION
WHEN others THEN
GET STACKED DIAGNOSTICS
m_errmsg = MESSAGE_TEXT
,m_errcontext = PG_EXCEPTION_CONTEXT
,m_errdetail = PG_EXCEPTION_DETAIL
,m_errhint = PG_EXCEPTION_HINT
,m_errstate = RETURNED_SQLSTATE;
p_errmsg := get_err_msg(m_funcname, m_errmsg, m_errcontext, m_errdetail, m_errhint, m_errstate);
p_retval = 1;
p_errmsg = nv(p_errmsg) || nv(format(E'\r\n p_doctype:%s, p_commtype:%s, p_data_prefix:%s, p_data_rid:%s, p_filterdata:%s'
,p_doctype,p_commtype,p_data_prefix,p_data_rid, p_filterdata));
if G_DEBUG
then
perform pl_writefile(r_template.debugsql_filename, convert_to(m_debug_exestr,'utf8'));
perform pl_writefile(r_template.debug_filename, convert_to(m_json_full::text,'utf8'));
end if;
END;
$$;