| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /**
- * @node mysql-query
- * @name MySQL Query
- * @category database
- * @version 1.0.0
- * @description Execute SQL queries against a MySQL database with schema introspection support
- * @icon database
- */
- const configSchema = {
- type: 'object',
- properties: {
- credentialId: {
- type: 'string',
- title: 'MySQL Credential',
- description: 'Select the MySQL credential to use for connection',
- dynamicOptions: {
- source: 'credentials',
- filter: { type: 'mysql' }
- }
- },
- database: {
- type: 'string',
- title: 'Database',
- description: 'Database name (overrides credential default if set)'
- },
- table: {
- type: 'string',
- title: 'Table',
- description: 'Table name for query operations'
- },
- queryType: {
- type: 'string',
- title: 'Query Type',
- description: 'Type of query to execute',
- enum: ['select', 'insert', 'update', 'delete', 'custom'],
- enumLabels: ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'Custom SQL'],
- default: 'select'
- },
- columns: {
- type: 'array',
- title: 'Columns',
- description: 'Columns to select (empty for all). Enter column names manually.',
- items: {
- type: 'string'
- },
- showWhen: { field: 'queryType', value: 'select' }
- },
- whereClause: {
- type: 'string',
- title: 'WHERE Clause',
- description: 'Optional WHERE conditions (without WHERE keyword). Supports {{variable}} interpolation.',
- showWhen: { field: 'queryType', valueIn: ['select', 'update', 'delete'] }
- },
- orderBy: {
- type: 'string',
- title: 'ORDER BY',
- description: 'Order by clause (without ORDER BY keyword)',
- showWhen: { field: 'queryType', value: 'select' }
- },
- limit: {
- type: 'number',
- title: 'Limit',
- description: 'Maximum number of rows to return',
- default: 100,
- showWhen: { field: 'queryType', value: 'select' }
- },
- insertData: {
- type: 'string',
- title: 'Data to Insert (JSON)',
- description: 'JSON object with column:value pairs. Supports {{variable}} interpolation.',
- showWhen: { field: 'queryType', value: 'insert' }
- },
- updateData: {
- type: 'string',
- title: 'Data to Update (JSON)',
- description: 'JSON object with column:value pairs to set. Supports {{variable}} interpolation.',
- showWhen: { field: 'queryType', value: 'update' }
- },
- customQuery: {
- type: 'string',
- title: 'Custom SQL',
- description: 'Custom SQL query. Supports {{variable}} interpolation. Use ? for parameters.',
- showWhen: { field: 'queryType', value: 'custom' }
- },
- parameters: {
- type: 'array',
- title: 'Query Parameters',
- description: 'Parameters for the query (for ? placeholders)',
- items: {
- type: 'string'
- },
- showWhen: { field: 'queryType', value: 'custom' }
- },
- returnInsertId: {
- type: 'boolean',
- title: 'Return Insert ID',
- description: 'Return the auto-generated ID for INSERT queries',
- default: true,
- showWhen: { field: 'queryType', value: 'insert' }
- }
- },
- required: ['credentialId', 'queryType']
- };
- const inputSchema = {
- type: 'object',
- properties: {
- data: {
- type: 'any',
- description: 'Input data available for interpolation in queries'
- },
- parameters: {
- type: 'array',
- description: 'Override query parameters from input'
- }
- }
- };
- const outputSchema = {
- type: 'object',
- properties: {
- rows: { type: 'array', description: 'Query result rows (for SELECT)' },
- affectedRows: { type: 'number', description: 'Number of affected rows (for INSERT/UPDATE/DELETE)' },
- insertId: { type: 'number', description: 'Auto-generated insert ID (for INSERT)' },
- columns: { type: 'array', description: 'Column names from the result' },
- query: { type: 'string', description: 'The executed SQL query' },
- error: { type: 'string', description: 'Error message if query failed' }
- }
- };
- const outputs = [
- { name: 'main', displayName: 'Query Result', type: 'object', color: '#06b6d4' }
- ];
- function buildSelectQuery(config, data) {
- const table = config.table;
- if (!table) {
- throw new Error('Table is required for SELECT queries');
- }
- const columns = config.columns && config.columns.length > 0
- ? config.columns.join(', ')
- : '*';
- let query = `SELECT ${columns} FROM \`${table}\``;
- if (config.whereClause) {
- const where = smartbotic.utils.interpolate(config.whereClause, data);
- query += ` WHERE ${where}`;
- }
- if (config.orderBy) {
- query += ` ORDER BY ${config.orderBy}`;
- }
- if (config.limit && config.limit > 0) {
- query += ` LIMIT ${config.limit}`;
- }
- return { query, params: [] };
- }
- function buildInsertQuery(config, data) {
- const table = config.table;
- if (!table) {
- throw new Error('Table is required for INSERT queries');
- }
- let insertData = config.insertData;
- if (typeof insertData === 'string') {
- insertData = smartbotic.utils.interpolate(insertData, data);
- try {
- insertData = JSON.parse(insertData);
- } catch (e) {
- throw new Error('Insert data must be valid JSON: ' + e.message);
- }
- }
- if (!insertData || typeof insertData !== 'object') {
- throw new Error('Insert data is required');
- }
- const columns = Object.keys(insertData);
- const values = Object.values(insertData);
- const placeholders = columns.map(() => '?').join(', ');
- const query = `INSERT INTO \`${table}\` (\`${columns.join('`, `')}\`) VALUES (${placeholders})`;
- return { query, params: values };
- }
- function buildUpdateQuery(config, data) {
- const table = config.table;
- if (!table) {
- throw new Error('Table is required for UPDATE queries');
- }
- let updateData = config.updateData;
- if (typeof updateData === 'string') {
- updateData = smartbotic.utils.interpolate(updateData, data);
- try {
- updateData = JSON.parse(updateData);
- } catch (e) {
- throw new Error('Update data must be valid JSON: ' + e.message);
- }
- }
- if (!updateData || typeof updateData !== 'object') {
- throw new Error('Update data is required');
- }
- const columns = Object.keys(updateData);
- const values = Object.values(updateData);
- const setClause = columns.map(col => `\`${col}\` = ?`).join(', ');
- let query = `UPDATE \`${table}\` SET ${setClause}`;
- if (config.whereClause) {
- const where = smartbotic.utils.interpolate(config.whereClause, data);
- query += ` WHERE ${where}`;
- }
- return { query, params: values };
- }
- function buildDeleteQuery(config, data) {
- const table = config.table;
- if (!table) {
- throw new Error('Table is required for DELETE queries');
- }
- let query = `DELETE FROM \`${table}\``;
- if (config.whereClause) {
- const where = smartbotic.utils.interpolate(config.whereClause, data);
- query += ` WHERE ${where}`;
- } else {
- throw new Error('WHERE clause is required for DELETE queries to prevent accidental data loss');
- }
- return { query, params: [] };
- }
- function buildCustomQuery(config, data, input) {
- if (!config.customQuery) {
- throw new Error('Custom query is required');
- }
- const query = smartbotic.utils.interpolate(config.customQuery, data);
- let params = input.parameters || config.parameters || [];
- if (params.length > 0) {
- params = params.map(p => {
- if (typeof p === 'string') {
- return smartbotic.utils.interpolate(p, data);
- }
- return p;
- });
- }
- return { query, params };
- }
- module.exports = {
- configSchema,
- inputSchema,
- outputSchema,
- outputs,
- async execute(config, input, context) {
- const credentialId = config.credentialId;
- const data = input.data || input;
- if (!credentialId) {
- throw new Error('MySQL credential is required');
- }
- let queryInfo;
- const queryType = config.queryType || 'select';
- switch (queryType) {
- case 'select':
- queryInfo = buildSelectQuery(config, data);
- break;
- case 'insert':
- queryInfo = buildInsertQuery(config, data);
- break;
- case 'update':
- queryInfo = buildUpdateQuery(config, data);
- break;
- case 'delete':
- queryInfo = buildDeleteQuery(config, data);
- break;
- case 'custom':
- queryInfo = buildCustomQuery(config, data, input);
- break;
- default:
- throw new Error('Invalid query type: ' + queryType);
- }
- const { query, params } = queryInfo;
- smartbotic.log.info(`MySQL ${queryType.toUpperCase()}: ${query.substring(0, 100)}...`);
- const options = {
- credentialId,
- database: config.database,
- query,
- params
- };
- const result = smartbotic.mysql.query(options);
- if (!result.success) {
- smartbotic.log.error(`MySQL query failed: ${result.error}`);
- throw new Error(`MySQL query failed: ${result.error}`);
- }
- smartbotic.log.info(`MySQL query completed: ${result.affectedRows || 0} affected, ${(result.rows || []).length} rows returned`);
- return {
- rows: result.rows || [],
- affectedRows: result.affectedRows || 0,
- insertId: result.insertId || null,
- columns: result.columns || [],
- query: query
- };
- }
- };
|