feat(format): skip non-plpgsql bodies; keep literals and embedded code safe
- Only restyle bodies for plpgsql/sql; skip pl* (plpython3u, plperl, ...), c and internal - Keep LANGUAGE clause on DO blocks - Carry multi-line string and dollar-quoted literals verbatim in bodies and DML - Format dollar-quoted literals that contain code (declare/begin/select/...), never format() templates - Fix code joined onto -- comments, early flush after raise exception, dropped comment before BEGIN - Regenerate test_mm_proc golden; document in README, plan, todo
This commit is contained in:
Vendored
+284
-146
@@ -1238,62 +1238,71 @@ BEGIN
|
||||
,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$
|
||||
'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$,
|
||||
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,
|
||||
@@ -1308,12 +1317,24 @@ BEGIN
|
||||
)
|
||||
||
|
||||
$S$
|
||||
||
|
||||
'}'
|
||||
)
|
||||
:
|
||||
:
|
||||
json
|
||||
$S$
|
||||
||
|
||||
'}'
|
||||
')'
|
||||
)
|
||||
:
|
||||
:
|
||||
json $S$ || ')' ) : : citext as qryblnk , nv (
|
||||
citext
|
||||
as
|
||||
qryblnk
|
||||
,
|
||||
nv
|
||||
(
|
||||
max
|
||||
(
|
||||
f
|
||||
@@ -1323,98 +1344,177 @@ BEGIN
|
||||
)
|
||||
:
|
||||
:
|
||||
citext as parent_order_string
|
||||
from tmp_merge_init_src f
|
||||
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 (
|
||||
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
|
||||
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,'') = ''
|
||||
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
|
||||
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 ;
|
||||
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;
|
||||
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
|
||||
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;
|
||||
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 ';
|
||||
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 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;
|
||||
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
|
||||
-- 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 = '';
|
||||
m_execfilter
|
||||
= '';
|
||||
m_execstr
|
||||
= '';
|
||||
m_blankexec
|
||||
= '';
|
||||
m_exec_orderstr
|
||||
= '';
|
||||
m_comma
|
||||
= '';
|
||||
m_tablefilter
|
||||
= '';
|
||||
end if;
|
||||
|
||||
if nv(m_comma) = '' and length(m_execstr) > 2
|
||||
if
|
||||
nv(m_comma) = '' and length(m_execstr) > 2
|
||||
then
|
||||
m_comma = ',';
|
||||
m_comma = ',';
|
||||
end if;
|
||||
end loop;
|
||||
end loop;
|
||||
|
||||
if G_DEBUG
|
||||
then
|
||||
perform pl_writefile(r_template.debugsql_filename, convert_to(m_debug_exestr,'utf8'));
|
||||
end if;
|
||||
if
|
||||
G_DEBUG
|
||||
then
|
||||
perform pl_writefile(r_template.debugsql_filename, convert_to(m_debug_exestr,'utf8'));
|
||||
end if;
|
||||
|
||||
EXCEPTION
|
||||
EXCEPTION
|
||||
WHEN others THEN
|
||||
GET STACKED DIAGNOSTICS
|
||||
m_errmsg = MESSAGE_TEXT
|
||||
@@ -1423,51 +1523,67 @@ EXCEPTION
|
||||
,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);
|
||||
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
|
||||
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'));
|
||||
perform
|
||||
pl_writefile(r_template.debugsql_filename, convert_to(m_debug_exestr,'utf8'));
|
||||
end if;
|
||||
p_retval = 1;
|
||||
p_errmsg = m_errmsg;
|
||||
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));
|
||||
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);
|
||||
m_json_full
|
||||
= json_build_object('fields',m_json_full, 'complexfields',m_json_full_complex);
|
||||
|
||||
if G_DEBUG
|
||||
if
|
||||
G_DEBUG
|
||||
then
|
||||
perform pl_writefile(r_template.debug_filename, convert_to(m_json_full::text,'utf8'));
|
||||
end if;
|
||||
|
||||
if G_BENCHMARK = 1
|
||||
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();
|
||||
m_ltime
|
||||
= clock_timestamp();
|
||||
end if;
|
||||
|
||||
if m_returnvalues
|
||||
if
|
||||
m_returnvalues
|
||||
then
|
||||
p_doc = convert_to(m_json_full::text, 'utf8');
|
||||
p_docguid = 'json:see->p_doc';
|
||||
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();
|
||||
m_ltime
|
||||
= clock_timestamp();
|
||||
end if;
|
||||
|
||||
if m_hasfilestream
|
||||
if
|
||||
m_hasfilestream
|
||||
then
|
||||
--filesystem
|
||||
select r.p_retval
|
||||
@@ -1476,33 +1592,41 @@ select r.p_retval
|
||||
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
|
||||
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();
|
||||
m_ltime
|
||||
= clock_timestamp();
|
||||
end if;
|
||||
|
||||
if r_retval.p_retval = 1
|
||||
if
|
||||
r_retval.p_retval = 1
|
||||
then
|
||||
raise '%',r_retval.p_errmsg;
|
||||
elseif r_retval.p_retval = 2
|
||||
elseif
|
||||
r_retval.p_retval = 2
|
||||
then
|
||||
p_retval = 2;
|
||||
p_errmsg = r_retval.p_errmsg;
|
||||
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;
|
||||
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
|
||||
if
|
||||
m_retval = 0
|
||||
then
|
||||
perform pl_deletefile(r_doc.filepath);
|
||||
perform pl_deletefile(r_template.filepath);
|
||||
perform
|
||||
pl_deletefile(r_template.filepath);
|
||||
end if;
|
||||
|
||||
else
|
||||
@@ -1522,32 +1646,41 @@ select r.p_retval
|
||||
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
|
||||
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();
|
||||
m_ltime
|
||||
= clock_timestamp();
|
||||
end if;
|
||||
|
||||
r_doc.blob = r_retval.p_file;
|
||||
p_doc = r_retval.p_file;
|
||||
r_doc.blob
|
||||
= r_retval.p_file;
|
||||
p_doc
|
||||
= r_retval.p_file;
|
||||
|
||||
if r_retval.p_retval = 1
|
||||
if
|
||||
r_retval.p_retval = 1
|
||||
then
|
||||
raise '%',r_retval.p_errmsg;
|
||||
elseif r_retval.p_retval = 2
|
||||
elseif
|
||||
r_retval.p_retval = 2
|
||||
then
|
||||
p_retval = 2;
|
||||
p_errmsg = r_retval.p_errmsg;
|
||||
p_errmsg
|
||||
= r_retval.p_errmsg;
|
||||
end if;
|
||||
|
||||
end if;
|
||||
|
||||
end if;
|
||||
|
||||
if G_BENCHMARK in (1,2)
|
||||
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();
|
||||
m_ltime
|
||||
= clock_timestamp();
|
||||
end if;
|
||||
|
||||
EXCEPTION
|
||||
@@ -1559,16 +1692,21 @@ WHEN others THEN
|
||||
,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
|
||||
:= 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_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
|
||||
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'));
|
||||
perform
|
||||
pl_writefile(r_template.debug_filename, convert_to(m_json_full::text,'utf8'));
|
||||
end if;
|
||||
|
||||
END;
|
||||
|
||||
Reference in New Issue
Block a user