flexgres
Version:
Flexibly change your Postgres schemas with Flexgres.
310 lines (273 loc) • 8.89 kB
JavaScript
/* Getting Started
** Before you test this script make sure you run two commands in PSQL
** 1) `CREATE USER chris WITH PASSWORD '12345'`
** 2) `CREATE DATABASE flexgres`
** You should now be able to test flexgres schemas
*/
var async = require("async");
module.exports = function(config, cb) {
//These are mandatory
if (!config.driver) throw "Must supply driver.";
if (!config.tables) throw "Must supply table structure.";
var client = config.driver;
var tables = config.tables;
//Optional features
var logging = config.logging || false;
//This is for the diffing computation
var diffs = [];
var diffsTbl = [];
//Run the query
client.connect(function(err) {
if (err) throw err;
async.series([
//Find diffs in existing tables
function(callback) {
async.eachSeries(config.tables, function(table,callbackEach) {
var q =
`
SELECT * FROM information_schema.columns
WHERE table_name = '`+ table.tableName +`'
`;
if (logging == true) console.log(q);
client.query(q, function(err, result) {
if (err) throw err
result.rows.map(function(row) {
diffs.push({
"table": row.table_name,
"column": row.column_name,
"null": row.is_nullable,
"dtype": row.data_type
})
})
callbackEach();
})
}, function() {
callback();
})
},
//Add columns based on if it's not found in diffs
function(callback) {
async.eachSeries(tables, function(table, callbackEach) {
var c = table.columns;
var t = c.filter(function(t) {
var truthy = true;
diffs.map(function(diff) {
if (t.name.toUpperCase() == diff.column.toUpperCase()) {
truthy = false;
}
})
return truthy;
});
if (t.length > 0) {
c = t[0];
} else {
return callbackEach();
}
console.log(c);
var d = c.type.toUpperCase();
var n = c.null;
if (n == false) {
n = "NOT NULL";
} else {
n = "";
}
q =
`
ALTER TABLE `+table.tableName+` ADD `+c.name+`
`+d+` `+n+`
`
if (logging == true) console.log(q);
client.query(q, function(err, result) {
if (err) throw err;
return callbackEach();
})
}, function() {
return callback();
})
},
//Change diffs based on existing schemas
function(callback) {
async.eachSeries(diffs, function(diff, callbackEach) {
var structure = "";
var q1, q2, flag = false;
var t = tables.filter(function(t) {
return t.tableName.toUpperCase() == diff.table.toUpperCase()
});
if (t.length == 0) {
return callbackEach();
}
var c = t[0].columns.filter(function(co) {
return co.name.toUpperCase() == diff.column.toUpperCase();
});
if (c.length == 0) {
q1 =
`
ALTER TABLE `+diff.table+` DROP COLUMN `+diff.column+`
`
if (logging == true) console.log("s1", q1);
flag = true;
client.query(q1, function(err, result) {
if (err) throw err;
})
} else if (c.length == 1 && diff) {
var dt = diff.dtype.toUpperCase();
var ct = c[0].type.toUpperCase();
var nt = ct;
ct = ct
.replace("INT", "INTEGER")
.replace("INT8", "BIGINT")
.replace("SERIAL8", "BIGSERIAL")
.replace("VARBIT", "BIT VARYING")
.replace("BOOL", "BOOLEAN")
.replace("CHAR", "CHARACTER")
.replace("VARCHAR", "CHARACTER VARYING")
.replace("FLOAT8", "DOUBLE PRECISION")
.replace("INT4", "INTEGER")
.replace("DECIMAL", "NUMERIC")
.replace("FLOAT4", "REAL")
.replace("INT2", "SMALLINT")
.replace("SERIAL2", "SMALLSERIAL")
.replace("SERIAL4", "SERIAL")
.replace("TIMETZ", "TIME")
.replace("TIMESTAMPTZ", "TIMESTAMP")
dt = dt
.replace("INT", "INTEGER")
.replace("INT8", "BIGINT")
.replace("SERIAL8", "BIGSERIAL")
.replace("VARBIT", "BIT VARYING")
.replace("BOOL", "BOOLEAN")
.replace("CHAR", "CHARACTER")
.replace("VARCHAR", "CHARACTER VARYING")
.replace("FLOAT8", "DOUBLE PRECISION")
.replace("INT4", "INTEGER")
.replace("DECIMAL", "NUMERIC")
.replace("FLOAT4", "REAL")
.replace("INT2", "SMALLINT")
.replace("SERIAL2", "SMALLSERIAL")
.replace("SERIAL4", "SERIAL")
.replace("TIMETZ", "TIME")
.replace("TIMESTAMPTZ", "TIMESTAMP")
if (
diff.null == "NO" && c[0].null == false && dt == ct
) {
return callbackEach();
}
if (diff.null == "NO") {
var n = "NOT NULL";
} else {
var n = "";
}
var d = nt;
q1 =
`
ALTER TABLE `+diff.table+` DROP `+diff.column+`
`
q2 =
`
ALTER TABLE `+diff.table+` ADD `+diff.column+` `+d+` `+n+`
`
} else {
return callbackEach();
}
async.series([
function(callback2) {
if (flag == true) return callbackEach();
if (logging == true) console.log("s2", q1);
client.query(q1, function(err, result) {
if (err) throw err;
return callback2();
})
},
function(callback2) {
if (logging == true) console.log(q2);
client.query(q2, function(err, result) {
if (err) throw err;
return callbackEach();
})
}
])
}, function() {
return callback();
})
},
//Find diffs in schema
function(callback) {
var q =
`
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
`
if (logging == true) console.log(q);
client.query(q, function(err, result) {
if (err) throw err;
result.rows.map(function(t) {
diffsTbl.push(t.table_name);
})
callback();
})
},
//Drop tables if they exist
function (callback) {
async.eachSeries(diffsTbl, function(dTbl, callbackEach) {
var m = tables.filter(function(t) {
return t.tableName.toLowerCase() == dTbl.toLowerCase();
})
if (m.length == 0) {
var q =
"\
DROP TABLE IF EXISTS "+dTbl+" \
";
if (logging == true) console.log(q);
client.query(q, function(err, result) {
if (err) throw err;
callbackEach();
})
} else {
callbackEach();
}
}, function() {
callback();
})
},
//Create new tables if they don't exist already
function(callback) {
async.eachSeries(tables, function(table, callbackEach) {
var m = false;
table.tableName = table.tableName.toLowerCase();
diffsTbl.map(function(d) {
if (d == table.tableName) m = true;
})
if (m == true) return callbackEach();
var structure = "";
table.columns.map(function(c, index) {
var p = "";
if (c["primary"] == true) p = "PRIMARY KEY";
var n = "";
if (c["null"] == false) n = "NOT NULL";
structure = structure + c["name"] + " " + c["type"] + " " + p + " " + n
if (index < table.columns.length - 1) {
structure = structure + ",";
}
})
var q =
"\
CREATE TABLE "+table.tableName+"(\
"+structure+"\
);\
";
if (logging == true) console.log(q);
client.query(q, function(err, result) {
if (err) throw err;
callbackEach();
})
}, function() {
callback();
})
}
], function() {
client.end();
cb();
})
})
}