diff --git a/pkg/format/body.go b/pkg/format/body.go index 286cdba..181517a 100644 --- a/pkg/format/body.go +++ b/pkg/format/body.go @@ -471,6 +471,30 @@ func formatBodyStatements(text string, st config.Style) string { fw := lowerASCII(firstBodyKeyword(stripped)) isColZero := indent == "" joinToPrev := isColZero && parenDepth == 0 && len(stmt) > 0 && !sqlClauseKw[fw] + // Don't join a col-0 continuation to a comment-only preceding bline: + // the comment has no structural keyword so `continue ;` at col-0 would + // disappear into the comment text and be invisible to the lexer. + if joinToPrev { + if lowerASCII(firstBodyKeyword(stmt[len(stmt)-1].text)) == "" { + joinToPrev = false + } + } + // Pre-flush pending comment-only blines before adding a new non-comment + // non-joined bline. Without this, a comment + `end if;` end up in the + // same stmt, `fw` comes from the comment (empty string), depth is never + // decremented, and the formatter diverges on the second pass. + if !joinToPrev && fw != "" && len(stmt) > 0 { + allComments := true + for _, ll := range stmt { + if lowerASCII(firstBodyKeyword(ll.text)) != "" { + allComments = false + break + } + } + if allComments { + flush() + } + } if joinToPrev { last := &stmt[len(stmt)-1] diff --git a/testdata/corpus/test_mm_proc.pgsql b/testdata/corpus/test_mm_proc.pgsql index aff3f27..3b420a3 100644 --- a/testdata/corpus/test_mm_proc.pgsql +++ b/testdata/corpus/test_mm_proc.pgsql @@ -97,9 +97,18 @@ BEGIN 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; + -- 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')); + -- 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 @@ -158,7 +167,8 @@ BEGIN ,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')); + -- 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 @@ -280,13 +290,16 @@ BEGIN , 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); + --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 + --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 + --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(); @@ -433,7 +446,8 @@ BEGIN ,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 + ---special update for tables linking + update tmp_merge_init_fields u set tblparent = r.p_tblparent , source = r.source , tblid = r.p_tblid from ( @@ -498,7 +512,9 @@ BEGIN 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')); -- + -- 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; @@ -536,7 +552,8 @@ BEGIN --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 @@ -631,9 +648,11 @@ BEGIN , 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) ; + , 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 ( + -----------------------------------------------------------[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 @@ -700,7 +719,8 @@ BEGIN , 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) ; + , parent_filter_string = replace(u.parent_filter_string, r_lp.key, r_lp.value) + ; end loop; @@ -750,7 +770,7 @@ BEGIN ) ,filtereddocfields as ( - select distinct on (d.originalmergetag, d.taglocation) + select distinct on (d.originalmergetag, d.taglocation) d.source ,d.tblid ,d.tblparent @@ -829,7 +849,8 @@ BEGIN --,(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 ; + --raise exception 'Blank field name for record %', r_lp; + continue ; end if; if nv(r_lp.ops_string) = '' @@ -1005,7 +1026,8 @@ BEGIN end if; m_json_full_complex = jsonb_build_object(); ---a_tblroot = array[-1]; BEGIN +--a_tblroot = array[-1]; +BEGIN for r_lp_t in ( with fields as ( @@ -1107,7 +1129,11 @@ m_json_full_complex = jsonb_build_object(); ,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 -- ); + -- 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 @@ -1115,7 +1141,7 @@ m_json_full_complex = jsonb_build_object(); 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) + 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 @@ -1127,7 +1153,8 @@ m_json_full_complex = jsonb_build_object(); --,(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; + --raise exception 'Blank field name for record %', r_lp; + continue; end if; @@ -1136,7 +1163,7 @@ m_json_full_complex = jsonb_build_object(); 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 + 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 @@ -1176,189 +1203,212 @@ m_json_full_complex = jsonb_build_object(); 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 + elseif r_lp.merge_type = G_MTYPE_PICTURE then - m_execstr = format($S$%s|| '%s"%s":' + 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) <> '' + 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 = 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; + end if; - m_execfilter = format('%s %s',nv(m_execfilter), r_lp_t.filter_string); + 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'); + --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 + if r_lp_t.rn = 1 then - --Inner level tables (2) + --Inner level tables (2) --raise notice 'Begin: parent: %', r_lp_t; - for r_lp_c in ( + for r_lp_c in ( - select f.rid + 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 + $(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); + 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 = 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 = 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; + end loop; - if ifblnk(r_lp_t.parent_table_name,'') = '' + 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 + 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 ; + 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 = 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; + end if; - m_blankexec = format(E'select (''{'' %s \r\n || ''}'')::json \r\n;',m_blankexec ); + 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; - - 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; + 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 G_DEBUG - then - perform pl_writefile(r_template.debugsql_filename, convert_to(m_debug_exestr,'utf8')); + 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 @@ -1383,7 +1433,8 @@ end if; 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; + --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); @@ -1451,7 +1502,14 @@ 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 +-- 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