soarjs
Version:
A simple relational DB to entity mapper.
397 lines (340 loc) • 10.1 kB
JavaScript
/*!
* soar
* authors: Ben Lue
* license: MIT License
* Copyright(c) 2015 Gocharm Inc.
*/
var mysql = require('./sqlGenMySql.js');
exports.describeTable = function(conn, tableName, cb) {
var sql = 'SHOW COLUMNS FROM ' + tableName;
conn.query(sql, function(err, rows) {
if (err)
cb( err );
else {
sql = 'SHOW INDEX FROM ' + tableName + " WHERE Key_name='PRIMARY'";
conn.query(sql, function(err, pkRows) {
if (err)
cb( err );
else {
sql = "SHOW TABLE STATUS WHERE name='" + tableName + "';";
conn.query(sql, function(err, tbData) {
//conn.release();
if (err)
cb( err );
else {
var schema = {
title: tableName,
columns: readColumns(rows),
primary: readPrimaryKeys(pkRows),
options: readTableStatus(tbData[0])
};
cb( null, schema );
}
});
}
});
}
});
};
/* sample schema:
{
title: 'Person',
columns: {
Person_id: {type: 'integer', format: 'int64'},
fname: {
type: 'string',
maxLength: 32,
options: {
notNull:
default:
autoInc:
comment
}
}
},
primary: ['Person_id'],
options: {
engine: 'InnoDB'
}
}
*/
exports.createTable = function(conn, schema, cb) {
var tableCol = schema.columns,
tableDef = {title: schema.title, columns: [],
primary: schema.primary, options: schema.options};
for (var key in tableCol) {
var prop = tableCol[key],
c = {title: key, type: toSQLType(prop)};
c.options = prop.options || {};
if (!c.options.engine)
c.options.engine = 'InnoDB';
tableDef.columns.push(c);
}
// sanity check...
if (!tableDef.title)
throw new Error('Missing table name');
if (Object.keys(tableDef.columns).length === 0)
throw new Error('No table columns');
if (!tableDef.primary || tableDef.primary.length === 0)
throw new Error('Primary key not specified.');
try {
var sql = mysql.createTable(tableDef);
//console.log( sql );
conn.query(sql, function(qErr, result) {
cb( qErr, result );
});
}
catch (e) {
console.log( e.stack );
cb(e);
}
};
/* sample:
{
title: 'Person',
add: {
column: {
age: {type:'integer', format: 'int8'},
},
index: {
IDX_BK_ISBN: {
columns: ['ISBN', 'title'],
unique: true
}
},
foreignKey: {
FK_bpdRbk: {
key: 'bkID',
reference: 'Books.bkID',
integrity: {
delete: 'cascade'
update: 'cascade'
}
}
}
},
drop: {
column: ['addr'],
index: ['index_name'],
foreignKey: ['FK_bpdRbk']
}
}
*/
exports.alterTable = function(conn, schema, cb) {
var updSchema = {},
props = Object.getOwnPropertyNames(schema);
for (var i in props) {
var key = props[i];
updSchema[key] = schema[key];
}
if (updSchema.add) {
var addCols = updSchema.add.column,
columns = [];
for (var key in addCols) {
var prop = addCols[key],
c = {title: key, type: toSQLType(prop)};
if (prop.options)
c.options = prop.options;
columns.push( c );
}
updSchema.add.column = columns;
}
try {
var sql = mysql.alterTable(updSchema);
//console.log( sql );
conn.query(sql, function(qErr, result) {
cb( qErr, result );
});
}
catch (e) {
console.log( e.stack );
cb(e);
}
};
exports.deleteTable = function(conn, tbName, cb) {
var sql = 'DROP TABLE ' + tbName;
try {
conn.query(sql, function(err) {
cb(err);
});
}
catch (e) {
cb(e);
}
};
/*
columns: {
Person_id: {type: 'integer', format: 'int64'},
fname: {
type: 'string',
maxLength: 32,
options: {
notNull:
default:
autoInc:
comment
}
}
}
*/
function readColumns(columns) {
var tableCol = {};
for (var i in columns) {
var c = columns[i],
prop = toSchemaType(c.Type);
if (c.Null)
prop.options.notNull = c.Null === 'NO';
if (c.Default)
prop.options.default = c.Default;
if (c.Extra === 'auto_increment')
prop.options.autoInc = c.Extra;
if (Object.keys(prop.options).length === 0)
delete prop.options;
tableCol[c.Field] = prop;
}
return tableCol;
};
function readPrimaryKeys(rows) {
var pk = [];
for (var i in rows)
pk.push( rows[i].Column_name );
return pk;
};
function readTableStatus(status) {
var options = {engine: status.Engine};
return options;
};
function toSQLType(prop) {
var dtype;
switch (prop.type) {
case 'boolean':
dtype = 'bool';
break;
case 'integer':
switch (prop.format) {
case 'int8':
dtype = 'tinyint';
break;
case 'int16':
dtype = 'smallint';
break;
case 'int64':
dtype = 'bigint';
break;
default:
dtype = 'int';
}
break;
case 'number':
if (prop.format) {
if (prop.format.indexOf('decimal') === 0)
dtype = 'decimal(' + prop.format.substring(7) + ')';
else
dtype = prop.format === 'double' ? 'double' : 'float';
}
else
dtype = 'float';
break;
case 'string':
if (prop.format === 'text')
dtype = 'text';
else {
var maxLen = prop.maxLength || 8;
dtype = 'varchar(' + maxLen + ')';
}
break;
case 'serial':
dtype = 'bigint unsigned not null auto_increment unique';
break;
default:
dtype = prop.type;
}
return dtype;
};
function toSchemaType(dtype) {
var prop = {options: {}},
isSolved = true;
switch (dtype) {
case 'tinyint(1)':
prop.type = 'boolean';
break;
case 'tinyint':
prop.type = 'integer';
prop.format = 'int8';
break;
case 'smallint':
prop.type = 'integer';
prop.format = 'int16';
break;
case 'int':
prop.type = 'integer';
break;
case 'bigint':
prop.type = 'integer';
prop.format = 'int64';
break;
case 'float':
prop.type = 'number';
prop.format = 'float';
break;
case 'double':
prop.type = 'number';
prop.format = 'double';
break;
default:
isSolved = false;
}
if (!isSolved) {
if (dtype.indexOf('decimal') === 0) {
prop.type = 'number';
var idx0 = dtype.indexOf('('),
idx1 = dtype.indexOf(')');
if (idx0 > 0)
prop.format = dtype.substring(idx0+1, idx1);
}
else if (dtype.indexOf('varchar(') === 0) {
var idx0 = dtype.indexOf('('),
idx1 = dtype.indexOf(')');
prop.type = 'string';
prop.maxLength = parseInt(dtype.substring(idx0 + 1, idx1));
}
else if (dtype.indexOf('bigint') === 0) {
var idx0 = dtype.indexOf('('),
idx1 = dtype.indexOf(')');
prop.type = 'integer';
prop.format = 'int64';
prop.maxLength = parseInt(dtype.substring(idx0 + 1, idx1));
if (dtype.indexOf('unsigned', idx1) > 0)
prop.options.unsigned = true;
}
else if (dtype.indexOf('int') === 0) {
var idx0 = dtype.indexOf('('),
idx1 = dtype.indexOf(')');
prop.type = 'integer';
prop.maxLength = parseInt(dtype.substring(idx0 + 1, idx1));
if (dtype.indexOf('unsigned', idx1) > 0)
prop.options.unsigned = true;
}
else if (dtype.indexOf('smallint') === 0) {
var idx0 = dtype.indexOf('('),
idx1 = dtype.indexOf(')');
prop.type = 'integer';
prop.format = 'int16';
prop.maxLength = parseInt(dtype.substring(idx0 + 1, idx1));
if (dtype.indexOf('unsigned', idx1) > 0)
prop.options.unsigned = true;
}
else if (dtype.indexOf('tinyint') === 0) {
var idx0 = dtype.indexOf('('),
idx1 = dtype.indexOf(')');
prop.type = 'integer';
prop.format = 'int8';
prop.maxLength = parseInt(dtype.substring(idx0 + 1, idx1));
if (dtype.indexOf('unsigned', idx1) > 0)
prop.options.unsigned = true;
}
else
prop.type = dtype;
}
return prop;
};