Skip to content

Fix: Response not iterable on trigger expansion#10177

Open
kundansable wants to merge 1 commit into
pgadmin-org:masterfrom
kundansable:fix-10117-response-not-iterable
Open

Fix: Response not iterable on trigger expansion#10177
kundansable wants to merge 1 commit into
pgadmin-org:masterfrom
kundansable:fix-10117-response-not-iterable

Conversation

@kundansable

@kundansable kundansable commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #10117 — expanding a Trigger node under a Table to view its trigger function throws 'Response' object is not iterable, crashing the tree instead of showing the function (or a clean error).

Root Cause

Two separate bugs on the same code path, TriggerView.get_children_nodes():

  1. The reported crash: get_children_nodes() can legitimately return a Flask Response instead of a list — e.g. gone("Could not find the specified trigger function") when the trigger's function has no matching row in the trigger-function node query (see #2). NodeView.children() / PGChildNodeView.children() unconditionally called sorted(...) on whatever get_children_nodes() returned, so a Response blew up with 'Response' object is not iterable instead of being passed through.
  2. A masked regression found while testing the fix for ISSUE2277-Internal server error displayed if table name contains long character #1: the render_template() call that builds the trigger-function node.sql query never passed conn= into the template context, even though the template quotes fnid via {{ fnid|qtLiteral(conn) }}. This call site predates qtLiteral requiring a connection; after qtLiteral was hardened (in 658bb585d) to raise ValueError instead of silently degrading when conn is missing, this call started raising on every trigger expansion (not just the internal-language case), which is what actually surfaced while verifying fix ISSUE2277-Internal server error displayed if table name contains long character #1 — before that, it looked like every trigger's function node was broken with a qtLiteral requires a connection 500 error, masking the narrower, real 'Response' object is not iterable #10117 repro underneath.

Fix

  1. In NodeView.children() and PGChildNodeView.children() (web/pgadmin/browser/utils.py), check isinstance(children, flask.Response) before sorting/iterating, and return the Response as-is when it is one.
  2. In TriggerView.get_children_nodes() (web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py), pass conn=self.conn into the node.sql render call, matching every other render_template call site in the file.

Test Steps

  1. Run the following against a test database — it creates a table with two triggers, one using a genuine PostgreSQL built-in internal-language trigger function, and one using a normal plpgsql trigger function:
    CREATE TABLE trigger_bug_test (id int PRIMARY KEY, val text);
    
    -- Built into every PostgreSQL server since 9.0, LANGUAGE internal.
    CREATE FUNCTION my_redundant_trigger() RETURNS trigger LANGUAGE internal
      AS 'suppress_redundant_updates_trigger';
    CREATE TRIGGER trg_suppress_redundant BEFORE UPDATE ON trigger_bug_test
      FOR EACH ROW EXECUTE FUNCTION my_redundant_trigger();
    
    -- A normal, ordinary plpgsql trigger function.
    CREATE FUNCTION trg_normal_plpgsql_fn() RETURNS trigger LANGUAGE plpgsql AS $$
    BEGIN
        NEW.val := coalesce(NEW.val, '') || '_touched';
        RETURN NEW;
    END;
    $$;
    CREATE TRIGGER trg_normal_plpgsql BEFORE INSERT ON trigger_bug_test
      FOR EACH ROW EXECUTE FUNCTION trg_normal_plpgsql_fn();
  2. In the Object Explorer, expand trigger_bug_test > Triggers > trg_suppress_redundant.
    • Before the fix: 'Response' object is not iterable.
    • After the fix: a clean "Could not find the specified trigger function" message — expected, since pgAdmin can't browse internal-language function source; no crash.
  3. Expand trigger_bug_test > Triggers > trg_normal_plpgsql.
    • Before the conn=self.conn fix: qtLiteral requires a connection 500 error.
    • After the fix: expands normally and shows the trg_normal_plpgsql_fn() function node.
  4. Regression: expand a trigger on any other existing table in the test database and confirm its function node still displays correctly.

Summary by CodeRabbit

  • Bug Fixes
    • Improved error handling when loading browser tree nodes, ensuring error responses are displayed correctly instead of causing additional processing failures.
    • Improved trigger-function detail loading for database trigger nodes.

…unction

Expanding a Trigger node under a Table to view its function threw
'Response' object is not iterable, crashing the tree.

get_children_nodes() can legitimately return a Flask Response instead
of a list (e.g. gone("Could not find the specified trigger
function") when the trigger's function has no matching row in the
trigger-function node query, as happens for a genuine internal-
language function such as suppress_redundant_updates_trigger).
NodeView.children() / PGChildNodeView.children() unconditionally
sorted whatever get_children_nodes() returned, crashing on a
Response instead of passing it through.

Separately, TriggerView.get_children_nodes() rendered the trigger-
function node.sql template (which quotes fnid via qtLiteral(conn))
without passing conn into the template context. This call site
predates qtLiteral requiring a connection; after qtLiteral was
hardened in 658bb58 to raise ValueError instead of silently
degrading when conn is missing, this broke node.sql rendering for
every trigger, not just the internal-language case.

Fix both: check isinstance(children, flask.Response) in children()
before sorting/iterating and return it as-is, and pass
conn=self.conn into the node.sql render call, matching every other
render_template call site in the file.

Fixes pgadmin-org#10117
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ed441c7b-edeb-4fc9-86c3-9a7fe711b8aa

📥 Commits

Reviewing files that changed from the base of the PR and between b15c745 and fdfd6ff.

📒 Files selected for processing (2)
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py
  • web/pgadmin/browser/utils.py

Walkthrough

Trigger-function SQL rendering now receives the active connection. Browser child-node methods detect Flask responses and return them directly, while preserving sorting and JSON response handling for normal node lists.

Changes

Trigger response handling

Layer / File(s) Summary
Trigger query connection context
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py
The trigger SQL template receives self.conn together with the function identifier.
Response-safe child traversal
web/pgadmin/browser/utils.py
NodeView.children and PGChildNodeView.children return Flask Response objects directly and continue sorting and wrapping normal node lists as JSON.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix for trigger expansion returning a Response.
Linked Issues check ✅ Passed The changes address issue #10117 by returning Response objects directly and fixing trigger-function rendering.
Out of Scope Changes check ✅ Passed No unrelated code changes are evident; both modifications support the trigger expansion fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

'Response' object is not iterable

1 participant