169 lines
6.0 KiB
JavaScript
169 lines
6.0 KiB
JavaScript
const mysql = require('mysql2/promise');
|
||
require('dotenv').config();
|
||
|
||
async function main() {
|
||
console.log('Comparing database structures between "igcrm" (local) and "crmlive1" (live)...');
|
||
|
||
// Parse credentials from local DATABASE_URL or default to root@127.0.0.1:3306
|
||
let connectionConfig = {
|
||
host: '127.0.0.1',
|
||
port: 3306,
|
||
user: 'root',
|
||
password: '',
|
||
};
|
||
|
||
const dbUrl = process.env.DATABASE_URL;
|
||
if (dbUrl) {
|
||
try {
|
||
const url = dbUrl.replace('mysql://', '');
|
||
const [auth, hostPortDb] = url.split('@');
|
||
const [user, password] = auth.split(':');
|
||
const [hostPort, database] = hostPortDb.split('/');
|
||
const [host, port] = hostPort.split(':');
|
||
connectionConfig.user = user;
|
||
connectionConfig.password = password || '';
|
||
connectionConfig.host = host;
|
||
connectionConfig.port = parseInt(port) || 3306;
|
||
} catch (e) {
|
||
console.log('Error parsing DATABASE_URL, using defaults.');
|
||
}
|
||
}
|
||
|
||
console.log(`Connecting to MySQL on ${connectionConfig.host}:${connectionConfig.port}...`);
|
||
|
||
try {
|
||
const connection = await mysql.createConnection(connectionConfig);
|
||
console.log('Connected successfully!');
|
||
|
||
// Check if databases exist
|
||
const [dbs] = await connection.query('SHOW DATABASES');
|
||
const dbNames = dbs.map(d => d.Database);
|
||
|
||
const localDb = 'igcrm';
|
||
const liveDb = 'crmlive1';
|
||
|
||
if (!dbNames.includes(localDb)) {
|
||
console.error(`Local database "${localDb}" not found! Available databases:`, dbNames);
|
||
await connection.end();
|
||
return;
|
||
}
|
||
if (!dbNames.includes(liveDb)) {
|
||
console.error(`Live database "${liveDb}" not found! Available databases:`, dbNames);
|
||
await connection.end();
|
||
return;
|
||
}
|
||
|
||
console.log(`Querying information_schema for ${localDb} and ${liveDb}...`);
|
||
|
||
// Fetch column definitions for local
|
||
const [localCols] = await connection.query(`
|
||
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_KEY
|
||
FROM information_schema.columns
|
||
WHERE TABLE_SCHEMA = ?
|
||
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
||
`, [localDb]);
|
||
|
||
// Fetch column definitions for live
|
||
const [liveCols] = await connection.query(`
|
||
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_KEY
|
||
FROM information_schema.columns
|
||
WHERE TABLE_SCHEMA = ?
|
||
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
||
`, [liveDb]);
|
||
|
||
await connection.end();
|
||
|
||
// Map columns to structured objects
|
||
const localSchema = {};
|
||
localCols.forEach(col => {
|
||
if (!localSchema[col.TABLE_NAME]) localSchema[col.TABLE_NAME] = {};
|
||
localSchema[col.TABLE_NAME][col.COLUMN_NAME] = col;
|
||
});
|
||
|
||
const liveSchema = {};
|
||
liveCols.forEach(col => {
|
||
if (!liveSchema[col.TABLE_NAME]) liveSchema[col.TABLE_NAME] = {};
|
||
liveSchema[col.TABLE_NAME][col.COLUMN_NAME] = col;
|
||
});
|
||
|
||
console.log('\n--- SCHEMA COMPARISON RESULTS ---\n');
|
||
|
||
// 1. Check for missing tables
|
||
const localTables = Object.keys(localSchema);
|
||
const liveTables = Object.keys(liveSchema);
|
||
|
||
const missingInLive = localTables.filter(t => !liveTables.includes(t));
|
||
const extraInLive = liveTables.filter(t => !localTables.includes(t));
|
||
|
||
if (missingInLive.length > 0) {
|
||
console.log(`❌ Tables in Local (${localDb}) but missing in Live (${liveDb}):`, missingInLive);
|
||
}
|
||
if (extraInLive.length > 0) {
|
||
console.log(`ℹ️ Tables in Live (${liveDb}) but missing in Local (${localDb}) (pre-existing/extra):`, extraInLive);
|
||
}
|
||
|
||
// 2. Compare shared tables
|
||
const sharedTables = localTables.filter(t => liveTables.includes(t));
|
||
let differencesFound = false;
|
||
|
||
sharedTables.forEach(tableName => {
|
||
const localTab = localSchema[tableName];
|
||
const liveTab = liveSchema[tableName];
|
||
|
||
const localColsList = Object.keys(localTab);
|
||
const liveColsList = Object.keys(liveTab);
|
||
|
||
const missingColsInLive = localColsList.filter(c => !liveColsList.includes(c));
|
||
const extraColsInLive = liveColsList.filter(c => !localColsList.includes(c));
|
||
|
||
let tableDiffHeaderPrinted = false;
|
||
const printTableDiffHeader = () => {
|
||
if (!tableDiffHeaderPrinted) {
|
||
console.log(`\nTable: "${tableName}"`);
|
||
tableDiffHeaderPrinted = true;
|
||
differencesFound = true;
|
||
}
|
||
};
|
||
|
||
if (missingColsInLive.length > 0) {
|
||
printTableDiffHeader();
|
||
console.log(` ❌ Columns in Local but MISSING in Live:`, missingColsInLive);
|
||
}
|
||
if (extraColsInLive.length > 0) {
|
||
printTableDiffHeader();
|
||
console.log(` ℹ️ Columns in Live but MISSING in Local:`, extraColsInLive);
|
||
}
|
||
|
||
// Compare details of shared columns
|
||
const sharedCols = localColsList.filter(c => liveColsList.includes(c));
|
||
sharedCols.forEach(colName => {
|
||
const localCol = localTab[colName];
|
||
const liveCol = liveTab[colName];
|
||
|
||
const typeDiff = localCol.COLUMN_TYPE !== liveCol.COLUMN_TYPE;
|
||
const nullDiff = localCol.IS_NULLABLE !== liveCol.IS_NULLABLE;
|
||
const defaultDiff = localCol.COLUMN_DEFAULT !== liveCol.COLUMN_DEFAULT;
|
||
const keyDiff = localCol.COLUMN_KEY !== liveCol.COLUMN_KEY;
|
||
|
||
if (typeDiff || nullDiff || defaultDiff || keyDiff) {
|
||
printTableDiffHeader();
|
||
console.log(` Column: "${colName}" has differences:`);
|
||
if (typeDiff) console.log(` - Type: Local="${localCol.COLUMN_TYPE}", Live="${liveCol.COLUMN_TYPE}"`);
|
||
if (nullDiff) console.log(` - Nullable: Local="${localCol.IS_NULLABLE}", Live="${liveCol.IS_NULLABLE}"`);
|
||
if (defaultDiff) console.log(` - Default: Local="${localCol.COLUMN_DEFAULT}", Live="${liveCol.COLUMN_DEFAULT}"`);
|
||
if (keyDiff) console.log(` - Key: Local="${localCol.COLUMN_KEY}", Live="${liveCol.COLUMN_KEY}"`);
|
||
}
|
||
});
|
||
});
|
||
|
||
if (!differencesFound && missingInLive.length === 0 && extraInLive.length === 0) {
|
||
console.log('✅ Local and Live schemas are identical!');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Comparison failed:', error);
|
||
}
|
||
}
|
||
|
||
main();
|