610 lines
18 KiB
JavaScript
610 lines
18 KiB
JavaScript
|
|
'use strict';
|
||
|
|
|
||
|
|
/* jshint -W110 */
|
||
|
|
var Utils = require('../../utils')
|
||
|
|
, DataTypes = require('../../data-types')
|
||
|
|
, Model = require('../../model')
|
||
|
|
, AbstractQueryGenerator = require('../abstract/query-generator');
|
||
|
|
|
||
|
|
/* istanbul ignore next */
|
||
|
|
var throwMethodUndefined = function(methodName) {
|
||
|
|
throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.');
|
||
|
|
};
|
||
|
|
|
||
|
|
var QueryGenerator = {
|
||
|
|
options: {},
|
||
|
|
dialect: 'mssql',
|
||
|
|
|
||
|
|
createSchema: function(schema) {
|
||
|
|
return [
|
||
|
|
'IF NOT EXISTS (SELECT schema_name',
|
||
|
|
'FROM information_schema.schemata',
|
||
|
|
'WHERE schema_name =', wrapSingleQuote(schema), ')',
|
||
|
|
'BEGIN',
|
||
|
|
"EXEC sp_executesql N'CREATE SCHEMA",
|
||
|
|
this.quoteIdentifier(schema),
|
||
|
|
";'",
|
||
|
|
'END;'
|
||
|
|
].join(' ');
|
||
|
|
},
|
||
|
|
|
||
|
|
showSchemasQuery: function() {
|
||
|
|
return [
|
||
|
|
'SELECT "name" as "schema_name" FROM sys.schemas as s',
|
||
|
|
'WHERE "s"."name" NOT IN (',
|
||
|
|
"'INFORMATION_SCHEMA', 'dbo', 'guest', 'sys', 'archive'",
|
||
|
|
')', 'AND', '"s"."name" NOT LIKE', "'db_%'"
|
||
|
|
].join(' ');
|
||
|
|
},
|
||
|
|
|
||
|
|
versionQuery: function() {
|
||
|
|
return "SELECT @@VERSION as 'version'";
|
||
|
|
},
|
||
|
|
|
||
|
|
createTableQuery: function(tableName, attributes, options) {
|
||
|
|
var query = "IF OBJECT_ID('<%= table %>', 'U') IS NULL CREATE TABLE <%= table %> (<%= attributes %>)"
|
||
|
|
, primaryKeys = []
|
||
|
|
, foreignKeys = {}
|
||
|
|
, attrStr = []
|
||
|
|
, self = this;
|
||
|
|
|
||
|
|
for (var attr in attributes) {
|
||
|
|
if (attributes.hasOwnProperty(attr)) {
|
||
|
|
var dataType = attributes[attr]
|
||
|
|
, match;
|
||
|
|
|
||
|
|
if (Utils._.includes(dataType, 'PRIMARY KEY')) {
|
||
|
|
primaryKeys.push(attr);
|
||
|
|
|
||
|
|
if (Utils._.includes(dataType, 'REFERENCES')) {
|
||
|
|
// MSSQL doesn't support inline REFERENCES declarations: move to the end
|
||
|
|
match = dataType.match(/^(.+) (REFERENCES.*)$/);
|
||
|
|
attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1].replace(/PRIMARY KEY/, ''));
|
||
|
|
foreignKeys[attr] = match[2];
|
||
|
|
} else {
|
||
|
|
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace(/PRIMARY KEY/, ''));
|
||
|
|
}
|
||
|
|
} else if (Utils._.includes(dataType, 'REFERENCES')) {
|
||
|
|
// MSSQL doesn't support inline REFERENCES declarations: move to the end
|
||
|
|
match = dataType.match(/^(.+) (REFERENCES.*)$/);
|
||
|
|
attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1]);
|
||
|
|
foreignKeys[attr] = match[2];
|
||
|
|
} else {
|
||
|
|
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var values = {
|
||
|
|
table: this.quoteTable(tableName),
|
||
|
|
attributes: attrStr.join(', '),
|
||
|
|
}
|
||
|
|
, pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk); }.bind(this)).join(', ');
|
||
|
|
|
||
|
|
if (!!options.uniqueKeys) {
|
||
|
|
Utils._.each(options.uniqueKeys, function(columns, indexName) {
|
||
|
|
if (!Utils._.isString(indexName)) {
|
||
|
|
indexName = 'uniq_' + tableName + '_' + columns.fields.join('_');
|
||
|
|
}
|
||
|
|
values.attributes += ', CONSTRAINT ' + self.quoteIdentifier(indexName) + ' UNIQUE (' + Utils._.map(columns.fields, self.quoteIdentifier).join(', ') + ')';
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pkString.length > 0) {
|
||
|
|
values.attributes += ', PRIMARY KEY (' + pkString + ')';
|
||
|
|
}
|
||
|
|
|
||
|
|
for (var fkey in foreignKeys) {
|
||
|
|
if (foreignKeys.hasOwnProperty(fkey)) {
|
||
|
|
values.attributes += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return Utils._.template(query)(values).trim() + ';';
|
||
|
|
},
|
||
|
|
|
||
|
|
describeTableQuery: function(tableName, schema) {
|
||
|
|
var sql = [
|
||
|
|
'SELECT',
|
||
|
|
"c.COLUMN_NAME AS 'Name',",
|
||
|
|
"c.DATA_TYPE AS 'Type',",
|
||
|
|
"c.IS_NULLABLE as 'IsNull',",
|
||
|
|
"COLUMN_DEFAULT AS 'Default'",
|
||
|
|
'FROM',
|
||
|
|
'INFORMATION_SCHEMA.TABLES t',
|
||
|
|
'INNER JOIN',
|
||
|
|
'INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME',
|
||
|
|
'WHERE t.TABLE_NAME =', wrapSingleQuote(tableName)
|
||
|
|
].join(' ');
|
||
|
|
|
||
|
|
if (schema) {
|
||
|
|
sql += 'AND t.TABLE_SCHEMA =' + wrapSingleQuote(schema);
|
||
|
|
}
|
||
|
|
|
||
|
|
return sql;
|
||
|
|
},
|
||
|
|
|
||
|
|
renameTableQuery: function(before, after) {
|
||
|
|
var query = 'EXEC sp_rename <%= before %>, <%= after %>;';
|
||
|
|
return Utils._.template(query)({
|
||
|
|
before: this.quoteTable(before),
|
||
|
|
after: this.quoteTable(after)
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
showTablesQuery: function () {
|
||
|
|
return 'SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES;';
|
||
|
|
},
|
||
|
|
|
||
|
|
dropTableQuery: function(tableName) {
|
||
|
|
var query = "IF OBJECT_ID('<%= table %>', 'U') IS NOT NULL DROP TABLE <%= table %>";
|
||
|
|
var values = {
|
||
|
|
table: this.quoteTable(tableName)
|
||
|
|
};
|
||
|
|
|
||
|
|
return Utils._.template(query)(values).trim() + ';';
|
||
|
|
},
|
||
|
|
|
||
|
|
addColumnQuery: function(table, key, dataType) {
|
||
|
|
// FIXME: attributeToSQL SHOULD be using attributes in addColumnQuery
|
||
|
|
// but instead we need to pass the key along as the field here
|
||
|
|
dataType.field = key;
|
||
|
|
|
||
|
|
var query = 'ALTER TABLE <%= table %> ADD <%= attribute %>;'
|
||
|
|
, attribute = Utils._.template('<%= key %> <%= definition %>')({
|
||
|
|
key: this.quoteIdentifier(key),
|
||
|
|
definition: this.attributeToSQL(dataType, {
|
||
|
|
context: 'addColumn'
|
||
|
|
})
|
||
|
|
});
|
||
|
|
|
||
|
|
return Utils._.template(query)({
|
||
|
|
table: this.quoteTable(table),
|
||
|
|
attribute: attribute
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
removeColumnQuery: function(tableName, attributeName) {
|
||
|
|
var query = 'ALTER TABLE <%= tableName %> DROP <%= attributeName %>;';
|
||
|
|
return Utils._.template(query)({
|
||
|
|
tableName: this.quoteTable(tableName),
|
||
|
|
attributeName: this.quoteIdentifier(attributeName)
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
changeColumnQuery: function(tableName, attributes) {
|
||
|
|
var query = 'ALTER TABLE <%= tableName %> ALTER COLUMN <%= attributes %>;';
|
||
|
|
var attrString = [];
|
||
|
|
|
||
|
|
for (var attrName in attributes) {
|
||
|
|
var definition = attributes[attrName];
|
||
|
|
|
||
|
|
attrString.push(Utils._.template('<%= attrName %> <%= definition %>')({
|
||
|
|
attrName: this.quoteIdentifier(attrName),
|
||
|
|
definition: definition
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
return Utils._.template(query)({
|
||
|
|
tableName: this.quoteTable(tableName),
|
||
|
|
attributes: attrString.join(', ')
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
renameColumnQuery: function(tableName, attrBefore, attributes) {
|
||
|
|
var query = "EXEC sp_rename '<%= tableName %>.<%= before %>', '<%= after %>', 'COLUMN';"
|
||
|
|
, newName = Object.keys(attributes)[0];
|
||
|
|
|
||
|
|
return Utils._.template(query)({
|
||
|
|
tableName: this.quoteTable(tableName),
|
||
|
|
before: attrBefore,
|
||
|
|
after: newName
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
bulkInsertQuery: function(tableName, attrValueHashes, options, attributes) {
|
||
|
|
var query = 'INSERT INTO <%= table %> (<%= attributes %>)<%= output %> VALUES <%= tuples %>;'
|
||
|
|
, emptyQuery = 'INSERT INTO <%= table %><%= output %> DEFAULT VALUES'
|
||
|
|
, tuples = []
|
||
|
|
, allAttributes = []
|
||
|
|
, needIdentityInsertWrapper = false
|
||
|
|
, allQueries = []
|
||
|
|
, outputFragment;
|
||
|
|
|
||
|
|
if (options.returning) {
|
||
|
|
outputFragment = ' OUTPUT INSERTED.*';
|
||
|
|
}
|
||
|
|
|
||
|
|
Utils._.forEach(attrValueHashes, function(attrValueHash) {
|
||
|
|
// special case for empty objects with primary keys
|
||
|
|
var fields = Object.keys(attrValueHash);
|
||
|
|
if (fields.length === 1 && attributes[fields[0]].autoIncrement && attrValueHash[fields[0]] === null) {
|
||
|
|
allQueries.push(emptyQuery);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// normal case
|
||
|
|
Utils._.forOwn(attrValueHash, function(value, key) {
|
||
|
|
if (value !== null && attributes[key].autoIncrement) {
|
||
|
|
needIdentityInsertWrapper = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (allAttributes.indexOf(key) === -1) {
|
||
|
|
if (value === null && attributes[key].autoIncrement)
|
||
|
|
return;
|
||
|
|
|
||
|
|
allAttributes.push(key);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
if (allAttributes.length > 0) {
|
||
|
|
Utils._.forEach(attrValueHashes, function(attrValueHash) {
|
||
|
|
tuples.push('(' +
|
||
|
|
allAttributes.map(function(key) {
|
||
|
|
return this.escape(attrValueHash[key]);
|
||
|
|
}.bind(this)).join(',') +
|
||
|
|
')');
|
||
|
|
}.bind(this));
|
||
|
|
|
||
|
|
allQueries.push(query);
|
||
|
|
}
|
||
|
|
|
||
|
|
var replacements = {
|
||
|
|
table: this.quoteTable(tableName),
|
||
|
|
attributes: allAttributes.map(function(attr) {
|
||
|
|
return this.quoteIdentifier(attr);
|
||
|
|
}.bind(this)).join(','),
|
||
|
|
tuples: tuples,
|
||
|
|
output: outputFragment
|
||
|
|
};
|
||
|
|
|
||
|
|
var generatedQuery = Utils._.template(allQueries.join(';'))(replacements);
|
||
|
|
if (needIdentityInsertWrapper) {
|
||
|
|
generatedQuery = [
|
||
|
|
'SET IDENTITY_INSERT', this.quoteTable(tableName), 'ON;',
|
||
|
|
generatedQuery,
|
||
|
|
'SET IDENTITY_INSERT', this.quoteTable(tableName), 'OFF;',
|
||
|
|
].join(' ');
|
||
|
|
}
|
||
|
|
|
||
|
|
return generatedQuery;
|
||
|
|
},
|
||
|
|
|
||
|
|
deleteQuery: function(tableName, where, options) {
|
||
|
|
options = options || {};
|
||
|
|
|
||
|
|
var table = this.quoteTable(tableName);
|
||
|
|
if (options.truncate === true) {
|
||
|
|
// Truncate does not allow LIMIT and WHERE
|
||
|
|
return 'TRUNCATE TABLE ' + table;
|
||
|
|
}
|
||
|
|
|
||
|
|
where = this.getWhereConditions(where);
|
||
|
|
var limit = ''
|
||
|
|
, query = 'DELETE<%= limit %> FROM <%= table %><%= where %>; ' +
|
||
|
|
'SELECT @@ROWCOUNT AS AFFECTEDROWS;';
|
||
|
|
|
||
|
|
if (Utils._.isUndefined(options.limit)) {
|
||
|
|
options.limit = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!!options.limit) {
|
||
|
|
limit = ' TOP(' + this.escape(options.limit) + ')';
|
||
|
|
}
|
||
|
|
|
||
|
|
var replacements = {
|
||
|
|
limit: limit,
|
||
|
|
table: table,
|
||
|
|
where: where,
|
||
|
|
};
|
||
|
|
|
||
|
|
if (replacements.where) {
|
||
|
|
replacements.where = ' WHERE ' + replacements.where;
|
||
|
|
}
|
||
|
|
|
||
|
|
return Utils._.template(query)(replacements);
|
||
|
|
},
|
||
|
|
|
||
|
|
showIndexesQuery: function(tableName) {
|
||
|
|
var sql = "EXEC sys.sp_helpindex @objname = N'<%= tableName %>';";
|
||
|
|
return Utils._.template(sql)({
|
||
|
|
tableName: this.quoteTable(tableName)
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
removeIndexQuery: 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: this.quoteIdentifiers(tableName),
|
||
|
|
indexName: indexName
|
||
|
|
};
|
||
|
|
|
||
|
|
return Utils._.template(sql)(values);
|
||
|
|
},
|
||
|
|
|
||
|
|
attributeToSQL: function(attribute) {
|
||
|
|
if (!Utils._.isPlainObject(attribute)) {
|
||
|
|
attribute = {
|
||
|
|
type: attribute
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// handle self referential constraints
|
||
|
|
if (attribute.references) {
|
||
|
|
attribute = Utils.formatReferences(attribute);
|
||
|
|
|
||
|
|
if (attribute.Model && attribute.Model.tableName === attribute.references.model) {
|
||
|
|
this.sequelize.log('MSSQL does not support self referencial constraints, '
|
||
|
|
+ 'we will remove it but we recommend restructuring your query');
|
||
|
|
attribute.onDelete = '';
|
||
|
|
attribute.onUpdate = '';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var template;
|
||
|
|
|
||
|
|
if (attribute.type instanceof DataTypes.ENUM) {
|
||
|
|
if (attribute.type.values && !attribute.values) attribute.values = attribute.type.values;
|
||
|
|
|
||
|
|
// enums are a special case
|
||
|
|
template = attribute.type.toSql();
|
||
|
|
template += ' CHECK (' + attribute.field + ' IN(' + Utils._.map(attribute.values, function(value) {
|
||
|
|
return this.escape(value);
|
||
|
|
}.bind(this)).join(', ') + '))';
|
||
|
|
return template;
|
||
|
|
} else {
|
||
|
|
template = attribute.type.toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (attribute.allowNull === false) {
|
||
|
|
template += ' NOT NULL';
|
||
|
|
} else if (!attribute.primaryKey && !Utils.defaultValueSchemable(attribute.defaultValue)) {
|
||
|
|
template += ' NULL';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (attribute.autoIncrement) {
|
||
|
|
template += ' IDENTITY(1,1)';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 (attribute.unique === true) {
|
||
|
|
template += ' UNIQUE';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (attribute.primaryKey) {
|
||
|
|
template += ' PRIMARY KEY';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (attribute.references) {
|
||
|
|
template += ' REFERENCES ' + this.quoteTable(attribute.references.model);
|
||
|
|
|
||
|
|
if (attribute.references.key) {
|
||
|
|
template += ' (' + this.quoteIdentifier(attribute.references.key) + ')';
|
||
|
|
} else {
|
||
|
|
template += ' (' + this.quoteIdentifier('id') + ')';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (attribute.onDelete) {
|
||
|
|
template += ' ON DELETE ' + attribute.onDelete.toUpperCase();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (attribute.onUpdate) {
|
||
|
|
template += ' ON UPDATE ' + attribute.onUpdate.toUpperCase();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return template;
|
||
|
|
},
|
||
|
|
|
||
|
|
attributesToSQL: function(attributes, options) {
|
||
|
|
var result = {}
|
||
|
|
, key
|
||
|
|
, attribute
|
||
|
|
, existingConstraints = [];
|
||
|
|
|
||
|
|
for (key in attributes) {
|
||
|
|
attribute = attributes[key];
|
||
|
|
|
||
|
|
if (attribute.references) {
|
||
|
|
attribute = Utils.formatReferences(attributes[key]);
|
||
|
|
|
||
|
|
if (existingConstraints.indexOf(attribute.references.model.toString()) !== -1) {
|
||
|
|
// no cascading constraints to a table more than once
|
||
|
|
attribute.onDelete = '';
|
||
|
|
attribute.onUpdate = '';
|
||
|
|
} else {
|
||
|
|
existingConstraints.push(attribute.references.model.toString());
|
||
|
|
|
||
|
|
// NOTE: this really just disables cascading updates for all
|
||
|
|
// definitions. Can be made more robust to support the
|
||
|
|
// few cases where MSSQL actually supports them
|
||
|
|
attribute.onUpdate = '';
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
if (key && !attribute.field) attribute.field = key;
|
||
|
|
result[attribute.field || key] = this.attributeToSQL(attribute, options);
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
},
|
||
|
|
|
||
|
|
findAutoIncrementField: function(factory) {
|
||
|
|
var fields = [];
|
||
|
|
for (var name in factory.attributes) {
|
||
|
|
if (factory.attributes.hasOwnProperty(name)) {
|
||
|
|
var definition = factory.attributes[name];
|
||
|
|
|
||
|
|
if (definition && definition.autoIncrement) {
|
||
|
|
fields.push(name);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return fields;
|
||
|
|
},
|
||
|
|
|
||
|
|
createTrigger: function() {
|
||
|
|
throwMethodUndefined('createTrigger');
|
||
|
|
},
|
||
|
|
|
||
|
|
dropTrigger: function() {
|
||
|
|
throwMethodUndefined('dropTrigger');
|
||
|
|
},
|
||
|
|
|
||
|
|
renameTrigger: function() {
|
||
|
|
throwMethodUndefined('renameTrigger');
|
||
|
|
},
|
||
|
|
|
||
|
|
createFunction: function() {
|
||
|
|
throwMethodUndefined('createFunction');
|
||
|
|
},
|
||
|
|
|
||
|
|
dropFunction: function() {
|
||
|
|
throwMethodUndefined('dropFunction');
|
||
|
|
},
|
||
|
|
|
||
|
|
renameFunction: function() {
|
||
|
|
throwMethodUndefined('renameFunction');
|
||
|
|
},
|
||
|
|
|
||
|
|
quoteIdentifier: function(identifier, force) {
|
||
|
|
if (identifier === '*') return identifier;
|
||
|
|
return '[' + identifier.replace(/[\[\]']+/g,'') + ']';
|
||
|
|
},
|
||
|
|
|
||
|
|
getForeignKeysQuery: function(table, databaseName) {
|
||
|
|
var tableName = table.tableName || table;
|
||
|
|
var sql = [
|
||
|
|
'SELECT',
|
||
|
|
'constraint_name = C.CONSTRAINT_NAME',
|
||
|
|
'FROM',
|
||
|
|
'INFORMATION_SCHEMA.TABLE_CONSTRAINTS C',
|
||
|
|
"WHERE C.CONSTRAINT_TYPE = 'FOREIGN KEY'",
|
||
|
|
'AND C.TABLE_NAME =', wrapSingleQuote(tableName)
|
||
|
|
].join(' ');
|
||
|
|
|
||
|
|
if (table.schema) {
|
||
|
|
sql += ' AND C.TABLE_SCHEMA =' + wrapSingleQuote(table.schema);
|
||
|
|
}
|
||
|
|
|
||
|
|
return sql;
|
||
|
|
},
|
||
|
|
|
||
|
|
dropForeignKeyQuery: function(tableName, foreignKey) {
|
||
|
|
return Utils._.template('ALTER TABLE <%= table %> DROP <%= key %>')({
|
||
|
|
table: this.quoteTable(tableName),
|
||
|
|
key: this.quoteIdentifier(foreignKey)
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
setAutocommitQuery: function(value) {
|
||
|
|
return '';
|
||
|
|
// return 'SET IMPLICIT_TRANSACTIONS ' + (!!value ? 'OFF' : 'ON') + ';';
|
||
|
|
},
|
||
|
|
|
||
|
|
setIsolationLevelQuery: function(value, options) {
|
||
|
|
if (options.parent) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'SET TRANSACTION ISOLATION LEVEL ' + value + ';';
|
||
|
|
},
|
||
|
|
|
||
|
|
startTransactionQuery: function(transaction, options) {
|
||
|
|
if (options.parent) {
|
||
|
|
return 'SAVE TRANSACTION ' + this.quoteIdentifier(transaction.name) + ';';
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'BEGIN TRANSACTION;';
|
||
|
|
},
|
||
|
|
|
||
|
|
commitTransactionQuery: function(options) {
|
||
|
|
if (options.parent) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'COMMIT TRANSACTION;';
|
||
|
|
},
|
||
|
|
|
||
|
|
rollbackTransactionQuery: function(transaction, options) {
|
||
|
|
if (options.parent) {
|
||
|
|
return 'ROLLBACK TRANSACTION ' + this.quoteIdentifier(transaction.name) + ';';
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'ROLLBACK TRANSACTION;';
|
||
|
|
},
|
||
|
|
|
||
|
|
addLimitAndOffset: function(options, model) {
|
||
|
|
var fragment = '';
|
||
|
|
var offset = options.offset || 0
|
||
|
|
, isSubQuery = options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation;
|
||
|
|
|
||
|
|
// FIXME: This is ripped from selectQuery to determine whether there is already
|
||
|
|
// an ORDER BY added for a subquery. Should be refactored so we dont' need
|
||
|
|
// the duplication. Also consider moving this logic inside the options.order
|
||
|
|
// check, so that we aren't compiling this twice for every invocation.
|
||
|
|
var mainQueryOrder = [];
|
||
|
|
var subQueryOrder = [];
|
||
|
|
if (options.order) {
|
||
|
|
if (Array.isArray(options.order)) {
|
||
|
|
options.order.forEach(function(t) {
|
||
|
|
if (!Array.isArray(t)) {
|
||
|
|
if (isSubQuery && !(t instanceof Model) && !(t.model instanceof Model)) {
|
||
|
|
subQueryOrder.push(this.quote(t, model));
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (isSubQuery && !(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 (options.limit || options.offset) {
|
||
|
|
if (!options.order || (options.include && !subQueryOrder.length)) {
|
||
|
|
fragment += (options.order && !isSubQuery) ? ', ' : ' ORDER BY ';
|
||
|
|
fragment += this.quoteIdentifier(model.primaryKeyAttribute);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (options.offset || options.limit) {
|
||
|
|
fragment += ' OFFSET ' + offset + ' ROWS';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (options.limit) {
|
||
|
|
fragment += ' FETCH NEXT ' + options.limit + ' ROWS ONLY';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return fragment;
|
||
|
|
},
|
||
|
|
|
||
|
|
booleanValue: function(value) {
|
||
|
|
return !!value ? 1 : 0;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// private methods
|
||
|
|
function wrapSingleQuote(identifier){
|
||
|
|
return Utils.addTicks(identifier, "'");
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = Utils._.extend(Utils._.clone(AbstractQueryGenerator), QueryGenerator);
|