{
  "id": "ssu8GdKQEJM3KbGx",
  "name": "Adhoc Query",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "path": "adhoc-query",
        "httpMethod": "POST",
        "authentication": "none",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-1",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "inputSource": "passthrough"
      },
      "id": "sub-workflow-trigger",
      "name": "When Called by Another Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        240,
        480
      ]
    },
    {
      "parameters": {
        "jsCode": "// Support both webhook JSON and MCP chat input\nconst raw = $input.item.json;\n\n// Debug: log what we're receiving\nconsole.log('Raw input:', JSON.stringify(raw, null, 2));\n\n// Check for chatInput at various levels\nif (raw.chatInput) {\n  console.log('Found chatInput:', raw.chatInput);\n  // Extract query from natural language input\n  const match = raw.chatInput.match(/select[\\s\\S]*$/i);\n  if (!match) {\n    throw new Error('Could not extract SQL query from chat input: ' + raw.chatInput);\n  }\n  return {\n    json: {\n      query: match[0].trim(),\n      agent: 'mcp',\n      description: 'MCP chat query',\n      timestamp: new Date().toISOString()\n    }\n  };\n}\n\n// Check if chatInput is in body or other locations\nif (raw.body && raw.body.chatInput) {\n  console.log('Found chatInput in body:', raw.body.chatInput);\n  const match = raw.body.chatInput.match(/select[\\s\\S]*$/i);\n  if (!match) {\n    throw new Error('Could not extract SQL query from chat input: ' + raw.body.chatInput);\n  }\n  return {\n    json: {\n      query: match[0].trim(),\n      agent: 'mcp',\n      description: 'MCP chat query',\n      timestamp: new Date().toISOString()\n    }\n  };\n}\n\n// Otherwise fall back to webhook JSON\nconst body = raw.body || raw;\nconst { query, params = {}, rawSqlParams = {}, agent = 'unknown', description = 'Adhoc query execution' } = body;\n\nif (!query) {\n  throw new Error('Query is required. Received: ' + JSON.stringify(raw));\n}\n\n// Simple parameter substitution\nlet processedQuery = query;\nObject.keys(params).forEach(key => {\n  const value = params[key];\n  const paramPattern = new RegExp(`:${key}\\\\b`, 'g');\n  \n  if (rawSqlParams[key]) {\n    processedQuery = processedQuery.replace(paramPattern, value.toString());\n  } else if (typeof value === 'string') {\n    processedQuery = processedQuery.replace(paramPattern, `'${value.replace(/'/g, \"''\")}'`);\n  } else if (typeof value === 'number') {\n    processedQuery = processedQuery.replace(paramPattern, value.toString());\n  } else if (Array.isArray(value)) {\n    const arrayValues = value.map(v => \n      typeof v === 'string' ? `'${v.replace(/'/g, \"''\")}'` : v\n    ).join(', ');\n    processedQuery = processedQuery.replace(paramPattern, `(${arrayValues})`);\n  }\n});\n\n// Basic safety check\nconst dangerousPatterns = [\n  /DROP\\s+TABLE/i,\n  /DROP\\s+DATABASE/i,\n  /TRUNCATE/i\n];\n\nconst isDangerous = dangerousPatterns.some(pattern => pattern.test(processedQuery));\nif (isDangerous) {\n  throw new Error('Query contains potentially dangerous operations');\n}\n\nreturn {\n  json: {\n    query: processedQuery,\n    agent: agent,\n    description: description,\n    timestamp: new Date().toISOString()\n  }\n};"
      },
      "id": "process-query",
      "name": "Process Query",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "={{ $('Process Query').item.json.query }}; SHOW WARNINGS",
        "options": {}
      },
      "id": "execute-query",
      "name": "Execute Query",
      "type": "n8n-nodes-base.mySql",
      "typeVersion": 2.5,
      "position": [
        680,
        300
      ],
      "credentials": {
        "mySql": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const queryInfo = $('Process Query').item.json;\nconst queryResults = $input.all();\n\n// Check if first result has error object\nif (queryResults.length > 0 && queryResults[0].json.error) {\n  return [{\n    json: {\n      success: false,\n      query: queryInfo.query,\n      error: queryResults[0].json.message,\n      errorCode: queryResults[0].json.error.description?.match(/code: (\\w+)/)?.[1],\n      timestamp: new Date().toISOString()\n    }\n  }];\n}\n\n// Separate main results from warnings\n// Last N items might be warnings (they have Level, Code, Message fields)\nconst warnings = [];\nconst data = [];\n\nqueryResults.forEach(item => {\n  if (item.json.Level && item.json.Message) {\n    warnings.push(item.json);\n  } else {\n    data.push(item.json);\n  }\n});\n\nreturn [{\n  json: {\n    success: true,\n    query: queryInfo.query,\n    agent: queryInfo.agent,\n    description: queryInfo.description,\n    results: {\n      count: data.length,\n      data: data\n    },\n    warnings: warnings.length > 0 ? warnings : undefined,\n    timestamp: new Date().toISOString()\n  }\n}];"
      },
      "id": "format-response",
      "name": "Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "allIncomingItems",
        "options": {}
      },
      "id": "webhook-response",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1120,
        300
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Process Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Called by Another Workflow": {
      "main": [
        [
          {
            "node": "Process Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Query": {
      "main": [
        [
          {
            "node": "Execute Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Query": {
      "main": [
        [
          {
            "node": "Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Response": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": true,
    "timeSavedMode": "fixed",
    "timezone": "America/Chicago"
  },
  "staticData": null,
  "versionId": "b209db9f-1c17-438b-abef-74a8a7a9504b",
  "createdAt": "2025-12-21 02:17:16.081",
  "updatedAt": "2025-12-25 19:16:11.611"
}