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

Commit 3ae6e0aa by Joel Trost Committed by Matt Broadstone

All but the last test pass in Query Interface

1 parent 0fdc27fe
......@@ -35,8 +35,5 @@
"beforeEach",
"after",
"afterEach"
],
"globals":{
"Promise": true
}
]
}
......@@ -451,7 +451,7 @@ module.exports = (function() {
result += ' COLLATE ' + this.quoteIdentifier(attribute.collate);
}
if (this._dialect.supports.index.length && attribute.length) {
if (this._dialect.supports.index.length && attribute.length) {
result += '(' + attribute.length + ')';
}
......
var DataTypes = require('../../data-types');
//drop table Group
DataTypes.BOOLEAN = 'BIT';
DataTypes.DATE = 'DATETIME2';
module.exports = DataTypes;
\ No newline at end of file
......@@ -12,6 +12,17 @@ var MssqlDialect = function(sequelize) {
};
MssqlDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supports), {
'RETURNING': true,
'LIMIT ON UPDATE': true,
lock: true,
forShare: 'LOCK IN SHARE MODE',
index: {
collate: false,
length: false,
parser: false,
type: true,
using: 1,
}
});
MssqlDialect.prototype.Query = Query;
......
'use strict';
var Utils = require('../../utils')
, DataTypes = require('../../data-types')
, DataTypes = require('./data-types')
, SqlGenerator = require('./sql-generator')
, Model = require('../../model')
, _ = require('lodash')
, util = require('util');
//drop table Group
DataTypes.BOOLEAN = 'BIT';
module.exports = (function() {
var QueryGenerator = {
dialect: 'mssql',
quoteIdentifier: function(identifier, force) {
if (identifier === '*') return identifier;
return Utils.addTicks(identifier, '"');
addSchema: function(param) {
var self = this
, schema = (param.options && param.options.schema ? param.options.schema : undefined)
, schemaDelimiter = (param.options && param.options.schemaDelimiter ? param.options.schemaDelimiter : undefined);
if (!schema) return param.tableName || param;
return {
tableName: param.tableName || param,
table: param.tableName || param,
name: param.name || param,
schema: schema,
delimiter: schemaDelimiter || '.',
toString: function() {
return self.quoteTable(this);
}
};
},
wrapSingleQuote: function(identifier){
return '\'' + identifier + '\'';
/*
Returns a query for creating a table.
Parameters:
- tableName: Name of the new table.
- attributes: An object with containing attribute-attributeType-pairs.
Attributes should have the format:
{attributeName: type, attr2: type2}
--> e.g. {title: 'VARCHAR(255)'}
- options: An object with options.
Defaults: { engine: 'InnoDB', charset: null }
*/
/* istanbul ignore next */
createTableQuery: function(tableName, attributes, options) {
return SqlGenerator.getCreateTableSql(tableName, attributes, options);
},
showTablesQuery: function () {
return 'SELECT name FROM sys.Tables';
......@@ -40,44 +68,57 @@ module.exports = (function() {
});
},
createTableQuery: function(tableName, attributes, options) {
var attrStr = []
, self = this
, primaryKeys = Utils._.keys(Utils._.pick(attributes, function(dataType){
return dataType.indexOf('PRIMARY KEY') >= 0;
removeColumnQuery: function(tableName, attributeName) {
var query = 'ALTER TABLE "<%= tableName %>" DROP "<%= attributeName %>";';
return Utils._.template(query)({ tableName: tableName, attributeName: attributeName });
},
changeColumnQuery: function(tableName, attributes) {
var query = 'ALTER TABLE "<%= tableName %>" CHANGE <%= attributes %>;';
var attrString = [];
for (var attrName in attributes) {
var definition = attributes[attrName];
attrString.push(Utils._.template('"<%= attrName %>" "<%= attrName %>" <%= definition %>')({
attrName: attrName,
definition: definition
}));
}
var query = "IF (NOT EXISTS ("
+ "SELECT * FROM INFORMATION_SCHEMA.TABLES"
+ " WHERE TABLE_NAME='<%= tableName %>'))"
+ " BEGIN"
+ " CREATE TABLE <%= tableName %> (<%= attributes%>)"
+ " END";
return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') });
},
for (var attr in attributes) {
if (attributes.hasOwnProperty(attr)) {
var dataType = attributes[attr];
if (primaryKeys.length > 1){
dataType = dataType.replace(/ PRIMARY KEY/, '');
}
attrStr.push(self.quote(attr) + " " + dataType);
renameColumnQuery: function(tableName, attrBefore, attributes) {
var query = 'EXEC SP_RENAME \'<%= tableName %>.<%= before %>\', \'<%= after %>\'; <%= alter %>;';
var attrString = [];
var columnName;
for (var attrName in attributes) {
columnName = attrName;
if(attributes[attrName]){
var definition = attributes[attrName];
attrString.push(Utils._.template('ALTER TABLE "<%= tableName %>" ALTER COLUMN "<%= after %>" <%= definition %>')({
tableName: tableName,
after: columnName,
definition: definition
}));
}
}
return Utils._.template(query)({ tableName: tableName
, before: attrBefore
, after: columnName
, alter: attrString.join(' ')});
},
if (primaryKeys.length > 1) {
attrStr.push('PRIMARY KEY(' + primaryKeys.map(function(column){
return self.quote(column);
}).join(', ') + ')');
}
/*
Returns an insert into command. Parameters: table name + hash of attribute-value-pairs.
*/
insertQuery: function(table, valueHash, modelAttributes, options) {
return SqlGenerator.insertSql(table,valueHash,modelAttributes, options);
},
var values = {
unquotedTable: tableName,
tableName: self.quote(tableName),
attributes: attrStr.join(", ")
};
return Utils._.template(query)(values).trim() + ";";
},
showIndexQuery: function(tableName, options) {
......@@ -103,201 +144,564 @@ module.exports = (function() {
},
addIndexQuery: function(tableName, attributes, options, rawTablename) {
options = options || {};
return SqlGenerator.addIndexSql(tableName, attributes, options, rawTablename);
},
removeIndexQuery: function(tableName, indexNameOrAttributes) {
return SqlGenerator.removeIndexSql(tableName, indexNameOrAttributes);
},
var transformedAttributes = attributes.map(function(attribute) {
if (typeof attribute === 'string') {
return this.quoteIdentifier(attribute);
} else {
var result = '';
attributesToSQL: function(attributes, options) {
var result = {}
, key
, attribute;
if (!attribute.attribute) {
throw new Error('The following index attribute has no attribute: ' + util.inspect(attribute));
}
for (key in attributes) {
attribute = attributes[key];
if(key && !attribute.field)
attribute.field = key;
result[attribute.field || key] = SqlGenerator.attributeToSQL(attribute, options);
}
result += this.quoteIdentifier(attribute.attribute);
return result;
},
if (this._dialect.supports.index.collate && attribute.collate) {
result += ' COLLATE ' + this.quoteIdentifier(attribute.collate);
}
describeTableQuery: function(tableName, schema, schemaDelimiter) {
return SqlGenerator.describeTableSql(tableName, schema, schemaDelimiter);
},
if (this._dialect.supports.index.length && attribute.length) {
result += '(' + attribute.length + ')';
/**
* Generates an SQL query that returns all foreign keys of a table.
*
* @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema.
* @return {String} The generated sql query.
*/
getForeignKeysQuery: function(tableName, schemaName) {
return SqlGenerator.getForeignKeysSql(tableName);
},
/**
* 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 SqlGenerator.dropForeignKeySql(tableName, foreignKey);
},
/*
Returns a query for selecting elements in the table <tableName>.
Options:
- attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: *
- where -> A hash with conditions (e.g. {name: 'foo'})
OR an ID as integer
OR a string with conditions (e.g. 'name="foo"').
If you use a string, you have to escape it on your own.
- order -> e.g. 'id DESC'
- group
- limit -> The maximum count you want to get.
- offset -> An offset value to start from. Only useable with limit!
*/
selectQuery: function(tableName, options, model) {
// Enter and change at your own peril -- Mick Hansen
options = options || {};
var table = null
, self = this
, query
, limit = options.limit
, mainQueryItems = []
, mainAttributes = options.attributes && options.attributes.slice(0)
, mainJoinQueries = []
// We'll use a subquery if we have hasMany associations and a limit and a filtered/required association
, subQuery = limit && (options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation) && options.subQuery !== false
, subQueryItems = []
, subQueryAttributes = null
, subJoinQueries = []
, mainTableAs = null;
if (!Array.isArray(tableName) && model) {
options.tableAs = mainTableAs = this.quoteTable(model.name);
}
options.table = table = !Array.isArray(tableName) ? this.quoteTable(tableName) : tableName.map(function(t) {
if (Array.isArray(t)) {
return this.quoteTable(t[0], t[1]);
}
return this.quoteTable(t, true);
}.bind(this)).join(', ');
if (subQuery && mainAttributes) {
model.primaryKeyAttributes.forEach(function(keyAtt) {
// Check if mainAttributes contain the primary key of the model either as a field or an aliased field
if (!_.find(mainAttributes, function (attr) {
return keyAtt === attr || keyAtt === attr[0] || keyAtt === attr[1];
})) {
mainAttributes.push(model.rawAttributes[keyAtt].field ? [keyAtt, model.rawAttributes[keyAtt].field] : keyAtt);
}
});
}
if (attribute.order) {
result += ' ' + attribute.order;
// Escape attributes
mainAttributes = mainAttributes && mainAttributes.map(function(attr) {
var addTable = true;
if (attr._isSequelizeMethod) {
return attr.toString(self);
}
if (Array.isArray(attr) && attr.length === 2) {
if (attr[0]._isSequelizeMethod) {
attr[0] = attr[0].toString(self);
addTable = false;
} else {
if (attr[0].indexOf('(') === -1 && attr[0].indexOf(')') === -1) {
attr[0] = self.quoteIdentifier(attr[0]);
}
}
attr = [attr[0], self.quoteIdentifier(attr[1])].join(' AS ');
} else {
attr = attr.indexOf(Utils.TICK_CHAR) < 0 && attr.indexOf('"') < 0 ? self.quoteIdentifiers(attr) : attr;
}
return result;
if (options.include && attr.indexOf('.') === -1 && addTable) {
attr = mainTableAs + '.' + attr;
}
}.bind(this));
var onlyAttributeNames = attributes.map(function(attribute) {
return (typeof attribute === 'string') ? attribute : attribute.attribute;
}.bind(this));
options = Utils._.defaults(options, {
type: '',
indicesType: options.type || '',
indexType: options.method || undefined,
indexName: options.name || Utils.inflection.underscore(rawTablename + '_' + onlyAttributeNames.join('_')),
parser: null
return attr;
});
if (options.indicesType.toLowerCase() === 'unique') {
options.unique = true;
delete options.indicesType;
}
// If no attributes specified, use *
mainAttributes = mainAttributes || (options.include ? [mainTableAs + '.*'] : ['*']);
if (!this._dialect.supports.index.type) {
delete options.indicesType;
// If subquery, we ad the mainAttributes to the subQuery and set the mainAttributes to select * from subquery
if (subQuery) {
// We need primary keys
subQueryAttributes = mainAttributes;
mainAttributes = [mainTableAs + '.*'];
}
return Utils._.compact([
'CREATE',
options.unique ? 'UNIQUE' : '',
options.indicesType, 'INDEX',
this._dialect.supports.index.concurrently && options.concurrently ? 'CONCURRENTLY' : undefined,
this.quoteIdentifiers(options.indexName),
this._dialect.supports.index.using === 1 && options.indexType ? 'USING ' + options.indexType : '',
'ON', this.quoteIdentifiers(tableName),
this._dialect.supports.index.using === 2 && options.indexType ? 'USING ' + options.indexType : '',
'(' + transformedAttributes.join(', ') + ')',
(this._dialect.supports.index.parser && options.parser ? 'WITH PARSER ' + options.parser : undefined)
]).join(' ');
},
if (options.include) {
var generateJoinQueries = function(include, parentTable) {
var table = include.model.getTableName()
, as = include.as
, joinQueryItem = ''
, joinQueries = {
mainQuery: [],
subQuery: []
}
, attributes
, association = include.association
, through = include.through
, joinType = include.required ? ' INNER JOIN ' : ' LEFT OUTER JOIN '
, includeWhere = {}
, whereOptions = Utils._.clone(options);
whereOptions.keysEscaped = true;
if (tableName !== parentTable && mainTableAs !== parentTable) {
as = parentTable + '.' + include.as;
}
attributeMap:{
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
// includeIgnoreAttributes is used by aggregate functions
if (options.includeIgnoreAttributes !== false) {
attributes = include.attributes.map(function(attr) {
var attrAs = attr,
verbatim = false;
if (Array.isArray(attr) && attr.length === 2) {
if (attr[0]._isSequelizeMethod) {
if (attr[0] instanceof Utils.literal ||
attr[0] instanceof Utils.cast ||
attr[0] instanceof Utils.fn
) {
verbatim = true;
}
}
attr = attr.map(function($attr) {
return $attr._isSequelizeMethod ? $attr.toString(self) : $attr;
});
attrAs = attr[1];
attr = attr[0];
} else if (attr instanceof Utils.literal) {
return attr.toString(self); // We trust the user to rename the field correctly
} else if (attr instanceof Utils.cast ||
attr instanceof Utils.fn
) {
throw new Error("Tried to select attributes using Sequelize.cast or Sequelize.fn without specifying an alias for the result, during eager loading. " +
"This means the attribute will not be added to the returned instance");
}
var prefix;
if (verbatim === true) {
prefix = attr;
} else {
prefix = self.quoteIdentifier(as) + '.' + self.quoteIdentifier(attr);
}
return prefix + ' AS ' + self.quoteIdentifier(as + '.' + attrAs);
});
if (include.subQuery && subQuery) {
subQueryAttributes = subQueryAttributes.concat(attributes);
} else {
mainAttributes = mainAttributes.concat(attributes);
}
}
if (through) {
var throughTable = through.model.getTableName()
, throughAs = as + '.' + through.as
, throughAttributes = through.attributes.map(function(attr) {
return self.quoteIdentifier(throughAs) + '.' + self.quoteIdentifier(attr) + ' AS ' + self.quoteIdentifier(throughAs + '.' + attr);
})
, primaryKeysSource = association.source.primaryKeyAttributes
, tableSource = parentTable
, identSource = association.identifier
, attrSource = primaryKeysSource[0]
, where
, primaryKeysTarget = association.target.primaryKeyAttributes
, tableTarget = as
, identTarget = association.foreignIdentifier
, attrTarget = primaryKeysTarget[0]
, sourceJoinOn
, targetJoinOn
, targetWhere;
if (options.includeIgnoreAttributes !== false) {
// Through includes are always hasMany, so we need to add the attributes to the mainAttributes no matter what (Real join will never be executed in subquery)
mainAttributes = mainAttributes.concat(throughAttributes);
}
// Filter statement for left side of through
// Used by both join and subquery where
sourceJoinOn = self.quoteTable(tableSource) + '.' + self.quoteIdentifier(attrSource) + ' = ';
sourceJoinOn += self.quoteIdentifier(throughAs) + '.' + self.quoteIdentifier(identSource);
// Filter statement for right side of through
// Used by both join and subquery where
targetJoinOn = self.quoteIdentifier(tableTarget) + '.' + self.quoteIdentifier(attrTarget) + ' = ';
targetJoinOn += self.quoteIdentifier(throughAs) + '.' + self.quoteIdentifier(identTarget);
if (self._dialect.supports.joinTableDependent) {
// Generate a wrapped join so that the through table join can be dependent on the target join
joinQueryItem += joinType + '(';
joinQueryItem += self.quoteTable(throughTable, throughAs);
joinQueryItem += joinType + self.quoteTable(table, as) + ' ON ';
joinQueryItem += targetJoinOn;
joinQueryItem += ') ON '+sourceJoinOn;
} else {
// Generate join SQL for left side of through
joinQueryItem += joinType + self.quoteTable(throughTable, throughAs) + ' ON ';
joinQueryItem += sourceJoinOn;
// Generate join SQL for right side of through
joinQueryItem += joinType + self.quoteTable(table, as) + ' ON ';
joinQueryItem += targetJoinOn;
}
if (include.where) {
targetWhere = self.getWhereConditions(include.where, self.sequelize.literal(self.quoteIdentifier(as)), include.model, whereOptions);
joinQueryItem += ' AND ' + targetWhere;
if (subQuery && include.required) {
if (!options.where) options.where = {};
// Creating the as-is where for the subQuery, checks that the required association exists
options.where['__' + throughAs] = self.sequelize.asIs(['(',
'SELECT ' + self.quoteIdentifier(throughAs) + '.' + self.quoteIdentifier(identSource) + ' FROM ' + self.quoteTable(throughTable, throughAs),
! include.required && joinType + self.quoteTable(association.source.tableName, tableSource) + ' ON ' + sourceJoinOn || '',
joinType + self.quoteTable(table, as) + ' ON ' + targetJoinOn,
'WHERE ' + (! include.required && targetWhere || sourceJoinOn + ' AND ' + targetWhere),
'LIMIT 1',
')', 'IS NOT NULL'].join(' '));
}
}
} else {
var left = association.associationType === 'BelongsTo' ? association.target : association.source
, primaryKeysLeft = left.primaryKeyAttributes
, tableLeft = association.associationType === 'BelongsTo' ? as : parentTable
, attrLeft = primaryKeysLeft[0]
, tableRight = association.associationType === 'BelongsTo' ? parentTable : as
, attrRight = association.identifier
, joinOn;
// Alias the left attribute if the left attribute is not from a subqueried main table
// When doing a query like SELECT aliasedKey FROM (SELECT primaryKey FROM primaryTable) only aliasedKey is available to the join, this is not the case when doing a regular select where you can't used the aliased attribute
if (!subQuery || parentTable !== mainTableAs || tableLeft !== parentTable) {
if (left.rawAttributes[attrLeft].field) {
attrLeft = left.rawAttributes[attrLeft].field;
}
}
// Filter statement
// Used by both join and subquery where
joinOn =
// Left side
(
(subQuery && !include.subQuery && include.parent.subQuery && !(include.hasParentRequired && include.hasParentWhere)) && self.quoteIdentifier(tableLeft + '.' + attrLeft) ||
self.quoteTable(tableLeft) + '.' + self.quoteIdentifier(attrLeft)
)
+ ' = ' +
// Right side
(
(subQuery && !include.subQuery && include.parent.subQuery && (include.hasParentRequired && include.hasParentWhere)) && self.quoteIdentifier(tableRight + '.' + attrRight) ||
self.quoteTable(tableRight) + '.' + self.quoteIdentifier(attrRight)
);
if (include.where) {
joinOn += ' AND ' + self.getWhereConditions(include.where, self.sequelize.literal(self.quoteIdentifier(as)), include.model, whereOptions);
// If its a multi association we need to add a where query to the main where (executed in the subquery)
if (subQuery && association.isMultiAssociation && include.required) {
if (!options.where) options.where = {};
// Creating the as-is where for the subQuery, checks that the required association exists
options.where['__' + as] = self.sequelize.asIs(['(',
'SELECT ' + self.quoteIdentifier(attrRight),
'FROM ' + self.quoteTable(table, as),
'WHERE ' + joinOn,
'LIMIT 1',
')', 'IS NOT NULL'].join(' '));
}
}
// Generate join SQL
joinQueryItem += joinType + self.quoteTable(table, as) + ' ON ' + joinOn;
}
if (include.subQuery && subQuery) {
joinQueries.subQuery.push(joinQueryItem);
} else {
joinQueries.mainQuery.push(joinQueryItem);
}
if (include.include) {
include.include.forEach(function(childInclude) {
if (childInclude._pseudo) return;
var childJoinQueries = generateJoinQueries(childInclude, as);
if (childInclude.subQuery && subQuery) {
joinQueries.subQuery = joinQueries.subQuery.concat(childJoinQueries.subQuery);
}
if (childJoinQueries.mainQuery) {
joinQueries.mainQuery = joinQueries.mainQuery.concat(childJoinQueries.mainQuery);
}
}.bind(this));
}
return joinQueries;
};
}
var template;
var isEnum = false;
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());
}
// Loop through includes and generate subqueries
options.include.forEach(function(include) {
var joinQueries = generateJoinQueries(include, options.tableAs);
template += ' ';
if (attribute.allowNull === false && !isEnum) {
template += this.attributeMap.notNull;
}else if(!isEnum){
template += this.attributeMap.isNull;
}
subJoinQueries = subJoinQueries.concat(joinQueries.subQuery);
mainJoinQueries = mainJoinQueries.concat(joinQueries.mainQuery);
if (attribute.autoIncrement) {
template += ' ' + this.attributeMap.autoIncrement;
}.bind(this));
}
// Blobs/texts cannot have a defaultValue
if (attribute.type !== 'TEXT' && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) {
template += ' DEFAULT ' + this.escape(attribute.defaultValue);
}
// If using subQuery select defined subQuery attributes and join subJoinQueries
if (subQuery) {
subQueryItems.push('SELECT ' + subQueryAttributes.join(', ') + ' FROM ' + options.table);
if (mainTableAs) {
subQueryItems.push(' AS ' + mainTableAs);
}
subQueryItems.push(subJoinQueries.join(''));
if (attribute.unique === true) {
template += ' UNIQUE';
// Else do it the reguar way
} else {
mainQueryItems.push('SELECT ' + mainAttributes.join(', ') + ' FROM ' + options.table);
if (mainTableAs) {
mainQueryItems.push(' AS ' + mainTableAs);
}
mainQueryItems.push(mainJoinQueries.join(''));
}
if (attribute.primaryKey) {
template += ' PRIMARY KEY';
// Add WHERE to sub or main query
if (options.hasOwnProperty('where')) {
options.where = this.getWhereConditions(options.where, mainTableAs || tableName, model, options);
if (options.where) {
if (subQuery) {
subQueryItems.push(' WHERE ' + options.where);
} else {
mainQueryItems.push(' WHERE ' + options.where);
}
}
}
if (attribute.references) {
template += ' REFERENCES ' + this.quoteTable(attribute.references);
if (attribute.referencesKey) {
template += ' (' + this.quoteIdentifier(attribute.referencesKey) + ')';
// Add GROUP BY to sub or main query
if (options.group) {
options.group = Array.isArray(options.group) ? options.group.map(function(t) { return this.quote(t, model); }.bind(this)).join(', ') : options.group;
if (subQuery) {
subQueryItems.push(' GROUP BY ' + options.group);
} else {
template += ' (' + this.quoteIdentifier('id') + ')';
mainQueryItems.push(' GROUP BY ' + options.group);
}
}
if (attribute.onDelete) {
template += ' ON DELETE ' + attribute.onDelete.toUpperCase();
// Add HAVING to sub or main query
if (options.hasOwnProperty('having')) {
options.having = this.getWhereConditions(options.having, tableName, model, options, false);
if (subQuery) {
subQueryItems.push(' HAVING ' + options.having);
} else {
mainQueryItems.push(' HAVING ' + options.having);
}
}
// Add ORDER to sub or main query
if (options.order) {
var mainQueryOrder = [];
var subQueryOrder = [];
if (Array.isArray(options.order)) {
options.order.forEach(function(t) {
if (subQuery && !(t[0] instanceof Model) && !(t[0].model instanceof Model)) {
subQueryOrder.push(this.quote(t, model));
}
mainQueryOrder.push(this.quote(t, model));
}.bind(this));
} else {
mainQueryOrder.push(options.order);
}
if (attribute.onUpdate) {
template += ' ON UPDATE ' + attribute.onUpdate.toUpperCase();
if (mainQueryOrder.length) {
mainQueryItems.push(' ORDER BY ' + mainQueryOrder.join(', '));
}
if (subQueryOrder.length) {
subQueryItems.push(' ORDER BY ' + subQueryOrder.join(', '));
}
}
return template;
},
var limitOrder = this.addLimitAndOffset(options, query);
attributesToSQL: function(attributes, options) {
var result = {}
, key
, attribute;
// Add LIMIT, OFFSET to sub or main query
if (limitOrder) {
if (subQuery) {
subQueryItems.push(limitOrder);
} else {
mainQueryItems.push(limitOrder);
}
}
for (key in attributes) {
attribute = attributes[key];
result[attribute.field || key] = this.attributeToSQL(attribute, options);
// If using subQuery, select attributes from wrapped subQuery and join out join tables
if (subQuery) {
query = 'SELECT ' + mainAttributes.join(', ') + ' FROM (';
query += subQueryItems.join('');
query += ') AS ' + options.tableAs;
query += mainJoinQueries.join('');
query += mainQueryItems.join('');
} else {
query = mainQueryItems.join('');
}
return result;
},
if (options.lock && this._dialect.supports.lock) {
if (options.lock === 'SHARE') {
query += ' ' + this._dialect.supports.forShare;
} else {
query += ' FOR UPDATE';
}
}
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;
},
query += ';';
return query;
},
/**
* Generates an SQL query that returns all foreign keys of a table.
* Returns a query that starts a transaction.
*
* @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema.
* @return {String} The generated sql query.
* @param {Boolean} value A boolean that states whether autocommit shall be done or not.
* @return {String} The generated sql query.
*/
getForeignKeysQuery: function(tableName, schemaName) {
return [
"SELECT",
"constraint_name = C.CONSTRAINT_NAME",
"FROM",
"INFORMATION_SCHEMA.TABLE_CONSTRAINTS C",
"WHERE C.CONSTRAINT_TYPE != 'PRIMARY KEY'",
"AND C.TABLE_NAME = ", this.wrapSingleQuote(tableName)
].join(" ");
setAutocommitQuery: function(value) {
return '';
//return 'SET autocommit = ' + (!!value ? 1 : 0) + ';';
},
/**
* Generates an SQL query that removes a foreign key from a table.
* Returns a query that sets the transaction isolation level.
*
* @param {String} tableName The name of the table.
* @param {String} foreignKey The name of the foreign key constraint.
* @return {String} The generated sql query.
* @param {String} value The isolation level.
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP ' + this.quoteIdentifier(foreignKey) + ';';
setIsolationLevelQuery: function(value, options) {
if (options.parent) {
return;
}
return '';
//return 'SET SESSION TRANSACTION ISOLATION LEVEL ' + value + ';';
},
/**
* Returns a query that starts a transaction.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
startTransactionQuery: function(transaction, options) {
if (options.parent) {
return 'SAVEPOINT ' + this.quoteIdentifier(transaction.name) + ';';
}
return '';
//return 'BEGIN TRY\nBEGIN TRANSACTION';
},
/**
* Returns a query that commits a transaction.
*
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
commitTransactionQuery: function(options) {
if (options.parent) {
return;
}
return 'COMMIT;';
},
/**
* Returns a query that rollbacks a transaction.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
*/
rollbackTransactionQuery: function(transaction, options) {
if (options.parent) {
return 'ROLLBACK TO SAVEPOINT ' + this.quoteIdentifier(transaction.name) + ';';
}
return 'ROLLBACK;';
},
addLimitAndOffset: function(options, query) {
query = query || '';
if (options.offset && !options.limit) {
query += ' LIMIT ' + options.offset + ', ' + 10000000000000;
} else if (options.limit) {
if (options.offset) {
query += ' LIMIT ' + options.offset + ', ' + options.limit;
} else {
query += ' LIMIT ' + options.limit;
}
}
return query;
}
};
return Utils._.extend(Utils._.clone(require('../mysql/query-generator')), QueryGenerator);
......
......@@ -31,15 +31,15 @@ module.exports = (function() {
}
var promise = new Utils.Promise(function(resolve, reject) {
//console.log(self.sql);
console.log(self.sql);
self
.connection
.lib
.execute(self.connection.config, { query: self.sql })
.then(
function (data) { resolve(self.formatResults(data.result)); },
function (err) { reject(self.formatError(err)); }
function (data) { resolve(self.formatResults(data.result)); }
//function (err) { reject(self.formatError(err)); }
);
});
......@@ -74,6 +74,8 @@ module.exports = (function() {
if (this.sql.toLowerCase().indexOf("select c.name, t.name as 'type', c.is_nullable as isnull") === 0) {
result = {};
data.forEach(function(_result) {
if(_result.Default)
_result.Default = _result.Default.replace('(\'','').replace('\')','').replace(/'/g,'');
result[_result.Name] = {
type: _result.Type.toUpperCase(),
allowNull: _result.IsNull,
......
'use strict';
var Utils = require('../../utils')
, SqlString = require('../../sql-string')
, DataTypes = require('./data-types');
/*
Escape a value (e.g. a string, number or date)
*/
var dialect = 'mssql';
//mssql doesnt support time zone
function escape(value, field) {
if (value && value._isSequelizeMethod) {
return value.toString();
} else {
return SqlString.escape(value, false, null, dialect, field);
}
}
function quoteIdentifier(identifier, force) {
if (identifier === '*') return identifier;
return Utils.addTicks(identifier, '"');
}
/*
Split an identifier into .-separated tokens and quote each part
*/
function quoteIdentifiers(identifiers, force) {
if (identifiers.indexOf('.') !== -1) {
identifiers = identifiers.split('.');
return quoteIdentifier(identifiers.slice(0, identifiers.length - 1).join('.'))
+ '.' + quoteIdentifier(identifiers[identifiers.length - 1]);
} else {
return quoteIdentifier(identifiers);
}
}
function wrapSingleQuote(identifier){
return Utils.addTicks(identifier, "'");
}
function nameIndexes(indexes, rawTablename) {
return Utils._.map(indexes, function (index) {
if (!index.hasOwnProperty('name')) {
var onlyAttributeNames = index.fields.map(function(attribute) {
return (typeof attribute === 'string') ? attribute : attribute.attribute;
}.bind(this));
index.name = Utils.inflection.underscore(rawTablename + '_' + onlyAttributeNames.join('_'));
}
return index;
});
}
function loadColumn(attributes){
var attrStr = [];
for (var attr in attributes) {
var dataType = attributes[attr];
attrStr.push(quoteIdentifier(dataType));
}
return attrStr;
}
function loadColumnWithTypes(attributes){
var attrStr = [];
for (var attr in attributes) {
var dataType = attributes[attr];
attrStr.push(quoteIdentifier(attr) + " " + dataType);
}
return attrStr;
}
function addTableExistsWrapper(tableName, query){
return [
"IF (NOT EXISTS (",
"SELECT * FROM INFORMATION_SCHEMA.TABLES",
"WHERE TABLE_NAME='<%= unquotedTable %>'))",
"BEGIN",
query,
"END"
].join(" ");
}
module.exports = {
getCreateTableSql: function(tableName, attributes, options) {
var query = "CREATE TABLE <%= tableName %> (<%= attributes%>)";
var attrStr = []
, self = this
, primaryKeys = Utils._.keys(Utils._.pick(attributes, function(dataType){
return dataType.indexOf('PRIMARY KEY') >= 0;
}));
attrStr = loadColumnWithTypes(attributes);
var values = {
unquotedTable: tableName,
tableName: quoteIdentifier(tableName),
attributes: attrStr.join(", ")
};
query = addTableExistsWrapper(tableName, query);
return Utils._.template(query)(values).trim() + ";";
},
insertSql: function(table, valueHash, modelAttributes, options) {
options = options || {};
var query
, valueQuery = 'INSERT INTO <%= table %> (<%= attributes %>)'
, emptyQuery = 'INSERT INTO <%= table %>'
, fields = []
, selFields = []
, values = []
, key
, value
, modelAttributeMap = {};
if (this._dialect.supports['RETURNING'] && options.returning) {
valueQuery += ' OUTPUT <%= selFields %> VALUES (<%= values %>)';
emptyQuery += ' OUTPUT <%= selFields %> VALUES ()';
}else{
valueQuery += ' VALUES (<%= values %>)';
}
valueHash = Utils.removeNullValuesFromHash(valueHash, this.options.omitNull);
for (key in valueHash) {
if (valueHash.hasOwnProperty(key)) {
value = valueHash[key];
// SERIALS' can't be NULL in postgresql, use DEFAULT where supported
if(modelAttributeMap && modelAttributeMap[key] && modelAttributeMap[key].autoIncrement && value){
fields.push(quoteIdentifier(key));
selFields.push(wrapSingleQuote(key));
valueQuery = 'SET IDENTITY_INSERT <%= table %> ON;'
+ valueQuery
+ '; SET IDENTITY_INSERT <%= table %> OFF';
values.push(escape(value, (modelAttributeMap && modelAttributeMap[key]) || undefined));
}else if(value) {
fields.push(quoteIdentifier(key));
selFields.push(wrapSingleQuote(key));
values.push(escape(value, (modelAttributeMap && modelAttributeMap[key]) || undefined));
}
}
}
var replacements = {
table: this.quoteTable(table),
attributes: fields.join(','),
selFields: selFields.join(','),
values: values.join(',')
};
query = (replacements.attributes.length ? valueQuery : emptyQuery) + ';';
return Utils._.template(query)(replacements);
},
addIndexSql: function(tableName, attributes, options, rawTablename){
if (!options.name) {
// Mostly for cases where addIndex is called directly by the user without an options object (for example in migrations)
// All calls that go through sequelize should already have a name
options.fields = options.fields || attributes;
options = nameIndexes([options], rawTablename)[0];
}
options = Utils._.defaults(options, {
type: '',
indicesType: options.type || '',
indexType: options.method || undefined,
indexName: options.name,
parser: null
});
var attrStr = loadColumn(attributes);
return Utils._.compact([
'CREATE',
options.unique ? 'UNIQUE' : '',
options.indicesType, 'INDEX',
quoteIdentifiers(options.indexName),
'ON', quoteIdentifiers(tableName),
'(' + attrStr.join(', ') + ')'
]).join(' ');
},
removeIndexSql: function(tableName, indexNameOrAttributes){
var sql = 'DROP INDEX <%= indexName %> ON <%= tableName %>'
, indexName = indexNameOrAttributes;
if (typeof indexName !== 'string') {
indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_'));
}
var values = {
tableName: quoteIdentifiers(tableName),
indexName: indexName
};
return Utils._.template(sql)(values);
},
attributeMap:{
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
};
}
var template;
var isEnum = false;
if (attribute.type.toString() === DataTypes.ENUM.toString()) {
isEnum = true;
template = 'VARCHAR(10) NOT NULL CHECK ("'
+ attribute.field + '" IN('
+ Utils._.map(attribute.values, function(value) {
return escape(value);
}.bind(this)).join(', ') + '))';
} else {
template = attribute.type.toString();
}
template += ' ';
if (attribute.allowNull === false && !isEnum && !attribute.primaryKey) {
template += this.attributeMap.notNull;
}else if(!isEnum && !attribute.primaryKey){
template += this.attributeMap.isNull;
}else if(!isEnum) {
template += 'PRIMARY KEY';
}
if (attribute.autoIncrement) {
template += ' ' + this.attributeMap.autoIncrement;
}
// Blobs/texts cannot have a defaultValue
if (attribute.type !== 'TEXT'
&& attribute.type._binary !== true
&& Utils.defaultValueSchemable(attribute.defaultValue)) {
if(options){
if(escape(attribute.defaultValue) !== 'NULL'){
template += ' DEFAULT ' + wrapSingleQuote(attribute.defaultValue);
}
}
}
if (attribute.unique === true) {
template += ' UNIQUE';
}
if (attribute.references) {
template += ' REFERENCES ' + quoteIdentifier(attribute.references);
if (attribute.referencesKey) {
template += ' (' + quoteIdentifier(attribute.referencesKey) + ')';
} else {
template += ' (' + quoteIdentifier('id') + ')';
}
if (attribute.onDelete) {
if(attribute.onDelete.toUpperCase() !== 'RESTRICT'){
template += ' ON DELETE ' + attribute.onDelete.toUpperCase();
}
}
if (attribute.onUpdate) {
template += ' ON UPDATE ' + attribute.onUpdate.toUpperCase();
}
}
return template;
},
describeTableSql: 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(",
wrapSingleQuote(tableName),
");"].join(" ");
return qry;
},
getForeignKeysSql: function(tableName){
return [
"SELECT",
"constraint_name = C.CONSTRAINT_NAME",
"FROM",
"INFORMATION_SCHEMA.TABLE_CONSTRAINTS C",
"WHERE C.CONSTRAINT_TYPE != 'PRIMARY KEY'",
"AND C.TABLE_NAME = ", wrapSingleQuote(tableName)
].join(" ");
},
dropForeignKeySql: function(tableName, foreignKey){
return ['ALTER TABLE',
quoteIdentifier(tableName),
'DROP',
quoteIdentifier(foreignKey),
';'].join(' ');
}
};
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!