pg-diff-cli
Advanced tools
+69
-55
@@ -16,5 +16,5 @@ #!/usr/bin/env node | ||
| var configName; | ||
| var scriptName; | ||
| var config; | ||
| global.configName = ''; | ||
| global.scriptName = ''; | ||
| global.config = null; | ||
@@ -30,8 +30,10 @@ __printIntro(); | ||
| function __printHelp() { | ||
| log(chalk.magenta("|==================|")); | ||
| log(chalk.magenta("|== pg-diff help ==|")); | ||
| log(chalk.magenta("|==================|")); | ||
| log(); | ||
| log(); | ||
| log(chalk.magenta("==============================")); | ||
| log(chalk.magenta("=== pg-diff-cli HELP ===")); | ||
| log(chalk.magenta("==============================")); | ||
| log(); | ||
| log(chalk.gray('OPTION \t\tDESCRIPTION')); | ||
| log(chalk.green('-h, --help \t\t') + chalk.blue('To show this help.')); | ||
| log(chalk.green('-h, --help\t\t') + chalk.blue('To show this help.')); | ||
| log(); | ||
@@ -42,2 +44,3 @@ log(); | ||
| log(); | ||
| log(); | ||
| } | ||
@@ -59,6 +62,7 @@ | ||
| log(chalk.gray('CONFIGURED OPTIONS')) | ||
| log(chalk.yellow(" Script Author: ") + chalk.green(config.options.author)); | ||
| log(chalk.yellow(" Output Directory: ") + chalk.green(path.resolve(process.cwd(), config.options.outputDirectory))); | ||
| log(chalk.yellow("Schema Namespaces: ") + chalk.green(config.options.schemaNamespace)); | ||
| log(chalk.yellow(" Data Compare: ") + chalk.green(config.options.dataCompare.enable ? 'ENABLED' : 'DISABLED')); | ||
| log(chalk.yellow(" Script Author: ") + chalk.green(global.config.options.author)); | ||
| log(chalk.yellow(" Output Directory: ") + chalk.green(path.resolve(process.cwd(), global.config.options.outputDirectory))); | ||
| log(chalk.yellow("Schema Namespaces: ") + chalk.green(global.config.options.schemaNamespace)); | ||
| log(chalk.yellow("Idempotent Script: ") + chalk.green(global.config.options.idempotent ? 'ENABLED' : 'DISABLED')); | ||
| log(chalk.yellow(" Data Compare: ") + chalk.green(global.config.options.dataCompare.enable ? 'ENABLED' : 'DISABLED')); | ||
| log(); | ||
@@ -69,3 +73,3 @@ } | ||
| var args = process.argv.slice(2); | ||
| if (args.length != 2) { | ||
| if (args.length <= 0) { | ||
| log(chalk.red('Missing arguments!')); | ||
@@ -76,12 +80,13 @@ __printHelp(); | ||
| switch (args[0]) { | ||
| case '-h': | ||
| case '--help': | ||
| { | ||
| if (args.length == 1) | ||
| switch (args[0]) { | ||
| case '-h': | ||
| case '--help': | ||
| __printHelp(); | ||
| process.exit(); | ||
| } | ||
| default: | ||
| configName = args[0]; | ||
| scriptName = args[1]; | ||
| } | ||
| if (args.length == 2) { | ||
| global.configName = args[0]; | ||
| global.scriptName = args[1]; | ||
| } | ||
@@ -92,23 +97,32 @@ } | ||
| try { | ||
| config = require(path.resolve(process.cwd(), 'pg-diff-config.json')); | ||
| if (!config[configName]) | ||
| throw new Error(`Impossible to find the configuration with name ${configName} !`); | ||
| let configFile = require(path.resolve(process.cwd(), 'pg-diff-config.json')); | ||
| if (!configFile[global.configName]) | ||
| throw new Error(`Impossible to find the configuration with name ${global.configName} !`); | ||
| config = config[configName]; | ||
| global.config = configFile[global.configName]; | ||
| if (!config.options) | ||
| if (!global.config.options) | ||
| throw new Error('The configuration section "options" must exists !'); | ||
| if (!config.options.outputDirectory) | ||
| throw new Error('The configuration section "options" must contains property "outputDirectory" !'); | ||
| if (!global.config.options.outputDirectory) | ||
| throw new Error('The configuration section "options" must contains property "outputDirectory (string)" !'); | ||
| if (!config.options.schemaNamespace) | ||
| throw new Error('The configuration section "options" must contains property "schemaNamespace" !'); | ||
| if (!global.config.options.schemaNamespace) | ||
| throw new Error('The configuration section "options" must contains property "schemaNamespace (array of strings)" !'); | ||
| if (!config.source) | ||
| throw new Error('The configuration doesn\'t contains the section "source" !'); | ||
| if (!global.config.options.hasOwnProperty('idempotent')) | ||
| throw new Error('The configuration section "options" must contains property "idempotent (boolean)" !'); | ||
| if (!config.target) | ||
| throw new Error('The configuration doesn\'t contains the section "target" !'); | ||
| if (!global.config.options.dataCompare) | ||
| throw new Error('The configuration section "options" must contains property "dataCompare (object)" !'); | ||
| if (!global.config.options.dataCompare.hasOwnProperty('enable')) | ||
| throw new Error('The configuration section "options.dataCompare" must contains property "enable (boolean)" !'); | ||
| if (!global.config.source) | ||
| throw new Error('The configuration doesn\'t contains the section "source (object)" !'); | ||
| if (!global.config.target) | ||
| throw new Error('The configuration doesn\'t contains the section "target (object)" !'); | ||
| } catch (e) { | ||
@@ -126,20 +140,20 @@ __handleError(e); | ||
| global.sourceClient = new Client({ | ||
| user: config.source.user, | ||
| host: config.source.host, | ||
| database: config.source.database, | ||
| password: config.source.password, | ||
| port: config.source.port, | ||
| user: global.config.source.user, | ||
| host: global.config.source.host, | ||
| database: global.config.source.database, | ||
| password: global.config.source.password, | ||
| port: global.config.source.port, | ||
| }) | ||
| global.sourceClient.connect(); | ||
| log(chalk.whiteBright(`Connected to [${config.source.host}:${config.source.port}/${config.source.database}] `) + chalk.green('✓')); | ||
| log(chalk.whiteBright(`Connected to [${global.config.source.host}:${global.config.source.port}/${global.config.source.database}] `) + chalk.green('✓')); | ||
| global.targetClient = new Client({ | ||
| user: config.target.user, | ||
| host: config.target.host, | ||
| database: config.target.database, | ||
| password: config.target.password, | ||
| port: config.target.port, | ||
| user: global.config.target.user, | ||
| host: global.config.target.host, | ||
| database: global.config.target.database, | ||
| password: global.config.target.password, | ||
| port: global.config.target.port, | ||
| }); | ||
| global.targetClient.connect(); | ||
| log(chalk.whiteBright(`Connected to [${config.target.host}:${config.target.port}/${config.target.database}] `) + chalk.green('✓')); | ||
| log(chalk.whiteBright(`Connected to [${global.config.target.host}:${global.config.target.port}/${global.config.target.database}] `) + chalk.green('✓')); | ||
@@ -153,3 +167,3 @@ spinner.stop(); | ||
| log(chalk.yellow("Collect SOURCE database objects")); | ||
| let sourceSchema = await schema.collectSchemaObjects(sourceClient, config.options.schemaNamespace); | ||
| let sourceSchema = await schema.collectSchemaObjects(sourceClient, global.config.options.schemaNamespace); | ||
@@ -159,3 +173,3 @@ log(); | ||
| log(chalk.yellow("Collect TARGET database objects")); | ||
| let targetSchema = await schema.collectSchemaObjects(targetClient, config.options.schemaNamespace); | ||
| let targetSchema = await schema.collectSchemaObjects(targetClient, global.config.options.schemaNamespace); | ||
@@ -169,3 +183,3 @@ log(); | ||
| if (config.options.dataCompare.enable) { | ||
| if (global.config.options.dataCompare.enable) { | ||
| global.dataTypes = (await sourceClient.query(`SELECT oid, typcategory FROM pg_type`)).rows; | ||
@@ -176,3 +190,3 @@ | ||
| log(chalk.yellow("Collect SOURCE tables records")); | ||
| let sourceTablesRecords = await data.collectTablesRecords(sourceClient, config.options.dataCompare.tables); | ||
| let sourceTablesRecords = await data.collectTablesRecords(sourceClient, global.config.options.dataCompare.tables); | ||
@@ -182,3 +196,3 @@ log(); | ||
| log(chalk.yellow("Collect TARGET tables records")); | ||
| let targetTablesRecords = await data.collectTablesRecords(targetClient, config.options.dataCompare.tables); | ||
| let targetTablesRecords = await data.collectTablesRecords(targetClient, global.config.options.dataCompare.tables); | ||
@@ -188,3 +202,3 @@ log(); | ||
| log(chalk.yellow("Compare SOURCE with TARGET database table records")); | ||
| scripts = scripts.concat(compareRecords.compareTablesRecords(config.options.dataCompare.tables, sourceTablesRecords, targetTablesRecords)); | ||
| scripts = scripts.concat(compareRecords.compareTablesRecords(global.config.options.dataCompare.tables, sourceTablesRecords, targetTablesRecords)); | ||
| } else { | ||
@@ -226,4 +240,4 @@ log(); | ||
| const now = new Date() | ||
| const fileName = `${(now).toISOString().replace(/[-:\.TZ]/g, '')}_${scriptName}.sql`; | ||
| const scriptPath = path.resolve(process.cwd(), config.options.outputDirectory, fileName); | ||
| const fileName = `${(now).toISOString().replace(/[-:\.TZ]/g, '')}_${global.scriptName}.sql`; | ||
| const scriptPath = path.resolve(process.cwd(), global.config.options.outputDirectory, fileName); | ||
@@ -236,6 +250,6 @@ var file = fs.createWriteStream(scriptPath); | ||
| let titleLength = config.options.author.length > (now).toISOString().length ? config.options.author.length : (now).toISOString().length; | ||
| let titleLength = global.config.options.author.length > (now).toISOString().length ? global.config.options.author.length : (now).toISOString().length; | ||
| file.write(`/******************${'*'.repeat(titleLength+2)}***/\n`); | ||
| file.write(`/*** SCRIPT AUTHOR: ${config.options.author.padEnd(titleLength)} ***/\n`); | ||
| file.write(`/*** SCRIPT AUTHOR: ${global.config.options.author.padEnd(titleLength)} ***/\n`); | ||
| file.write(`/*** CREATED ON: ${(now).toISOString().padEnd(titleLength)} ***/\n`); | ||
@@ -242,0 +256,0 @@ file.write(`/******************${'*'.repeat(titleLength+2)}***/\n`); |
+1
-1
| { | ||
| "name": "pg-diff-cli", | ||
| "version": "1.0.2", | ||
| "version": "1.0.3", | ||
| "description": "PostgreSQL schema and data comparing tool", | ||
@@ -5,0 +5,0 @@ "pgver": "9.6+", |
@@ -21,2 +21,3 @@ { | ||
| "author": "@MSO - Michael Sogos", | ||
| "idempotent": false, | ||
| "dataCompare": { | ||
@@ -23,0 +24,0 @@ "enable": true, |
@@ -62,3 +62,3 @@ const hints = { | ||
| generateCreateSchemaScript: function(schema, owner) { | ||
| let script = `\nCREATE SCHEMA ${schema} AUTHORIZATION ${owner};\n`; | ||
| let script = `\nCREATE ${global.config.options.idempotent?'SCHEMA IF NOT EXISTS':'SCHEMA'} ${schema} AUTHORIZATION ${owner};\n`; | ||
| //console.log(script); | ||
@@ -85,3 +85,9 @@ return script; | ||
| for (let index in schema.indexes) { | ||
| indexes.push(`\n${schema.indexes[index].definition};\n`); | ||
| let definition = schema.indexes[index].definition; | ||
| if (global.config.options.idempotent) { | ||
| definition = definition.replace('CREATE INDEX', 'CREATE INDEX IF NOT EXISTS'); | ||
| definition = definition.replace('CREATE UNIQUE INDEX', 'CREATE UNIQUE INDEX IF NOT EXISTS'); | ||
| } | ||
| indexes.push(`\n${definition};\n`); | ||
| } | ||
@@ -91,3 +97,3 @@ | ||
| let privileges = []; | ||
| privileges.push(`ALTER TABLE ${table} OWNER TO ${schema.owner};\n`); | ||
| privileges.push(`ALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} OWNER TO ${schema.owner};\n`); | ||
| for (let role in schema.privileges) { | ||
@@ -97,3 +103,3 @@ privileges = privileges.concat(this.__generateTableGrantsDefinition(table, role, schema.privileges[role])) | ||
| let script = `\nCREATE TABLE ${table} (\n\t${columns.join(',\n\t')}\n)\n${options};\n${indexes.join('\n')}\n${privileges.join('\n')}\n` | ||
| let script = `\nCREATE ${global.config.options.idempotent?'TABLE IF NOT EXISTS ':'TABLE'} ${table} (\n\t${columns.join(',\n\t')}\n)\n${options};\n${indexes.join('\n')}\n${privileges.join('\n')}\n` | ||
| //console.log(script) | ||
@@ -103,3 +109,3 @@ return script; | ||
| generateAddTableColumnScript: function(table, column, schema) { | ||
| let script = `\nALTER TABLE ${table} ADD COLUMN ${this.__generateColumnDefinition(column, schema)};` | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} ADD ${global.config.options.idempotent?'COLUMN IF NOT EXISTS':'COLUMN'} ${this.__generateColumnDefinition(column, schema)};` | ||
| if (script.includes('NOT NULL') && !script.includes('DEFAULT')) | ||
@@ -126,3 +132,3 @@ script += hints.addColumnNotNullableWithoutDefaultValue; | ||
| let script = `\nALTER TABLE ${table}\n\t${definitions.join(',\n\t')};\n` | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table}\n\t${definitions.join(',\n\t')};\n` | ||
@@ -136,3 +142,3 @@ //console.log(script); | ||
| generateDropTableColumnScript: function(table, column) { | ||
| let script = `\nALTER TABLE ${table} DROP COLUMN ${column} CASCADE;${hints.dropColumn}\n`; | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} DROP ${global.config.options.idempotent?'COLUMN IF EXISTS':'COLUMN'} ${column} CASCADE;${hints.dropColumn}\n`; | ||
| //console.log(script); | ||
@@ -142,3 +148,3 @@ return script; | ||
| generateAddTableConstraintScript: function(table, constraint, schema) { | ||
| let script = `\nALTER TABLE ${table} ADD CONSTRAINT ${constraint} ${schema.definition};\n`; | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} ADD CONSTRAINT ${constraint} ${schema.definition};\n`; | ||
| //console.log(script); | ||
@@ -148,3 +154,3 @@ return script; | ||
| generateChangeTableConstraintScript: function(table, constraint, schema) { | ||
| let script = `\nALTER TABLE ${table} DROP CONSTRAINT ${constraint}, ADD CONSTRAINT ${constraint} ${schema.definition};\n`; | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} DROP ${global.config.options.idempotent?'CONSTRAINT IF EXISTS':'CONSTRAINT'} ${constraint}, ADD CONSTRAINT ${constraint} ${schema.definition};\n`; | ||
| //console.log(script); | ||
@@ -154,3 +160,3 @@ return script; | ||
| generateDropTableConstraintScript: function(table, constraint) { | ||
| let script = `\nALTER TABLE ${table} DROP CONSTRAINT ${constraint};\n`; | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} DROP ${global.config.options.idempotent?'CONSTRAINT IF EXISTS':'CONSTRAINT'} ${constraint};\n`; | ||
| //console.log(script); | ||
@@ -160,3 +166,3 @@ return script; | ||
| generateChangeTableOptionsScript: function(table, options) { | ||
| let script = `\nALTER TABLE ${table} SET ${options.withOids?'WITH':'WITHOUT'} OIDS;\n`; | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} SET ${options.withOids?'WITH':'WITHOUT'} OIDS;\n`; | ||
| //console.log(script); | ||
@@ -166,3 +172,3 @@ return script; | ||
| generateChangeIndexScript: function(index, definition) { | ||
| let script = `\nDROP INDEX ${index};\n${definition};\n`; | ||
| let script = `\nDROP ${global.config.options.idempotent?'INDEX IF EXISTS':'INDEX'} ${index};\n${definition};\n`; | ||
| //console.log(script); | ||
@@ -172,3 +178,3 @@ return script; | ||
| generateDropIndexScript: function(index) { | ||
| let script = `\nDROP INDEX ${index};\n`; | ||
| let script = `\nDROP ${global.config.options.idempotent?'INDEX IF EXISTS':'INDEX'} ${index};\n`; | ||
| //console.log(script); | ||
@@ -211,3 +217,3 @@ return script; | ||
| generateChangeTableOwnerScript: function(table, owner) { | ||
| let script = `\nALTER TABLE ${table} OWNER TO ${owner};\n`; | ||
| let script = `\nALTER ${global.config.options.idempotent?'TABLE IF EXISTS':'TABLE'} ${table} OWNER TO ${owner};\n`; | ||
| //console.log(script); | ||
@@ -219,3 +225,3 @@ return script; | ||
| let privileges = []; | ||
| privileges.push(`ALTER TABLE ${view} OWNER TO ${schema.owner};`); | ||
| privileges.push(`ALTER ${global.config.options.idempotent?'VIEW IF EXISTS':'VIEW'} ${view} OWNER TO ${schema.owner};`); | ||
| for (let role in schema.privileges) { | ||
@@ -225,3 +231,3 @@ privileges = privileges.concat(this.__generateTableGrantsDefinition(view, role, schema.privileges[role])) | ||
| let script = `\nCREATE VIEW ${view} AS ${schema.definition}\n${privileges.join('\n')}\n`; | ||
| let script = `\nCREATE ${global.config.options.idempotent? 'OR REPLACE VIEW':'VIEW'} ${view} AS ${schema.definition}\n${privileges.join('\n')}\n`; | ||
| //console.log(script) | ||
@@ -231,3 +237,3 @@ return script; | ||
| generateChangeViewScript: function(view, schema) { | ||
| let script = `\nDROP VIEW ${view};\n${this.generateCreateViewScript(view,schema)}`; | ||
| let script = `\nDROP ${global.config.options.idempotent?'VIEW IF EXISTS':'VIEW'} ${view};\n${this.generateCreateViewScript(view,schema)}`; | ||
| //console.log(script) | ||
@@ -246,3 +252,3 @@ return script; | ||
| let privileges = []; | ||
| privileges.push(`ALTER TABLE ${view} OWNER TO ${schema.owner};\n`); | ||
| privileges.push(`ALTER ${global.config.options.idempotent?'MATERIALIZED VIEW IF EXISTS':'MATERIALIZED VIEW'} ${view} OWNER TO ${schema.owner};\n`); | ||
| for (let role in schema.privileges) { | ||
@@ -252,3 +258,3 @@ privileges = privileges.concat(this.__generateTableGrantsDefinition(view, role, schema.privileges[role])) | ||
| let script = `\nCREATE MATERIALIZED VIEW ${view} AS ${schema.definition}\n${indexes.join('\n')}\n${privileges.join('\n')}\n`; | ||
| let script = `\nCREATE ${global.config.options.idempotent?'MATERIALIZED VIEW IF NOT EXISTS':'MATERIALIZED VIEW'} ${view} AS ${schema.definition}\n${indexes.join('\n')}\n${privileges.join('\n')}\n`; | ||
| //console.log(script) | ||
@@ -258,3 +264,3 @@ return script; | ||
| generateChangeMaterializedViewScript: function(view, schema) { | ||
| let script = `\nDROP MATERIALIZED VIEW ${view};\n${this.generateCreateMaterializedViewScript(view,schema)}`; | ||
| let script = `\nDROP ${global.config.options.idempotent?'MATERIALIZED VIEW IF EXISTS':'MATERIALIZED VIEW'} ${view};\n${this.generateCreateMaterializedViewScript(view,schema)}`; | ||
| //console.log(script) | ||
@@ -276,3 +282,3 @@ return script; | ||
| generateChangeProcedureScript: function(procedure, schema) { | ||
| let script = `\nDROP FUNCTION ${procedure}(${schema.argTypes});\n${this.generateCreateProcedureScript(procedure,schema)}`; | ||
| let script = `\nDROP ${global.config.options.idempotent?'FUNCTION IF EXISTS':'FUNCTION'} ${procedure}(${schema.argTypes});\n${this.generateCreateProcedureScript(procedure,schema)}`; | ||
| //console.log(script) | ||
@@ -279,0 +285,0 @@ return script; |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
74383
4.09%1283
1.26%0
-100%