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('');
}
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
* @param owner
* @param table
* @param cb
*/
DataSource.prototype.discoverSchema = function (owner, table, cb) {
DataSource.prototype.discoverSchemas = function (owner, table, options, cb) {
var self = this;
var dataSourceName = this.name || this.adapter.name;
async.parallel(
[
this.discoverModelProperties.bind(this, owner, table),
this.discoverPrimaryKeys.bind(this, owner, table),
this.discoverForeignKeys.bind(this, owner, table)
], function (err, results) {
var tasks = [
this.discoverModelProperties.bind(this, owner, table),
this.discoverPrimaryKeys.bind(this, owner, table) ];
if (err) {
cb && cb(err);
return;
if (options.associations) {
tasks.push(this.discoverForeignKeys.bind(this, owner, table));
}
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];
var primaryKeys = results[1];
var pks = {}, fks = {};
primaryKeys.forEach(function(pk) {
pks[pk.columnName] = pk.keySeq;
});
schema.options[dataSourceName] = {
schema: columns[0].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
};
});
// 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) {
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];
foreignKeys.forEach(function(fk) {
var fkInfo = {
keySeq: fk.keySeq,
owner: fk.pkOwner,
tableName: fk.pkTableName,
columnName: fk.pkColumnName
};
if(fks[fk.fkName]) {
fks[fk.fkName].push(fkInfo);
} else {
fks[fk.fkName] = [fkInfo];
}
foreignKeys.forEach(function (fk) {
var fkInfo = {
keySeq: fk.keySeq,
owner: fk.pkOwner,
tableName: fk.pkTableName,
columnName: fk.pkColumnName
};
if (fks[fk.fkName]) {
fks[fk.fkName].push(fkInfo);
} else {
fks[fk.fkName] = [fkInfo];
}
});
if(self.settings.debug) {
if (self.settings.debug) {
console.log('Foreign keys: ', fks);
}
if (!columns) {
cb && cb();
return;
}
var schema = {
name: fromDBName(table, false),
options: {
idInjection: false // DO NOT add id property
},
properties: {
foreignKeys.forEach(function (fk) {
var key = fk.pkOwner + '.' + fk.pkTableName;
if (!options.visited.hasOwnProperty(key) && !otherTables.hasOwnProperty(key)) {
otherTables[key] = {owner: fk.pkOwner, tableName: fk.pkTableName};
}
};
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);
});
}
});
}
/**