From 2d62b5ba6a60bbccfe32aa2c862460e27fdce5c3 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 30 May 2013 23:13:04 -0700 Subject: [PATCH] Add support to discover related schemas by foreign keys --- lib/datasource.js | 190 +++++++++++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 69 deletions(-) diff --git a/lib/datasource.js b/lib/datasource.js index a5e106e5..c49b8d8d 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -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); + }); + } + }); } /**