6 Commits
Author SHA1 Message Date
Hein 60602d1de7 chore(release): bump version to 0.0.5
CI / Test (push) Successful in 25s
CI / Build (push) Successful in 20s
Release / Test (push) Failing after 1m51s
Release / Release (push) Has been skipped
Release / Debian packages (push) Has been skipped
Release / RPM package (push) Has been skipped
Release / Windows installer (push) Has been skipped
Release / AUR package (push) Has been skipped
Release / VSCode Extension (push) Has been skipped
Release / DataGrip Plugin (push) Has been skipped
2026-07-17 09:40:51 +02:00
warkanum 8d19258aa0 Merge pull request 'Fix PL/pgSQL formatting indentation' (#2) from issue-1-formatting-indenting into main
CI / Test (push) Successful in 26s
CI / Build (push) Successful in 29s
Reviewed-on: #2
2026-07-16 19:38:55 +00:00
Hein 5cba6beeb1 fix: align PLpgSQL formatting clauses
CI / Build (pull_request) Successful in 1m2s
CI / Test (pull_request) Successful in 43s
2026-07-15 21:11:58 +02:00
Hein a0838e4bdc chore(release): bump version to 0.0.4
Release / Test (push) Successful in 34s
CI / Test (push) Successful in 38s
Release / Windows installer (push) Successful in 58s
CI / Build (push) Successful in 27s
Release / Release (push) Successful in 37s
Release / VSCode Extension (push) Successful in 43s
Release / Debian packages (push) Successful in 59s
Release / RPM package (push) Successful in 1m3s
Release / AUR package (push) Successful in 1m32s
Release / DataGrip Plugin (push) Successful in 3m17s
2026-07-02 14:42:16 +02:00
Hein 44fb77efd6 fix(body): handle CASE depth in body statement formatting
CI / Test (push) Successful in 28s
CI / Build (push) Successful in 25s
2026-07-02 14:40:54 +02:00
Hein 4fab2fe652 feat(ui): add version display action and status bar widget
CI / Test (push) Successful in 29s
CI / Build (push) Successful in 25s
* Implement PgTidyShowVersionAction to show pgtidy version
* Create PgTidyStatusBarWidget for real-time version display
* Update plugin.xml to register new action and widget
2026-07-02 12:58:28 +02:00
8 changed files with 359 additions and 28 deletions
@@ -0,0 +1,42 @@
package com.pgtidy.datagrip
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.ui.Messages
class PgTidyShowVersionAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val project = e.project
object : Task.Backgroundable(project, "PgTidy: checking version…", false) {
override fun run(indicator: ProgressIndicator) {
val proc = try {
ProcessBuilder("pgtidy", "version")
.redirectErrorStream(false)
.start()
} catch (ex: Exception) {
ApplicationManager.getApplication().invokeLater {
Messages.showErrorDialog(project, "Cannot start pgtidy: ${ex.message}", "PgTidy")
}
return
}
val output = proc.inputStream.bufferedReader().readText()
val stderr = proc.errorStream.bufferedReader().readText()
val exit = proc.waitFor()
ApplicationManager.getApplication().invokeLater {
if (exit != 0) {
Messages.showErrorDialog(project, stderr.ifBlank { "pgtidy exited with code $exit" }, "PgTidy")
return@invokeLater
}
Messages.showInfoMessage(project, output.trim(), "PgTidy Version")
}
}
}.queue()
}
}
@@ -0,0 +1,72 @@
package com.pgtidy.datagrip
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.StatusBar
import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.util.Consumer
import java.awt.event.MouseEvent
private const val WIDGET_ID = "com.pgtidy.StatusBarWidget"
class PgTidyStatusBarWidget(private val project: Project) : StatusBarWidget, StatusBarWidget.TextPresentation {
private var statusBar: StatusBar? = null
private var text: String = "pgtidy: …"
override fun ID(): String = WIDGET_ID
override fun install(statusBar: StatusBar) {
this.statusBar = statusBar
refresh()
}
override fun dispose() {
statusBar = null
}
override fun getPresentation(): StatusBarWidget.WidgetPresentation = this
override fun getText(): String = text
override fun getAlignment(): Float = java.awt.Component.CENTER_ALIGNMENT
override fun getTooltipText(): String = "PgTidy version — click to refresh"
override fun getClickConsumer(): Consumer<MouseEvent> = Consumer { refresh() }
fun refresh() {
ApplicationManager.getApplication().executeOnPooledThread {
text = try {
val proc = ProcessBuilder("pgtidy", "version")
.redirectErrorStream(true)
.start()
val output = proc.inputStream.bufferedReader().readText().trim()
val exit = proc.waitFor()
if (exit == 0 && output.isNotBlank()) output else "pgtidy: not found"
} catch (ex: Exception) {
"pgtidy: not found"
}
ApplicationManager.getApplication().invokeLater {
statusBar?.updateWidget(WIDGET_ID)
}
}
}
}
class PgTidyStatusBarWidgetFactory : StatusBarWidgetFactory {
override fun getId(): String = WIDGET_ID
override fun getDisplayName(): String = "PgTidy Version"
override fun isAvailable(project: Project): Boolean = true
override fun createWidget(project: Project): StatusBarWidget = PgTidyStatusBarWidget(project)
override fun disposeWidget(widget: StatusBarWidget) {
widget.dispose()
}
override fun canBeEnabledOn(statusBar: StatusBar): Boolean = true
}
@@ -21,8 +21,20 @@
<add-to-group group-id="EditorPopupMenu" anchor="first"/> <add-to-group group-id="EditorPopupMenu" anchor="first"/>
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt shift P"/> <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt shift P"/>
</action> </action>
<action id="com.pgtidy.ShowVersion"
class="com.pgtidy.datagrip.PgTidyShowVersionAction"
text="Show PgTidy Version"
description="Show the installed pgtidy binary version">
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
</actions> </actions>
<extensions defaultExtensionNs="com.intellij">
<statusBarWidgetFactory id="com.pgtidy.StatusBarWidget"
implementation="com.pgtidy.datagrip.PgTidyStatusBarWidgetFactory"
order="last"/>
</extensions>
<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij"> <extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">
<server id="com.pgtidy.lsp" <server id="com.pgtidy.lsp"
name="PgTidy" name="PgTidy"
+1 -1
View File
@@ -1,6 +1,6 @@
# Maintainer: Hein (Warky Devs) <hein@warky.dev> # Maintainer: Hein (Warky Devs) <hein@warky.dev>
pkgname=pgtidy-bin pkgname=pgtidy-bin
pkgver=0.0.3 pkgver=0.0.5
pkgrel=1 pkgrel=1
pkgdesc="PostgreSQL SQL formatter and linter" pkgdesc="PostgreSQL SQL formatter and linter"
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
+1 -1
View File
@@ -1,5 +1,5 @@
Name: pgtidy Name: pgtidy
Version: 0.0.3 Version: 0.0.5
Release: 1%{?dist} Release: 1%{?dist}
Summary: PostgreSQL SQL formatter and linter Summary: PostgreSQL SQL formatter and linter
+153 -8
View File
@@ -379,6 +379,7 @@ func formatBodyStatements(text string, st config.Style) string {
stmt []bline stmt []bline
parenDepth int parenDepth int
blockDepth int // 0=col-0 (BEGIN/END/EXCEPTION), 1=body, 2=nested… blockDepth int // 0=col-0 (BEGIN/END/EXCEPTION), 1=body, 2=nested…
caseDepth int // depth of open CASE…END expressions (WHEN…THEN is not a block opener)
inException bool inException bool
pendingBlanks int pendingBlanks int
depthInc bool // increment blockDepth after next flush depthInc bool // increment blockDepth after next flush
@@ -440,13 +441,9 @@ func formatBodyStatements(text string, st config.Style) string {
stmtLines = joinThenToCondition(stmt) stmtLines = joinThenToCondition(stmt)
} }
for i, ll := range stmtLines { formattedLines := formatBodyStmtLines(stmtLines, baseIndent, st)
if i == 0 || ll.indent == "" { for _, line := range formattedLines {
result.WriteString(baseIndent) result.WriteString(line)
} else {
result.WriteString(ll.indent)
}
result.WriteString(ll.text)
result.WriteString(nl) result.WriteString(nl)
} }
@@ -529,18 +526,41 @@ func formatBodyStatements(text string, st config.Style) string {
} }
if parenDepth == 0 && tok.Kind == lexer.Ident { if parenDepth == 0 && tok.Kind == lexer.Ident {
lastD0Kw = lowerASCII(tok.Text) lastD0Kw = lowerASCII(tok.Text)
switch lastD0Kw {
case "case":
caseDepth++
case "end":
if caseDepth > 0 {
caseDepth--
}
}
} }
} }
if parenDepth == 0 && len(stmt) > 0 { if parenDepth == 0 && len(stmt) > 0 {
switch lastD0Kw { switch lastD0Kw {
case "then", "loop", "begin": case "then":
// A THEN ending a CASE…WHEN branch is not a PL/pgSQL block
// opener; only one matching END closes the whole CASE, so
// treating each WHEN…THEN as a block open would permanently
// inflate blockDepth.
if caseDepth == 0 {
fw0 := lowerASCII(firstBodyKeyword(stmt[0].text))
if fw0 != "elsif" && fw0 != "elseif" {
depthInc = true
}
flush()
}
case "loop", "begin":
fw0 := lowerASCII(firstBodyKeyword(stmt[0].text)) fw0 := lowerASCII(firstBodyKeyword(stmt[0].text))
if fw0 != "elsif" && fw0 != "elseif" { if fw0 != "elsif" && fw0 != "elseif" {
depthInc = true depthInc = true
} }
flush() flush()
case "else", "exception": case "else", "exception":
if caseDepth > 0 {
break
}
flush() flush()
} }
} }
@@ -550,6 +570,131 @@ func formatBodyStatements(text string, st config.Style) string {
return result.String() return result.String()
} }
// formatBodyStmtLines formats one flushed PL/pgSQL statement at its contextual
// base indent. Multi-line UPDATE/DELETE statements inside PL/pgSQL get their
// top-level SET/WHERE/AND/OR clauses realigned under the statement while nested
// subqueries keep their original indentation. Non-DML statements keep
// continuation indentation, except that standalone structural keywords such as
// THEN are aligned with the block opener.
func formatBodyStmtLines(lines []bline, baseIndent string, st config.Style) []string {
if len(lines) == 0 {
return nil
}
if looksLikeMultiLineBodyDML(lines) {
return reindentBodyDML(lines, baseIndent, st)
}
out := make([]string, 0, len(lines))
for i, ll := range lines {
text := ll.text
indent := baseIndent
if i > 0 && ll.indent != "" && !isStandaloneBodyKeyword(ll.text, "then", "else", "elsif", "elseif") {
indent = ll.indent
}
out = append(out, indent+text)
}
return out
}
func looksLikeMultiLineBodyDML(lines []bline) bool {
if len(lines) < 2 {
return false
}
kw := lowerASCII(firstBodyKeyword(lines[0].text))
return kw == "update" || kw == "delete"
}
func reindentBodyDML(lines []bline, baseIndent string, st config.Style) []string {
out := make([]string, 0, len(lines)+1)
afterWhere := false
parenDepth := 0
for i, ll := range lines {
text := strings.TrimRight(ll.text, " ")
lineDepth := parenDepth
kw := lowerASCII(firstBodyKeyword(text))
if afterWhere && lineDepth == 0 && kw != "and" && kw != "or" {
out = append(out, baseIndent+st.Indent+st.Indent+strings.TrimSpace(text))
afterWhere = false
updateBodyParenDepth(text, &parenDepth)
continue
}
if lineDepth == 0 && (kw == "set" || kw == "where" || kw == "values" || kw == "returning") {
if kw == "where" {
whereText := strings.TrimSpace(text)
fields := strings.Fields(whereText)
nextKw := ""
if i+1 < len(lines) {
nextKw = lowerASCII(firstBodyKeyword(lines[i+1].text))
}
if len(fields) > 1 && (nextKw == "and" || nextKw == "or") {
out = append(out, baseIndent+fields[0])
out = append(out, baseIndent+st.Indent+st.Indent+strings.TrimSpace(whereText[len(fields[0]):]))
afterWhere = false
continue
}
afterWhere = len(fields) == 1
}
out = append(out, baseIndent+strings.TrimSpace(text))
continue
}
if lineDepth == 0 && (kw == "and" || kw == "or") {
out = append(out, baseIndent+st.Indent+strings.TrimSpace(text))
afterWhere = false
updateBodyParenDepth(text, &parenDepth)
continue
}
if i == 0 {
out = append(out, baseIndent+strings.TrimSpace(text))
} else if ll.indent != "" {
out = append(out, ll.indent+strings.TrimSpace(text))
} else {
out = append(out, baseIndent+strings.TrimSpace(text))
}
afterWhere = false
updateBodyParenDepth(text, &parenDepth)
}
return out
}
func updateBodyParenDepth(s string, depth *int) {
for _, tok := range lexer.Lex(s) {
switch tok.Kind {
case lexer.LParen, lexer.LBracket:
(*depth)++
case lexer.RParen, lexer.RBracket:
if *depth > 0 {
(*depth)--
}
}
}
}
func significantBodyTokens(s string) []cst.Tok {
var toks []cst.Tok
for _, tok := range lexer.Lex(s) {
if tok.IsTrivia() || tok.Kind == lexer.EOF {
continue
}
toks = append(toks, cst.Tok{Tok: tok})
}
return toks
}
func isStandaloneBodyKeyword(s string, kws ...string) bool {
toks := significantBodyTokens(s)
if len(toks) != 1 || toks[0].Tok.Kind != lexer.Ident {
return false
}
low := lowerASCII(toks[0].Tok.Text)
for _, kw := range kws {
if low == kw {
return true
}
}
return false
}
// joinThenToCondition merges a THEN line (on its own bline) into the preceding // joinThenToCondition merges a THEN line (on its own bline) into the preceding
// condition line when plpgsql_if_then_newline is false. // condition line when plpgsql_if_then_newline is false.
func joinThenToCondition(lines []bline) []bline { func joinThenToCondition(lines []bline) []bline {
+60
View File
@@ -107,6 +107,66 @@ func TestFormatMmProcBroken(t *testing.T) {
} }
} }
func TestFormatIssue1PLpgSQLIndenting(t *testing.T) {
src := "CREATE FUNCTION f() RETURNS void LANGUAGE plpgsql AS $$\n" +
"DECLARE\n" +
" r_lp record;\n" +
"BEGIN\n" +
" if r_lp.total > 0\n" +
" and r_lp.totaldone >= r_lp.total\n" +
" then\n" +
" update core.process u\n" +
" set status = 'done'\n" +
" where u.rid_process = r_lp.rid_process\n" +
" and nv(u.status) <> 'done';\n" +
" elsif r_lp.total > 0\n" +
" then\n" +
" update core.process u\n" +
" set status = 'open'\n" +
" where u.rid_process = r_lp.rid_process\n" +
" and nv(u.status) <> 'open';\n" +
"\n" +
" end if;\n" +
"$$;\n"
want := "CREATE FUNCTION f(\n" +
")\n" +
"RETURNS void\n" +
"LANGUAGE plpgsql\n" +
"AS\n" +
"$$\n" +
"DECLARE\n" +
" r_lp record;\n" +
"BEGIN\n" +
" if r_lp.total > 0\n" +
" and r_lp.totaldone >= r_lp.total\n" +
" then\n" +
" update core.process u\n" +
" set status = 'done'\n" +
" where\n" +
" u.rid_process = r_lp.rid_process\n" +
" and nv(u.status) <> 'done';\n" +
" elsif r_lp.total > 0\n" +
" then\n" +
" update core.process u\n" +
" set status = 'open'\n" +
" where\n" +
" u.rid_process = r_lp.rid_process\n" +
" and nv(u.status) <> 'open';\n" +
"\n" +
" end if;\n" +
"$$;\n"
got := format(src)
if got != want {
t.Errorf("issue #1 PL/pgSQL indenting\n--- got ---\n%s\n--- want ---\n%s", got, want)
}
checkDML(t, "issue #1 PL/pgSQL indenting", got)
if !semanticallyEqual(src, got) {
t.Errorf("issue #1 PL/pgSQL indenting changed semantics")
}
}
func TestCorpusIdempotentAndSafe(t *testing.T) { func TestCorpusIdempotentAndSafe(t *testing.T) {
dir := filepath.Join("..", "..", "testdata", "corpus") dir := filepath.Join("..", "..", "testdata", "corpus")
entries, err := os.ReadDir(dir) entries, err := os.ReadDir(dir)
+18 -18
View File
@@ -632,7 +632,7 @@ BEGIN
end if;*/ end if;*/
if G_BENCHMARK = 1 if G_BENCHMARK = 1
then 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')); 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(); m_ltime = clock_timestamp();
end if; end if;
@@ -735,7 +735,7 @@ BEGIN
, '\[(.*?)\]', 'ig') r(v) into m_errmsg,m_retval; , '\[(.*?)\]', 'ig') r(v) into m_errmsg,m_retval;
if m_retval > 0 if m_retval > 0
then 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'; 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;
@@ -1136,16 +1136,16 @@ BEGIN
-- ); -- );
if r_lp_t.table_name = any(a_inner_selected) and nv(r_lp_t.table_name ) <> '' if r_lp_t.table_name = any(a_inner_selected) and nv(r_lp_t.table_name ) <> ''
then then
raise notice 'Table used as inner table: %', r_lp_t.table_name; raise notice 'Table used as inner table: %', r_lp_t.table_name;
continue; continue;
end if; 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; --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 then
if G_DEBUG if G_DEBUG
then 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 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 field_name=%s, merge_type=%s, table_name=%s
' ,p_doctype, p_commtype,p_data_prefix,p_data_rid ' ,p_doctype, p_commtype,p_data_prefix,p_data_rid
@@ -1159,7 +1159,7 @@ BEGIN
end if; end if;
if nv(m_exec_orderstr) = '' and nv(r_lp_t.parent_order_string) <> '' if nv(m_exec_orderstr) = '' and nv(r_lp_t.parent_order_string) <> ''
then then
m_exec_orderstr = r_lp_t.parent_order_string; 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; --raise notice 'Applying order % by for % %.', r_lp_t.parent_order_string, r_lp_t.parent_table_name,r_lp_t.field_name;
/* /*
@@ -1176,7 +1176,7 @@ BEGIN
end if; end if;
if nv(r_lp_t.ops_string) = '' if nv(r_lp_t.ops_string) = ''
then then
r_lp_t.ops_string = r_lp_t.field_name; r_lp_t.ops_string = r_lp_t.field_name;
end if; end if;
@@ -1192,19 +1192,19 @@ BEGIN
end loop; end loop;
if r_lp_t.merge_type in (G_MTYPE_TBLFIELD, G_MTYPE_CONDFIELD) if r_lp_t.merge_type in (G_MTYPE_TBLFIELD, G_MTYPE_CONDFIELD)
then then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',json_agg(%s::text %s), 'type', '%s')::text %s$S$ 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 ,m_execstr,m_comma,r_lp_t.mergetag
,r_lp_t.ops_string ,r_lp_t.ops_string
, m_exec_orderstr, r_lp_t.merge_type, E'\r\n'); , m_exec_orderstr, r_lp_t.merge_type, E'\r\n');
elseif r_lp_t.merge_type = G_MTYPE_SPECIAL--special fields elseif r_lp_t.merge_type = G_MTYPE_SPECIAL--special fields
then then
m_execstr = format($S$%s|| '%s"%s":' || json_build_object('value',%s, 'type', '%s')::text %s$S$ 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'); ,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; --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 then
m_execstr = format($S$%s|| '%s"%s":' m_execstr = format($S$%s|| '%s"%s":'
|| json_build_object('value',%s::text, 'type', '%s' || json_build_object('value',%s::text, 'type', '%s'
, 'w', mailmerge_specialfield('width', '%s', %s) ,'h', mailmerge_specialfield('height', '%s', %s))::text %s$S$ , 'w', mailmerge_specialfield('width', '%s', %s) ,'h', mailmerge_specialfield('height', '%s', %s))::text %s$S$
@@ -1212,7 +1212,7 @@ BEGIN
,r_lp.mergetag,quote_nullable(m_data_rid),r_lp.mergetag,quote_nullable(m_data_rid), E'\r\n'); ,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 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 ,m_execstr,m_comma,r_lp_t.mergetag
, r_lp_t.ops_string , r_lp_t.ops_string
@@ -1225,7 +1225,7 @@ BEGIN
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'); 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 then
--Inner level tables (2) --Inner level tables (2)
--raise notice 'Begin: parent: %', r_lp_t; --raise notice 'Begin: parent: %', r_lp_t;
for r_lp_c in ( for r_lp_c in (
@@ -1345,7 +1345,7 @@ BEGIN
end loop; end loop;
if ifblnk(r_lp_t.parent_table_name,'') = '' if ifblnk(r_lp_t.parent_table_name,'') = ''
then then
m_execstr = format(E'select (''{'' %s \r\n || ''}'')::json ;',m_execstr ); m_execstr = format(E'select (''{'' %s \r\n || ''}'')::json ;',m_execstr );
else else
select string_agg(s.filter_string, ' ') select string_agg(s.filter_string, ' ')
@@ -1364,7 +1364,7 @@ BEGIN
from exec_json(m_execstr, 'str json') r into m_retval,m_errmsg, m_json; from exec_json(m_execstr, 'str json') r into m_retval,m_errmsg, m_json;
if m_json is null if m_json is null
then then
select r.p_retval, r.p_errmsg, r.p_json - > 'str' 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; from exec_json(m_execstr, 'str json') r into m_retval,m_errmsg, m_json;
@@ -1373,12 +1373,12 @@ BEGIN
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 if m_json_full_complex is null
then then
m_json_full_complex = jsonb_build_object(r_lp_t.tblid::text,m_json); m_json_full_complex = jsonb_build_object(r_lp_t.tblid::text,m_json);
end if; end if;
if (m_json_full_complex->r_lp_t.tblid::text) is null if (m_json_full_complex->r_lp_t.tblid::text) is null
then then
m_json_full_complex = jsonb_set(m_json_full_complex, format('{%s}',r_lp_t.tblid)::text[], m_json::jsonb,true); m_json_full_complex = jsonb_set(m_json_full_complex, format('{%s}',r_lp_t.tblid)::text[], m_json::jsonb,true);
else 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); 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);
@@ -1399,13 +1399,13 @@ BEGIN
end if; end if;
if nv(m_comma) = '' and length(m_execstr) > 2 if nv(m_comma) = '' and length(m_execstr) > 2
then then
m_comma = ','; m_comma = ',';
end if; end if;
end loop; end loop;
if G_DEBUG if G_DEBUG
then then
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; end if;