Merge pull request #75 from strongloop/refactor-discovery
Refactor SQL discovery methods
This commit is contained in:
commit
22a39025a1
297
lib/sql.js
297
lib/sql.js
|
@ -1508,3 +1508,300 @@ SQLConnector.prototype.getInsertedId = function(model, info) {
|
||||||
SQLConnector.prototype.executeSQL = function(sql, params, options, callback) {
|
SQLConnector.prototype.executeSQL = function(sql, params, options, callback) {
|
||||||
throw new Error(g.f('{{executeSQL()}} must be implemented by the connector'));
|
throw new Error(g.f('{{executeSQL()}} must be implemented by the connector'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Refactored Discovery methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build sql for listing schemas
|
||||||
|
* @param {Object} options Options for discoverDatabaseSchemas
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.buildQuerySchemas = function(options) {
|
||||||
|
var sql = 'SELECT catalog_name as "catalog",' +
|
||||||
|
' schema_name as "schema"' +
|
||||||
|
' FROM information_schema.schemata';
|
||||||
|
return this.paginateSQL(sql, 'schema_name', options);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paginate the results returned from database
|
||||||
|
* @param {String} sql The sql to execute
|
||||||
|
* @param {Object} orderBy The property name by which results are ordered
|
||||||
|
* @param {Object} options Options for discoverDatabaseSchemas
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.paginateSQL = function(sql, orderBy, options) {
|
||||||
|
throw new Error(g.f('{{paginateSQL}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Discover database schemas
|
||||||
|
*
|
||||||
|
// * @param {Object} options Options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.discoverDatabaseSchemas = function(options, cb) {
|
||||||
|
if (!cb && typeof options === 'function') {
|
||||||
|
cb = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
|
var self = this;
|
||||||
|
this.execute(self.buildQuerySchemas(options), cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Build sql for listing tables
|
||||||
|
* @param options {all: for all owners, owner: for a given owner}
|
||||||
|
* @returns {string} The sql statement
|
||||||
|
*/
|
||||||
|
// Due to the different implementation structure of information_schema across
|
||||||
|
// connectors, each connector will have to generate its own query
|
||||||
|
SQLConnector.prototype.buildQueryTables = function(options) {
|
||||||
|
throw new Error(g.f('{{buildQueryTables}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Build sql for listing views
|
||||||
|
* @param options {all: for all owners, owner: for a given owner}
|
||||||
|
* @returns {string} The sql statement
|
||||||
|
*/
|
||||||
|
// Due to the different implementation structure of information_schema across
|
||||||
|
// connectors, each connector will have to generate its own query
|
||||||
|
SQLConnector.prototype.buildQueryViews = function(options) {
|
||||||
|
throw new Error(g.f('{{buildQueryViews}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover model definitions
|
||||||
|
*
|
||||||
|
* @param {Object} options Options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.discoverModelDefinitions = function(options, cb) {
|
||||||
|
if (!cb && typeof options === 'function') {
|
||||||
|
cb = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var calls = [function(callback) {
|
||||||
|
self.execute(self.buildQueryTables(options), callback);
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (options.views) {
|
||||||
|
calls.push(function(callback) {
|
||||||
|
self.execute(self.buildQueryViews(options), callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async.parallel(calls, function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, data);
|
||||||
|
} else {
|
||||||
|
var merged = [];
|
||||||
|
merged = merged.concat(data.shift());
|
||||||
|
if (data.length) {
|
||||||
|
merged = merged.concat(data.shift());
|
||||||
|
}
|
||||||
|
cb(err, merged);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build sql for listing columns
|
||||||
|
* @param {String} schema The schema name
|
||||||
|
* @param {String} table The table name
|
||||||
|
*/
|
||||||
|
// Due to the different implementation structure of information_schema across
|
||||||
|
// connectors, each connector will have to generate its own query
|
||||||
|
SQLConnector.prototype.buildQueryColumns = function(schema, table) {
|
||||||
|
throw new Error(g.f('{{buildQueryColumns}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the property type from database to loopback
|
||||||
|
* @param {Object} columnDefinition The columnDefinition of the table/schema
|
||||||
|
* @param {Object} options The options for the connector
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.buildPropertyType = function(columnDefinition, options) {
|
||||||
|
throw new Error(g.f('{{buildPropertyType}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Normalize the arguments
|
||||||
|
* @param table string, required
|
||||||
|
* @param options object, optional
|
||||||
|
* @param cb function, optional
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.getArgs = function(table, options, cb) {
|
||||||
|
throw new Error(g.f('{{getArgs}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover model properties from a table
|
||||||
|
* @param {String} table The table name
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.discoverModelProperties = function(table, options, cb) {
|
||||||
|
var self = this;
|
||||||
|
var args = self.getArgs(table, options, cb);
|
||||||
|
var schema = args.schema;
|
||||||
|
|
||||||
|
table = args.table;
|
||||||
|
options = args.options;
|
||||||
|
|
||||||
|
if (!schema) {
|
||||||
|
schema = self.getDefaultSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setDefaultOptions(options);
|
||||||
|
cb = args.cb;
|
||||||
|
|
||||||
|
var sql = self.buildQueryColumns(schema, table);
|
||||||
|
var callback = function(err, results) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, results);
|
||||||
|
} else {
|
||||||
|
results.map(function(r) {
|
||||||
|
r.type = self.buildPropertyType(r, options);
|
||||||
|
self.setNullableProperty(r);
|
||||||
|
});
|
||||||
|
cb(err, results);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.execute(sql, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Build the sql statement for querying primary keys of a given table
|
||||||
|
* @param schema
|
||||||
|
* @param table
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
// http://docs.oracle.com/javase/6/docs/api/java/sql/DatabaseMetaData.html
|
||||||
|
// #getPrimaryKeys(java.lang.String, java.lang.String, java.lang.String)
|
||||||
|
// Due to the different implementation structure of information_schema across
|
||||||
|
// connectors, each connector will have to generate its own query
|
||||||
|
SQLConnector.prototype.buildQueryPrimaryKeys = function(schema, table) {
|
||||||
|
throw new Error(g.f('{{buildQueryPrimaryKeys}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover primary keys for a given table
|
||||||
|
* @param {String} table The table name
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.discoverPrimaryKeys = function(table, options, cb) {
|
||||||
|
var self = this;
|
||||||
|
var args = self.getArgs(table, options, cb);
|
||||||
|
var schema = args.schema;
|
||||||
|
|
||||||
|
if (typeof(self.getDefaultSchema) === 'function' && !schema) {
|
||||||
|
schema = self.getDefaultSchema();
|
||||||
|
}
|
||||||
|
table = args.table;
|
||||||
|
options = args.options;
|
||||||
|
cb = args.cb;
|
||||||
|
|
||||||
|
var sql = self.buildQueryPrimaryKeys(schema, table);
|
||||||
|
this.execute(sql, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Build the sql statement for querying foreign keys of a given table
|
||||||
|
* @param schema
|
||||||
|
* @param table
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
// Due to the different implementation structure of information_schema across
|
||||||
|
// connectors, each connector will have to generate its own query
|
||||||
|
SQLConnector.prototype.buildQueryForeignKeys = function(schema, table) {
|
||||||
|
throw new Error(g.f('{{buildQueryForeignKeys}} must be implemented by the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover foreign keys for a given table
|
||||||
|
* @param {String} table The table name
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.discoverForeignKeys = function(table, options, cb) {
|
||||||
|
var self = this;
|
||||||
|
var args = self.getArgs(table, options, cb);
|
||||||
|
var schema = args.schema;
|
||||||
|
|
||||||
|
if (typeof(self.getDefaultSchema) === 'function' && !schema) {
|
||||||
|
schema = self.getDefaultSchema();
|
||||||
|
}
|
||||||
|
table = args.table;
|
||||||
|
options = args.options;
|
||||||
|
cb = args.cb;
|
||||||
|
|
||||||
|
var sql = self.buildQueryForeignKeys(schema, table);
|
||||||
|
this.execute(sql, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Retrieves a description of the foreign key columns that reference the
|
||||||
|
* given table's primary key columns (the foreign keys exported by a table).
|
||||||
|
* They are ordered by fkTableOwner, fkTableName, and keySeq.
|
||||||
|
* @param schema
|
||||||
|
* @param table
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
// Due to the different implementation structure of information_schema across
|
||||||
|
// connectors, each connector will have to generate its own query
|
||||||
|
SQLConnector.prototype.buildQueryExportedForeignKeys = function(schema, table) {
|
||||||
|
throw new Error(g.f('{{buildQueryExportedForeignKeys}} must be implemented by' +
|
||||||
|
'the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover foreign keys that reference to the primary key of this table
|
||||||
|
* @param {String} table The table name
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.discoverExportedForeignKeys = function(table, options, cb) {
|
||||||
|
var self = this;
|
||||||
|
var args = self.getArgs(table, options, cb);
|
||||||
|
var schema = args.schema;
|
||||||
|
|
||||||
|
if (typeof(self.getDefaultSchema) === 'function' && !schema) {
|
||||||
|
schema = self.getDefaultSchema();
|
||||||
|
}
|
||||||
|
table = args.table;
|
||||||
|
options = args.options;
|
||||||
|
cb = args.cb;
|
||||||
|
|
||||||
|
var sql = self.buildQueryExportedForeignKeys(schema, table);
|
||||||
|
this.execute(sql, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover default schema of a database
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.getDefaultSchema = function(options) {
|
||||||
|
throw new Error(g.f('{{getDefaultSchema}} must be implemented by' +
|
||||||
|
'the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default options for the connector
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.setDefaultOptions = function(options) {
|
||||||
|
throw new Error(g.f('{{setDefaultOptions}} must be implemented by' +
|
||||||
|
'the connector'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the nullable value for the property
|
||||||
|
* @param {Object} property The property to set nullable
|
||||||
|
*/
|
||||||
|
SQLConnector.prototype.setNullableProperty = function(property) {
|
||||||
|
throw new Error(g.f('{{setNullableProperty}} must be implemented by' +
|
||||||
|
'the connector'));
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue