diff --git a/.gitignore b/.gitignore index 9daa824..4dab68d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .DS_Store node_modules +db +myt.config.yml \ No newline at end of file diff --git a/assets/myt.default.yml b/assets/myt.default.yml index ca5ac48..008abb8 100755 --- a/assets/myt.default.yml +++ b/assets/myt.default.yml @@ -6,6 +6,11 @@ mockFunctions: - mockTime - mockUtcTime sumViews: true +defaultDefiner: root@localhost +deprecMarkRegex: __$ +deprecCommentRegex: ^@deprecated [0-9]{4}-[0-9]{2}-[0-9]{2} +deprecDateRegex: '[0-9]{4}-[0-9]{2}-[0-9]{2}' +deprecRetentionPeriod: 60 # Days privileges: userTable: global_priv userWhere: >- diff --git a/myt-create.js b/myt-create.js index 1754d9a..84f282b 100755 --- a/myt-create.js +++ b/myt-create.js @@ -43,7 +43,7 @@ class Create extends Command { const params = { schema, name, - definer: 'root@localhost' + definer: opts.defaultDefiner }; switch (opts.type) { diff --git a/myt-version.js b/myt-version.js index 4fc74ce..9dba08f 100644 --- a/myt-version.js +++ b/myt-version.js @@ -1,6 +1,7 @@ const Myt = require('./myt'); const Command = require('./lib/command'); const fs = require('fs-extra'); +const SqlString = require('sqlstring'); /** * Creates a new version. @@ -18,7 +19,7 @@ class Version extends Command { static opts = { alias: { name: 'n', - deprecate: 'kk' + deprecate: 'p' }, string: [ 'name' @@ -128,12 +129,15 @@ class Version extends Command { ); await fs.mkdir(newVersionDir); - await fs.writeFile( - `${newVersionDir}/00-firstScript.sql`, - opts.deprecate - ? (this.emit('deprecate'), await deprecate(conn)) - : '-- Place your SQL code here\n' - ); + if (opts.deprecate) { + this.emit('deprecate'); + await deprecate(conn, opts, newVersionDir); + } + else + await fs.writeFile( + `${newVersionDir}/00-firstScript.sql`, + '-- Place your SQL code here\n' + ); this.emit('versionCreated', versionFolder); @@ -147,67 +151,103 @@ class Version extends Command { } } -async function deprecate(conn) { - const [[config]] = await conn.query(` - SELECT dateRegex, - deprecatedMarkRegex, - VN_CURDATE() - INTERVAL daysKeepDeprecatedObjects DAY dated - FROM config - `); +async function deprecate(conn, opts, newVersionDir) { + const now = new Date(); + const minDeprecDate = new Date(now.getTime() - opts.deprecRetentionPeriod * 24 * 60 * 60 * 1000); + const deprecMarkRegex = opts.deprecMarkRegex; + const deprecCommentRegex = opts.deprecCommentRegex; + const deprecDateRegex = opts.deprecDateRegex; + const filePath = `${newVersionDir}/00-deprecate.sql`; - if (!config || Object.values(config).some(value => value == null || value === '')) - throw new Error('missing configuration variables'); - - const [sql] = await conn.query(` - WITH variables AS ( - SELECT ? markRegex, ? dateRegex, ? dated - ) - SELECT CONCAT('ALTER TABLE ', c.TABLE_SCHEMA, '.', c.TABLE_NAME, ' DROP PRIMARY KEY;') "sql" + // Generate the drops of the primary keys + const [primaryKeys] = await conn.query(` + SELECT c.TABLE_SCHEMA 'schema', c.TABLE_NAME 'table' FROM information_schema.COLUMNS c LEFT JOIN information_schema.VIEWS v ON v.TABLE_SCHEMA = c.TABLE_SCHEMA AND v.TABLE_NAME = c.TABLE_NAME JOIN information_schema.STATISTICS s ON s.TABLE_SCHEMA = c.TABLE_SCHEMA AND s.TABLE_NAME = c.TABLE_NAME AND s.COLUMN_NAME = c.COLUMN_NAME - JOIN variables var - WHERE c.COLUMN_NAME REGEXP var.markRegex COLLATE utf8mb4_unicode_ci - AND REGEXP_SUBSTR(c.COLUMN_COMMENT, var.dateRegex COLLATE utf8mb4_unicode_ci) < var.dated + WHERE c.COLUMN_NAME REGEXP ? COLLATE utf8mb4_unicode_ci + AND c.COLUMN_COMMENT REGEXP ? COLLATE utf8mb4_unicode_ci + AND REGEXP_SUBSTR(c.COLUMN_COMMENT, ? COLLATE utf8mb4_unicode_ci) < ? AND v.TABLE_NAME IS NULL AND s.INDEX_NAME = 'PRIMARY' - UNION - SELECT CONCAT('ALTER TABLE ', c.TABLE_SCHEMA, '.', c.TABLE_NAME, ' DROP FOREIGN KEY ', kcu.CONSTRAINT_NAME, ';') + `, [deprecMarkRegex, deprecCommentRegex, deprecDateRegex, minDeprecDate]); + + primaryKeys.map(async row => { + await fs.appendFile( + filePath, + 'ALTER TABLE ' + SqlString.escapeId(row.schema, true) + '.' + + SqlString.escapeId(row.table, true) + ' DROP PRIMARY KEY;\n' + ); + }); + + // Generate the drops of the foreign keys + const [foreignKeys] = await conn.query(` + SELECT c.TABLE_SCHEMA 'schema', c.TABLE_NAME 'table', kcu.CONSTRAINT_NAME 'constraint' FROM information_schema.COLUMNS c LEFT JOIN information_schema.VIEWS v ON v.TABLE_SCHEMA = c.TABLE_SCHEMA AND v.TABLE_NAME = c.TABLE_NAME JOIN information_schema.KEY_COLUMN_USAGE kcu ON kcu.TABLE_SCHEMA = c.TABLE_SCHEMA AND kcu.TABLE_NAME = c.TABLE_NAME AND kcu.COLUMN_NAME = c.COLUMN_NAME - JOIN variables var - WHERE c.COLUMN_NAME REGEXP var.markRegex COLLATE utf8mb4_unicode_ci - AND REGEXP_SUBSTR(c.COLUMN_COMMENT, var.dateRegex COLLATE utf8mb4_unicode_ci) < var.dated + WHERE c.COLUMN_NAME REGEXP ? COLLATE utf8mb4_unicode_ci + AND c.COLUMN_COMMENT REGEXP ? COLLATE utf8mb4_unicode_ci + AND REGEXP_SUBSTR(c.COLUMN_COMMENT, ? COLLATE utf8mb4_unicode_ci) < ? AND v.TABLE_NAME IS NULL AND kcu.REFERENCED_COLUMN_NAME IS NOT NULL - UNION - SELECT CONCAT('ALTER TABLE ', c.TABLE_SCHEMA, '.', c.TABLE_NAME, ' DROP COLUMN ', c.COLUMN_NAME, ';') + `, [deprecMarkRegex, deprecCommentRegex, deprecDateRegex, minDeprecDate]); + + foreignKeys.map(async row => { + await fs.appendFile( + filePath, + 'ALTER TABLE ' + SqlString.escapeId(row.schema, true) + '.' + + SqlString.escapeId(row.table, true) + ' DROP FOREIGN KEY ' + + SqlString.escapeId(row.constraint, true) + ';\n' + ); + }); + + // Generate the drops of the columns + const [columns] = await conn.query(` + SELECT c.TABLE_SCHEMA 'schema', c.TABLE_NAME 'table', c.COLUMN_NAME 'column' FROM information_schema.COLUMNS c LEFT JOIN information_schema.VIEWS v ON v.TABLE_SCHEMA = c.TABLE_SCHEMA AND v.TABLE_NAME = c.TABLE_NAME LEFT JOIN information_schema.KEY_COLUMN_USAGE kcu ON kcu.TABLE_SCHEMA = c.TABLE_SCHEMA AND kcu.TABLE_NAME = c.TABLE_NAME AND kcu.COLUMN_NAME = c.COLUMN_NAME - JOIN variables var - WHERE c.COLUMN_NAME REGEXP var.markRegex COLLATE utf8mb4_unicode_ci - AND REGEXP_SUBSTR(c.COLUMN_COMMENT, var.dateRegex COLLATE utf8mb4_unicode_ci) < var.dated + WHERE c.COLUMN_NAME REGEXP ? COLLATE utf8mb4_unicode_ci + AND c.COLUMN_COMMENT REGEXP ? COLLATE utf8mb4_unicode_ci + AND REGEXP_SUBSTR(c.COLUMN_COMMENT, ? COLLATE utf8mb4_unicode_ci) row.sql).join('\n'); + columns.map(async row => { + await fs.appendFile( + filePath, + 'ALTER TABLE ' + SqlString.escapeId(row.schema, true) + '.' + + SqlString.escapeId(row.table, true) + ' DROP COLUMN ' + + SqlString.escapeId(row.column, true) + ';\n' + ); + }); + + // Generate the drops of the tables + const [tables] = await conn.query(` + SELECT TABLE_SCHEMA 'schema', TABLE_NAME 'table' + FROM information_schema.TABLES + WHERE TABLE_NAME REGEXP ? COLLATE utf8mb4_unicode_ci + AND TABLE_COMMENT REGEXP ? COLLATE utf8mb4_unicode_ci + AND REGEXP_SUBSTR(TABLE_COMMENT, ? COLLATE utf8mb4_unicode_ci) < ? + `, [deprecMarkRegex, deprecCommentRegex, deprecDateRegex, minDeprecDate]); + + tables.map(async row => { + await fs.appendFile( + filePath, + 'DROP TABLE ' + SqlString.escapeId(row.schema, true) + '.' + + SqlString.escapeId(row.table, true) + ';\n' + ); + }); } function randomName() {