Add support to discover related schemas by foreign keys

This commit is contained in:
Raymond Feng 2013-05-30 23:13:04 -07:00
parent ad14bd9dbc
commit 2d62b5ba6a
1 changed files with 121 additions and 69 deletions

View File

@ -415,100 +415,152 @@ function fromDBName(dbName, camelCase) {
return parts.join(''); return parts.join('');
} }
DataSource.prototype.discoverSchema = function (owner, table, cb) {
this.discoverSchemas(owner, table, {visited: {}, associations: false}, function(err, schemas) {
if(err) {
cb && cb(err, schemas);
return;
}
for(var s in schemas) {
cb && cb(null, schemas[s]);
return;
}
});
}
/** /**
* Discover schema from a given table/view * Discover schema from a given table/view
* @param owner * @param owner
* @param table * @param table
* @param cb * @param cb
*/ */
DataSource.prototype.discoverSchema = function (owner, table, cb) { DataSource.prototype.discoverSchemas = function (owner, table, options, cb) {
var self = this; var self = this;
var dataSourceName = this.name || this.adapter.name; var dataSourceName = this.name || this.adapter.name;
async.parallel( var tasks = [
[ this.discoverModelProperties.bind(this, owner, table),
this.discoverModelProperties.bind(this, owner, table), this.discoverPrimaryKeys.bind(this, owner, table) ];
this.discoverPrimaryKeys.bind(this, owner, table),
this.discoverForeignKeys.bind(this, owner, table)
], function (err, results) {
if (err) { if (options.associations) {
cb && cb(err); tasks.push(this.discoverForeignKeys.bind(this, owner, table));
return; }
async.parallel(tasks, function (err, results) {
if (err) {
cb && cb(err);
return;
}
var columns = results[0];
if (!columns) {
cb && cb();
return;
}
// Handle primary keys
var primaryKeys = results[1];
var pks = {};
primaryKeys.forEach(function (pk) {
pks[pk.columnName] = pk.keySeq;
});
if (self.settings.debug) {
console.log('Primary keys: ', pks);
}
var schema = {
name: fromDBName(table, false),
options: {
idInjection: false // DO NOT add id property
},
properties: {
} }
};
var columns = results[0]; schema.options[dataSourceName] = {
var primaryKeys = results[1]; schema: columns[0].owner,
var pks = {}, fks = {}; table: table
primaryKeys.forEach(function(pk) { };
pks[pk.columnName] = pk.keySeq;
});
columns.forEach(function (item) {
var i = item;
var propName = fromDBName(item.columnName, true);
schema.properties[propName] = {
type: item.type,
required: (item.nullable === 'N'),
length: item.dataLength
};
if (pks[item.columnName]) {
schema.properties[propName].id = pks[item.columnName];
}
schema.properties[propName][dataSourceName] = {
columnName: i.columnName,
dataType: i.dataType,
dataLength: i.dataLength,
nullable: i.nullable
};
});
// Add current table to the visited tables
options.visited = options.visited || {};
var schemaKey = columns[0].owner + '.' + table;
if (!options.visited.hasOwnProperty(schemaKey)) {
if(self.settings.debug) { if(self.settings.debug) {
console.log('Primary keys: ', pks); console.log('Adding schema for ' + schemaKey);
} }
options.visited[schemaKey] = schema;
}
var otherTables = {};
if (options.associations) {
// Handle foreign keys
var fks = {};
var foreignKeys = results[2]; var foreignKeys = results[2];
foreignKeys.forEach(function(fk) { foreignKeys.forEach(function (fk) {
var fkInfo = { var fkInfo = {
keySeq: fk.keySeq, keySeq: fk.keySeq,
owner: fk.pkOwner, owner: fk.pkOwner,
tableName: fk.pkTableName, tableName: fk.pkTableName,
columnName: fk.pkColumnName columnName: fk.pkColumnName
}; };
if(fks[fk.fkName]) { if (fks[fk.fkName]) {
fks[fk.fkName].push(fkInfo); fks[fk.fkName].push(fkInfo);
} else { } else {
fks[fk.fkName] = [fkInfo]; fks[fk.fkName] = [fkInfo];
} }
}); });
if(self.settings.debug) { if (self.settings.debug) {
console.log('Foreign keys: ', fks); console.log('Foreign keys: ', fks);
} }
if (!columns) { foreignKeys.forEach(function (fk) {
cb && cb(); var key = fk.pkOwner + '.' + fk.pkTableName;
return; if (!options.visited.hasOwnProperty(key) && !otherTables.hasOwnProperty(key)) {
} otherTables[key] = {owner: fk.pkOwner, tableName: fk.pkTableName};
var schema = {
name: fromDBName(table, false),
options: {
idInjection: false // DO NOT add id property
},
properties: {
} }
};
schema.options[dataSourceName] = {
schema: owner,
table: table
};
columns.forEach(function (item) {
var i = item;
var propName = fromDBName(item.columnName, true);
schema.properties[propName] =
{
type: item.type,
required: (item.nullable === 'N'),
length: item.dataLength
};
if(pks[item.columnName]) {
schema.properties[propName].id = pks[item.columnName];
}
schema.properties[propName][dataSourceName] = {
columnName: i.columnName,
dataType: i.dataType,
dataLength: i.dataLength,
nullable: i.nullable
};
}); });
}
cb && cb(null, schema); if (Object.keys(otherTables).length === 0) {
}); cb && cb(null, options.visited);
} else {
var moreTasks = [];
for (var t in otherTables) {
if(self.settings.debug) {
console.log('Discovering related schema for ' + schemaKey);
}
moreTasks.push(DataSource.prototype.discoverSchemas.bind(self, otherTables[t].owner, otherTables[t].tableName, options));
}
async.parallel(moreTasks, function (err, results) {
var result = results && results[0];
cb && cb(err, result);
});
}
});
} }
/** /**