{
  "updatedAt": "2025-12-24T08:53:15.515Z",
  "createdAt": "2025-12-23T09:30:30.697Z",
  "id": "5HGb0EQGtOj85F3c",
  "name": "Query_DB",
  "description": null,
  "active": false,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "inputSource": "passthrough"
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ],
      "id": "trigger-execute-query",
      "name": "ExecuteWorkflowTrigger"
    },
    {
      "parameters": {
        "jsCode": "// Validate input and initialize loop state\nconst ctx = $json.ctx;\n\nif (!ctx?.db_queries || !Array.isArray(ctx.db_queries) || ctx.db_queries.length === 0) {\n  throw new Error('Query_DB requires ctx.db_queries array with at least one query');\n}\n\n// Validate each query has required fields\nfor (const q of ctx.db_queries) {\n  if (!q.key || !q.sql) {\n    throw new Error('Each query in ctx.db_queries must have key and sql fields');\n  }\n}\n\n// Initialize loop state\nreturn {\n  json: {\n    original_ctx: ctx,\n    queries: ctx.db_queries,\n    current_index: 0,\n    results: {}  // Will accumulate {key: {results, count}}\n  }\n};",
        "mode": "runOnceForEachItem"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "id": "initialize-loop",
      "name": "InitializeLoop"
    },
    {
      "parameters": {
        "jsCode": "// Prepare current query for execution\nconst state = $json;\nconst query = state.queries[state.current_index];\n\nreturn {\n  json: {\n    ...state,\n    current_query: query,\n    sql: query.sql,\n    params: query.params || []\n  }\n};",
        "mode": "runOnceForEachItem"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ],
      "id": "prepare-query",
      "name": "PrepareQuery"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "={{ $json.sql }}",
        "options": {
          "values": "={{ $json.params }}"
        }
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        660,
        0
      ],
      "id": "execute-postgres-query",
      "name": "ExecuteQuery",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "mode": "append",
        "numberInputs": 2,
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        880,
        0
      ],
      "id": "merge-results",
      "name": "MergeResults"
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Collect query results and check if more queries remain\nconst items = $input.all();\n\n// Find state from merge (has original_ctx)\nlet state = null;\nconst queryResults = [];\n\nfor (const item of items) {\n  if (item.json.original_ctx) {\n    state = item.json;\n  } else {\n    // This is a Postgres result row\n    queryResults.push(item.json);\n  }\n}\n\nif (!state) {\n  throw new Error('Query_DB: Lost loop state');\n}\n\n// Store results for current query\nconst currentKey = state.current_query.key;\nstate.results[currentKey] = {\n  results: queryResults,\n  count: queryResults.length\n};\n\n// Move to next query\nstate.current_index++;\n\n// Check if more queries remain\nconst hasMore = state.current_index < state.queries.length;\n\nreturn [{\n  json: {\n    ...state,\n    has_more: hasMore\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        0
      ],
      "id": "collect-results",
      "name": "CollectResults"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "has-more",
              "leftValue": "={{ $json.has_more }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1320,
        0
      ],
      "id": "check-more-queries",
      "name": "MoreQueries?"
    },
    {
      "parameters": {
        "jsCode": "// Restore ctx with all query results in ctx.db namespace\nconst state = $json;\n\n// Remove db_queries since it was consumed\nconst { db_queries, ...restCtx } = state.original_ctx;\n\nreturn {\n  json: {\n    ctx: {\n      ...restCtx,\n      db: {\n        ...(restCtx.db || {}),\n        ...state.results\n      }\n    }\n  }\n};",
        "mode": "runOnceForEachItem"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        100
      ],
      "id": "finalize-context",
      "name": "FinalizeContext"
    }
  ],
  "connections": {
    "ExecuteWorkflowTrigger": {
      "main": [
        [
          {
            "node": "InitializeLoop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "InitializeLoop": {
      "main": [
        [
          {
            "node": "PrepareQuery",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PrepareQuery": {
      "main": [
        [
          {
            "node": "ExecuteQuery",
            "type": "main",
            "index": 0
          },
          {
            "node": "MergeResults",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "ExecuteQuery": {
      "main": [
        [
          {
            "node": "MergeResults",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MergeResults": {
      "main": [
        [
          {
            "node": "CollectResults",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CollectResults": {
      "main": [
        [
          {
            "node": "MoreQueries?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MoreQueries?": {
      "main": [
        [
          {
            "node": "PrepareQuery",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FinalizeContext",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "staticData": null,
  "meta": null,
  "versionId": "325028f9-499d-4013-8747-2955fc201732",
  "activeVersionId": null,
  "versionCounter": 116,
  "triggerCount": 0,
  "shared": [
    {
      "updatedAt": "2025-12-23T09:30:30.697Z",
      "createdAt": "2025-12-23T09:30:30.697Z",
      "role": "workflow:owner",
      "workflowId": "5HGb0EQGtOj85F3c",
      "projectId": "erM3nntdLL53noWi",
      "project": {
        "updatedAt": "2025-12-23T09:23:39.658Z",
        "createdAt": "2025-12-23T09:16:56.460Z",
        "id": "erM3nntdLL53noWi",
        "name": "Chris Irineo <chriskevini@gmail.com>",
        "type": "personal",
        "icon": null,
        "description": null,
        "projectRelations": [
          {
            "updatedAt": "2025-12-23T09:16:56.460Z",
            "createdAt": "2025-12-23T09:16:56.460Z",
            "userId": "2a851a2d-b7e5-4b3c-aefb-6eaaa79e0659",
            "projectId": "erM3nntdLL53noWi",
            "user": {
              "updatedAt": "2025-12-24T08:40:46.063Z",
              "createdAt": "2025-12-23T09:16:54.881Z",
              "id": "2a851a2d-b7e5-4b3c-aefb-6eaaa79e0659",
              "email": "chriskevini@gmail.com",
              "firstName": "Chris",
              "lastName": "Irineo",
              "personalizationAnswers": {
                "version": "v4",
                "personalization_survey_submitted_at": "2025-12-23T09:23:43.723Z",
                "personalization_survey_n8n_version": "1.123.5"
              },
              "settings": {
                "userActivated": true,
                "firstSuccessfulWorkflowId": "CgUAxK0i4YhrZ2Wp",
                "userActivatedAt": 1766487000077,
                "easyAIWorkflowOnboarded": true
              },
              "disabled": false,
              "mfaEnabled": false,
              "lastActiveAt": "2025-12-24",
              "isPending": false
            }
          }
        ]
      }
    }
  ],
  "tags": [],
  "activeVersion": null
}