不要怂,就是干,撸起袖子干!

Commit 0fdc27fe by Joel Trost Committed by Matt Broadstone

"query-interface describeTable" test now passes

1 parent 151fa019
...@@ -64,7 +64,6 @@ module.exports = (function() { ...@@ -64,7 +64,6 @@ module.exports = (function() {
} }
}) })
); );
return 'DESCRIBE ' + table + ';'; return 'DESCRIBE ' + table + ';';
}, },
......
...@@ -6,6 +6,7 @@ var Utils = require('../../utils') ...@@ -6,6 +6,7 @@ var Utils = require('../../utils')
//drop table Group //drop table Group
DataTypes.BOOLEAN = 'BIT';
module.exports = (function() { module.exports = (function() {
var QueryGenerator = { var QueryGenerator = {
...@@ -16,6 +17,10 @@ module.exports = (function() { ...@@ -16,6 +17,10 @@ module.exports = (function() {
return Utils.addTicks(identifier, '"'); return Utils.addTicks(identifier, '"');
}, },
wrapSingleQuote: function(identifier){
return '\'' + identifier + '\'';
},
showTablesQuery: function () { showTablesQuery: function () {
return 'SELECT name FROM sys.Tables'; return 'SELECT name FROM sys.Tables';
}, },
...@@ -74,19 +79,11 @@ module.exports = (function() { ...@@ -74,19 +79,11 @@ module.exports = (function() {
return Utils._.template(query)(values).trim() + ";"; return Utils._.template(query)(values).trim() + ";";
}, },
getDataType: function(type){
switch(type){
case 'TINYINT(1)':
return 'BIT';
default:
return type;
}
},
showIndexQuery: function(tableName, options) { showIndexQuery: function(tableName, options) {
var sql = "SELECT" var sql = "SELECT"
+ " TableName = t.name," + " TableName = t.name,"
+ " IndexName = ind.name," + " name = ind.name,"
+ " IndexId = ind.index_id," + " IndexId = ind.index_id,"
+ " ColumnId = ic.index_column_id," + " ColumnId = ic.index_column_id,"
+ " ColumnName = col.name" + " ColumnName = col.name"
...@@ -101,7 +98,7 @@ module.exports = (function() { ...@@ -101,7 +98,7 @@ module.exports = (function() {
+ " WHERE t.name = '<%= tableName %>'<%= options %>"; + " WHERE t.name = '<%= tableName %>'<%= options %>";
return Utils._.template(sql)({ return Utils._.template(sql)({
tableName: tableName, tableName: tableName,
options: (options || {}).database ? ' FROM `' + options.database + '`' : '' options: (options || {}).database ? ' FROM \'' + options.database + '\'' : ''
}); });
}, },
...@@ -171,79 +168,109 @@ module.exports = (function() { ...@@ -171,79 +168,109 @@ module.exports = (function() {
]).join(' '); ]).join(' ');
}, },
attributesToSQL: function(attributes) {
var result = {};
for (var name in attributes) {
var dataType = attributes[name];
if (Utils._.isPlainObject(dataType)) {
var template;
if (dataType.type.toString() === DataTypes.ENUM.toString()) {
if (Array.isArray(dataType.values) && (dataType.values.length > 0)) {
template = 'ENUM(' + Utils._.map(dataType.values, function(value) {
return this.escape(value);
}.bind(this)).join(', ') + ')';
} else {
throw new Error('Values for ENUM haven\'t been defined.');
}
} else {
template = this.getDataType(dataType.type.toString());
}
if (dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) {
template += ' NOT NULL';
}
if (dataType.autoIncrement) { attributeMap:{
template += ' auto_increment'; notNull:"NOT NULL",
} isNull:"NULL",
autoIncrement:"IDENTITY(1,1)",
defaultValue:"DEFAULT",
unique:"UNIQUE",
primaryKey:"PRIMARY KEY",
comment:"COMMENT",
references:"REFERENCES",
onDelete:"ON DELETE",
onUpdate:"ON UPDATE"
},
attributeToSQL: function(attribute, options) {
if (!Utils._.isPlainObject(attribute)) {
attribute = {
type: attribute
};
}
// Blobs/texts cannot have a defaultValue var template;
if (dataType.type !== 'TEXT' && dataType.type._binary !== true && Utils.defaultValueSchemable(dataType.defaultValue)) { var isEnum = false;
template += ' DEFAULT ' + this.escape(dataType.defaultValue); if (attribute.type.toString() === DataTypes.ENUM.toString()) {
} isEnum = true;
template = 'VARCHAR(10) NOT NULL CHECK ("'
+ attribute.fieldName + '" IN('
+ Utils._.map(attribute.values, function(value) {
return this.escape(value);
}.bind(this)).join(', ') + '))';
} else {
template = this.dataTypeMapping(null, null, attribute.type.toString());
}
if (dataType.unique === true) { template += ' ';
template += ' UNIQUE'; if (attribute.allowNull === false && !isEnum) {
} template += this.attributeMap.notNull;
}else if(!isEnum){
template += this.attributeMap.isNull;
}
if (dataType.primaryKey) { if (attribute.autoIncrement) {
template += ' PRIMARY KEY'; template += ' ' + this.attributeMap.autoIncrement;
} }
if (dataType.comment && Utils._.isString(dataType.comment) && dataType.comment.length) { // Blobs/texts cannot have a defaultValue
template += ' COMMENT ' + this.escape(dataType.comment); if (attribute.type !== 'TEXT' && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) {
} template += ' DEFAULT ' + this.escape(attribute.defaultValue);
}
if (dataType.references) { if (attribute.unique === true) {
template += ' REFERENCES ' + this.quoteTable(dataType.references); template += ' UNIQUE';
}
if (dataType.referencesKey) { if (attribute.primaryKey) {
template += ' (' + this.quoteIdentifier(dataType.referencesKey) + ')'; template += ' PRIMARY KEY';
} else { }
template += ' (' + this.quoteIdentifier('id') + ')';
}
if (dataType.onDelete) { if (attribute.references) {
template += ' ON DELETE ' + dataType.onDelete.toUpperCase(); template += ' REFERENCES ' + this.quoteTable(attribute.references);
}
if (dataType.onUpdate) { if (attribute.referencesKey) {
template += ' ON UPDATE ' + dataType.onUpdate.toUpperCase(); template += ' (' + this.quoteIdentifier(attribute.referencesKey) + ')';
} } else {
template += ' (' + this.quoteIdentifier('id') + ')';
}
} if (attribute.onDelete) {
template += ' ON DELETE ' + attribute.onDelete.toUpperCase();
}
result[name] = template; if (attribute.onUpdate) {
} else { template += ' ON UPDATE ' + attribute.onUpdate.toUpperCase();
result[name] = dataType;
} }
} }
console.log('results', result);
return template;
},
attributesToSQL: function(attributes, options) {
var result = {}
, key
, attribute;
for (key in attributes) {
attribute = attributes[key];
result[attribute.field || key] = this.attributeToSQL(attribute, options);
}
return result; return result;
}, },
describeTableQuery: function(tableName, schema, schemaDelimiter) {
var qry = "SELECT c.Name, t.Name AS 'Type', c.IS_NULLABLE as IsNull"
+ ", object_definition(c.default_object_id) AS 'Default'"
+ " FROM sys.Columns c"
+ " INNER JOIN sys.types t"
+ " ON t.system_type_id = c.system_type_id"
+ " WHERE object_id = object_id("
+ this.wrapSingleQuote(tableName) + ");";
return qry;
},
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
* *
...@@ -254,25 +281,23 @@ module.exports = (function() { ...@@ -254,25 +281,23 @@ module.exports = (function() {
getForeignKeysQuery: function(tableName, schemaName) { getForeignKeysQuery: function(tableName, schemaName) {
return [ return [
"SELECT", "SELECT",
"K_Table = FK.TABLE_NAME, FK_Column = CU.COLUMN_NAME, PK_Table = PK.TABLE_NAME, PK_Column = PT.COLUMN_NAME, Constraint_Name = C.CONSTRAINT_NAME", "constraint_name = C.CONSTRAINT_NAME",
"FROM", "FROM",
"INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C", "INFORMATION_SCHEMA.TABLE_CONSTRAINTS C",
"INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME", "WHERE C.CONSTRAINT_TYPE != 'PRIMARY KEY'",
"INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME", "AND C.TABLE_NAME = ", this.wrapSingleQuote(tableName)
"INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME",
"INNER JOIN (",
"SELECT",
"i1.TABLE_NAME,",
"i2.COLUMN_NAME",
"FROM",
"INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1",
"INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2",
"ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME",
"WHERE",
"i1.CONSTRAINT_TYPE = 'PRIMARY KEY'",
") PT ON PT.TABLE_NAME = PK.TABLE_NAME"
].join(" "); ].join(" ");
} },
/**
* Generates an SQL query that removes a foreign key from a table.
*
* @param {String} tableName The name of the table.
* @param {String} foreignKey The name of the foreign key constraint.
* @return {String} The generated sql query.
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP ' + this.quoteIdentifier(foreignKey) + ';';
},
}; };
return Utils._.extend(Utils._.clone(require('../mysql/query-generator')), QueryGenerator); return Utils._.extend(Utils._.clone(require('../mysql/query-generator')), QueryGenerator);
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
var Utils = require('../../utils') var Utils = require('../../utils')
, AbstractQuery = require('../abstract/query') , AbstractQuery = require('../abstract/query')
, uuid = require('node-uuid'); , uuid = require('node-uuid')
, sequelizeErrors = require('../../errors.js');
module.exports = (function() { module.exports = (function() {
var Query = function(connection, sequelize, callee, options) { var Query = function(connection, sequelize, callee, options) {
...@@ -21,7 +22,6 @@ module.exports = (function() { ...@@ -21,7 +22,6 @@ module.exports = (function() {
}; };
Utils.inherit(Query, AbstractQuery); Utils.inherit(Query, AbstractQuery);
Query.prototype.run = function(sql) { Query.prototype.run = function(sql) {
var self = this; var self = this;
this.sql = sql; this.sql = sql;
...@@ -38,13 +38,140 @@ module.exports = (function() { ...@@ -38,13 +38,140 @@ module.exports = (function() {
.lib .lib
.execute(self.connection.config, { query: self.sql }) .execute(self.connection.config, { query: self.sql })
.then( .then(
function (data) { resolve(data.result); }, function (data) { resolve(self.formatResults(data.result)); },
function (err) { reject(err); } function (err) { reject(self.formatError(err)); }
); );
}); });
return promise; return promise;
}; };
/**
* High level function that handles the results of a query execution.
*
*
* Example:
* query.formatResults([
* {
* id: 1, // this is from the main table
* attr2: 'snafu', // this is from the main table
* Tasks.id: 1, // this is from the associated table
* Tasks.title: 'task' // this is from the associated table
* }
* ])
*
* @param {Array} data - The result of the query execution.
*/
Query.prototype.formatResults = function(data) {
var result = this.callee;
if (this.isInsertQuery(data)) {
this.handleInsertQuery(data);
} else if (this.isShowTableQuery()) {
result = this.handleShowTableQuery(data);
} else if (this.isShowOrDescribeQuery()) {
result = data;
if (this.sql.toLowerCase().indexOf("select c.name, t.name as 'type', c.is_nullable as isnull") === 0) {
result = {};
data.forEach(function(_result) {
result[_result.Name] = {
type: _result.Type.toUpperCase(),
allowNull: _result.IsNull,
defaultValue: _result.Default
};
});
} else if (this.isShowIndexesQuery()) {
result = data;
}
} else if (this.isSelectQuery()) {
result = this.handleSelectQuery(data);
} else if (this.isCallQuery()) {
result = data[0];
} else if (this.isBulkUpdateQuery() || this.isBulkDeleteQuery()) {
result = data.affectedRows;
}
return result;
};
Query.prototype.formatError = function (err) {
var match;
switch (err.errno || err.code) {
case 1062:
match = err.message.match(/Duplicate entry '(.*)' for key '?(.*?)$/);
return new sequelizeErrors.UniqueConstraintError({
fields: null,
index: match[2],
value: match[1],
parent: err
});
case 1451:
match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(?: ON .*)?\)$/);
return new sequelizeErrors.ForeignKeyConstraintError({
fields: null,
index: match[3],
parent: err
});
case 1452:
match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(.*)\)$/);
return new sequelizeErrors.ForeignKeyConstraintError({
fields: null,
index: match[1],
parent: err
});
default:
return new sequelizeErrors.DatabaseError(err);
}
};
Query.prototype.isShowOrDescribeQuery = function() {
var result = false;
result = result || (this.sql.toLowerCase().indexOf("select c.name, t.name as 'type', c.is_nullable as isnull") === 0);
result = result || (this.sql.toLowerCase().indexOf('select tablename = t.name, name = ind.name,') === 0);
return result;
};
Query.prototype.isShowIndexesQuery = function () {
return this.sql.toLowerCase().indexOf('select tablename = t.name, name = ind.name,') === 0;
};
Query.prototype.handleShowIndexesQuery = function (data) {
// Group by index name, and collect all fields
data = Utils._.foldl(data, function (acc, item) {
if (!(item.Key_name in acc)) {
acc[item.Key_name] = item;
item.fields = [];
}
acc[item.Key_name].fields[item.Seq_in_index - 1] = {
attribute: item.Column_name,
length: item.Sub_part || undefined,
order: item.Collation === 'A' ? 'ASC' : undefined
};
delete item.column_name;
return acc;
}, {});
return Utils._.map(data, function(item) {
return {
primary: item.Key_name === 'PRIMARY',
fields: item.fields,
name: item.Key_name,
tableName: item.Table,
unique: (item.Non_unique !== 1),
type: item.Index_type,
};
});
};
return Query; return Query;
})(); })();
...@@ -455,7 +455,6 @@ module.exports = (function() { ...@@ -455,7 +455,6 @@ module.exports = (function() {
return self.sequelize.query(self.QueryGenerator.getForeignKeysQuery(tableName, self.sequelize.config.database)); return self.sequelize.query(self.QueryGenerator.getForeignKeysQuery(tableName, self.sequelize.config.database));
}).then(function(results) { }).then(function(results) {
var result = {}; var result = {};
tableNames.forEach(function(tableName, i) { tableNames.forEach(function(tableName, i) {
result[tableName] = Utils._.compact(results[i]).map(function(r) { result[tableName] = Utils._.compact(results[i]).map(function(r) {
return r.constraint_name; return r.constraint_name;
......
...@@ -87,12 +87,11 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -87,12 +87,11 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
self.queryInterface.showIndex('Group').complete(function(err, indexes) { self.queryInterface.showIndex('Group').complete(function(err, indexes) {
expect(err).to.be.null expect(err).to.be.null
var indexColumns = _.uniq(indexes.map(function(index) { var indexColumns = _.uniq(indexes.map(function(index) {
console.log('index:', index)
return index.name return index.name
})) }))
expect(indexColumns).to.include('group_username_is_admin') expect(indexColumns).to.include('group_username_is_admin')
self.queryInterface.removeIndex('Group', ['username', 'isAdmin']).complete(function(err) { self.queryInterface.removeIndex('Group', ['username', 'isAdmin']).complete(function(err) {
...@@ -128,16 +127,33 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -128,16 +127,33 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
Users.sync({ force: true }).success(function() { Users.sync({ force: true }).success(function() {
self.queryInterface.describeTable('_Users').complete(function(err, metadata) { self.queryInterface.describeTable('_Users').complete(function(err, metadata) {
expect(err).to.be.null expect(err).to.be.null
var username = metadata.username var username = metadata.username
var isAdmin = metadata.isAdmin var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals var enumVals = metadata.enumVals
expect(username.type).to.equal(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)') var assertVal = 'VARCHAR(255)';
switch(dialect){
case 'postgres':
assertVal = 'CHARACTER VARYING';
break;
case 'mssql':
assertVal = 'VARCHAR';
break;
}
expect(username.type).to.equal(assertVal)
expect(username.allowNull).to.be.true expect(username.allowNull).to.be.true
expect(username.defaultValue).to.be.null expect(username.defaultValue).to.be.null
expect(isAdmin.type).to.equal(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)') assertVal = 'TINYINT(1)';
switch(dialect){
case 'postgres':
assertVal = 'BOOLEAN';
break;
case 'mssql':
assertVal = 'BIT';
break;
}
expect(isAdmin.type).to.equal(assertVal)
expect(isAdmin.allowNull).to.be.true expect(isAdmin.allowNull).to.be.true
expect(isAdmin.defaultValue).to.be.null expect(isAdmin.defaultValue).to.be.null
...@@ -145,7 +161,6 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -145,7 +161,6 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
expect(enumVals.special).to.be.instanceof(Array) expect(enumVals.special).to.be.instanceof(Array)
expect(enumVals.special).to.have.length(2); expect(enumVals.special).to.have.length(2);
} }
done() done()
}) })
}) })
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!