const { driverBase } = global.DBGATE_PACKAGES['dbgate-tools']; const { mysqlSplitterOptions } = require('dbgate-query-splitter/lib/options'); const Dumper = require('./Dumper'); const spatialTypes = [ 'POINT', 'LINESTRING', 'POLYGON', 'GEOMETRY', 'MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON', 'GEOMCOLLECTION', 'GEOMETRYCOLLECTION', ]; /** @type {import('dbgate-types').SqlDialect} */ const dialect = { rangeSelect: true, stringEscapeChar: '\\', fallbackDataType: 'longtext', enableConstraintsPerTable: false, anonymousPrimaryKey: true, explicitDropConstraint: true, allowMultipleValuesInsert: true, quoteIdentifier(s) { return '`' + s + '`'; }, createColumn: true, dropColumn: true, changeColumn: true, changeAutoIncrement: true, createIndex: true, dropIndex: true, createForeignKey: true, dropForeignKey: true, createPrimaryKey: true, dropPrimaryKey: true, dropIndexContainsTableSpec: true, createUnique: true, dropUnique: true, createCheck: true, dropCheck: true, dropReferencesWhenDropTable: false, requireStandaloneSelectForScopeIdentity: true, columnProperties: { columnComment: true, isUnsigned: true, isZerofill: true, }, predefinedDataTypes: [ 'char(20)', 'varchar(250)', 'binary(250)', 'varbinary(250)', 'tinyblob', 'tinytext', 'text(1000)', 'blob(1000)', 'mediumtext', 'mediumblob', 'longtext', 'longblob', "enum('val1','val2','val3')", "set('val1','val2','val3')", 'bit(32)', 'tinyint', 'bool', 'smallint', 'mediumint', 'int', 'bigint', 'float', 'double', 'decimal', 'date', 'datetime', 'timestamp', 'time', 'year', ], createColumnViewExpression(columnName, dataType, source, alias) { if (dataType && spatialTypes.includes(dataType.toUpperCase())) { return { exprType: 'call', func: 'ST_AsText', alias: alias || columnName, args: [ { exprType: 'column', columnName, source, }, ], }; } }, getSupportedEngines() { return []; }, getTableFormOptions(intent) { return [ { type: 'dropdowntext', options: this.getSupportedEngines(), label: 'Engine', name: 'tableEngine', sqlFormatString: '^engine = %s', }, { type: 'text', label: 'Comment', name: 'objectComment', sqlFormatString: '^comment = %v', allowEmptyValue: true, }, ]; }, }; const mysqlDialect = { ...dialect, getSupportedEngines() { const mysqlEngines = [ 'InnoDB', // Default and most commonly used engine with ACID transaction support and referential integrity. 'MyISAM', // Older engine without transaction or referential integrity support. 'MEMORY', // Tables stored in memory, very fast but volatile, used for temporary data. 'CSV', // Tables stored in CSV format, useful for import/export of data. 'ARCHIVE', // Engine for storing large amounts of historical data with compression. 'BLACKHOLE', // Engine that discards data, useful for replication. 'FEDERATED', // Access tables on remote MySQL servers. 'MRG_MYISAM', // Merges multiple MyISAM tables into one. 'NDB', // Cluster storage engine for MySQL Cluster. 'EXAMPLE', // Example engine for developers, has no real functionality. 'PERFORMANCE_SCHEMA', // Engine used for performance monitoring in MySQL. 'SEQUENCE', // Special engine for sequences, used in MariaDB. 'SPIDER', // Engine for horizontal partitioning, often used in MariaDB. 'ROCKSDB', // Engine optimized for read-heavy workloads, commonly used in Facebook MySQL. 'TokuDB', // Engine with high data compression and SSD optimization. ]; return mysqlEngines; }, }; const mysqlDriverBase = { ...driverBase, showConnectionField: (field, values) => { if (['authType', 'user', 'defaultDatabase', 'singleDatabase', 'isReadOnly'].includes(field)) { return true; } if (values.authType == 'awsIam') { return ['awsRegion', 'secretAccessKey', 'accessKeyId', 'server', 'port'].includes(field); } if (['password'].includes(field)) { return true; } if (values.authType == 'socket') { return ['socketPath'].includes(field); } return ['server', 'port'].includes(field); }, dumperClass: Dumper, defaultPort: 3306, getQuerySplitterOptions: usage => usage == 'editor' ? { ...mysqlSplitterOptions, ignoreComments: true, preventSingleLineSplit: true } : mysqlSplitterOptions, readOnlySessions: true, supportsDatabaseBackup: true, supportsDatabaseRestore: true, authTypeLabel: 'Connection mode', defaultAuthTypeName: 'hostPort', defaultSocketPath: '/var/run/mysqld/mysqld.sock', supportsTransactions: true, getNewObjectTemplates() { return [ { label: 'New view', sql: 'CREATE VIEW myview\nAS\nSELECT * FROM table1' }, { label: 'New procedure', sql: 'DELIMITER //\n\nCREATE PROCEDURE myproc (IN arg1 INT)\nBEGIN\n SELECT * FROM table1;\nEND\n\nDELIMITER ;', }, { label: 'New function', sql: 'CREATE FUNCTION myfunc (arg1 INT)\nRETURNS INT DETERMINISTIC\nRETURN 1' }, { label: 'New trigger', sql: 'CREATE TRIGGER myTrigger AFTER INSERT ON myTable FOR EACH ROW BEGIN\n END' }, { label: 'New event', sql: 'CREATE EVENT `event_name`\nON SCHEDULE EVERY 1 HOUR\nDO\nBEGIN\n\nEND', }, ]; }, getCliConnectionArgs(connection, externalTools) { const args = [`--user=${connection.user}`, `--password=${connection.password}`, `--host=${connection.server}`]; if (connection.port) { args.push(`--port=${connection.port}`); } if (externalTools.mysqlPlugins) { args.push(`--plugin-dir=${externalTools.mysqlPlugins}`); } if (connection.server == 'localhost') { args.push(`--protocol=tcp`); } return args; }, backupDatabaseCommand(connection, settings, externalTools) { const { outputFile, database, skippedTables, options } = settings; const command = externalTools.mysqldump || 'mysqldump'; const args = this.getCliConnectionArgs(connection, externalTools); args.push(`--result-file=${outputFile}`); args.push('--verbose'); for (const table of skippedTables) { args.push(`--ignore-table=${database}.${table.pureName}`); } if (options.noData) { args.push('--no-data'); } if (options.noStructure) { args.push('--no-create-info'); } if (options.includeEvents !== false && !options.noStructure) { args.push('--events'); } if (options.includeRoutines !== false && !options.noStructure) { args.push('--routines'); } if (options.includeTriggers !== false && !options.noStructure) { args.push('--triggers'); } if (options.force) { args.push('--force'); } if (options.lockTables) { args.push('--lock-tables'); } if (options.skipLockTables) { args.push('--skip-lock-tables'); } if (options.singleTransaction) { args.push('--single-transaction'); } if (options.customArgs?.trim()) { const customArgs = options.customArgs.split(/\s+/).filter(arg => arg.trim() != ''); args.push(...customArgs); } if (options.createDatabase) { args.push('--databases', database); if (options.dropDatabase) { args.push('--add-drop-database'); } } else { args.push(database); } return { command, args }; }, restoreDatabaseCommand(connection, settings, externalTools) { const { inputFile, database } = settings; const command = externalTools.mysql || 'mysql'; const args = this.getCliConnectionArgs(connection, externalTools); if (database) { args.push(database); } return { command, args, stdinFilePath: inputFile }; }, transformNativeCommandMessage(message) { if (message.message?.startsWith('--')) { if (message.message.startsWith('-- Retrieving table structure for table')) { return { ...message, severity: 'info', message: message.message.replace('-- Retrieving table structure for table', 'Processing table'), }; } else { return { ...message, severity: 'debug', message: message.message.replace('-- ', ''), }; } } return message; }, getNativeOperationFormArgs(operation) { if (operation == 'backup') { return [ { type: 'checkbox', label: 'No data (dump only structure)', name: 'noData', default: false, }, { type: 'checkbox', label: 'No structure (dump only data)', name: 'noStructure', default: false, }, { type: 'checkbox', label: 'Force (ignore all errors)', name: 'force', default: false, }, { type: 'checkbox', label: 'Backup events', name: 'includeEvents', default: true, disabledFn: values => values.noStructure, }, { type: 'checkbox', label: 'Backup routines', name: 'includeRoutines', default: true, disabledFn: values => values.noStructure, }, { type: 'checkbox', label: 'Backup triggers', name: 'includeTriggers', default: true, disabledFn: values => values.noStructure, }, { type: 'checkbox', label: 'Lock tables', name: 'lockTables', default: false, disabledFn: values => values.skipLockTables || values.singleTransaction, }, { type: 'checkbox', label: 'Skip lock tables', name: 'skipLockTables', default: false, disabledFn: values => values.lockTables || values.singleTransaction, }, { type: 'checkbox', label: 'Single transaction', name: 'singleTransaction', default: false, disabledFn: values => values.lockTables || values.skipLockTables, }, { type: 'checkbox', label: 'Create database', name: 'createDatabase', default: false, }, { type: 'checkbox', label: 'Drop database before import', name: 'dropDatabase', default: false, disabledFn: values => !values.createDatabase, }, { type: 'text', label: 'Custom arguments', name: 'customArgs', }, ]; } return null; }, adaptDataType(dataType) { if (dataType?.toLowerCase() == 'money') return 'decimal(15,2)'; return dataType; }, }; /** @type {import('dbgate-types').EngineDriver} */ const mysqlDriver = { ...mysqlDriverBase, dialect: mysqlDialect, engine: 'mysql@dbgate-plugin-mysql', title: 'MySQL', __analyserInternals: { quoteDefaultValues: true, }, }; const mariaDbDialect = { ...dialect, getSupportedEngines() { const mariaDBEngines = [ 'InnoDB', // Main transactional engine, similar to MySQL, supports ACID transactions and referential integrity. 'Aria', // Replacement for MyISAM, supports crash recovery and optimized for high speed. 'MyISAM', // Older engine without transaction support, still supported for compatibility. 'MEMORY', // Tables stored in memory, suitable for temporary data. 'CSV', // Stores data in CSV format, easy for export/import. 'ARCHIVE', // Stores compressed data, suitable for historical records. 'BLACKHOLE', // Engine that does not store data, often used for replication. 'FEDERATED', // Allows access to tables on remote MariaDB/MySQL servers. 'MRG_MyISAM', // Allows merging multiple MyISAM tables into one. 'SEQUENCE', // Special engine for generating sequences. 'SphinxSE', // Engine for full-text search using Sphinx. 'SPIDER', // Engine for sharding, supports horizontal partitioning. 'TokuDB', // High-compression engine optimized for large data sets and SSDs. 'RocksDB', // Read-optimized engine focused on performance with large data. 'CONNECT', // Engine for accessing external data sources (e.g., files, web services). 'OQGRAPH', // Graph engine, suitable for hierarchical and graph structures. 'ColumnStore', // Analytical engine for columnar data storage, suitable for Big Data. 'Mroonga', // Engine supporting full-text search in Japanese and other languages. 'S3', // Allows storing data in Amazon S3-compatible storage. 'XtraDB', // Enhanced InnoDB engine with optimizations from Percona (commonly used in older MariaDB versions). ]; return mariaDBEngines; }, }; /** @type {import('dbgate-types').EngineDriver} */ const mariaDriver = { ...mysqlDriverBase, dialect: mariaDbDialect, engine: 'mariadb@dbgate-plugin-mysql', title: 'MariaDB', __analyserInternals: { quoteDefaultValues: false, }, }; module.exports = [mysqlDriver, mariaDriver];