diff --git a/.gitignore b/.gitignore index 57a13ca..9c00449 100644 --- a/.gitignore +++ b/.gitignore @@ -164,4 +164,6 @@ dist .pnp.* temp/* -dist/* \ No newline at end of file +dist/* +# Local build of the CLI binary +/pgtidy diff --git a/AGENTS.md b/AGENTS.md index 91eb68e..5be4cd4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,6 +68,12 @@ testdata/corpus/ — real-world .pgsql procedures used as the safety/idempoten not just a test assertion (`pkg/format/format_test.go`); it is enforced at runtime so a formatter bug can never silently drop or alter code. +**Line endings**: the formatter re-emits all layout with `st.Newline` (default `\n`), so a +CRLF input file is normalised to LF on write. This includes `\r\n` that sits *inside* a +multi-line string literal — a line-ending change there is layout normalisation, not a change +of code content, so `SemanticallyEqual` compares string tokens modulo `\r\n` ↔ `\n` +(`normNL` in `pkg/format/safety.go`). Set `newline: "\r\n"` in `.pgtidy.yaml` to keep CRLF. + ## Commands ```bash diff --git a/docs/config/default.pgtidy.yaml b/docs/config/default.pgtidy.yaml index 545c425..1bdd92a 100644 --- a/docs/config/default.pgtidy.yaml +++ b/docs/config/default.pgtidy.yaml @@ -47,14 +47,14 @@ insert_collapse_values: true # Fold multiple VALUES rows onto fewer lines # --- Routines (functions / procedures) --- -align_param_types: true # Pad param names so type column aligns across all params +align_param_types: false # Pad param names so type column aligns across all params routine_as_wrap: true # Newline before AS $$ (false = keep AS on same line) # --- PL/pgSQL body --- -plpgsql_max_blank_lines: 1 # Max consecutive blank lines in body -plpgsql_declare_align_type: false # Align type column in DECLARE block -plpgsql_declare_align_eq: false # Align := / = in DECLARE block +plpgsql_max_blank_lines: 1 # Max consecutive blank lines in body +plpgsql_declare_align_type: true # Align type column in DECLARE block +plpgsql_declare_align_eq: true # Align := / = in DECLARE block (padded to the widest assigned declaration) plpgsql_if_then_newline: true # THEN on its own line (false = same line as condition) plpgsql_loop_collapse: true # Collapse empty loop bodies to one line diff --git a/docs/plan.md b/docs/plan.md index cdc175c..6d7fdc0 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -128,7 +128,7 @@ DataGrip enum conventions used below: | DataGrip key | PgTidy key | Default | Notes | |---|---|---|---| | `ROUTINE_ARG_COMMA` | uses `commas` | `leading` | same setting as query lists | -| `ROUTINE_ARG_ALIGN_TYPES` | `align_param_types` | `true` | align type column in param list | +| `ROUTINE_ARG_ALIGN_TYPES` | `align_param_types` | `false` | align type column in param list | | `ROUTINE_AS_WRAP` | `routine_as_wrap` | `true` | newline before `AS $$` | ### PL/pgSQL body @@ -136,8 +136,8 @@ DataGrip enum conventions used below: | DataGrip key | PgTidy key | Default | Notes | |---|---|---|---| | `IMP_COMMON_KEEP_BLANK_LINES_IN_CODE` | `plpgsql_max_blank_lines` | `1` | max consecutive blank lines in body | -| `IMP_DECLARE_ALIGN_TYPE` | `plpgsql_declare_align_type` | `false` | align type column in DECLARE block | -| `IMP_DECLARE_ALIGN_EQ` | `plpgsql_declare_align_eq` | `false` | align `:=` / `=` in DECLARE block | +| `IMP_DECLARE_ALIGN_TYPE` | `plpgsql_declare_align_type` | `true` | align type column in DECLARE block | +| `IMP_DECLARE_ALIGN_EQ` | `plpgsql_declare_align_eq` | `true` | align `:=` / `=` in DECLARE block | | `IMP_IF_THEN_WRAP_THEN` | `plpgsql_if_then_newline` | `true` | THEN on its own line | | `IMP_LOOP_COLLAPSE` | `plpgsql_loop_collapse` | `true` | collapse empty loop bodies | diff --git a/docs/todo.md b/docs/todo.md index e1d0b68..2d27afd 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -213,7 +213,7 @@ as a proxy (new_line for content, inline for single-arg subexpressions). ### ✅ Formatter — routine param alignment (`pkg/format/format.go`) -- `align_param_types`: `alignParamTypes()` pads param names so type columns align; default `true`. +- `align_param_types`: `alignParamTypes()` pads param names so type columns align; default `false` (house style: no type-column alignment in param lists). - `routine_as_wrap`: when `false`, AS stays on the same line as the last option clause. - Golden file `testdata/corpus/test_a.pgsql` updated to reflect aligned params. @@ -221,7 +221,9 @@ as a proxy (new_line for content, inline for single-arg subexpressions). - `plpgsql_max_blank_lines`: blank-line runs capped at the configured limit; default `1`. - `plpgsql_declare_align_type` + `plpgsql_declare_align_eq`: two-pass declare formatter - measures name/type widths then pads for alignment; `writeDeclareAligned` helper. + measures name/type widths then pads for alignment; `writeDeclareAligned` helper. Both + default `true` (house style). The `=` column is padded only to the widest type among + declarations that actually carry an assignment, so a lone `x text = '…';` stays tight. - `plpgsql_if_then_newline`: when `false`, `joinThenToCondition` merges THEN onto the preceding condition line. - `plpgsql_loop_collapse`: `tryCollapseLoop` detects empty FOR/WHILE loop bodies and diff --git a/pkg/config/config.go b/pkg/config/config.go index 6db8828..0d20c7a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -130,12 +130,12 @@ func Default() Style { InsertCollapseValues: true, - AlignParamTypes: true, + AlignParamTypes: false, RoutineAsWrap: true, PlpgsqlMaxBlankLines: 1, - PlpgsqlDeclareAlignType: false, - PlpgsqlDeclareAlignEq: false, + PlpgsqlDeclareAlignType: true, + PlpgsqlDeclareAlignEq: true, PlpgsqlIfThenNewline: true, PlpgsqlLoopCollapse: true, diff --git a/pkg/format/body.go b/pkg/format/body.go index 10a97cc..bc83119 100644 --- a/pkg/format/body.go +++ b/pkg/format/body.go @@ -177,7 +177,10 @@ func formatDeclareVars(b *strings.Builder, toks []cst.Tok, st config.Style) { if nw > nameColW { nameColW = nw } - if tw > typeColW { + // typeColW drives the '='/':='/DEFAULT column (align_eq only), so + // only declarations that actually carry an assignment participate — + // a bare "name type;" must not widen it. + if tw > typeColW && declHasAssignment(body) { typeColW = tw } } @@ -207,7 +210,7 @@ func formatDeclareVars(b *strings.Builder, toks []cst.Tok, st config.Style) { writeDeclareAligned(b, body, st, nameColW, typeColW) } else { for j, t := range body { - if j > 0 && needSpace(body[j-1].Tok, t.Tok) { + if j > 0 && needSpace(body[j-1].Tok, t.Tok) && !isPctTypeBoundary(body, j) { b.WriteByte(' ') } b.WriteString(caseText(t.Tok, st)) @@ -220,6 +223,32 @@ func formatDeclareVars(b *strings.Builder, toks []cst.Tok, st config.Style) { } } +// declHasAssignment reports whether a DECLARE variable body (name type … ) has +// a default assignment ( := / = / DEFAULT ) at paren depth 0. +func declHasAssignment(body []cst.Tok) bool { + depth := 0 + for _, t := range body { + switch t.Tok.Kind { + case lexer.LParen, lexer.LBracket: + depth++ + case lexer.RParen, lexer.RBracket: + if depth > 0 { + depth-- + } + } + if depth != 0 { + continue + } + if t.Tok.Kind == lexer.Operator && (t.Tok.Text == ":=" || t.Tok.Text == "=") { + return true + } + if t.Tok.Kind == lexer.Ident && lowerASCII(t.Tok.Text) == "default" { + return true + } + } + return false +} + // declareNameTypeWidth returns the rendered width of the name and type portions // of a DECLARE variable declaration (without the default assignment). // Format is: [name type [:= default]] or [name type [DEFAULT default]]. @@ -256,7 +285,7 @@ func declareNameTypeWidth(body []cst.Tok, st config.Style) (nameW, typeW int) { } var tb strings.Builder for j, t := range typeTokens { - if j > 0 && needSpace(typeTokens[j-1].Tok, t.Tok) { + if j > 0 && needSpace(typeTokens[j-1].Tok, t.Tok) && !isPctTypeBoundary(typeTokens, j) { tb.WriteByte(' ') } tb.WriteString(caseText(t.Tok, st)) @@ -315,7 +344,7 @@ func writeDeclareAligned(b *strings.Builder, body []cst.Tok, st config.Style, na var typeStr strings.Builder for j, t := range typeTokens { - if j > 0 && needSpace(typeTokens[j-1].Tok, t.Tok) { + if j > 0 && needSpace(typeTokens[j-1].Tok, t.Tok) && !isPctTypeBoundary(typeTokens, j) { typeStr.WriteByte(' ') } typeStr.WriteString(caseText(t.Tok, st)) @@ -429,7 +458,12 @@ func formatBodyStatements(text string, st config.Style) string { } case "exception": inException = true - effectiveDepth = 0 + // EXCEPTION belongs to its nearest enclosing BEGIN, so align it one + // level in from the current block body (col 0 for the outermost). + effectiveDepth = blockDepth - 1 + if effectiveDepth < 0 { + effectiveDepth = 0 + } } baseIndent := strings.Repeat(st.Indent, effectiveDepth) @@ -589,7 +623,13 @@ func formatBodyStmtLines(lines []bline, baseIndent string, st config.Style) []st for i, ll := range lines { text := ll.text indent := baseIndent - if i > 0 && ll.indent != "" && !isStandaloneBodyKeyword(ll.text, "then", "else", "elsif", "elseif") { + switch { + case i > 0 && ll.indent == "" && len(significantBodyTokens(text)) == 0: + // A column-0 comment line trailing a multi-line (commented-out) + // statement is a continuation the author left flush-left — keep it + // there rather than re-indenting it to block depth. + indent = "" + case i > 0 && ll.indent != "" && !isStandaloneBodyKeyword(ll.text, "then", "else", "elsif", "elseif"): indent = ll.indent } out = append(out, indent+text) @@ -639,7 +679,9 @@ func reindentBodyDML(lines []bline, baseIndent string, st config.Style) []string continue } if lineDepth == 0 && (kw == "and" || kw == "or") { - out = append(out, baseIndent+st.Indent+strings.TrimSpace(text)) + // House style: AND/OR line up with the WHERE keyword; the first + // predicate is indented two levels under it. + out = append(out, baseIndent+strings.TrimSpace(text)) afterWhere = false updateBodyParenDepth(text, &parenDepth) continue diff --git a/pkg/format/dml.go b/pkg/format/dml.go index 81af369..e36f80c 100644 --- a/pkg/format/dml.go +++ b/pkg/format/dml.go @@ -462,7 +462,7 @@ func dmlInline(toks []cst.Tok, st config.Style) string { } var b strings.Builder for i, t := range toks { - if i > 0 && needSpace(toks[i-1].Tok, t.Tok) { + if i > 0 && needSpace(toks[i-1].Tok, t.Tok) && !isPctTypeBoundary(toks, i) { b.WriteByte(' ') } // Space after comma in calls: func(a, b) vs func(a,b). diff --git a/pkg/format/format.go b/pkg/format/format.go index da1de3b..da0ec2c 100644 --- a/pkg/format/format.go +++ b/pkg/format/format.go @@ -147,8 +147,9 @@ func (p *printer) writeCreateFunction(cf *cst.CreateFunction) { paramTexts = alignParamTypes(paramTexts) } - first := p.st.Indent + " " // align item text one column past the comma - cont := p.st.Indent + // House style: first parameter indented one level; leading-comma + // continuation lines carry the comma at column 0 followed by one space. + first := p.st.Indent for i, text := range paramTexts { param := cf.Params[i] p.nl() @@ -159,8 +160,7 @@ func (p *printer) writeCreateFunction(cf *cst.CreateFunction) { p.b.WriteString(",") } } else { - p.b.WriteString(cont) - p.b.WriteString(",") + p.b.WriteString(", ") p.b.WriteString(text) } // Emit trailing inline comment from the separator (e.g. --description after param). @@ -179,6 +179,7 @@ func (p *printer) writeCreateFunction(cf *cst.CreateFunction) { for _, clause := range cf.Options { p.nl() + p.b.WriteString(p.st.Indent) p.b.WriteString(p.inline(clause)) } if cf.As != nil { @@ -218,7 +219,7 @@ func (p *printer) inline(toks []cst.Tok) string { } var b strings.Builder for i, t := range toks { - if i > 0 && needSpace(toks[i-1].Tok, t.Tok) { + if i > 0 && needSpace(toks[i-1].Tok, t.Tok) && !isPctTypeBoundary(toks, i) { b.WriteByte(' ') } var prev lexer.Token @@ -254,6 +255,31 @@ func (p *printer) trailingComments(lead cst.Trivia) { // tightOps are operators printed without surrounding spaces. var tightOps = map[string]bool{"::": true, ":": true, "->": true, "->>": true} +// isPctTypeBoundary reports whether the gap between toks[i-1] and toks[i] sits +// inside a %TYPE / %ROWTYPE modifier (e.g. core.tbl%rowtype), which is printed +// tight like "::" rather than as the modulo operator. +func isPctTypeBoundary(toks []cst.Tok, i int) bool { + if i <= 0 || i >= len(toks) { + return false + } + isPct := func(t lexer.Token) bool { return t.Kind == lexer.Operator && t.Text == "%" } + isTypeWord := func(t lexer.Token) bool { + if t.Kind != lexer.Ident { + return false + } + l := lowerASCII(t.Text) + return l == "type" || l == "rowtype" + } + prev, cur := toks[i-1].Tok, toks[i].Tok + if isPct(prev) && isTypeWord(cur) { + return true // space after % + } + if isPct(cur) && i+1 < len(toks) && isTypeWord(toks[i+1].Tok) { + return true // space before % + } + return false +} + // parenKws are keywords that always take a space before '(' because they // introduce a subquery or a bracketed clause, not a function-call argument list. var parenKws = map[string]bool{ diff --git a/pkg/format/format_test.go b/pkg/format/format_test.go index 743c8d5..a1ebedb 100644 --- a/pkg/format/format_test.go +++ b/pkg/format/format_test.go @@ -23,13 +23,13 @@ func TestFormatHeaderGolden(t *testing.T) { want := "--select * from dropall('resolvespec_login');\n" + "CREATE OR REPLACE FUNCTION resolvespec_login(\n" + - " INOUT p_data jsonb\n" + - " ,OUT p_success boolean\n" + - " ,OUT p_error text\n" + + " INOUT p_data jsonb\n" + + ", OUT p_success boolean\n" + + ", OUT p_error text\n" + ")\n" + - "LANGUAGE plpgsql\n" + - "VOLATILE\n" + - "SECURITY DEFINER\n" + + " LANGUAGE plpgsql\n" + + " VOLATILE\n" + + " SECURITY DEFINER\n" + "AS\n" + "$$\nbegin end;\n$$;\n" @@ -130,8 +130,8 @@ func TestFormatIssue1PLpgSQLIndenting(t *testing.T) { want := "CREATE FUNCTION f(\n" + ")\n" + - "RETURNS void\n" + - "LANGUAGE plpgsql\n" + + " RETURNS void\n" + + " LANGUAGE plpgsql\n" + "AS\n" + "$$\n" + "DECLARE\n" + @@ -144,14 +144,14 @@ func TestFormatIssue1PLpgSQLIndenting(t *testing.T) { " set status = 'done'\n" + " where\n" + " u.rid_process = r_lp.rid_process\n" + - " and nv(u.status) <> 'done';\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" + + " and nv(u.status) <> 'open';\n" + "\n" + " end if;\n" + "$$;\n" diff --git a/pkg/format/safety.go b/pkg/format/safety.go index f1629bd..f598411 100644 --- a/pkg/format/safety.go +++ b/pkg/format/safety.go @@ -32,6 +32,14 @@ func SemanticallyEqual(a, b string) bool { if !strings.EqualFold(ta[i].Text, tb[i].Text) { return false } + case lexer.String, lexer.EscapeString, lexer.BitString, lexer.HexString, lexer.UnicodeString: + // A CRLF vs LF difference inside a multi-line string literal is a + // line-ending normalisation, not a change of code content — the + // formatter always re-emits layout with st.Newline. Compare the + // literal modulo \r\n ↔ \n. + if normNL(ta[i].Text) != normNL(tb[i].Text) { + return false + } case lexer.DollarString: _, innerA, _, okA := splitDollarQuote(ta[i].Text) _, innerB, _, okB := splitDollarQuote(tb[i].Text) @@ -47,6 +55,10 @@ func SemanticallyEqual(a, b string) bool { return true } +// normNL collapses CRLF to LF so string literals compare independent of the +// source file's line-ending convention. +func normNL(s string) string { return strings.ReplaceAll(s, "\r\n", "\n") } + // significantTokens lexes src and returns its tokens excluding EOF and trivia // (whitespace/comments). func significantTokens(src string) []lexer.Token { diff --git a/testdata/corpus/5791089_create_core.action_init.pgsql b/testdata/corpus/5791089_create_core.action_init.pgsql new file mode 100755 index 0000000..e64def3 --- /dev/null +++ b/testdata/corpus/5791089_create_core.action_init.pgsql @@ -0,0 +1,797 @@ +--select * from dropall('action_init','core'); +CREATE OR REPLACE FUNCTION core.action_init( + p_table text +, p_rid integer +, INOUT p_parms jsonb default json_build_object() +, OUT p_retval integer +, OUT p_errmsg text +) + LANGUAGE plpgsql + VOLATILE + SECURITY DEFINER +AS +$$ +DECLARE + --Error Handling-- + m_funcname text = 'core.action_init'; + m_errmsg text; + m_errcontext text; + m_errdetail text; + m_errhint text; + m_errstate text; + m_retval integer; + --Error Handling-- + --r_workflow core.workflowitem%rowtype; + r_tasklist core.tasklist%rowtype; + r_taskitem core.taskitem%rowtype; + r_taskitem_prev core.taskitem%rowtype; + r_taskitem_parent core.taskitem%rowtype; + r_mastertaskitem core.mastertaskitem%rowtype; + r_mastertaskitemevent core.mastertaskitemevent%rowtype; + r_taskitemevent core.taskitemevent%rowtype; + r_taskitemevent_parent core.taskitemevent%rowtype; + m_rid_hub_user integer; + m_rid_hub_payload integer; + m_temp_rid integer; + m_temp_rid_list integer[]; + j_results jsonb; +BEGIN + p_retval = 0; + p_errmsg = ''; + m_rid_hub_user = _bv(core.f_get_user_hub_rid(), ( + select hub.rid_hub + from core.hub + where hub.hubtype = 'program' + limit 1 + )); + m_rid_hub_payload = _try_integers(p_parms ->> 'rid_hub', p_parms ->> 'rid_hub_payload'); + + if nv(p_rid) = 0 + then + raise 'Invalid or no p_rid'; + end if; + + ---select * from meta.table_prefix t where t.tablename = 'mastertaskitemevent' + if p_table::citext in ('taskitemevent', 'tiv') + then + raise exception 'Action Init is not allowed on task item events. Please use the mastertaskitemevent or taskitem as entry point.'; + + elsif p_table::citext in ('mastertaskitemevent', 'mtev') + then + select * + from core.mastertaskitemevent mtev + where mtev.rid_mastertaskitemevent = p_rid + into r_mastertaskitemevent; + + if r_mastertaskitemevent.inactive > 0 + then + raise exception 'The template task item event is inactive. rid_mastertaskitemevent = %',r_mastertaskitemevent.rid_mastertaskitemevent; + end if; + + select ttie.* + from core.taskitemevent ttie + where ttie.rid_taskitemevent = _try_integer(p_parms ->> 'parent_rid_taskitemevent', 0) + into r_taskitemevent_parent; + + -- try to get the given task item + select * + from core.taskitem ti + where + ti.rid_taskitem = _try_integer(p_parms ->> 'rid_taskitem', -1) + and ti.rid_mastertaskitem = r_mastertaskitemevent.rid_mastertaskitem + into r_taskitem; + + if _try_integer(p_parms ->> 'parent_rid_taskitem', 0) > 0 + then + select * + from core.taskitem ti + where ti.rid_taskitem = _try_integer(p_parms ->> 'parent_rid_taskitem', 0) + into r_taskitem_parent; + end if; + + select * + from core.tasklist tl + where tl.rid_tasklist = r_taskitem.rid_tasklist + into r_tasklist; + + -- perform log_event(m_funcname,format(E'Action Init via %s=%s Item:%s(%s rid_taskitem:%s[%s] < %s %s[%s]) Event:%s(%s[%s],parent_rid_taskitemevent:%s[%s]) +-- p_parms:%s',p_table,p_rid +-- , r_mastertaskitem.description, r_mastertaskitem.rid_mastertaskitem,r_taskitem.rid_taskitem +-- ,r_taskitem.status, r_taskitem_parent.description +-- ,r_taskitem_parent.rid_taskitem,r_taskitem_parent.status +-- , r_mastertaskitemevent.description,r_mastertaskitemevent.status, r_mastertaskitemevent.rid_mastertaskitemevent +-- , r_taskitemevent_parent.rid_taskitemevent, r_taskitemevent_parent.status +-- , p_parms),bt_enum('eventlog','local notice')); + +-- raise 'p_rid=%=% r_taskitem.status =% r_taskitem_parent.status=%,% r_mastertaskitemevent.status=% pps=%' +-- ,p_table,p_rid, r_taskitem.status,r_taskitem_parent.description,r_taskitem_parent.status, r_mastertaskitemevent.status +-- ,(select ti.status +-- from core.taskitem ti +-- where ti.rid_taskitem = r_taskitem_parent.rid_taskitem_parentaction); + +-- if r_mastertaskitemevent.status = core._enumi('eventstatus','complete') +-- and r_taskitem_parent.rid_taskitem > 0 +-- and exists ( +-- select 1 +-- from core.taskitem ti +-- where ti.rid_taskitem = r_taskitem_parent.rid_taskitem_parentaction +-- and ti.status in (core._enumi('eventstatus','complete') +-- ,core._enumi('eventstatus','error') +-- ,core._enumi('eventstatus','retry')) +-- ) +-- then +-- r_taskitem = null; +-- +-- end if; + + --If there is no action, try getting the same open one of this type for this hub + if nv(r_taskitem.rid_taskitem) = 0 + then + --Create item linked to task for this hub if not found + + select r.p_retval, r.p_errmsg, r.p_a_rid_taskitem, r.p_parameters + from core.taskitem_get_or_prime(_jsonb_object_cat( + _jsonb_object_cat(r_taskitem_parent.jsonvalue + , jsonb_build_object('AOP', null, 'controls', null, 'rid_taskitem', null, 'message', null, 'subject', null, + 'alerttype', null) + ) + , p_parms + , jsonb_build_object( + 'rid_mastertaskitem', r_mastertaskitemevent.rid_mastertaskitem + , 'rid_mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , 'parent_rid_taskitemevent', r_taskitemevent_parent.rid_taskitemevent + , 'parent_rid_taskitem', _bv(r_taskitem_parent.rid_taskitem + , _try_integer(p_parms ->> 'parent_rid_taskitem', 0) + ) + , 'rid_tasklist_prev', _try_integers(p_parms ->> 'rid_tasklist_prev', p_parms ->> 'rid_tasklist' + , r_tasklist.rid_tasklist::text, r_taskitem.rid_tasklist::text) + , 'rid_taskitem_parentaction', p_parms -> 'rid_taskitem_parentaction' + --,'rid_tasklist', r_tasklist.rid_tasklist + ) + )) r + into m_retval,m_errmsg,m_temp_rid_list,j_results; + + if m_retval > 0 + then + raise exception '%',m_errmsg using hint = 'in taskitem_get_or_prime 2'; + end if; + + else + m_temp_rid_list = array [r_taskitem.rid_taskitem]::integer[]; + end if; + + + p_parms = jsonb_build_object(); + + for m_temp_rid in ( + select u.n + from unnest(m_temp_rid_list) u(n) + ) + loop + select * + from core.taskitem ti + where ti.rid_taskitem = m_temp_rid + into r_taskitem; + + select * + from core.tasklist tl + where tl.rid_tasklist = r_taskitem.rid_tasklist + into r_tasklist; + + if nv(r_tasklist.rid_tasklist) = 0 + then + select * + from core.tasklist tl + where + tl.rid_tasklist = r_taskitem.rid_tasklist + or nv(r_taskitem.rid_tasklist) = 0 + and + (tl.rid_hub = m_rid_hub_payload + or tl.rid_hub = r_taskitem_parent.rid_hub + ) + and tl.rid_mastertask in ( + select mti.rid_mastertask + from core.mastertaskitem mti + where + mti.rid_mastertaskitem = r_mastertaskitemevent.rid_mastertaskitem + and coalesce(mti.inactive, 0) = 0 + ) + + order by tl.rid_tasklist desc + limit 1 + into r_tasklist; + + end if; + + select tie.* + from core.taskitemevent tie + where + tie.rid_taskitem = r_taskitem.rid_taskitem + and tie.rid_mastertaskitemevent = r_mastertaskitemevent.rid_mastertaskitemevent + --and tie.status = r_mastertaskitemevent.status + order by tie.rid_taskitemevent desc + limit 1 + into r_taskitemevent; + + perform log_event(m_funcname, format(E'__Processing task item %s(%s, status=%s) for event %s(%s) status=%s' + , r_taskitem.description, r_taskitem.rid_taskitem, r_taskitem.status + , r_taskitemevent.description, r_mastertaskitemevent.rid_mastertaskitemevent, r_taskitemevent.status + ), bt_enum('eventlog', 'local notice')); + + if nv(r_taskitem.status) in (4, 5, 6, 11) --11 Resolved + and r_mastertaskitemevent.status is distinct from r_taskitem.status + and existS ( + select 1 + from core.taskitemevent e + where + e.rid_taskitem = r_taskitem.rid_taskitem + and e.status = r_taskitem.status + ) + --Or errors to errors +-- or nv(r_taskitem.status) in (10, 11) +-- and nv(r_taskitemevent.status) in (1, 2, 11, 10) +-- and not exists ( +-- select 1 +-- from core.taskitem ti2 +-- where ti2.rid_parenttaskitem = r_taskitem.rid_taskitem +-- and ti2.status in (1,2,10,11) +-- ) +-- ) +-- and nv(r_mastertaskitem.jsonvalue->'AOP'->>'errorcode') = '' + or p_parms ->> 'new_task' in ('1', 'true') + then + select r.p_retval, r.p_errmsg, r.p_a_rid_taskitem, r.p_parameters + from core.taskitem_get_or_prime(_jsonb_object_cat( + _jsonb_object_cat(r_taskitem.jsonvalue, jsonb_build_object('AOP', null, 'controls', null)), + p_parms + , jsonb_build_object( + 'rid_mastertaskitem', r_mastertaskitemevent.rid_mastertaskitem + , 'rid_mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , 'parent_rid_taskitemevent', r_taskitemevent.rid_taskitemevent + , 'rid_tasklist', r_tasklist.rid_tasklist + , 'parent_rid_taskitem' + , _bv(r_taskitem.rid_taskitem + , _try_integer(p_parms ->> 'parent_rid_taskitem', 0) + , r_taskitem_parent.rid_taskitem) + , 'new_item', false + , 'status', 1 + , 'rid_tasklist_prev', _try_integers(p_parms ->> 'rid_tasklist_prev', p_parms ->> 'rid_tasklist', + r_taskitem.rid_tasklist::text) + , 'rid_taskitem_parentaction', p_parms -> 'rid_taskitem_parentaction' + ) + )) r + into m_retval,m_errmsg,m_temp_rid_list,j_results; + + if m_retval > 0 + then + raise exception '%',m_errmsg using hint = 'in taskitem_get_or_prime 2'; + end if; + + --raise notice 'Re-evaluated task item % for event % m_temp_rid_list=%',r_taskitem.rid_taskitem,r_mastertaskitemevent.rid_mastertaskitemevent, m_temp_rid_list; + + if array_length(m_temp_rid_list, 1) > 1 + and r_taskitem.rid_taskitem <> any (m_temp_rid_list) + or (p_parms ->> 'new_task' in ('1', 'true') + and r_taskitem.status in (4, 11) + ) + + then + perform log_event(m_funcname, format(E'*.* Fired event %s(%s) for completed action : %s New Actions: %s' + , r_mastertaskitemevent.description, r_mastertaskitemevent.rid_mastertaskitemevent, r_taskitem.description + , m_temp_rid_list), bt_enum('eventlog', 'debug')); + + r_taskitem_prev = r_taskitem; + + select ti.rid_taskitem + from core.taskitem ti + where + ti.rid_taskitem = any (m_temp_rid_list) + and ti.rid_mastertaskitem = r_taskitem.rid_mastertaskitem + into r_taskitem; + + select * + from core.mastertaskitem mti + where mti.rid_mastertaskitem = r_taskitem.rid_mastertaskitem + into r_mastertaskitem; + + r_taskitemevent = null; + + -- else +-- +-- perform log_event(m_funcname,format(E'Cannot fire event (Already Done,Cancelled,Error) (%s,%s,%s), on %s(%s) status = %s' +-- ,r_mastertaskitemevent.description +-- ,r_mastertaskitemevent.rid_mastertaskitemevent +-- ,r_mastertaskitemevent.status +-- ,r_taskitem.rid_taskitem +-- ,r_taskitem.description +-- ,r_taskitem.status +-- ),bt_enum('eventlog','debug')); +-- +-- continue; + + end if; + end if; + + ---Allow firing events again. + if nv(r_taskitemevent.rid_taskitemevent) > 0 + then + -- if nv(r_taskitemevent.retval) < 2 +-- then + + perform log_event(m_funcname, format(E'**Updating event %s(%s) for action : %s' + , r_mastertaskitemevent.description, r_mastertaskitemevent.rid_mastertaskitemevent, r_taskitem.description + ), bt_enum('eventlog', 'debug')); + + --SET session_replication_role = DEFAULT; + + update core.taskitemevent u + set + retval = 0 + , createddatetime = now() + , jsonvalue = _jsonb_object_cat(u.jsonvalue, p_parms -> '_event_jsonvalue', + jsonb_build_object('rid_mastertaskitem_complete', case + when + _try_integer( + r_taskitemevent_parent.jsonvalue ->> + 'rid_mastertaskitem_complete', + 0) <> + r_mastertaskitemevent.rid_mastertaskitem + then r_taskitemevent_parent.jsonvalue ->> 'rid_mastertaskitem_complete' + else null + end + )) + where u.rid_taskitemevent = r_taskitemevent.rid_taskitemevent + returning u.* + into r_taskitemevent; + + + select r.p_retval, r.p_errmsg + from core.event_created(r_taskitemevent.rid_taskitemevent) r + into m_retval,m_errmsg; + + if m_retval > 0 + then + raise exception '%',m_errmsg; + end if; + + + update core.taskitemevent u + set + jsonvalue = _jsonb_object_cat(u.jsonvalue, jsonb_build_object('rid_taskitemevent_retries', + _jsonb_object_cat(u.jsonvalue -> + 'rid_taskitemevent_retries', + jsonb_build_array(r_taskitemevent.rid_taskitemevent)))) + where u.rid_taskitemevent = r_taskitemevent_parent.rid_taskitemevent; + +-- end if; + else + --SET session_replication_role = DEFAULT; + + insert + into core.taskitemevent( createddatetime + , description + , guid + , rid_mastertaskitemevent + , rid_taskitem + , duedatetime + , errmsg + , escalated + , outcome + , status + , jsonvalue) + select now() + , format('%s %s', evt.description, nv(( + select count(1) + from core.taskitemevent ev2 + where + ev2.rid_taskitem = r_taskitem.rid_taskitem + and ev2.rid_mastertaskitemevent = r_mastertaskitemevent.rid_mastertaskitemevent + )) + 1 + ) + , newid() + , r_mastertaskitemevent.rid_mastertaskitemevent + , r_taskitem.rid_taskitem + , ( + select eri.p_timestamp + from core.interval_fetch('mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , r_taskitem.rid_hub, 'duedatetime', + jsonb_build_object('rid_taskitem', r_taskitem.rid_taskitem)) eri + limit 1 + ) + , null + , _try_integer(p_parms ->> 'escalated', 0) + , evt.outcome + , evt.status + , _jsonb_object_cat(p_parms -> '_event_jsonvalue', case + when nv(r_taskitem.rid_taskitem) = 0 + then jsonb_build_object('rid_hub', m_rid_hub_payload) + else jsonb_build_object() + end + , jsonb_build_object('parent_rid_taskitem', + _try_integer(p_parms ->> 'parent_rid_taskitem', r_taskitem.rid_taskitem) + , 'parent_rid_taskitemevent', r_taskitemevent_parent.rid_taskitemevent + , 'rid_mastertaskitem_complete', case + when _try_integer( + r_taskitemevent_parent.jsonvalue ->> + 'rid_mastertaskitem_complete', 0) <> + evt.rid_mastertaskitem + then r_taskitemevent_parent.jsonvalue ->> 'rid_mastertaskitem_complete' + else null + end) + ) + from core.mastertaskitemevent evt + where evt.rid_mastertaskitemevent = r_mastertaskitemevent.rid_mastertaskitemevent + returning taskitemevent.* + into r_taskitemevent; + + if r_taskitemevent.rid_taskitemevent > 0 + then + perform log_event(m_funcname, format(E'**Created event %s(%s) for action : %s' + , r_mastertaskitemevent.description, r_mastertaskitemevent.rid_mastertaskitemevent, r_taskitem.description + ), bt_enum('eventlog', 'debug')); + + + update core.taskitemevent u + set + jsonvalue = _jsonb_object_cat(u.jsonvalue, jsonb_build_object('rid_taskitemevent_spawned', + _jsonb_object_cat(u.jsonvalue -> + 'rid_taskitemevent_spawned', + jsonb_build_array(r_taskitemevent.rid_taskitemevent)))) + where u.rid_taskitemevent = r_taskitemevent_parent.rid_taskitemevent; + + + end if; + + end if; + + /* + if nv(r_taskitemevent.rid_taskitemevent) > 0 + then +-- if nv(r_taskitemevent.retval) < 2 +-- then + + perform log_event(m_funcname,format(E'Updating event %s(%s) for action : %s' + ,r_mastertaskitemevent.description,r_mastertaskitemevent.rid_mastertaskitemevent, r_taskitem.description + ),bt_enum('eventlog','debug')); + + + + perform meta.add_transaction_event(txid_current(), 'core.action_init' + ,replace(replace(replace(replace(replace($CC$ + DO $ICC$ + DECLARE + m_retval integer; + m_errmsg text; + r_taskitemevent core.taskitemevent%rowtype; + r_taskitemevent_parent core.taskitemevent%rowtype; + r_mastertaskitemevent core.mastertaskitemevent%rowtype; + BEGIN + + select * + from core.taskitemevent tie + where tie.rid_taskitemevent = [rid_taskitemevent] + into r_taskitemevent; + + select * + from core.taskitemevent tie + where tie.rid_taskitemevent = [rid_taskitemevent_parent] + into r_taskitemevent_parent; + + select * + from core.mastertaskitemevent t + where t.rid_mastertaskitemevent = [rid_mastertaskitemevent] + into r_mastertaskitemevent; + + + update core.taskitemevent u + set + retval = 0 + , createddatetime = now() + , jsonvalue = _jsonb_object_cat(u.jsonvalue,$JJA$[event_json_value]$JJA$::jsonb, jsonb_build_object('rid_mastertaskitem_complete', case + when + _try_integer( + r_taskitemevent_parent.jsonvalue ->> + 'rid_mastertaskitem_complete', + 0) <> + r_mastertaskitemevent.rid_mastertaskitem + then r_taskitemevent_parent.jsonvalue ->> 'rid_mastertaskitem_complete' + else null + end + )) + where u.rid_taskitemevent = r_taskitemevent.rid_taskitemevent + returning u.* + into r_taskitemevent; + + + + select r.p_retval,r.p_errmsg + from core.event_created(r_taskitemevent.rid_taskitemevent) r + into m_retval,m_errmsg; + + if m_retval > 0 + then + raise exception '%',m_errmsg; + end if; + + + update core.taskitemevent u + set + jsonvalue = _jsonb_object_cat(u.jsonvalue, jsonb_build_object('rid_taskitemevent_retries', + _jsonb_object_cat(u.jsonvalue -> 'rid_taskitemevent_retries', + jsonb_build_array(r_taskitemevent.rid_taskitemevent)))) + where u.rid_taskitemevent = r_taskitemevent_parent.rid_taskitemevent; + + + END; + $ICC$; + $CC$,'[rid_taskitemevent_parent]', nv(r_taskitemevent_parent.rid_taskitemevent)::text) + ,'[rid_taskitemevent]', nv(r_taskitemevent.rid_taskitemevent)::text) + ,'[rid_mastertaskitemevent]', nv(r_mastertaskitemevent.rid_mastertaskitemevent)::text) + ,'[rid_taskitem]', nv(r_taskitem.rid_taskitem)::text) + ,'[event_json_value]' ,coalesce((p_parms->'_event_jsonvalue')::text,'{}')) + ) + ,30, format('action_init_%s',r_taskitemevent.rid_taskitemevent); + + SET session_replication_role = DEFAULT; + + */ + + -------------------------------------------__Recursive Rules__--------------------------------------------------- +-- --activate rule +-- if r_taskitemevent_parent.rid_taskitemevent > 0 +-- and r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_activate' is not null +-- and _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_activate',-1) > 0 +-- and not exists ( +-- select 1 +-- from core.taskitem ti +-- where ti.rid_mastertaskitem = _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_activate',-1) +-- and ti.rid_hub = m_rid_hub_payload +-- ) +-- then +-- if _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_activate',-1) <> r_taskitem.rid_mastertaskitem +-- and _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_activate',-1) <> nv(r_mastertaskitem.rid_parentmastertaskitem) +-- then +-- insert into core.taskitemevent(rid_taskitem,rid_mastertaskitemevent,description,status,createddatetime,guid,jsonvalue) +-- select r_taskitem.rid_taskitem +-- , mtev.rid_mastertaskitemevent +-- , mtev.description +-- , mtev.status +-- , now() +-- , newid() +-- , _jsonb_object_cat(mtev.jsonvalue,p_parms->'_event_jsonvalue', jsonb_build_object('rid_mastertaskitem_activate',r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_activate', 'simulated', true +-- , 'parent_rid_taskitem',_try_integer(p_parms ->> 'parent_rid_taskitem',0) +-- )) +-- from core.mastertaskitemevent mtev +-- where mtev.rid_mastertaskitem = r_taskitem.rid_mastertaskitem +-- and coalesce(mtev.inactive,0) = 0 +-- and mtev.status = 2 +-- and not exists ( +-- select 1 +-- from core.taskitemevent tie +-- where tie.rid_taskitem = r_taskitem.rid_taskitem +-- and tie.rid_mastertaskitemevent = mtev.rid_mastertaskitemevent +-- ) +-- ; +-- elseif _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_activate',-1) = r_taskitem.rid_mastertaskitem +-- then +-- --We reached the target taskitem, so we can mark it activate +-- insert into core.taskitemevent(rid_taskitem,rid_mastertaskitemevent,description,status,createddatetime,guid,jsonvalue) +-- select r_taskitem.rid_taskitem +-- , mtev.rid_mastertaskitemevent +-- , mtev.description +-- , mtev.status +-- , now() +-- , newid() +-- , _jsonb_object_cat( mtev.jsonvalue,p_parms->'_event_jsonvalue',jsonb_build_object('parent_rid_taskitem',_try_integer(p_parms ->> 'parent_rid_taskitem',0))) +-- from core.mastertaskitemevent mtev +-- where mtev.rid_mastertaskitem = r_taskitem.rid_mastertaskitem +-- and coalesce(mtev.inactive,0) = 0 +-- and mtev.status = 2 +-- and not exists ( +-- select 1 +-- from core.taskitemevent tie +-- where tie.rid_taskitem = r_taskitem.rid_taskitem +-- and tie.rid_mastertaskitemevent = mtev.rid_mastertaskitemevent +-- ) +-- ; +-- end if; +-- +-- end if; +-- +-- --complete rule +-- if r_taskitemevent_parent.rid_taskitemevent > 0 +-- and r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete' is not null +-- and _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete',-1) > 0 +-- and not exists ( +-- select 1 +-- from core.taskitem ti +-- where ti.rid_mastertaskitem = _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete',-1) +-- and ti.rid_hub = m_rid_hub_payload +-- ) +-- then +-- if _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete',-1) <> r_taskitem.rid_mastertaskitem +-- and _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete',-1) <> nv(r_mastertaskitem.rid_parentmastertaskitem) +-- then +-- insert into core.taskitemevent(rid_taskitem,rid_mastertaskitemevent,description,status,createddatetime,guid,jsonvalue) +-- select r_taskitem.rid_taskitem +-- , mtev.rid_mastertaskitemevent +-- , mtev.description +-- , mtev.status +-- , now() +-- , newid() +-- , _jsonb_object_cat(mtev.jsonvalue,p_parms->'_event_jsonvalue', jsonb_build_object('rid_mastertaskitem_complete',r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete', 'simulated', true, 'parent_rid_taskitem',_try_integer(p_parms ->> 'parent_rid_taskitem',0))) +-- from core.mastertaskitemevent mtev +-- where mtev.rid_mastertaskitem = r_taskitem.rid_mastertaskitem +-- and coalesce(mtev.inactive,0) = 0 +-- and mtev.status = 4 +-- and not exists ( +-- select 1 +-- from core.taskitemevent tie +-- where tie.rid_taskitem = r_taskitem.rid_taskitem +-- and tie.rid_mastertaskitemevent = mtev.rid_mastertaskitemevent +-- ) +-- ; +-- elseif _try_integer(r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete',-1) = r_taskitem.rid_mastertaskitem +-- then +-- --We reached the target taskitem, so we can mark it activate +-- insert into core.taskitemevent(rid_taskitem,rid_mastertaskitemevent,description,status,createddatetime,guid,jsonvalue) +-- select r_taskitem.rid_taskitem +-- , mtev.rid_mastertaskitemevent +-- , mtev.description +-- , mtev.status +-- , now() +-- , newid() +-- , _jsonb_object_cat(mtev.jsonvalue,p_parms->'_event_jsonvalue',jsonb_build_object('parent_rid_taskitem',_try_integer(p_parms ->> 'parent_rid_taskitem',0))) +-- from core.mastertaskitemevent mtev +-- where mtev.rid_mastertaskitem = r_taskitem.rid_mastertaskitem +-- and coalesce(mtev.inactive,0) = 0 +-- and mtev.status = 2 +-- and not exists ( +-- select 1 +-- from core.taskitemevent tie +-- where tie.rid_taskitem = r_taskitem.rid_taskitem +-- and tie.rid_mastertaskitemevent = mtev.rid_mastertaskitemevent +-- ) +-- ; +-- end if; +-- end if; + + p_parms := _jsonb_object_cat(p_parms, to_jsonb(r_taskitemevent)); + + end loop; + + -- perform log_event(m_funcname +-- ,format('Post event insert: rid_taskitemevent=%s rid_mastertaskitem_complete=%s rid_mastertaskitem=%s' +-- ,r_taskitemevent_parent.rid_taskitemevent +-- ,r_taskitemevent_parent.jsonvalue->>'rid_mastertaskitem_complete' +-- ,r_taskitem.rid_mastertaskitem +-- ),bt_enum('eventlog','local notice')); + + elsif p_table::citext in ('taskitem', 'wfl') + then + select ti.* + from core.taskitem ti + where ti.rid_taskitem = p_rid + into r_taskitem; + + select ti.* + from core.mastertaskitem ti + where ti.rid_mastertaskitem = r_taskitem.rid_mastertaskitem + into r_mastertaskitem; + + select tl.* + from core.tasklist tl + where tl.rid_tasklist = r_taskitem.rid_tasklist + into r_tasklist; + + --Create item linked to task for this hub if not found + + perform log_event(m_funcname, format(E'Action Init via %s=%s Item:%s(%s rid_taskitem:%s) + p_parms:%s', p_table, p_rid + , r_mastertaskitem.description, r_mastertaskitem.rid_mastertaskitem, r_taskitem.rid_taskitem + , p_parms), bt_enum('eventlog', 'local notice')); + + select r.p_retval, r.p_errmsg, r.p_a_rid_taskitem + from core.taskitem_get_or_prime(_jsonb_object_cat( + jsonb_build_object('rid_tasklist', r_tasklist.rid_tasklist + , 'rid_mastertaskitemevent', + r_mastertaskitemevent.rid_mastertaskitemevent + , 'rid_taskitem_parentaction', p_parms -> 'rid_taskitem_parentaction' + ), to_jsonb(r_taskitem), p_parms)) r + into m_retval,m_errmsg,m_temp_rid_list; + + if m_retval > 0 + then + raise exception '%',m_errmsg using hint = 'in taskitem_get_or_prime 3'; + end if; + + if array_length(m_temp_rid_list, 1) > 1 + then + select array_agg(to_jsonb(ti)) + from core.taskitem ti + where ti.rid_taskitem = any (m_temp_rid_list) + into p_parms; + + else + m_temp_rid = m_temp_rid_list[1]; + select * + from core.taskitem ti + where ti.rid_taskitem = m_temp_rid + into r_taskitem; + + p_parms = to_jsonb(r_taskitem); + end if; + + + elsif p_table::citext in ('mastertaskitem', 'mal') + then + select ti.* + from core.mastertaskitem ti + where ti.rid_mastertaskitem = p_rid + into r_mastertaskitem; + + if r_mastertaskitem.inactive > 0 + then + raise exception 'The template task item is inactive. rid_mastertaskitem = %',r_mastertaskitem.rid_mastertaskitem; + end if; + + perform log_event(m_funcname, format(E'Action Init via %s=%s Item:%s(%s) + p_parms:%s', p_table, p_rid + , r_mastertaskitem.description, r_mastertaskitem.rid_mastertaskitem + , p_parms), bt_enum('eventlog', 'local notice')); + + select r.p_retval, r.p_errmsg, r.p_a_rid_taskitem + from core.taskitem_get_or_prime(_jsonb_object_cat(to_jsonb(r_mastertaskitem) + , p_parms + , jsonb_build_object('rid_mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , 'rid_taskitem_parentaction', + p_parms -> 'rid_taskitem_parentaction' + )) + ) r + into m_retval,m_errmsg,m_temp_rid_list; + + if m_retval > 0 + then + raise exception '%',m_errmsg using hint = 'in taskitem_get_or_prime 4'; + end if; + + if array_length(m_temp_rid_list, 1) > 1 + then + select array_agg(to_jsonb(ti)) + from core.taskitem ti + where ti.rid_taskitem = any (m_temp_rid_list) + into p_parms; + + else + m_temp_rid = m_temp_rid_list[1]; + select * + from core.taskitem ti + where ti.rid_taskitem = m_temp_rid + into r_taskitem; + + p_parms = to_jsonb(r_taskitem); + end if; + end if; + + if nv(r_taskitem.rid_mastertaskitem) = 0 + then + raise warning 'No template for the task item. rid_mastertaskitem = %',r_taskitem.rid_mastertaskitem; + + 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 := format('%s table:%s rid:%s', + get_err_msg(m_funcname, m_errmsg, m_errcontext, m_errdetail, m_errhint, m_errstate), p_table, + p_rid); + p_retval = 1; +END; +$$; \ No newline at end of file diff --git a/testdata/corpus/5795535_create_core.event_exec_func.pgsql b/testdata/corpus/5795535_create_core.event_exec_func.pgsql new file mode 100755 index 0000000..7bb4cc5 --- /dev/null +++ b/testdata/corpus/5795535_create_core.event_exec_func.pgsql @@ -0,0 +1,988 @@ +--select * from dropall('event_exec_func','core'); +CREATE OR REPLACE FUNCTION core.event_exec_func( + p_event_parameters jsonb +, p_item_parameters jsonb +, OUT p_retval integer +, OUT p_errorcode text +, OUT p_errmsg text +, OUT p_output jsonb +) + LANGUAGE plpgsql + VOLATILE + SECURITY DEFINER +AS +$$ +DECLARE + --Error Handling-- + m_funcname text = 'core.event_exec_func'; + m_errmsg text; + m_errcode text; + m_errcontext text; + m_errdetail text; + m_errhint text; + m_errstate text; + m_retval integer; + --Error Handling-- + r_taskitem core.taskitem%rowtype; + r_taskitem_parent core.taskitem%rowtype; + r_mastertaskitem core.mastertaskitem%rowtype; + r_taskitemevent core.taskitemevent%rowtype; + m_rid integer; + j_output jsonb; + j_all_parms jsonb; + j_controls jsonb; + j_tmp jsonb; + m_exec_funcname citext; + m_exec_schema citext; + m_sql citext; + r_lp record; + r_hub core.hub%rowtype; + a_async_procs citext[]; + a_broker_procs citext[]; + a_noerror_procs citext[]; +BEGIN + j_controls = jsonb_build_array(); + p_retval = 0; + p_errmsg = ''; + m_exec_schema = 'public'; + a_async_procs = array ['']; --hsync_upload_clienttohyphen + a_broker_procs = array ['']; --hsync_upload_clienttohyphen + a_noerror_procs = array ['ui_clientscenario_commit']; + + -- if p_event_parameters -> 'AFN_RID_ACTIONFUNCTION_CODE' is null +-- then +-- p_retval = 1; +-- p_errmsg = '[ECR0001] No AFN_RID_ACTIONFUNCTION_CODE inside the AOP pack.'; +-- +-- select m.v[1] +-- from regexp_matches(p_errmsg, '\[([^\]]+)\]','ig') m(v) +-- where length(m.v[1]) between 2 and 10 +-- order by m.v[1] +-- into p_errorcode; +-- +-- return; +-- end if; + + select * + from core.taskitemevent tie + where tie.rid_taskitemevent = _try_integer(p_item_parameters ->> 'rid_taskitemevent', 0) + into r_taskitemevent; + + select * + from core.taskitem ti + where ti.rid_taskitem = _try_integer(p_item_parameters ->> 'rid_taskitem', r_taskitemevent.rid_taskitem) + into r_taskitem; + + select * + from core.mastertaskitem mti + where + mti.rid_mastertaskitem = _try_integer(p_item_parameters ->> 'rid_mastertaskitem', 0) + or r_taskitem.rid_mastertaskitem > 0 and mti.rid_mastertaskitem = r_taskitem.rid_mastertaskitem + order by mti.rid_mastertaskitem desc + limit 1 + into r_mastertaskitem; + + m_exec_funcname = r_mastertaskitem.functionname_code; + + if m_exec_funcname ilike '%.%' + then + m_exec_schema = split_part(m_exec_funcname, '.', 1); + m_exec_funcname = split_part(m_exec_funcname, '.', 2); + end if; + + + if nv(m_exec_funcname) = '' + then + perform log_event(m_funcname, format( + E'No function to execute for MasterTaskItem %s (rid_mastertaskitem=%s) and TaskItem %s (rid_taskitem=%s)', + r_mastertaskitem.description, r_mastertaskitem.rid_mastertaskitem, r_taskitem.description, + r_taskitem.rid_taskitem), bt_enum('eventlog', 'local notice')); + return; + end if; + + -- if m_exec_funcname ilike '%payment%plan%' +-- then +-- raise exception 'Payment Plan Test Error % [RTY0001]', m_exec_funcname; +-- end if; + + + j_all_parms = _jsonb_object_cat(r_taskitem.jsonvalue, + j_all_parms + , ( + select jsonb_object_agg(k, p_event_parameters -> k) + from jsonb_object_keys(p_event_parameters) keys(k) + where jsonb_typeof(p_event_parameters -> keys.k) not in ('object', 'array', 'null') + ), ( + select jsonb_object_agg(k, p_item_parameters -> k) + from jsonb_object_keys(p_item_parameters) keys(k) + where jsonb_typeof(p_item_parameters -> keys.k) not in ('object', 'array', 'null') + ) + ); + + + if r_taskitem.rid_taskitem > 0 and r_taskitem.rid_taskitem_parentaction > 0 + then + select ti.* + from core.taskitem ti + where ti.rid_taskitem = r_taskitem.rid_taskitem_parentaction + into r_taskitem_parent; + end if; + + select af.jsonvalue -> 'controls' + from core.actionfunction af + where + ( + af.functionname = m_exec_funcname + or af.functionname = r_mastertaskitem.functionname_code + ) + and af.fntype = 'pgsql-function'::citext + into j_controls; + + + if jsonb_typeof(p_item_parameters -> 'AOP' -> 'jsonvalue' -> 'controls_code') = 'array' + then + j_controls = _jsonb_object_cat(j_controls, p_item_parameters -> 'AOP' -> 'jsonvalue' -> 'controls'); + end if; + + if jsonb_typeof(p_event_parameters -> 'jsonvalue' -> 'controls_code') = 'array' + then + j_controls = _jsonb_object_cat(j_controls, p_event_parameters -> 'jsonvalue' -> 'controls'); + end if; + + if jsonb_typeof(j_all_parms -> 'controls_code') = 'array' + then + j_controls = _jsonb_object_cat(j_controls, j_all_parms -> 'controls_code'); + end if; + + if jsonb_typeof(r_mastertaskitem.jsonvalue -> 'controls_code') = 'array' + then + j_controls = _jsonb_object_cat(j_controls, r_mastertaskitem.jsonvalue -> 'controls_code'); + end if; + + raise notice 'Controls: %', j_controls::text; + + j_all_parms = _jsonb_object_cat(j_all_parms, + ( + select jsonb_object_agg(ctr.j ->> 'name' + , case + when nv(j_all_parms ->> (ctr.j ->> 'name')) = '' + then ctr.j -> 'props' ->> 'value' + else j_all_parms ->> (ctr.j ->> 'name') + end + order by i + ) + from jsonb_array_elements(j_controls) with ordinality ctr(j, i) + where nv(ctr.j -> 'props' ->> 'value') <> '' + ) + ); + + + if nv(j_all_parms ->> 'rid_hub_user') = '' + then + j_all_parms = _jsonb_object_cat(j_all_parms, jsonb_build_object( + 'rid_hub_user', _bv(core.f_get_user_hub_rid(), ( + select hub.rid_hub + from core.hub + where hub.hubtype = 'program' + limit 1 + )), + 'login', f_getuser() + )); + + end if; + + if nv(j_all_parms ->> 'login') = '' + then + j_all_parms = _jsonb_object_cat(j_all_parms, jsonb_build_object( + 'login', f_getuser() + )); + end if; + + if _try_integer(j_all_parms ->> 'rid_hub', 0) > 0 + then + select * + from core.hub + where rid_hub = _try_integer(j_all_parms ->> 'rid_hub', 0) + into r_hub; + + if r_hub.hubtype in ('client', 'lead') + then + j_all_parms = _jsonb_object_cat(j_all_parms, ( + select jsonb_build_object( + 'rid_adproclient', cli.rid_adproclient + , 'rid_client', cli.rid_adproclient + ) + from t_adproclient cli + where cli.rid_hub = r_hub.rid_hub + limit 1 + )); + elsif r_hub.hubtype in ('trader') + then + j_all_parms = _jsonb_object_cat(j_all_parms, ( + select jsonb_build_object( + 'rid_trader', tdr.rid_trader + , 'rid_adprocreditor', tdr.rid_trader + ) + from t_trader tdr + where tdr.rid_hub = r_hub.rid_hub + limit 1 + )); + + end if; + + end if; + + j_all_parms = _jsonb_object_cat(j_all_parms, jsonb_build_object('AOP', null)); + + -- perform log_event(m_funcname, format('p_event_parameters=%s p_item_parameters=%s j_all_parms=%s', p_event_parameters::text, +-- p_item_parameters::text, j_all_parms::text), bt_enum('eventlog', 'local notice')); + + if ( + select 1 + from core.actionfunction af + where + ( + af.functionname = m_exec_funcname + or af.functionname = r_mastertaskitem.functionname_code + ) + and af.fntype = 'pgsql-function'::citext + ) + then + if m_exec_funcname = 'event_nextaction' + then + select r.p_retval, r.p_errmsg, r.p_data + from core.event_nextaction(j_all_parms) r + into m_retval,m_errmsg, j_output; + + if m_retval > 0 + then + p_retval = m_retval; + p_errmsg = m_errmsg; + + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + return; + end if; + + elsif m_exec_funcname in ('event_pre_error_check') + then + + select r.p_retval, r.p_errmsg, r.p_errorcode, r.p_parms + from core.event_pre_error_check(_jsonb_object_cat(j_all_parms, jsonb_build_object( + 'rid_taskitem', r_taskitem.rid_taskitem + , 'rid_tasklist', r_taskitem.rid_tasklist + , 'rid_mastertaskitem', r_mastertaskitem.rid_mastertaskitem + ))) r + into p_retval,p_errmsg, p_errorcode, p_output; + + + return; + elsif m_exec_funcname = 'ui_maint_commitem' + then + select r.p_retval, r.p_errmsg, r.p_options + from core.ui_maint_commitem(_jsonb_object_cat( + r_taskitem.jsonvalue + , r_taskitemevent.jsonvalue + , jsonb_build_object( + 'rid_taskitem', r_taskitem.rid_taskitem, + 'rid_mastertaskitem', r_taskitem.rid_mastertaskitem, + 'rid_hub_user', j_all_parms ->> 'rid_hub_user', + 'rid_hub_payload', _try_integers(j_all_parms ->> 'rid_hub', j_all_parms ->> 'rid_hub_payload') + ) + )) r + into m_retval,m_errmsg, j_output; + + if m_retval > 0 + then + p_retval = m_retval; + p_errmsg = m_errmsg; + + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + return; + end if; + + elsif m_exec_funcname = 'ui_modify_paymentplan' + then + + select r.p_retval, r.p_errmsg, r.p_data + from ui_modify_paymentplan(j_all_parms) r + into m_retval,m_errmsg, j_output; + + if m_retval > 0 + then + p_retval = m_retval; + p_errmsg = m_errmsg; + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + + return; + end if; + elsif m_exec_funcname in ('f_checkforreapprove_client') + then + select f_checkforreapprove_client(cli.rid_adproclient) + from t_adproclient cli + where cli.rid_hub = r_taskitem.rid_hub + into m_retval; + + if m_retval > 0 + then + p_retval = 1; + p_errmsg = 'Re-approval is required for this client and it needs to be uploaded to the PDA.'; + p_errorcode = 'CLIAPRV1'; + p_output = jsonb_build_object('message', p_errmsg, 'actionstatus', 'error', + 'errorcode', p_errorcode + , 'alerttype', 'error' + , 'in_modal', true + ); + + return; + else + p_retval = 0; + p_errmsg = ''; + p_errorcode = ''; + p_output = jsonb_build_object('actionstatus', 'success', 'delete_after', true); + return; + end if; + + elsif m_exec_funcname in ('c_checkforreapprove') + then + select c_checkforreapprove(cli.rid_adproclient) + from t_adproclient cli + where cli.rid_hub = r_taskitem.rid_hub + into m_retval; + + if m_retval > 0 + then + p_retval = 1; + p_errmsg = 'Re-approval is required for this client`s payment plan and it needs to be uploaded to the PDA.'; + p_errorcode = 'CLIAPRV2'; + p_output = jsonb_build_object('message', p_errmsg, 'actionstatus', 'error', 'errorcode' + , p_errorcode + , 'alerttype', 'error' + , 'in_modal', true); + + return; + else + p_retval = 0; + p_errmsg = ''; + p_errorcode = ''; + p_output = jsonb_build_object('actionstatus', 'success', 'delete_after', true); + return; + end if; + elsif m_exec_funcname in ('action_init_hublinks') + then + + select r.p_retval, r.p_errmsg + from core.action_init_hublinks(_jsonb_object_cat(p_item_parameters, jsonb_build_object( + 'rid_taskitem', r_taskitem.rid_taskitem, + 'rid_mastertaskitem', r_taskitem.rid_mastertaskitem, + 'rid_hub_user', j_all_parms ->> 'rid_hub_user', + 'event_status', r_taskitemevent.status, + 'parent_rid_taskitemevent', r_taskitemevent.rid_taskitemevent, + 'calling_function', m_funcname + ))) r + into m_retval, m_errmsg; + + if m_retval > 0 + then + p_retval = m_retval; + p_errmsg = m_errmsg; + + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + return; + end if; + elsif m_exec_funcname in ('ui_clientscenario_commit') + then + + if coalesce(_try_integers(j_all_parms ->> 'rid_clientscenario', j_all_parms ->> 'p_rid_clientscenario'), 0) = 0 + then + select s.rid_clientscenario + from clientscenario s + inner join t_adproclient c on c.rid_adproclient = s.rid_adproclient + and c.rid_hub = r_taskitem.rid_hub + where s.status in (1, 2) + order by s.status, s.rid_clientscenario desc + limit 1 + into m_rid; + + if nv(m_rid) = 0 and r_taskitem.rid_hub_link > 0 + then + --Falback to link parent client when tasklink is for client. + select s.rid_clientscenario + from core.hub_link h + inner join t_adproclient c on c.rid_hub = h.rid_hub_parent + and h.parent_hubtype = 'client' + inner join clientscenario s on s.rid_adproclient = c.rid_adproclient + and s.status in (1, 2) + where h.rid_hub_link = r_taskitem.rid_hub_link + order by s.status, s.rid_clientscenario desc + limit 1 + into m_rid; + end if; + + j_all_parms = _jsonb_object_cat(j_all_parms, jsonb_build_object( + 'rid_clientscenario', m_rid + )); + + end if; + + select r.p_retval, r.p_errmsg + from public.ui_clientscenario_commit(_try_integers(j_all_parms ->> 'rid_clientscenario' + , j_all_parms ->> 'p_rid_clientscenario', null) + , null + , _jsonb_object_cat(p_item_parameters, jsonb_build_object( + 'rid_taskitem', r_taskitem.rid_taskitem, + 'rid_hub', r_taskitem.rid_hub, + 'rid_mastertaskitem', r_taskitem.rid_mastertaskitem, + 'rid_hub_user', j_all_parms ->> 'rid_hub_user', + 'event_status', r_taskitemevent.status, + 'parent_rid_taskitemevent', r_taskitemevent.rid_taskitemevent, + 'calling_function', m_funcname + ))) r + into m_retval, m_errmsg; + + if p_event_parameters ->> 'no_error' in ('1', 'true') or p_item_parameters ->> 'no_error' in ('1', 'true') + then + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + return; + end if; + + if m_retval > 0 + then + p_retval = m_retval; + p_errmsg = m_errmsg; + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + return; + end if; + elsif exists ( + select ns.nspname, pro.* + from pg_proc pro + inner join pg_namespace ns on ns.oid = pro.pronamespace + and ns.nspname not in ('postgres', 'pg_catalog') + and ns.nspname = m_exec_schema + where pro.proname = m_exec_funcname + ) + and m_exec_funcname in + ('clfrm_builderrorlist', 'upl_builduploaderrors', 'clfrm_propcalc_errlist', 'ui_clientscenarion_errorlist') + then + raise notice 'Running %',m_exec_funcname; + + p_retval = 0; + p_errmsg = ''; + m_errdetail = ''; + SET session_replication_role = DEFAULT; + + if not exists ( + select cli.rid_adproclient + from t_adproclient cli + where cli.rid_hub = r_taskitem.rid_hub + ) + then + p_retval = 1; + p_errmsg = 'No client found for the hub.'; + return; + end if; + + with + cli as ( + select cli.rid_adproclient + from t_adproclient cli + where cli.rid_hub = r_taskitem.rid_hub + ) + , src as ( + select o.errordescription as errordescription + , o.errorcode as errorcode + , o.error_priority as priority + , o.errortype + from clfrm_builderrorlist(( + select cli.rid_adproclient + from cli + limit 1 + ), 0, 0) o + where m_exec_funcname in ('clfrm_builderrorlist') + union + select o.errordescription as errordescription + , o.errorcode as errorcode + , o.error_priority as priority + , o.errortype + from clfrm_builderrorlist(( + select cli.rid_adproclient + from cli + limit 1 + ), 0, 1) o + where m_exec_funcname in ('clfrm_builderrorlist') + union + select o.errordescription as errordescription, o.errorcode as errorcode, 1 as priority, 'error' as errortype + from clfrm_propcalc_errlist(( + select cli.rid_adproclient + from cli + limit 1 + )) o + where m_exec_funcname = 'm_exec_funcname' + + union + select o.errdescription as errordescription + , o.errorcode as errorcode + , o.severity as priority + , o.errtype as errortype + from upl_builduploaderrors(( + select cli.rid_adproclient + from cli + limit 1 + ) + , _bv(j_all_parms ->> 'p_approve_mode', j_all_parms ->> 'approve_mode', j_all_parms ->> 'mode', 'normal')) o + where + m_exec_funcname in ('upl_builduploaderrors') + union + select elm.j ->> 'errordescription' as errordescription + , elm.j ->> 'errorcode' as errorcode + , _try_integer(elm.j ->> 'priority', 1) as priority + , _bv(elm.j ->> 'errortype', 'error') as errortype + from ui_clientscenarion_errorlist(( + select sc.rid_clientscenario + from cli + inner join clientscenario sc on sc.rid_adproclient = cli.rid_adproclient + and sc.status = 1 + order by sc.rid_clientscenario desc + limit 1 + )) o + cross join jsonb_array_elements(case + when jsonb_typeof(o.p_data) = 'object' then jsonb_build_array(o.p_data) + when jsonb_typeof(o.p_data) = 'array' then o.p_data + else jsonb_build_array() + end) elm(j) + where m_exec_funcname in ('ui_clientscenarion_errorlist') + order by priority + ) + , composed as ( + select +-- src.errorcode +-- , src.errordescription +-- , src.priority as errorpriority +-- , src.errortype + row_number() over (partition by src.errorcode, src.errordescription order by src.priority desc) as rn + , evt2.rid_mastertaskitemevent as rid_mastertaskitemevent + , mti.rid_mastertaskitem as rid_mastertaskitem + , ti.rid_taskitem as rid_taskitem + , core.f_get_user_hub_rid() as rid_hub_user + , jsonb_build_object( + 'errordescription', src.errordescription + , 'errorcode', src.errorcode + , 'errortype', src.errortype + , 'errorpriority', src.priority + ) as jsonvalue + from src + --Get the main linked event + left outer join core.mastertaskitemevent ev1 on ev1.rid_mastertaskitem = r_taskitem.rid_mastertaskitem + and nv(ev1.errorcode) <> '' + and ( + ev1.errorcode = src.errorcode + or ev1.errorcode in ( + select m.interface_code + from core.error_code_map(jsonb_build_object('filter_code', src.errorcode)) m + where nv(m.interface_code) <> '' + ) + ) + left outer join core.mastertaskitemeventreaction er1 + on er1.rid_mastertaskitemevent = ev1.rid_mastertaskitemevent + -- Fallback to the global event list + left outer join core.mastertaskitemevent evt2 on ( + evt2.rid_mastertaskitemevent = + er1.rid_mastertaskitemevent_target + or evt2.status in (2) + and nv(evt2.errorcode) <> '' + and ( + evt2.errorcode = src.errorcode + or evt2.errorcode in ( + select m.interface_code + from core.error_code_map(jsonb_build_object('filter_code', src.errorcode)) m + where nv(m.interface_code) <> '' + ) + ) + ) + and nv(evt2.inactive) = 0 + left outer join core.mastertaskitem mti on mti.rid_mastertaskitem = evt2.rid_mastertaskitem + left outer join core.taskitem ti on ti.rid_mastertaskitem = mti.rid_mastertaskitem + and ti.rid_hub = r_taskitem.rid_hub + and nv(ti.rid_hub_link) = nv(r_taskitem.rid_hub_link) + and ti.status in (1, 2, 3) + ) + select jsonb_agg(_jsonb_object_cat(to_jsonb(mti), to_jsonb(ti), jsonb_strip_nulls(to_jsonb(c)))) + filter ( where c.rn = 1 ) + , count(1) + from composed c + left outer join core.mastertaskitem mti on mti.rid_mastertaskitem = c.rid_mastertaskitem + left outer join core.taskitem ti on ti.rid_taskitem = c.rid_taskitem + into j_tmp, m_retval; + + if m_retval > 0 + then + -- update core.taskitem u +-- set jsonvalue = jsonb_build_object('errorlist', j_tmp) +-- where u.rid_taskitem = r_taskitem.rid_taskitem; + + p_output = + jsonb_build_object('actionstatus', 'error', 'errorcode', 'ERRLIST', 'errorlist', j_tmp, 'delete_caller', + true); + else + p_output = + jsonb_build_object('message', null, 'actionstatus', 'success', 'errorcode', null, 'delete_after', true, + 'errorlist', null); + + end if; + + perform log_event(m_funcname, format(E'List Function (%s) Results Code=%s Error=%s. + MasterTaskItem:%s \n p_output:%s \nm_errdetail:%s' + , m_exec_funcname + , p_errorcode + , p_errmsg + , r_mastertaskitem.description + , p_output + , m_errdetail + ) + , bt_enum('eventlog', 'local error')); + + return; + + --m_retval =0; + + elsif exists ( + select ns.nspname, pro.* + from pg_proc pro + inner join pg_namespace ns on ns.oid = pro.pronamespace + and ns.nspname not in ('postgres', 'pg_catalog') + and ns.nspname = m_exec_schema + where pro.proname = m_exec_funcname + ) + then + with + args as ( + select ns.nspname + , pro.proname + , t.typname + , i.i + , pro.proargnames[i - 1] as argname + , pro.proargmodes[i - 1] as mode + , CASE + WHEN pro.proargdefaults IS NOT NULL + AND i.i - 1 > (pro.pronargs - pro.pronargdefaults) + THEN split_part( + pg_get_expr(pro.proargdefaults, 0), + ',', + i.i - 1 - (pro.pronargs - pro.pronargdefaults) + )::text + ELSE NULL::text + END as default_value + , row_number() over (order by i.i) as number + --,pro.* + from pg_proc pro + inner join pg_namespace ns on ns.oid = pro.pronamespace + and ns.nspname not in ('postgres', 'pg_catalog') + JOIN LATERAL generate_subscripts(pro.proallargtypes, 1) AS i ON true + JOIN pg_type t ON t.oid = pro.proallargtypes[i - 1] + where + pro.proname = m_exec_funcname + and ns.nspname = m_exec_schema + --pro.proname in ('hsync_upload_clienttohyphen') + AND (pro.proargmodes IS NULL OR pro.proargmodes[i - 1] in ('i', 'b')) + ) + , argval as ( + select args.* + --, _bv(j_all_parms ->> args.argname,args.default_value) as argvalue + , case + when nv(j_all_parms ->> args.argname) in ('', '0', 'null') + and args.argname ilike 'p_%' and + nv(j_all_parms ->> replace(args.argname, 'p_', '')) not in ('', '0', 'null') + then j_all_parms ->> replace(args.argname, 'p_', '') + + when nv(j_all_parms ->> args.argname) <> '' then j_all_parms ->> args.argname + when args.argname ilike 'p_%' and nv(j_all_parms ->> replace(args.argname, 'p_', '')) <> '' + then j_all_parms ->> replace(args.argname, 'p_', '') + when args.argname ilike 'p_login%' and j_all_parms ->> 'login' is not null + then j_all_parms ->> 'login' + when args.argname ilike 'p_user%' and j_all_parms ->> 'login' is not null + then j_all_parms ->> 'login' + + when nv(args.default_value) <> '' then args.default_value + else null::text + end as argvalue + from args + + ) + select ( + select string_agg(format('%s::%s', case + when a.typname ilike '%int%' then a.argvalue + else quote_literal(a.argvalue) + end, a.typname), ', ') as args + from argval a + ) + , ( + select count(1) + from argval a + where a.argvalue is null + ) + , ( + select format('[ECR0002] Cannot execute function %s. The following arguments are missing: %s (Defaults: %s) ', + m_exec_funcname, string_agg(a.argname, ','), string_agg(a.default_value, ',')) + from argval a + where a.argvalue is null + ) + into m_sql,m_retval, m_errmsg, m_errcode; + + raise notice 'Function % executed with arguments: %', m_exec_funcname, m_sql; + + if m_retval > 0 + then + p_retval = 1; + p_errmsg = m_errmsg; + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + perform log_event(m_funcname, format( + E'Error on Function (%s) Code=%s Error=%s. MasterTaskItem:%s \n SQL:%s \n j_all_parms:%s \n Controls: %s' + , m_exec_funcname + , p_errorcode + , p_errmsg + , r_mastertaskitem.description + , m_sql + , substr((j_all_parms || jsonb_build_object('AOP', null))::text, 1, 100000) + , j_controls::text + ) + , bt_enum('eventlog', 'local error')); + + return; + end if; + + m_sql = format('select to_jsonb(r) as val from %s.%s(%s) r ;', m_exec_schema, m_exec_funcname, m_sql); + + perform log_event(m_funcname, format('Executing Function (%s) with SQL: %s', m_exec_funcname, m_sql), + bt_enum('eventlog', 'local notice')); + + if m_exec_funcname::citext = any (a_broker_procs) + then + select r.p_retval, r.p_errmsg + from agent_job_add(format('exec %s', m_sql) + , get_agent_freequeue(3) + , format('%s(taskitemevent=%s)', m_exec_funcname, r_taskitemevent.rid_taskitemevent) + , 15 + ) r + into m_retval,m_errmsg; + + elsif m_exec_funcname::citext = any (a_async_procs) + then + select _bv(j.val -> 'val', j.val) + from autoexec_query(m_sql, jsonb_build_object()) j(val) + into j_output; + + else + execute m_sql into j_output; + end if; + + p_output = j_output; + + if p_event_parameters ->> 'no_error' in ('1', 'true') or p_item_parameters ->> 'no_error' in ('1', 'true') + then + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + perform log_event(m_funcname, format('Error(!) on Function (%s) Code=%s Error=%s. MasterTaskItem:%s Response:%s' + , m_exec_funcname + , p_errorcode + , p_errmsg + , r_mastertaskitem.description + , substr(j_output::text, 1, 10000) + ) + , bt_enum('eventlog', 'local error')); + + return; + end if; + + if jsonb_typeof(j_output) in ('number', 'boolean', 'string') + then + perform log_event(m_funcname, + format('Function (%s) returned scalar value: %s', m_exec_funcname, j_output::text), + bt_enum('eventlog', 'debug')); + + if j_all_parms -> 'p_output' is not null + and (j_all_parms ->> 'p_output')::citext is distinct from j_output::citext + then + p_retval = 1; + p_errmsg = + 'Output value does not match expected value. Expected: ' || j_all_parms ->> 'p_output' || ' Actual: ' || + j_output::text; + return; + + end if; + end if; + + if _try_integer(j_output ->> 'p_retval', 0) > 0 + and m_exec_funcname::citext <> all (a_noerror_procs) + then + p_retval = _try_integer(j_output ->> 'p_retval', 0); + p_errmsg = format('%s', j_output ->> 'p_errmsg'); + p_errorcode = _bv(j_output ->> 'p_error_code', j_output ->> 'p_errorcode', ''); + + + perform log_event(m_funcname, format('Error on Function (%s) Code=%s Error=%s. MasterTaskItem:%s Response:%s' + , m_exec_funcname + , p_errorcode + , p_errmsg + , r_mastertaskitem.description + , substr(j_output::text, 1, 10000) + ) + , bt_enum('eventlog', 'local error')); + + + if nv(p_errorcode) = '' + then + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + if nv(p_errorcode) = '' + then + p_errorcode = 'ECR0003'; + end if; + end if; + return; + end if; + + else + perform log_event(m_funcname, format('Function (%s) not defined for this event (%s).', + p_event_parameters -> 'AFN_RID_ACTIONFUNCTION_CODE' ->> 'functionname' + , r_mastertaskitem.description) + , bt_enum('eventlog', 'local error')); + + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + return; + end if; + + + else + raise notice 'Type not supported: %', p_event_parameters -> 'AFN_RID_ACTIONFUNCTION_CODE' ->> 'fntype'; + end if; + + --select * from core.actionfunction + if nv(p_errorcode) = '' and nv(p_errmsg) <> '' + then + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + if nv(p_errorcode) = '' + then + p_errorcode = 'PRCERR'; + end if; + 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; + if nv(p_errorcode) = '' and nv(p_errmsg) <> '' + then + select m.v[1] + from regexp_matches(p_errmsg, '\[([^\]]+)\]', 'ig') m(v) + where length(m.v[1]) between 2 and 10 + order by m.v[1] + into p_errorcode; + + if nv(p_errorcode) = '' + then + p_errorcode = 'PRCERR'; + end if; + end if; +END; +$$; + + +--Tests +/* +select (regexp_matches('[ECR0001] No AFN inside the AOP pack.', '\[([^\]]+)\]'))[1] +, (regexp_matches('[21] No AFN inside the AOP pack. [45345]', '\[([^\]]+)\]'))[1] +, (regexp_matches('A Test', '\[([^\]]+)\]'))[1] + */ +/* +select * +from core.taskitemevent e +where e.rid_taskitem = 26344 +limit +;*/ + +/* +select * +from core.event_exec_func(( + select _jsonb_object_cat( to_jsonb(e) + ,jsonb_build_object('AFN_RID_ACTIONFUNCTION_CODE',to_jsonb(af))) + from core.taskitemevent e + inner join core.taskitem ti on ti.rid_taskitem = e.rid_taskitem + inner join core.actionoption ao on ao.guid = ti.jsonvalue -> 'AOP' ->> 'guid' + inner join core.actionfunction af on af.rid_actionfunction = ao.rid_actionfunction_code + where e.rid_taskitem = 26344 + ),(select _jsonb_object_cat( to_jsonb(e) + ,jsonb_build_object('AFN_RID_ACTIONFUNCTION_CODE',to_jsonb(af))) + from core.taskitem e + inner join core.mastertaskitem mti + on mti.rid_mastertaskitem = e.rid_mastertaskitem + inner join core.actionoption ao on ao.guid = e.jsonvalue -> 'AOP' ->> 'guid' + inner join core.actionfunction af on af.rid_actionfunction = ao.rid_actionfunction_code + where e.rid_taskitem = 26344 + )) + + +selecT * from v_eventlog + +select * from core.taskitem ti +order by ti.rid_taskitem desc + + */ diff --git a/testdata/corpus/5795536_create_core.event_created.pgsql b/testdata/corpus/5795536_create_core.event_created.pgsql new file mode 100755 index 0000000..0d5d00e --- /dev/null +++ b/testdata/corpus/5795536_create_core.event_created.pgsql @@ -0,0 +1,1105 @@ +--select * from dropall('core.event_created'); +CREATE OR REPLACE FUNCTION core.event_created( + p_rid_taskitemevent integer +, INOUT p_parms jsonb DEFAULT json_build_object() +, OUT p_retval integer +, OUT p_errmsg text +) + LANGUAGE plpgsql + VOLATILE + SECURITY DEFINER +AS +$$ +DECLARE + --Error Handling-- + m_funcname text = 'core.event_created'; + m_errmsg text; + m_errcode text; + m_errcontext text; + m_errdetail text; + m_errhint text; + m_errstate text; + m_retval integer; + --Error Handling-- + r_taskitemevent core.taskitemevent % rowtype; + r_taskitem core.taskitem % rowtype; + r_taskitem_parent core.taskitem % rowtype; + r_mastertaskitemevent core.mastertaskitemevent % rowtype; + r_mastertaskitem core.mastertaskitem % rowtype; + m_rid_mastertaskitemevent integer; + m_skip_checks boolean := false; + m_rid_taskitem integer; + m_rid_taskitem_parent_error integer; + m_allow_handlers boolean; + m_new_task boolean; + m_rollback boolean := false; + m_delete_self boolean := false; + a_rid_taskitem integer[]; + r_lp record; + r_lp2 record; + j_exec_options jsonb; +BEGIN + p_retval = 0; + p_errmsg = ''; + m_skip_checks = p_parms ->> 'skip_checks' in ('1', 'true', 'yes', 'on', 'y'); + m_allow_handlers = false; + + select * + from core.taskitemevent tie + where tie.rid_taskitemevent = p_rid_taskitemevent + into r_taskitemevent; + + select * + from core.taskitem ti + where ti.rid_taskitem = r_taskitemevent.rid_taskitem + into r_taskitem; + + --perform log_event(m_funcname,format('init: id:%s status:%s name:%s for %s (%s)',r_taskitemevent.rid_taskitemevent,r_taskitemevent.status, r_taskitemevent.description,r_taskitem.description, r_taskitemevent.rid_taskitem),bt_enum('eventlog','debug')); + + if not m_skip_checks + and nv(r_taskitemevent.retval) = 0 + and r_taskitemevent.duedatetime is not null and r_taskitemevent.duedatetime > '2000-01-01'::date + and r_taskitemevent.duedatetime > now() + then + raise notice 'Event % not yet due: %',r_mastertaskitemevent.description, r_taskitemevent.duedatetime; + return; + end if; + + select * + from core.mastertaskitemevent mte + where mte.rid_mastertaskitemevent = r_taskitemevent.rid_mastertaskitemevent + into r_mastertaskitemevent; + + select * + from core.taskitem ti + where ti.rid_taskitem = r_taskitemevent.rid_taskitem + into r_taskitem; + + select * + from core.taskitem ti + where ti.rid_taskitem = r_taskitem.rid_taskitem_parentaction + into r_taskitem_parent; + + if r_mastertaskitemevent.inactive > 0 + then + raise exception 'The template task item event is inactive. rid_mastertaskitemevent = %',r_mastertaskitemevent.rid_mastertaskitemevent; + end if; + + select * + from core.mastertaskitem mti + where mti.rid_mastertaskitem = r_mastertaskitemevent.rid_mastertaskitem + into r_mastertaskitem; + + -- Begin Execution + begin + if nv(r_taskitemevent.retval) = 0 or m_skip_checks + then + + -- Update early so that re-entrance is avoided and status changes are visible + update core.taskitemevent evt + set retval = 1 + where evt.rid_taskitemevent = r_taskitemevent.rid_taskitemevent; + + -- Run event action status changes + --raise notice 'State Change for Task Item % to Status %',r_taskitem.rid_taskitem, _bv(r_taskitemevent.status, r_mastertaskitemevent.status); + + update core.taskitem u + set + status = case + when r_taskitemevent.status in (14, 13, 9, 8) + then u.status + else _bv(r_taskitemevent.status, r_mastertaskitemevent.status) + end + , rid_taskitemevent = coalesce(r_taskitemevent.rid_taskitemevent, u.rid_taskitemevent) + where + u.rid_taskitem = r_taskitem.rid_taskitem + and u.status is distinct from r_taskitemevent.status + and rid_taskitemevent is distinct from r_taskitemevent.rid_taskitemevent; + + -- perform log_event(m_funcname,format('Processing Event: %s Status=%s(%s) For %s (status:%s id:%s) noexec:%s' +-- ,r_taskitemevent.description,r_taskitemevent.status,r_taskitemevent.rid_taskitemevent, r_taskitem.description,r_taskitem.status +-- , r_taskitem.rid_taskitem, r_taskitem.jsonvalue->>'noexec'),bt_enum('eventlog','local notice')); + + m_allow_handlers = true; + + if r_taskitemevent.status = 10 + then + m_rid_mastertaskitemevent = 0; + if r_taskitem.rid_taskitem_parentaction > 0 + and exists ( + select 1 + from core.taskitem ti + where + ti.rid_taskitem = r_taskitem.rid_taskitem_parentaction + and ti.status not in (4, 10, 7, 12, 6) + ) + then + update core.taskitem u + set status = 7 + where u.rid_taskitem = r_taskitem.rid_taskitem_parentaction; + end if; + + elsif r_taskitemevent.status = 11 + then + update core.taskitem u + set jsonvalue = _jsonb_object_cat(u.jsonvalue, jsonb_build_object('noexec', null)) + where u.rid_taskitem = r_taskitem.rid_taskitem; + + m_allow_handlers = true; + + elsif r_taskitemevent.status = 4 + then + if nv(r_taskitem.jsonvalue ->> 'noexec') in ('true', '1') + then + perform log_event(m_funcname, format( + 'Skipping execution of Event Action due to noexec flag set on Task Item. Event: %s Task Item: %s', + r_taskitemevent.rid_taskitemevent, r_taskitem.rid_taskitem), bt_enum('eventlog', 'debug')); + m_allow_handlers = false; + else + if nv(r_mastertaskitem.functionname_code) <> '' + then + + select r.p_retval, r.p_errmsg, r.p_errorcode, r.p_output + from core.event_exec_func(to_jsonb(r_taskitemevent), + _jsonb_object_cat( + r_taskitem.jsonvalue + , r_taskitemevent.jsonvalue + , jsonb_build_object( + 'rid_taskitemevent', r_taskitemevent.rid_taskitemevent + , 'rid_mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , 'rid_mastertaskitem', r_mastertaskitem.rid_mastertaskitem + , 'rid_taskitem', r_taskitemevent.rid_taskitem + , 'rid_hub', r_taskitem.rid_hub + , 'rid_hub_link', r_taskitem.rid_hub_link + , 'source', 'actual' + )) + ) r + into m_retval,m_errmsg, m_errcode, j_exec_options; + + m_rollback = j_exec_options ->> 'delete_after' in ('1', 'true'); + m_delete_self = j_exec_options ->> 'delete_caller' in ('1', 'true'); --this is used to roll back the calling action and only show errors. + + perform log_event(m_funcname, + format('EXEC RESULTS (%s): Rollback: %s OPTIONS:%s Error: %s', m_retval, m_rollback, + j_exec_options::text, m_errmsg), bt_enum('eventlog', 'debug')); + + if m_retval > 0 + then + m_allow_handlers = false; + if nv(m_errcode) = '' + then + m_errcode = 'PRCERR'; + end if; + + update core.taskitemevent u + set + errmsg = m_errmsg + , retval = 3 + where u.rid_taskitemevent = r_taskitemevent.rid_taskitemevent; + + update core.taskitem u + set + status = 10 + , jsonvalue = _jsonb_object_cat(u.jsonvalue + , jsonb_build_object('message', m_errmsg, 'errorcode', m_errcode + , '_errorcode', m_errcode + --,'in_modal',true + , 'show_alert', m_errmsg + ), j_exec_options) + where u.rid_taskitem = r_taskitem.rid_taskitem; + + + return; + + end if; + + end if; + + delete + from core.taskitem ti + where + ti.rid_taskitem_parentaction = r_taskitem.rid_taskitem + -- and ti.rid_taskitem is distinct from r_taskitem.rid_taskitem + and nv(ti.status) in (0, 1); + + end if; + ------------------------------------------------------------------------------------------------------------------ + elsif r_taskitemevent.status = 12 --cancellation + then + m_allow_handlers = true; + + update core.taskitem u + set status = 12 + where + u.rid_taskitem = r_taskitem.rid_taskitem + or u.rid_taskitem = r_taskitem.rid_parenttaskitem; + + update core.tasklist u + set status = 12 + where + u.rid_tasklist = r_taskitem.rid_tasklist + and nv(u.status) not in (12, 4); + + if r_taskitem.rid_taskitem_parentaction > 0 + then + select er.rid_mastertaskitemevent + from core.taskitem ti + inner join core.mastertaskitemevent er on er.rid_mastertaskitem = ti.rid_mastertaskitem + and nv(er.inactive) = 0 + and er.status = 12 + where ti.rid_taskitem = r_taskitem.rid_taskitem_parentaction + limit 1 + into m_rid_mastertaskitemevent; + + if m_rid_mastertaskitemevent > 0 + then + insert + into core.taskitemevent(rid_taskitem, status, description, rid_mastertaskitemevent, jsonvalue) + select r_taskitem.rid_taskitem_parentaction + , 12 + , format('Task Canceled due to cancellation of sub task %s', r_taskitem.rid_taskitem) + , m_rid_mastertaskitemevent + , jsonb_build_object('message', 'Task Canceled due to cancellation of sub task') + where + not exists ( + select 1 + from core.taskitemevent tie + where + tie.rid_taskitem = r_taskitem.rid_taskitem_parentaction + and tie.status = 12 + and tie.rid_mastertaskitemevent = m_rid_mastertaskitemevent + ); + --Post cancelation event + + end if; + end if; + + m_rid_taskitem = 0; + + delete + from core.taskitem ti + where + ti.rid_tasklist = r_taskitem.rid_tasklist + -- and ti.rid_taskitem is distinct from r_taskitem.rid_taskitem + and nv(ti.status) in (2, 1, 3, 6, 12, 0, 14, 13); + + ------------------------------------------------------------------------------------------------------------------ + --- Abort Task --- + elsif r_taskitemevent.status = 6 + then + + m_allow_handlers = true; + + select ti.rid_taskitem + , array [ti.rid_taskitem]::integer[] + from core.taskitem ti + where + ti.rid_taskitem = r_taskitem.rid_taskitem_parentaction + and ti.status not in (10, 7) + and nv(ti.jsonvalue ->> '_errorcode') <> '' + into m_rid_taskitem, a_rid_taskitem; + + if nv(m_rid_taskitem) = 0 + then + select ti2.rid_taskitem + , array [ti.rid_taskitem]::integer[] + from core.taskitem ti + inner join core.taskitem ti2 on ti2.rid_taskitem = ti.rid_taskitem_parentaction + where + ti.rid_taskitem = r_taskitem.rid_taskitem_parentaction + and ti2.status not in (10, 7) + into m_rid_taskitem, a_rid_taskitem; + + if nv(m_rid_taskitem) = 0 + then + select ti3.rid_taskitem + , array [ti2.rid_taskitem,ti.rid_taskitem]::integer[] + from core.taskitem ti + inner join core.taskitem ti2 on ti2.rid_taskitem = ti.rid_taskitem_parentaction + inner join core.taskitem ti3 on ti3.rid_taskitem = ti2.rid_taskitem_parentaction + where + ti.rid_taskitem = r_taskitem.rid_taskitem_parentaction + and ti2.status not in (10, 7) + into m_rid_taskitem, a_rid_taskitem; + + end if; + + -- if nv(m_rid_taskitem) = 0 + -- then + -- select ti.rid_taskitem + -- from core.taskitem ti + -- where ti.rid_tasklist = r_taskitem.rid_tasklist + -- and nv(ti.status) not in ( + -- 10 + -- ,6 + -- ,12 + -- ) + -- and ti.rid_taskitem is distinct from r_taskitem.rid_taskitem + -- and ti.rid_mastertaskitem is distinct from r_taskitem.rid_mastertaskitem + -- order by ti.seqno nulls last + -- limit 1 + -- into m_rid_taskitem; + -- + -- end if; + end if; + + -- delete from core.taskitem ti + -- where ti.rid_tasklist = r_taskitem.rid_tasklist + -- and ti.rid_taskitem is distinct from m_rid_taskitem + -- and ti.status in ( 2 , 1,3 , 6, 12,0,14,13); + -- + + delete + from core.taskitem d + where d.rid_taskitem = any (a_rid_taskitem); + + if nv(m_rid_taskitem) > 0 + then + update core.taskitem u + set status = 2 + where u.rid_taskitem = m_rid_taskitem; + + select mtie.rid_mastertaskitemevent + from core.taskitem ti + inner join core.mastertaskitem mti on ti.rid_mastertaskitem = mti.rid_mastertaskitem + inner join core.mastertaskitemevent mtie on mti.rid_mastertaskitem = mtie.rid_mastertaskitem + and mtie.status = 2 + where ti.rid_taskitem = m_rid_taskitem + limit 1 + into m_rid_mastertaskitemevent; + + if m_rid_mastertaskitemevent > 0 + then + select r.p_retval, r.p_errmsg, r.p_parms + from core.taskitem ti + cross join core.action_init( + 'mastertaskitemevent', + m_rid_mastertaskitemevent, + _jsonb_object_cat(jsonb_build_object( + 'parent_rid_taskitemevent', r_taskitemevent.rid_taskitemevent + , 'parent_rid_taskitem', m_rid_taskitem + , 'rid_tasklist', ti.rid_tasklist + , 'rid_hub', ti.rid_hub + , 'rid_hub_link', ti.rid_hub_link + ) + ) + ) r + where ti.rid_taskitem = m_rid_taskitem + into m_retval, m_errmsg; + + if m_retval > 0 + then + raise exception '%s',m_errmsg; + end if; + end if; + end if; + + + elsif r_taskitemevent.status = 13 + and nv(r_mastertaskitem.jsonvalue ->> 'errorcode')::citext = 'ERRLIST'::citext + then + if nv(r_taskitem.jsonvalue ->> 'noexec') in ('true', '1') + then + --perform log_event(m_funcname,format('Skipping execution of Event Action due to noexec flag set on Task Item. Event: %s Task Item: %s', r_taskitemevent.rid_taskitemevent, r_taskitem.rid_taskitem),bt_enum('eventlog', 'debug')); + m_allow_handlers = false; + else + if nv(r_mastertaskitem.functionname_code) <> '' + then + + select r.p_retval, r.p_errmsg, r.p_errorcode, r.p_output + from core.event_exec_func(to_jsonb(r_taskitemevent), + _jsonb_object_cat( + r_taskitem.jsonvalue + , r_taskitemevent.jsonvalue + , jsonb_build_object( + 'rid_taskitemevent', r_taskitemevent.rid_taskitemevent + , 'rid_mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , 'rid_mastertaskitem', r_mastertaskitem.rid_mastertaskitem + , 'rid_taskitem', r_taskitemevent.rid_taskitem + , 'rid_hub', r_taskitem.rid_hub + , 'rid_hub_link', r_taskitem.rid_hub_link + , 'source', 'actual' + )) + ) r + into m_retval,m_errmsg, m_errcode, j_exec_options; + + m_rollback = j_exec_options ->> 'delete_after' in ('1', 'true'); + m_delete_self = j_exec_options ->> 'delete_caller' in ('1', 'true'); + --this is used to roll back the calling action and only show errors. + + --perform log_event(m_funcname,format('EXEC RESULTS (%s): Rollback: %s OPTIONS:%s Error: %s', m_retval,m_rollback, j_exec_options::text,m_errmsg), bt_enum('eventlog', 'debug')); + + if m_retval > 0 + then + m_allow_handlers = false; + if nv(m_errcode) = '' + then + m_errcode = 'ERRLIST'; + end if; + + update core.taskitemevent u + set + errmsg = m_errmsg + , retval = 3 + where u.rid_taskitemevent = r_taskitemevent.rid_taskitemevent; + + update core.taskitem u + set + status = 10 + , jsonvalue = _jsonb_object_cat(u.jsonvalue + , jsonb_build_object('message', m_errmsg, 'errorcode', m_errcode + , '_errorcode', m_errcode + --,'in_modal',true + , 'show_alert', m_errmsg + ), j_exec_options) + where u.rid_taskitem = r_taskitem.rid_taskitem; + + + return; + + end if; + + end if; + + delete + from core.taskitem ti + where + ti.rid_taskitem_parentaction = r_taskitem.rid_taskitem + -- and ti.rid_taskitem is distinct from r_taskitem.rid_taskitem + and nv(ti.status) in (0, 1); + + end if; + + end if; + + --Mark Event as executed. + update core.taskitemevent evt + set + retval = 2 + , errmsg = null + where evt.rid_taskitemevent = r_taskitemevent.rid_taskitemevent; + + if j_exec_options ->> 'actionstatus' in ('error', 'errors') + then + update core.taskitem u + set + status = 10 + , jsonvalue = _jsonb_object_cat(u.jsonvalue, jsonb_build_object('alerttype', 'error'), j_exec_options) + , rid_taskitemevent = r_taskitemevent.rid_taskitemevent + where u.rid_taskitem = r_taskitem.rid_taskitem; + + m_allow_handlers = false; + elsif j_exec_options ->> 'actionstatus' = 'success' + and r_taskitem.status not in (4, 6) + then + m_allow_handlers = true; + + update core.taskitem u + set + status = 4 + , rid_taskitemevent = r_taskitemevent.rid_taskitemevent + , jsonvalue = _jsonb_object_cat(u.jsonvalue, j_exec_options) + where u.rid_taskitem = r_taskitem.rid_taskitem; + + else + + update core.taskitem u + set + status = case + when exists ( + select 1 + from core.taskitem ti2 + where + ti2.rid_taskitem_parentaction = u.rid_taskitem + and ti2.status in (3, 7) + ) + then u.status + when r_taskitemevent.status in (14, 13, 9, 8) + then u.status + else r_taskitemevent.status + end + , rid_taskitemevent = r_taskitemevent.rid_taskitemevent + where u.rid_taskitem = r_taskitem.rid_taskitem; + + end if; + + --Handle task status events + if _try_integer(r_mastertaskitemevent.jsonvalue ->> 'taskstatus', 0) in (1, 2) + then + update core.tasklist u + set + status = case + when _try_integer(r_mastertaskitemevent.jsonvalue ->> 'taskstatus', 0) = 1 + then 4 + when _try_integer(r_mastertaskitemevent.jsonvalue ->> 'taskstatus', 0) = 2 + then 12 + else u.status + end + where + u.rid_tasklist = r_taskitem.rid_tasklist + and u.status not in (4, 12); + + m_new_task = true; + + end if; + else + --When the event was already executed, just log and update status if needed + m_allow_handlers = false; + perform log_event(m_funcname, + format('Skip Event ("%s",%s,%s,%s "%s") Skip: %s Status: rid_taskitem=%s status=%s', + r_taskitemevent.description, r_taskitemevent.rid_taskitemevent, r_taskitemevent.status, + r_taskitemevent.retval, r_taskitem.description, m_skip_checks, + r_taskitemevent.rid_taskitem, + r_taskitemevent.status + ), bt_enum('eventlog', 'debug')); + + -- if r_taskitemevent.rid_taskitemevent > 0 + -- and r_taskitemevent.status = 4 + -- then + -- --Mark Event as executed. + -- update core.taskitem u + -- set + -- status = r_taskitemevent.status + -- , rid_taskitemevent = r_taskitemevent.rid_taskitemevent + -- where u.rid_taskitem = r_taskitemevent.rid_taskitem + -- and u.status is distinct from r_taskitemevent.status + -- and u.rid_taskitemevent is distinct from r_taskitemevent.rid_taskitemevent + -- ; + -- + -- end if; + + end if; + + select * + from core.taskitem ti + where ti.rid_taskitem = r_taskitem.rid_taskitem + into r_taskitem; + + --No sure about this, I don't like it. + if m_delete_self + then + update core.taskitem u + set + rid_parenttaskitem = p.rid_taskitem_parentaction + , rid_taskitem_parentaction = p.rid_taskitem_parentaction + from core.taskitem p + where + u.rid_taskitem = r_taskitem.rid_taskitem + and p.rid_taskitem = r_taskitem.rid_taskitem_parentaction; + + delete + from core.taskitem d + where d.rid_taskitem = r_taskitem.rid_parenttaskitem; + + return; + end if; + + -------------------------------------------- Run event reactions ------------------------------------------------------ + if m_allow_handlers + then + + for r_lp in ( + select er.rid_mastertaskitemevent_target + , er.rid_mastertaskitemeventreaction + , met.* + , mti.description as mti_description + , mti.rid_mastertask as rid_mastertask_target + ---rid_mastertaskitem is target + , core._mt(mti.rid_mastertype_hubtype) as target_hubtype + , ( + select h.hubtype from core.hub h where h.rid_hub = r_taskitem.rid_hub limit 1 + ) as source_hubtype + , er.parenteventrequired + , jsonb_build_object() as cumulated_parms + , null::integer as items_todo + , null::integer[] as items_todo_rid_events + , null::integer as items_open + , null::integer as items_done + , null::boolean as skip + , er.jsonvalue ->> 'linktask' in ('true', '1', 't') as linktask + , er.jsonvalue ->> 'show_in_wizard' in ('true', '1', 't') as show_in_wizard + , er.jsonvalue ->> 'allow_rollback' in ('true', '1', 't') as allow_rollback + , nv(r_taskitem.jsonvalue ->> 'errorcode') <> '' as has_error + , _try_integer(er.jsonvalue ->> 'rid_mastertaskoption', 0) as rid_mastertaskoption_event + , m_new_task as new_task + , row_number() + over (order by er.seqno nulls last,met.status, met.seqno nulls last, met.rid_mastertaskitemevent) as rn + from core.mastertaskitemeventreaction er + inner join core.mastertaskitemevent met on met.rid_mastertaskitemevent = er.rid_mastertaskitemevent_target + and nv(met.inactive) = 0 + inner join core.mastertaskitem mti on mti.rid_mastertaskitem = met.rid_mastertaskitem + and nv(mti.inactive) = 0 + left outer join core.actionoption aop on aop.rid_actionoption = mti.rid_actionoption + where + er.rid_mastertaskitemevent = r_mastertaskitemevent.rid_mastertaskitemevent + and er.rid_mastertaskitemevent_target > 0 + order by + case when _try_integer(met.jsonvalue ->> 'taskstatus', 0) in (1, 2) then 99 else 1 end + , case + when coalesce(met.status, 0) in (0, 1, 2) then 10 + when met.status in (4) then 50 + when met.status in (11) then 40 + when met.status in (3, 10) then 20 + else 15 + end + , er.seqno, met.seqno, mti.seqno, met.status, met.rid_mastertaskitemevent + ) + loop + SET session_replication_role = DEFAULT; + perform log_event(m_funcname, format('Event-Reaction "%s"[%s,%s] -> "%s"[%s] for HUB(%s,%s)' + , r_taskitem.description, r_taskitemevent.description, r_taskitemevent.status + , r_lp.mti_description, r_lp.description, r_lp.status + , r_taskitem.rid_hub, r_taskitem.rid_hub_link), bt_enum('eventlog', 'debug')); + -- perform log_event(m_funcname, format('EVENT REACTION DEBUG: %s (%s) [%s] <- %s (%s) [%s, %s] Codes: a=%s, b=%s (%s)' + -- ,r_lp.description, r_lp.rid_mastertaskitemevent, r_lp.mti_description, r_taskitemevent.description + -- ,r_taskitemevent.rid_taskitemevent, r_taskitem.description, r_taskitem.status + -- ,r_lp.interface_error_code, r_lp.actual_interface_code, r_taskitem.jsonvalue ->> 'errorcode' + -- ), bt_enum('eventlog', 'debug')); + + if r_lp.new_task and exists ( + select 1 + from core.taskitem t + where + t.rid_mastertaskitem = r_lp.rid_mastertaskitem + and t.rid_hub = r_taskitem.rid_hub + and nv(t.rid_hub_link) = nv(r_taskitem.rid_hub_link) + and t.rid_tasklist = r_taskitem.rid_tasklist + and t.status not in (4, r_lp.status) + ) + then + perform log_event(m_funcname, format( + 'Skipping Event Reaction due to new_task flag and already existing open task: %s -> %s', + r_lp.rid_mastertaskitemevent, + r_lp.description), bt_enum('eventlog', 'debug')); + r_lp.new_task = false; + end if; + + --Determine target task items to run the event reaction on + for r_lp2 in ( + select * + from ( + select row_number() over (partition by r.rid_hub, r.rid_hub_link + order by r.rid_taskitem_parentaction, r.rid_parenttaskitem,r.rid_taskitem desc + ) as seqno + , r.* + from ( + ---Fire to self, same hub/link + select r_taskitem.rid_hub + , r_taskitem.rid_hub_link as rid_hub_link + , r_taskitem.rid_taskitem + , 'self' as source + , r_taskitem.rid_tasklist + , r_taskitem.rid_taskitem as rid_parenttaskitem --If we recreate ourselves, we are the parent + , r_taskitem.rid_taskitem_parentaction + , r_lp.source_hubtype as hubtype + + union + --- Fire to hub links other hub type + select h2.rid_hub + , hl.rid_hub_link + , null::integer as rid_taskitem + , 'hublink' as source + , r_taskitem.rid_tasklist + , r_taskitem.rid_taskitem as rid_parenttaskitem -- if we fire to ourselves, we are the parent + , r_taskitem.rid_taskitem_parentaction + , h2.hubtype as hubtype + from core.hub_link hl + inner join core.hub h2 on h2.rid_hub = hl.rid_hub_child + and h2.hubtype = r_lp.target_hubtype + where + hl.rid_hub_link = r_taskitem.rid_hub_link + and r_taskitem.rid_hub_link > 0 + union + --- Fire to hub links other hub type if not same as parent + select h2.rid_hub + , hl.rid_hub_link + , null::integer as rid_taskitem + , 'hublink' as source + , r_taskitem.rid_tasklist + , r_taskitem.rid_taskitem -- if we fire to ourselves, we are the parent + , r_taskitem.rid_taskitem_parentaction + , h2.hubtype as hubtype + from core.hub_link hl + inner join core.hub h2 on h2.rid_hub = hl.rid_hub_parent + and h2.hubtype = r_lp.target_hubtype + where + hl.rid_hub_link = r_taskitem.rid_hub_link + and r_taskitem.rid_hub_link > 0 + + + ) r + ) r2 + where r2.seqno = 1 + ) + loop + r_lp.skip = false; + + + if r_lp.parenteventrequired > 0 and not r_lp.skip + then + --- We count the distinct setup items, including self + select count(distinct mti1.rid_mastertaskitem) + , array_agg(mte.rid_mastertaskitemevent) + from core.mastertaskitemeventreaction evr --ALl the targets (the place where we are reacting to) + inner join core.mastertaskitemevent mte on mte.rid_mastertaskitemevent = evr.rid_mastertaskitemevent + inner join core.mastertaskitem mti1 on mti1.rid_mastertaskitem = mte.rid_mastertaskitem + where + evr.rid_mastertaskitemevent_target = r_lp.rid_mastertaskitemevent_target + and evr.parenteventrequired > 0 + into r_lp.items_todo, r_lp.items_todo_rid_events; + + --Count actual done/open items + select count(distinct ti.rid_taskitem) filter ( where ti.status = mte.status + or ti.status = 3 and mte.status = 4 + ) as done_count + , count(distinct ti.rid_taskitem) filter ( where nv(ti.status) <> mte.status ) as open_count + from core.mastertaskitemevent mte + inner join core.taskitem ti on ti.rid_mastertaskitem = mte.rid_mastertaskitem + and ti.rid_hub = r_lp2.rid_hub + and nv(ti.rid_hub_link) = nv(r_lp2.rid_hub_link) + left outer join core.taskitemevent evt on evt.rid_mastertaskitemevent = mte.rid_mastertaskitemevent + and evt.rid_taskitem = ti.rid_taskitem + where mte.rid_mastertaskitemevent = any (r_lp.items_todo_rid_events) + into r_lp.items_done, r_lp.items_open; + + if r_lp.items_todo > 0 and r_lp.items_open > 0 + and r_lp.items_done < r_lp.items_open + then + r_lp.skip = true; + end if; + end if; + + perform log_event(m_funcname, format( + E'%s Event Reaction [O:%s][D/T:%s/%s] (%s[%s<-%s,%s,%s]) on Item(%s (%s,%s) Event %s:%s Status:%s) \n--> Target Event: (%s, %s, %s) Item: (%s %s,%s) Events(%s) rid_taskitem_parent_error =%s ', + case when r_lp.skip then 'Skipping' else 'Running' end + , r_lp.items_open, r_lp.items_done, r_lp.items_todo + , r_lp2.source + , r_lp.target_hubtype + , r_lp.source_hubtype + , r_lp2.rid_hub + , r_lp2.rid_hub_link + , r_taskitem.description, r_taskitem.rid_taskitem, r_taskitem.rid_parenttaskitem + , r_mastertaskitemevent.description, r_mastertaskitemevent.rid_mastertaskitemevent, + r_taskitemevent.status + , r_lp.mti_description, r_lp.description, r_lp.rid_mastertaskitemevent, r_lp2.source, + r_lp2.rid_taskitem, r_lp2.rid_parenttaskitem + , r_lp.items_todo_rid_events + , m_rid_taskitem_parent_error + ), bt_enum('eventlog', 'debug')); + + if r_lp.skip + then + continue; + end if; + + select r.p_retval, r.p_errmsg, r.p_parms + from core.action_init( + 'mastertaskitemevent', + r_lp.rid_mastertaskitemevent, + _jsonb_object_cat(jsonb_build_object( + 'parent_rid_taskitemevent', r_taskitemevent.rid_taskitemevent + , 'parent_rid_taskitem', _bv(r_lp2.rid_taskitem, r_lp2.rid_parenttaskitem, + r_lp2.rid_taskitem_parentaction) + , 'rid_taskitem_parentaction', case + + when m_rid_taskitem_parent_error > 0 + then m_rid_taskitem_parent_error + when r_taskitemevent.status in (13, 10) + then r_taskitem.rid_taskitem + + else _try_integer(r_taskitemevent.jsonvalue ->> + 'rid_taskitem_parentaction', + null) + end + , 'rid_taskitem', case + when r_lp.rid_mastertaskitem = r_mastertaskitemevent.rid_mastertaskitem + then r_taskitem.rid_taskitem + else null + end + , 'create_mode', case + when nv(r_taskitemevent.jsonvalue ->> 'create_mode') <> '' + then r_taskitemevent.jsonvalue ->> 'create_mode' + when r_lp.rid_mastertaskitem = r_mastertaskitemevent.rid_mastertaskitem + then 'existing' + else null + end + , 'source', r_lp2.source + , 'rid_tasklist_prev', r_taskitem.rid_tasklist + , 'rid_tasklist', case + when r_lp.new_task then null + when r_lp.linktask then r_taskitem.rid_tasklist + else r_taskitem.rid_tasklist + end + , 'start_sequence', case + when _try_integer(r_taskitemevent.jsonvalue ->> 'start_sequence', 0) > 0 + then + _try_integer(r_taskitemevent.jsonvalue ->> 'start_sequence', 0) + + nv(r_lp.rn, 1) + when r_lp.linktask then r_taskitem.seqno + end + , 'linked_rid_mastertask', case + when r_lp.linktask then r_lp.rid_mastertask_target + else _try_integer(r_taskitem.jsonvalue ->> + 'linked_rid_mastertaskitem', null) + end + , 'rid_hub', r_lp2.rid_hub + , 'rid_hub_link', r_lp2.rid_hub_link + , 'rid_mastertaskoption_event', r_lp.rid_mastertaskoption_event + , 'show_in_wizard', r_lp.show_in_wizard + , 'allow_rollback', r_lp.allow_rollback + , 'new_task', case when r_lp.status in (4, 11) then null else r_lp.new_task end + ) + , r_taskitemevent.jsonvalue -> '_event_reaction_parms' + , jsonb_build_object( + 'taskitem_jsonvalue', + _jsonb_object_cat(r_taskitemevent.jsonvalue -> 'taskitem_jsonvalue' + , jsonb_build_object('_errorcode', r_taskitem.jsonvalue ->> '_errorcode') + ) + , '_event_jsonvalue', _jsonb_object_cat( + r_taskitemevent.jsonvalue -> '_event_jsonvalue', jsonb_build_object( + 'show_in_wizard', r_lp.show_in_wizard + , 'allow_rollback', r_lp.allow_rollback + ))) + ) + ) r + into m_retval, m_errmsg, r_lp.cumulated_parms; + + if m_retval > 0 + then + perform log_event(m_funcname, + format('Event Reaction Error: %s -> %s : %s', r_lp.rid_mastertaskitemevent, + r_lp.description, m_errmsg), bt_enum('eventlog', 'error')); + --raise exception 'Event Init Failure: %(%) -> %', r_lp.description, r_lp.rid_mastertaskitemevent, m_errmsg; + + end if; + end loop; + + end loop; + else + perform log_event(m_funcname, + format('No Event Reactions to process for event %s(%s)', r_mastertaskitemevent.description, + r_mastertaskitemevent.rid_mastertaskitemevent), bt_enum('eventlog', 'debug')); + end if; + + if m_rollback + and not exists ( + select 1 + from core.taskitem ti + where ti.rid_taskitem_parentaction = r_taskitem.rid_taskitem + ) + then + + + delete + from core.taskitem m + where m.rid_taskitem = r_taskitem.rid_taskitem; + + end if; + + /* + Planned,1 + Todo,2 + In Progress,3 + Completed,4 + Redo,5 + Canceled,6 + Required,7 + Delayed,8 + View,9 + Error,10 + Resolved,11 + */ + + select * + from core.taskitem ti + where ti.rid_taskitem = r_taskitem.rid_taskitem + into r_taskitem; + + if r_taskitem.rid_tasklist > 0 + then + --When all active task items in the list are either in-progress or complete, + -- promote any still-in-progress items to completed but only if they are either a + -- parent container with children, or have a qualifying event record attached. + + select count(1) filter ( where ti.status in (3, 4, 7) ) as inprogress + , count(1) filter ( where ti.status in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) ) as total + from core.taskitem ti + where ti.rid_tasklist = r_taskitem.rid_tasklist + into r_lp; + + if r_lp.total > 0 and r_lp.inprogress >= r_lp.total + then + update core.taskitem u + set status = 4 + where + u.rid_tasklist = r_taskitem.rid_tasklist + and u.status in (3) + + and not exists ( + select 1 + from core.mastertaskitemevent evt + where + evt.rid_mastertaskitem = u.rid_mastertaskitem + and evt.status = 4 + and ( + evt.outcome > 0 + or exists ( + select 1 + from core.mastertaskitemeventreaction r + where r.rid_mastertaskitemevent = evt.rid_mastertaskitemevent + ) + ) + ) + and ( + u.rid_taskitem_parentaction is null + and exists ( + select 1 + from core.taskitem tie2 + where tie2.rid_taskitem_parentaction = u.rid_taskitem + ) + or + exists ( + select 1 + from core.taskitemevent evt + where + evt.rid_taskitem = u.rid_taskitem + and evt.status in (3, 4) + ) + ); + end if; + + --Update tasklist status if all tasks are completed + select count(1) filter ( where ti.status in (4, 10) ) as complete + , count(1) filter ( where ti.status in (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) ) as total + --,count(1) filter ( where ti.status in (2,3) ) as active + , count(1) filter ( where ti.status in (5, 6, 11) ) as cancelredo + from core.taskitem ti + where ti.rid_tasklist = r_taskitem.rid_tasklist + into r_lp; + + -- perform log_event(m_funcname, format('Updating task totals %s(%s,%s) totals:%s',r_taskitem.description + -- , r_taskitem.rid_taskitem, r_taskitem.rid_tasklist, + -- to_jsonb(r_lp)), bt_enum('eventlog', 'local notice')); + + if r_lp.total > 0 and r_lp.complete + r_lp.cancelredo >= r_lp.total + then + + update core.tasklist u + set status = 4 + where + u.rid_tasklist = r_taskitem.rid_tasklist + and nv(u.status) <> 4; + elsif r_lp.total > 0 + then + update core.tasklist u + set status = 2 + where + u.rid_tasklist = r_taskitem.rid_tasklist + and u.status not in (2, 6); + + end if; + + select count(1) filter ( where nv(tl2.status) not in (3, 6) ) as total + , count(1) filter ( where tl2.status = 2 ) as totaldone + , max(tl2.rid_process) as rid_process + from core.tasklist tl + inner join core.tasklist tl2 on tl2.rid_process = tl.rid_process + where tl.rid_tasklist = r_taskitem.rid_tasklist + into r_lp; + + if r_lp.total > 0 + and r_lp.totaldone >= r_lp.total + then + update core.process u + set status = 'done' + where + u.rid_process = r_lp.rid_process + and nv(u.status) <> 'done'; + elsif r_lp.total > 0 + then + update core.process u + set status = 'open' + where + u.rid_process = r_lp.rid_process + and nv(u.status) <> 'open'; + + end if; + 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 := get_err_msg(m_funcname, m_errmsg, m_errcontext, m_errdetail, m_errhint, m_errstate, 1); + if nv(m_errcode) = '' and p_errmsg ilike '%[%]%' + then + m_errcode = (regexp_matches(p_errmsg, '\[([^\]]+)\]'))[1]; + end if; + + perform log_event(m_funcname, + format('Event Errored: rid_taskitemevent:%s, rid_taskitem:%s (%s) > m_errmsg=%s m_errcode=%s', + r_taskitemevent.rid_taskitemevent, r_taskitem.rid_taskitem, + r_taskitemevent.description, m_errmsg, m_errcode), bt_enum('eventlog', 'debug')); + m_errmsg = regexp_replace(m_errmsg, '\[([^\]]+)\]', '', 'g'); + + if r_taskitem.rid_taskitem is null + then + select ti.* + from core.taskitem ti + where ti.rid_taskitem = r_taskitemevent.rid_taskitem; + end if; + + update core.taskitemevent evt + set + errmsg = m_errmsg + , retval = 3 + , jsonvalue = _jsonb_object_cat(evt.jsonvalue, + jsonb_build_object('errorcode', m_errcode, 'message', m_errmsg)) + where evt.rid_taskitemevent = r_taskitemevent.rid_taskitemevent; + + update core.taskitem u + set + jsonvalue = _jsonb_object_cat(u.jsonvalue, jsonb_build_object('errorcode', _bv(m_errcode, 'PRCERR') + , 'message', nv(m_errmsg, 'Unknown Error') + , 'alerttype', 'error' + , 'subject', format('Error on %s', r_taskitem.description) + , 'show_alert', nv(m_errmsg, 'Unknown Error') + )) + , status = case + when u.status not in (6) + then 10 + else u.status + end + where + u.rid_taskitem = r_taskitem.rid_taskitem + and u.rid_taskitem > 0; + + if nv(m_errcode) = '' + then + m_errcode = 'PRCERR'; + end if; + + + SET session_replication_role = DEFAULT; + END; + +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; + SET session_replication_role = DEFAULT; +END; +$$; diff --git a/testdata/corpus/5796956_create_core.ui_action_event.pgsql b/testdata/corpus/5796956_create_core.ui_action_event.pgsql new file mode 100755 index 0000000..7487dc5 --- /dev/null +++ b/testdata/corpus/5796956_create_core.ui_action_event.pgsql @@ -0,0 +1,613 @@ +--select * from dropall('ui_action_event','core'); +CREATE OR REPLACE FUNCTION core.ui_action_event( + p_payload INOUT jsonb +, OUT p_retval integer +, OUT p_errmsg text +) + LANGUAGE plpgsql + VOLATILE + SECURITY DEFINER +AS +$$ +DECLARE + --Error Handling-- + m_funcname text = 'core.ui_action_event'; + m_errmsg text; + m_errcontext text; + m_errdetail text; + m_errhint text; + m_errstate text; + m_retval integer; + --Error Handling-- + m_rid_hub integer; + m_rid_hub_link integer; + m_rid_hub_user integer; + m_temp_rid integer; + m_final_rid_taskitem integer; + m_temp_rid_list integer[]; + m_status integer; + m_outcome integer; + r_mastertaskitemevent core.mastertaskitemevent%rowtype; + r_taskitem core.taskitem%rowtype; + r_taskitemevent core.taskitemevent%rowtype; + r_mastertaskitem core.mastertaskitem%rowtype; + r_mastertask core.mastertask%rowtype; + r_lp record; + m_check_error boolean := false; + m_cancel boolean := false; + j_result jsonb; + j_obj jsonb; + m_tm timestamp ; +BEGIN + m_tm = clock_timestamp(); + p_retval = 0; + p_errmsg = ''; + j_result = jsonb_build_array(); + + m_rid_hub = _try_integer(p_payload ->> 'rid_hub', 0); + m_rid_hub_user = _try_integer(p_payload ->> 'rid_hub_user', core.f_get_user_hub_rid()); + m_status = _try_integer(p_payload ->> 'status', 0); + m_outcome = _try_integer(p_payload ->> 'outcome', 0); + m_check_error = (p_payload ->> 'check_error')::citext in ('1', 'true'); + m_cancel = (p_payload ->> 'cancel')::citext in ('1', 'true'); + + select * + from core.mastertaskitemevent mte + where mte.rid_mastertaskitemevent = _try_integer(p_payload ->> 'rid_mastertaskitemevent', 0) + into r_mastertaskitemevent; + + select mti.* + from core.mastertaskitem mti + where + mti.rid_mastertaskitem = _try_integer(p_payload ->> 'rid_mastertaskitem', 0) + and _try_integer(p_payload ->> 'rid_mastertaskitem', 0) > 0 + or mti.rid_mastertaskitem = r_mastertaskitemevent.rid_mastertaskitem + into r_mastertaskitem; + + select mt.* + from core.mastertask mt + where + mt.rid_mastertask = _try_integer(p_payload ->> 'rid_mastertask', 0) + and _try_integer(p_payload ->> 'rid_mastertask', 0) > 0 + or mt.rid_mastertask = r_mastertaskitem.rid_mastertask + into r_mastertask; + + select * + from core.taskitem ti + where ti.rid_taskitem = _try_integer(p_payload ->> 'rid_taskitem', 0) + into r_taskitem; + + if r_taskitem.rid_taskitem > 0 + then + perform set_config('session.rid_taskitem', r_taskitem.rid_taskitem::text, false); + end if; + + if nv(r_mastertaskitem.rid_mastertaskitem) = 0 + then + if r_taskitem.rid_taskitem > 0 + then + select * + from core.mastertaskitem mti + where mti.rid_mastertaskitem = r_taskitem.rid_mastertaskitem + into r_mastertaskitem; + elsif r_mastertask.rid_mastertask > 0 + then + select * + from core.mastertaskitem mti + where + mti.rid_mastertask = r_mastertask.rid_mastertask + and coalesce(mti.inactive, 0) = 0 + and coalesce(mti.rid_parentmastertaskitem, 0) = 0 + order by mti.seqno nulls first, mti.rid_mastertaskitem + limit 1 + into r_mastertaskitem; + end if; + end if; + + if nv(r_mastertaskitemevent.rid_mastertaskitemevent) = 0 + then + if r_mastertaskitem.rid_mastertaskitem > 0 + then + select * + from core.mastertaskitemevent mte + where + mte.rid_mastertaskitem = r_mastertaskitem.rid_mastertaskitem + and coalesce(mte.inactive, 0) = 0 + and mte.status in (1, 2) + order by mte.status desc, mte.seqno nulls first, mte.rid_mastertaskitemevent + limit 1 + into r_mastertaskitemevent; + end if; + + end if; + + ---Check error handler + if m_check_error + then + if nv(r_mastertaskitemevent.status) <> 13 + then + select evt.* + from core.mastertaskitemevent evt + where + evt.rid_mastertaskitem = r_mastertaskitem.rid_mastertaskitem + and evt.status = 13 + order by evt.seqno desc, evt.rid_mastertaskitemevent desc + into r_mastertaskitemevent; + end if; + + --perform log_event(m_funcname,format('Init check error for rid_mastertaskitem:%s hub:%s ',r_mastertaskitemevent.rid_mastertaskitem,m_rid_hub),bt_enum('eventlog','local notice')); + + if r_mastertaskitemevent.rid_mastertaskitemevent > 0 + then + select r.p_retval, r.p_errmsg, r.p_parms + from core.action_init('mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , _jsonb_object_cat(p_payload, + jsonb_build_object('check_error', true, 'rid_hub', m_rid_hub, 'createmode', 'existing')) + ) r + into m_retval,m_errmsg, p_payload; + + if m_retval > 0 + then + p_retval = m_retval; + p_errmsg = m_errmsg; + return; + end if; + else + p_retval = 0; + p_errmsg = 'Error check enabled but no error event found for mastertaskitem.'; + end if; + + return; + end if; + + ---Check cancel handler + if m_cancel + then + if nv(r_mastertaskitemevent.status) <> 6 + then + select evt.* + from core.mastertaskitemevent evt + where + evt.rid_mastertaskitem = r_mastertaskitem.rid_mastertaskitem + and evt.status = 6 + order by evt.seqno desc, evt.rid_mastertaskitemevent desc + into r_mastertaskitemevent; + end if; + + perform log_event(m_funcname, + format('Init cancel for rid_mastertaskitem:%s hub:%s ', r_mastertaskitemevent.rid_mastertaskitem, + m_rid_hub), bt_enum('eventlog', 'local notice')); + + if r_mastertaskitemevent.rid_mastertaskitemevent > 0 + then + select r.p_retval, r.p_errmsg, r.p_parms + from core.action_init('mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , _jsonb_object_cat(p_payload, + jsonb_build_object('cancel', true, 'rid_hub', m_rid_hub, 'createmode', 'existing')) + ) r + into m_retval,m_errmsg, p_payload; + + if m_retval > 0 + then + p_retval = m_retval; + p_errmsg = m_errmsg; + return; + end if; + else + p_retval = 0; + p_errmsg = 'cancel called but no cancel event found for mastertaskitem.'; + end if; + + return; + end if; + + + if nv(m_rid_hub) = 0 and r_taskitem.rid_taskitem > 0 and r_taskitem.rid_hub > 0 + then + m_rid_hub = r_taskitem.rid_hub; + end if; + + --perform log_event(m_funcname,format('Init %s(%s) m_rid_hub: %s p_payload: %s',r_taskitem.description,r_taskitem.rid_taskitem,m_rid_hub,p_payload::text),bt_enum('eventlog','local notice')); + + if nv(m_rid_hub) = 0 + then + p_errmsg := 'Missing required parameter: rid_hub'; + p_retval = 1; + return; + end if; + + if nv(r_mastertaskitemevent.rid_mastertaskitemevent) = 0 + then + p_errmsg := 'Missing required parameter: rid_mastertaskitemevent'; + p_retval = 1; + return; + end if; + + perform log_event(m_funcname, + format(E'Init rid_mastertaskitemevent %s for rid_mastertaskitem:%s hub:%s \np_payload:%s', + r_mastertaskitemevent.rid_mastertaskitemevent, r_mastertaskitem.rid_mastertaskitem, + m_rid_hub, p_payload), bt_enum('eventlog', 'local notice')); + + select r.p_retval, r.p_errmsg, r.p_a_rid_taskitem + from core.taskitem_get_or_prime(_jsonb_object_cat(p_payload, jsonb_build_object( + 'parent_rid_taskitem', case + when r_taskitem.rid_taskitem = _try_integer(p_payload ->> 'parent_rid_taskitem', 0) + then r_taskitem.rid_taskitem_parentaction + else _try_integer(p_payload ->> 'parent_rid_taskitem', + r_taskitem.rid_taskitem_parentaction) + end + , 'rid_mastertaskitem', r_mastertaskitem.rid_mastertaskitem + , 'rid_mastertaskitemevent', r_mastertaskitemevent.rid_mastertaskitemevent + , 'status', r_mastertaskitemevent.status + , 'create_mode', p_payload ->> 'create_mode' + --,'create_mode','new' + ))) r + into m_retval,m_errmsg,m_temp_rid_list; + + if m_retval > 0 + then + raise 'Error initializing taskitem %',m_errmsg; + end if; + + for m_temp_rid in select * from unnest(m_temp_rid_list) + loop + + select * + from core.taskitem ti + where ti.rid_taskitem = m_temp_rid + into r_taskitem; + + if nv(r_taskitem.rid_taskitem) = 0 + then + p_retval = 1; + p_errmsg = format('Task item not created or found. %s', m_temp_rid); + return; + end if; + + + select tie.* + from core.taskitemevent tie + where + tie.rid_mastertaskitemevent = r_mastertaskitemevent.rid_mastertaskitemevent + and tie.rid_taskitem = r_taskitem.rid_taskitem + into r_taskitemevent; + + --raise exception 'Show me the error: % %',r_taskitemevent.rid_taskitemevent, r_taskitemevent.rid_taskitem; + --raise notice 'New/Update r_taskitemevent? %',r_taskitemevent; +-- if r_taskitem.rid_taskitem > 0 +-- and p_payload->'jsonvalue'->>'errorcode' is not null +-- then +-- update core.taskitem u +-- set jsonvalue = _jsonb_object_cat(u.jsonvalue, jsonb_build_object( +-- 'errorcode', _bv(p_payload->'jsonvalue'->>'errorcode',u.jsonvalue->>'errorcode') +-- ,'errormessage',_bv( p_payload->'jsonvalue'->>'errormessage',u.jsonvalue->>'errormessage') +-- ,'alerttype', _bv(p_payload->'jsonvalue'->>'alerttype',u.jsonvalue->>'alerttype') +-- )) +-- where u.rid_taskitem = r_taskitem.rid_taskitem; +-- +-- end if; + + if r_taskitemevent.rid_taskitemevent > 0 + then + + + update core.taskitemevent u + set + retval = 0 + , errmsg = null + , jsonvalue = _jsonb_object_cat(u.jsonvalue, ( + select mte.jsonvalue + from core.mastertaskitemevent mte + where mte.rid_mastertaskitemevent = u.rid_mastertaskitemevent + limit 1 + ), p_payload -> 'jsonvalue') + where u.rid_taskitemevent = r_taskitemevent.rid_taskitemevent; + + select r.p_retval, r.p_errmsg + from core.event_created(r_taskitemevent.rid_taskitemevent + , jsonb_build_object('operation', 'UPDATE')) r + into m_retval,m_errmsg; + + if m_retval > 0 + then + raise '%',m_errmsg; + end if; + + select e.* + from core.taskitemevent e + where e.rid_taskitemevent = r_taskitemevent.rid_taskitemevent + into r_taskitemevent; + + if length(r_taskitemevent.errmsg) > 0 + then + p_retval = 1; + p_errmsg = r_taskitemevent.errmsg; + end if; + + + else + + + insert + into core.taskitemevent( createddatetime, description, rid_mastertaskitemevent, rid_taskitem, status + --, duedatetime + --, escalated + , jsonvalue, outcome, rid_hub_user, retval) + select now() + , r_mastertaskitemevent.description + , r_mastertaskitemevent.rid_mastertaskitemevent + , r_taskitem.rid_taskitem + , _bv(r_mastertaskitemevent.status, m_status) + , _jsonb_object_cat(( + select mte.jsonvalue + from core.mastertaskitemevent mte + where mte.rid_mastertaskitemevent = r_mastertaskitemevent.rid_mastertaskitemevent + limit 1 + ), p_payload -> 'jsonvalue') + , m_outcome + , m_rid_hub_user + , 0 + where + not exists ( + select 1 + from core.taskitemevent tie + where + tie.rid_mastertaskitemevent = r_mastertaskitemevent.rid_mastertaskitemevent + and tie.rid_taskitem = r_taskitem.rid_taskitem + ) + returning taskitemevent.* + into r_taskitemevent; + + -- select r.p_retval,r.p_errmsg +-- from core.event_created(r_taskitemevent.rid_taskitemevent +-- ,jsonb_build_object('operation','UPDATE')) r +-- into m_retval,m_errmsg; +-- +-- if m_retval > 0 +-- then +-- raise '%',m_errmsg; +-- end if; + + + select e.* + from core.taskitemevent e + where e.rid_taskitemevent = r_taskitemevent.rid_taskitemevent + into r_taskitemevent; + + if length(r_taskitemevent.errmsg) > 1 + then + p_retval = 1; + p_errmsg = r_taskitemevent.errmsg; + end if; + + end if; + + with + recursive + tasklist_events as ( + select ti.rid_taskitem + , 1 as level + , coalesce(ti.seqno, 0) as seqno + , ti.status + from core.taskitem ti + inner join core.mastertaskitem mti on mti.rid_mastertaskitem = ti.rid_mastertaskitem + where + (ti.rid_tasklist = r_taskitem.rid_tasklist + or ti.rid_tasklist in ( + select tl.rid_tasklist_parent + from core.tasklistlink tl + where tl.rid_tasklist_child = r_taskitem.rid_tasklist + union + select tl.rid_tasklist_child + from core.tasklistlink tl + where tl.rid_tasklist_parent = r_taskitem.rid_tasklist + ) + ) + -- and mti.rid_mastertaskitem in ( +-- select ev2.rid_mastertaskitem +-- from core.mastertaskitemeventreaction er +-- inner join core.mastertaskitemevent ev2 on ev2.rid_mastertaskitemevent = er.rid_mastertaskitemevent_target +-- where er.rid_mastertaskitemevent = r_taskitemevent.rid_mastertaskitemevent +-- ) + union + select ti.rid_taskitem + , te.level + 1 + , coalesce(ti.seqno, 0) as seqno + , ti.status + from core.taskitem ti + inner join tasklist_events te on te.rid_taskitem = ti.rid_parenttaskitem + + ) + select e.rid_taskitem + from tasklist_events e + where nv(e.status) in (2, 10) + order by e.level desc, e.status desc, e.seqno desc + limit 1 + into m_final_rid_taskitem; + + j_obj = _jsonb_object_cat(to_jsonb(r_taskitemevent), jsonb_build_object( + 'jsonvalue', _jsonb_object_cat(r_taskitemevent.jsonvalue, jsonb_build_object( + 'rid_tasklist', r_taskitem.rid_tasklist, + 'rid_tasklist_event', ( + select jsonb_agg(distinct ti.rid_tasklist) + from core.mastertaskitemeventreaction er + inner join core.mastertaskitemevent mtiet + on mtiet.rid_mastertaskitemevent = er.rid_mastertaskitemevent_target + inner join core.taskitem ti on ti.rid_mastertaskitem = mtiet.rid_mastertaskitem + and ti.rid_hub = r_taskitem.rid_hub + and ti.rid_tasklist = r_taskitem.rid_tasklist + where er.rid_mastertaskitemevent = r_taskitemevent.rid_mastertaskitemevent + ) + , 'rid_tasklist_next', ( + select jsonb_agg(distinct tl.rid_tasklist) + from core.taskitem ti + inner join core.mastertaskitem mti on mti.rid_mastertaskitem = ti.rid_mastertaskitem + inner join core.mastertask mtj on mti.guid = mti.jsonvalue ->> 'guid_mastertask_next' + inner join core.tasklist tl on tl.rid_mastertask = mtj.rid_mastertask + and tl.rid_hub = r_taskitem.rid_hub + where ti.rid_taskitem = r_taskitemevent.rid_taskitem + ) + , 'select_taskitem_guid', ( + select ti.guid + from core.taskitem ti + where + (m_final_rid_taskitem > 0 + and ti.rid_taskitem = m_final_rid_taskitem + or nv(m_final_rid_taskitem) = 0 + and ti.rid_tasklist = r_taskitem.rid_tasklist + ) + order by + case + when ti.status in (10) then 2 + when ti.status in (0, 1, 2) then 1 + else 99 + end + , ti.status desc, ti.seqno desc + limit 1 + ) + , 'openactions', ( + select count(ti.rid_taskitem) filter ( where + coalesce(ti.status, 0) in ( + core._enumi('eventstatus', 'todo'), core._enumi('eventstatus', 'planned') + ) + ) + from core.taskitem ti + where + ti.rid_tasklist in ( + select r_taskitem.rid_tasklist + union + select tl.rid_tasklist_child + from core.tasklistlink tl + where tl.rid_tasklist_parent = r_taskitem.rid_tasklist + union + select tl.rid_tasklist_parent + from core.tasklistlink tl + where tl.rid_tasklist_child = r_taskitem.rid_tasklist + ) + and nv(ti.subitem) = 0 + and coalesce(ti.status, 0) <> coalesce(core._enumi('eventstatus', 'Canceled'), 0) + ) + )) + , 'WFL', to_jsonb(r_taskitem))); + + j_obj = _jsonb_object_cat(j_obj + , jsonb_build_object('jsonvalue' + , _jsonb_object_cat( + j_obj -> 'jsonvalue' + , jsonb_build_object('complete' + , (j_obj -> 'jsonvalue' ->> 'rid_tasklist_event' is null + and j_obj -> 'jsonvalue' ->> 'rid_tasklist_next' is null + and _try_integer(j_obj -> 'jsonvalue' ->> 'openactions', 0) = 0 + and nv(r_taskitemevent.jsonvalue ->> 'errorcode') not ilike 'E%' + ) + ) + ) + ) + ); + + j_result = j_result || j_obj; + + if m_final_rid_taskitem > 0 and m_final_rid_taskitem is distinct from r_taskitem.rid_taskitem + then + j_obj = _jsonb_object_cat(to_jsonb(ev), jsonb_build_object( + 'jsonvalue', jsonb_build_object( + 'rid_tasklist', ti.rid_tasklist + , 'select_taskitem_guid', ( + select ti.guid + from core.taskitem ti + where + (m_final_rid_taskitem > 0 + and ti.rid_taskitem = m_final_rid_taskitem + or nv(m_final_rid_taskitem) = 0 + and ti.rid_tasklist = r_taskitem.rid_tasklist + ) + order by case when ti.status in (0, 1, 2) then 1 else 99 end, ti.status desc, ti.seqno desc + limit 1 + ) + , 'openactions', ( + select count(ti.rid_taskitem) filter ( where + coalesce(ti.status, 0) in ( + core._enumi('eventstatus', 'todo'), core._enumi('eventstatus', 'planned') + ) + ) + from core.taskitem ti + where + ti.rid_tasklist = r_taskitem.rid_tasklist + and nv(ti.subitem) = 0 + and coalesce(ti.status, 0) <> coalesce(core._enumi('eventstatus', 'Canceled'), 0) + limit 1 + ) + ) + , 'WFL', to_jsonb(ti) + ) + ) + from core.taskitem ti + inner join lateral ( + select * + from core.taskitemevent ev + where + ev.rid_taskitem = ti.rid_taskitem + and ev.status in (2, 4) + order by ev.status desc + limit 1 + ) ev on ev.rid_taskitem = ti.rid_taskitem + where ti.rid_taskitem = m_final_rid_taskitem; + + j_result = j_result || j_obj; + end if; + + + end loop; + + --perform log_event(m_funcname,format('Result j_result: %s',j_result),bt_enum('eventlog','local notice')); + + p_payload = j_result; +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; + perform log_event(m_funcname, format('Benchmark Err %s Items: %s', array_length(m_temp_rid_list, 1), + (clock_timestamp() - m_tm)::interval::text), + bt_enum('eventlog', 'local notice')); + +END; +$$; + + +/* +select * from core.ui_action_event(jsonb_build_object('check_error',true + ,'rid_mastertaskitem',( + + select mti.rid_mastertaskitem from core.mastertaskitem mti + where mti.guid = '7a7348ec-7db6-4f13-bca7-c7840de1d22c' +),'rid_hub',20405)) +; + +select * from core.ui_action_event(jsonb_build_object('check_error',true + ,'rid_mastertaskitem',( + + select mti.rid_mastertaskitem from core.mastertaskitem mti + where mti.guid = '7a7348ec-7db6-4f13-bca7-c7840de1d22c' +),'rid_hub',20405)) +; + +select * from v_eventlog + +selecT * from core.taskitem ti +order by ti.rid_taskitem desc + + + select mti.rid_mastertask,mti.rid_mastertaskitem, mti.description, mti.guid from core.mastertaskitem mti order by mti.rid_mastertaskitem desc; + + + */ + + +--select cli.rid_hub from t_adproclient cli where cli.clientid = '92092101980891' \ No newline at end of file diff --git a/testdata/corpus/test_a.pgsql b/testdata/corpus/test_a.pgsql index 694e2b9..f69fc0c 100755 --- a/testdata/corpus/test_a.pgsql +++ b/testdata/corpus/test_a.pgsql @@ -1,28 +1,28 @@ --select * from dropall('resolvespec_login'); CREATE OR REPLACE FUNCTION resolvespec_login( - INOUT p_data jsonb - ,OUT p_success boolean - ,OUT p_error text + INOUT p_data jsonb +, OUT p_success boolean +, OUT p_error text ) -LANGUAGE plpgsql -VOLATILE -SECURITY DEFINER + LANGUAGE plpgsql + VOLATILE + SECURITY DEFINER AS $$ DECLARE --Error Handling-- - m_funcname text = 'resolvespec_login'; - m_errmsg text; - m_errcontext text; - m_errdetail text; - m_errhint text; - m_errstate text; - m_retval integer; + m_funcname text = 'resolvespec_login'; + m_errmsg text; + m_errcontext text; + m_errdetail text; + m_errhint text; + m_errstate text; + m_retval integer; --Error Handling-- - m_rid_user integer; - m_rid_hub integer; - m_pass_hashed citext[]; - m_session jsonb; + m_rid_user integer; + m_rid_hub integer; + m_pass_hashed citext[]; + m_session jsonb; m_allow_hash_auth boolean; BEGIN m_allow_hash_auth = _try_integer( p_data->'claims'->>'rid_user',0) > 0; diff --git a/testdata/corpus/test_mm_proc.pgsql b/testdata/corpus/test_mm_proc.pgsql index e39f5e5..5d7d8cb 100644 --- a/testdata/corpus/test_mm_proc.pgsql +++ b/testdata/corpus/test_mm_proc.pgsql @@ -1,84 +1,84 @@ 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 + 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 + 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; + 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_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; + 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(); + m_start timestamp = clock_timestamp(); BEGIN p_retval = 0; p_errmsg = ''; @@ -98,17 +98,17 @@ BEGIN 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; +-- 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')); +-- ,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 @@ -168,7 +168,7 @@ BEGIN 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')); +-- ,p_doctype,p_data_prefix,p_data_rid,octet_length(p_template)),bt_enum('eventlog','local notice')); if p_doctype = 'allfieldvalues' then @@ -291,7 +291,7 @@ BEGIN 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_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 @@ -513,8 +513,8 @@ BEGIN ) 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_doctype, p_commtype,p_data_prefix,p_data_rid),bt_enum('eventlog','local notice')); +-- p_doc = p_template; return; @@ -553,7 +553,7 @@ BEGIN where tat.description ilike 'Form 16 (%' ), 'TCLI',1000016) r */ - /* +/* drop table if exists debug_merge_init_src; create table debug_merge_init_src as @@ -1130,10 +1130,10 @@ BEGIN ) 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 - -- ); +-- ,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 @@ -1385,10 +1385,10 @@ BEGIN 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_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 = '';