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

Commit be958ac1 by Simon Schick Committed by Sushant

refactor(query-generation): cleanup template usage (#10047)

1 parent d3451363
...@@ -108,10 +108,11 @@ class QueryGenerator { ...@@ -108,10 +108,11 @@ class QueryGenerator {
const fields = []; const fields = [];
const values = []; const values = [];
const bind = []; const bind = [];
const quotedTable = this.quoteTable(table);
const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam; const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
let query; let query;
let valueQuery = '<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO <%= table %> (<%= attributes %>)<%= output %> VALUES (<%= values %>)<%= onConflictDoNothing %>'; let valueQuery = `<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO ${quotedTable} (<%= attributes %>)<%= output %> VALUES (<%= values %>)<%= onConflictDoNothing %>`;
let emptyQuery = '<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO <%= table %><%= output %><%= onConflictDoNothing %>'; let emptyQuery = `<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO ${quotedTable}<%= output %><%= onConflictDoNothing %>`;
let outputFragment; let outputFragment;
let identityWrapperRequired = false; let identityWrapperRequired = false;
let tmpTable = ''; //tmpTable declaration for trigger let tmpTable = ''; //tmpTable declaration for trigger
...@@ -143,7 +144,6 @@ class QueryGenerator { ...@@ -143,7 +144,6 @@ class QueryGenerator {
let tmpColumns = ''; let tmpColumns = '';
let outputColumns = ''; let outputColumns = '';
tmpTable = 'declare @tmp table (<%= columns %>); ';
for (const modelKey in modelAttributes) { for (const modelKey in modelAttributes) {
const attribute = modelAttributes[modelKey]; const attribute = modelAttributes[modelKey];
...@@ -158,11 +158,7 @@ class QueryGenerator { ...@@ -158,11 +158,7 @@ class QueryGenerator {
} }
} }
const replacement = { tmpTable = `declare @tmp table (${tmpColumns});`;
columns: tmpColumns
};
tmpTable = _.template(tmpTable, this._templateSettings)(replacement).trim();
outputFragment = ' OUTPUT ' + outputColumns + ' into @tmp'; outputFragment = ' OUTPUT ' + outputColumns + ' into @tmp';
const selectFromTmp = ';select * from @tmp'; const selectFromTmp = ';select * from @tmp';
...@@ -187,12 +183,12 @@ class QueryGenerator { ...@@ -187,12 +183,12 @@ class QueryGenerator {
const delimiter = '$func_' + uuidv4().replace(/-/g, '') + '$'; const delimiter = '$func_' + uuidv4().replace(/-/g, '') + '$';
options.exception = 'WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL;'; options.exception = 'WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL;';
valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response <%= table %>, OUT sequelize_caught_exception text) RETURNS RECORD AS ' + delimiter + valueQuery = `CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response ${quotedTable}, OUT sequelize_caught_exception text) RETURNS RECORD AS ${delimiter}` +
' BEGIN ' + valueQuery + ' INTO response; EXCEPTION ' + options.exception + ' END ' + delimiter + ' BEGIN ' + valueQuery + ' INTO response; EXCEPTION ' + options.exception + ' END ' + delimiter +
' LANGUAGE plpgsql; SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc()'; ' LANGUAGE plpgsql; SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc()';
} else { } else {
options.exception = 'WHEN unique_violation THEN NULL;'; options.exception = 'WHEN unique_violation THEN NULL;';
valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc() RETURNS SETOF <%= table %> AS $body$ BEGIN RETURN QUERY ' + valueQuery + '; EXCEPTION ' + options.exception + ' END; $body$ LANGUAGE plpgsql; SELECT * FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();'; valueQuery = `CREATE OR REPLACE FUNCTION pg_temp.testfunc() RETURNS SETOF ${quotedTable} AS $body$ BEGIN RETURN QUERY ${valueQuery}; EXCEPTION ${options.exception} END; $body$ LANGUAGE plpgsql; SELECT * FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();`;
} }
} }
...@@ -233,7 +229,6 @@ class QueryGenerator { ...@@ -233,7 +229,6 @@ class QueryGenerator {
const replacements = { const replacements = {
ignoreDuplicates: options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '', ignoreDuplicates: options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '',
onConflictDoNothing: options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : '', onConflictDoNothing: options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : '',
table: this.quoteTable(table),
attributes: fields.join(','), attributes: fields.join(','),
output: outputFragment, output: outputFragment,
values: values.join(','), values: values.join(','),
...@@ -242,11 +237,7 @@ class QueryGenerator { ...@@ -242,11 +237,7 @@ class QueryGenerator {
query = (replacements.attributes.length ? valueQuery : emptyQuery) + ';'; query = (replacements.attributes.length ? valueQuery : emptyQuery) + ';';
if (identityWrapperRequired && this._dialect.supports.autoIncrement.identityInsert) { if (identityWrapperRequired && this._dialect.supports.autoIncrement.identityInsert) {
query = [ query = `SET IDENTITY_INSERT ${quotedTable} ON; ${query} SET IDENTITY_INSERT ${quotedTable} OFF;`;
'SET IDENTITY_INSERT', this.quoteTable(table), 'ON;',
query,
'SET IDENTITY_INSERT', this.quoteTable(table), 'OFF;'
].join(' ');
} }
query = _.template(query, this._templateSettings)(replacements); query = _.template(query, this._templateSettings)(replacements);
...@@ -272,7 +263,6 @@ class QueryGenerator { ...@@ -272,7 +263,6 @@ class QueryGenerator {
options = options || {}; options = options || {};
fieldMappedAttributes = fieldMappedAttributes || {}; fieldMappedAttributes = fieldMappedAttributes || {};
const query = 'INSERT<%= ignoreDuplicates %> INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %><%= onDuplicateKeyUpdate %><%= onConflictDoNothing %><%= returning %>;';
const tuples = []; const tuples = [];
const serials = {}; const serials = {};
const allAttributes = []; const allAttributes = [];
...@@ -314,17 +304,12 @@ class QueryGenerator { ...@@ -314,17 +304,12 @@ class QueryGenerator {
}).join(','); }).join(',');
} }
const replacements = { const ignoreDuplicates = options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '';
ignoreDuplicates: options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '', const attributes = allAttributes.map(attr => this.quoteIdentifier(attr)).join(',');
table: this.quoteTable(tableName), const returning = this._dialect.supports.returnValues && options.returning ? ' RETURNING *' : '';
attributes: allAttributes.map(attr => this.quoteIdentifier(attr)).join(','), const onConflictDoNothing = options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : '';
tuples: tuples.join(','),
onDuplicateKeyUpdate,
returning: this._dialect.supports.returnValues && options.returning ? ' RETURNING *' : '',
onConflictDoNothing: options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : ''
};
return _.template(query, this._templateSettings)(replacements); return `INSERT${ignoreDuplicates} INTO ${this.quoteTable(tableName)} (${attributes}) VALUES ${tuples.join(',')}${onDuplicateKeyUpdate}${onConflictDoNothing}${returning};`;
} }
/** /**
...@@ -348,14 +333,14 @@ class QueryGenerator { ...@@ -348,14 +333,14 @@ class QueryGenerator {
const bind = []; const bind = [];
const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam; const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
const modelAttributeMap = {}; const modelAttributeMap = {};
let query = '<%= tmpTable %>UPDATE <%= table %> SET <%= values %><%= output %> <%= where %>'; let outputFragment ='';
let outputFragment;
let tmpTable = ''; // tmpTable declaration for trigger let tmpTable = ''; // tmpTable declaration for trigger
let selectFromTmp = ''; // Select statement for trigger let selectFromTmp = ''; // Select statement for trigger
let suffix = '';
if (this._dialect.supports['LIMIT ON UPDATE'] && options.limit) { if (this._dialect.supports['LIMIT ON UPDATE'] && options.limit) {
if (this.dialect !== 'mssql') { if (this.dialect !== 'mssql') {
query += ' LIMIT ' + this.escape(options.limit) + ' '; suffix = ' LIMIT ' + this.escape(options.limit) + ' ';
} }
} }
...@@ -366,7 +351,6 @@ class QueryGenerator { ...@@ -366,7 +351,6 @@ class QueryGenerator {
//To capture output rows when there is a trigger on MSSQL DB //To capture output rows when there is a trigger on MSSQL DB
if (attributes && options.hasTrigger && this._dialect.supports.tmpTableTrigger) { if (attributes && options.hasTrigger && this._dialect.supports.tmpTableTrigger) {
tmpTable = 'declare @tmp table (<%= columns %>); ';
let tmpColumns = ''; let tmpColumns = '';
let outputColumns = ''; let outputColumns = '';
...@@ -383,20 +367,16 @@ class QueryGenerator { ...@@ -383,20 +367,16 @@ class QueryGenerator {
} }
} }
const replacement ={ tmpTable = `declare @tmp table (${tmpColumns}); `;
columns: tmpColumns
};
tmpTable = _.template(tmpTable, this._templateSettings)(replacement).trim();
outputFragment = ' OUTPUT ' + outputColumns + ' into @tmp'; outputFragment = ' OUTPUT ' + outputColumns + ' into @tmp';
selectFromTmp = ';select * from @tmp'; selectFromTmp = ';select * from @tmp';
query += selectFromTmp; suffix += selectFromTmp;
} }
} else if (this._dialect.supports.returnValues && options.returning) { } else if (this._dialect.supports.returnValues && options.returning) {
// ensure that the return output is properly mapped to model fields. // ensure that the return output is properly mapped to model fields.
options.mapToModel = true; options.mapToModel = true;
query += ' RETURNING *'; suffix += ' RETURNING *';
} }
} }
...@@ -427,19 +407,12 @@ class QueryGenerator { ...@@ -427,19 +407,12 @@ class QueryGenerator {
} }
const whereOptions = _.defaults({ bindParam }, options); const whereOptions = _.defaults({ bindParam }, options);
const replacements = {
table: this.quoteTable(tableName),
values: values.join(','),
output: outputFragment,
where: this.whereQuery(where, whereOptions),
tmpTable
};
if (values.length === 0) { if (values.length === 0) {
return ''; return '';
} }
query = _.template(query, this._templateSettings)(replacements).trim(); const query = `${tmpTable}UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')}${outputFragment} ${this.whereQuery(where, whereOptions)}${suffix}`.trim();
// Used by Postgres upsertQuery and calls to here with options.exception set to true // Used by Postgres upsertQuery and calls to here with options.exception set to true
const result = { query }; const result = { query };
if (options.bindParam !== false) { if (options.bindParam !== false) {
...@@ -465,13 +438,13 @@ class QueryGenerator { ...@@ -465,13 +438,13 @@ class QueryGenerator {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull); attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull);
const values = []; const values = [];
let query = 'UPDATE <%= table %> SET <%= values %><%= output %> <%= where %>'; let outputFragment = '';
let outputFragment; let returningFragment = '';
if (this._dialect.supports.returnValues && options.returning) { if (this._dialect.supports.returnValues && options.returning) {
if (this._dialect.supports.returnValues.returning) { if (this._dialect.supports.returnValues.returning) {
options.mapToModel = true; options.mapToModel = true;
query += ' RETURNING *'; returningFragment = 'RETURNING *';
} else if (this._dialect.supports.returnValues.output) { } else if (this._dialect.supports.returnValues.output) {
outputFragment = ' OUTPUT INSERTED.*'; outputFragment = ' OUTPUT INSERTED.*';
} }
...@@ -488,14 +461,7 @@ class QueryGenerator { ...@@ -488,14 +461,7 @@ class QueryGenerator {
values.push(this.quoteIdentifier(key) + '=' + this.escape(value)); values.push(this.quoteIdentifier(key) + '=' + this.escape(value));
} }
const replacements = { return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')}${outputFragment} ${this.whereQuery(where)} ${returningFragment}`.trim();
table: this.quoteTable(tableName),
values: values.join(','),
output: outputFragment,
where: this.whereQuery(where)
};
return _.template(query, this._templateSettings)(replacements);
} }
/* /*
...@@ -1321,7 +1287,7 @@ class QueryGenerator { ...@@ -1321,7 +1287,7 @@ class QueryGenerator {
mainQueryItems.push(' WHERE ' + options.where); mainQueryItems.push(' WHERE ' + options.where);
// Walk the main query to update all selects // Walk the main query to update all selects
mainQueryItems.forEach((value, key) => { mainQueryItems.forEach((value, key) => {
if (value.match(/^SELECT/)) { if (value.startsWith('SELECT')) {
mainQueryItems[key] = this.selectFromTableFragment(options, model, attributes.main, mainTable.quotedName, mainTable.as, options.where); mainQueryItems[key] = this.selectFromTableFragment(options, model, attributes.main, mainTable.quotedName, mainTable.as, options.where);
} }
}); });
......
...@@ -20,19 +20,15 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -20,19 +20,15 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
collate: null collate: null
}, options || {}); }, options || {});
const query = [ const collation = options.collate ? 'COLLATE ' + this.escape(options.collate) : '';
return [
'IF NOT EXISTS (SELECT * FROM sys.databases WHERE name =', wrapSingleQuote(databaseName), ')', 'IF NOT EXISTS (SELECT * FROM sys.databases WHERE name =', wrapSingleQuote(databaseName), ')',
'BEGIN', 'BEGIN',
'CREATE DATABASE', this.quoteIdentifier(databaseName), 'CREATE DATABASE', this.quoteIdentifier(databaseName),
'<%= collation %>;', `${collation};`,
'END;' 'END;'
].join(' '); ].join(' ');
const values = {
collation: options.collate ? 'COLLATE ' + this.escape(options.collate) : ''
};
return _.template(query, this._templateSettings)(values).trim();
} }
dropDatabaseQuery(databaseName) { dropDatabaseQuery(databaseName) {
...@@ -111,15 +107,15 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -111,15 +107,15 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
} }
createTableQuery(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
const query = "IF OBJECT_ID('<%= table %>', 'U') IS NULL CREATE TABLE <%= table %> (<%= attributes %>)", const query = (table, attrs) => `IF OBJECT_ID('${table}', 'U') IS NULL CREATE TABLE ${table} (${attrs})`,
primaryKeys = [], primaryKeys = [],
foreignKeys = {}, foreignKeys = {},
attrStr = [], attrStr = [],
commentTemplate = ' EXEC sp_addextendedproperty ' + commentTemplate = (comment, table, column) => ' EXEC sp_addextendedproperty ' +
'@name = N\'MS_Description\', @value = <%= comment %>, ' + `@name = N\'MS_Description\', @value = ${comment}, ` +
'@level0type = N\'Schema\', @level0name = \'dbo\', ' + '@level0type = N\'Schema\', @level0name = \'dbo\', ' +
'@level1type = N\'Table\', @level1name = <%= table %>, ' + `@level1type = N\'Table\', @level1name = ${table}, ` +
'@level2type = N\'Column\', @level2name = <%= column %>;'; `@level2type = N\'Column\', @level2name = ${column};`;
let commentStr = ''; let commentStr = '';
...@@ -130,12 +126,8 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -130,12 +126,8 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
if (dataType.includes('COMMENT ')) { if (dataType.includes('COMMENT ')) {
const commentMatch = dataType.match(/^(.+) (COMMENT.*)$/); const commentMatch = dataType.match(/^(.+) (COMMENT.*)$/);
const commentText = commentMatch[2].replace(/COMMENT/, '').trim(); const commentText = commentMatch[2].replace('COMMENT', '').trim();
commentStr += _.template(commentTemplate, this._templateSettings)({ commentStr += commentTemplate(this.escape(commentText), this.quoteIdentifier(tableName), this.quoteIdentifier(attr));
table: this.quoteIdentifier(tableName),
comment: this.escape(commentText),
column: this.quoteIdentifier(attr)
});
// remove comment related substring from dataType // remove comment related substring from dataType
dataType = commentMatch[1]; dataType = commentMatch[1];
} }
...@@ -146,10 +138,10 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -146,10 +138,10 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
if (dataType.includes('REFERENCES')) { if (dataType.includes('REFERENCES')) {
// MSSQL doesn't support inline REFERENCES declarations: move to the end // MSSQL doesn't support inline REFERENCES declarations: move to the end
match = dataType.match(/^(.+) (REFERENCES.*)$/); match = dataType.match(/^(.+) (REFERENCES.*)$/);
attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1].replace(/PRIMARY KEY/, '')); attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1].replace('PRIMARY KEY', ''));
foreignKeys[attr] = match[2]; foreignKeys[attr] = match[2];
} else { } else {
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace(/PRIMARY KEY/, '')); attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace('PRIMARY KEY', ''));
} }
} else if (dataType.includes('REFERENCES')) { } else if (dataType.includes('REFERENCES')) {
// MSSQL doesn't support inline REFERENCES declarations: move to the end // MSSQL doesn't support inline REFERENCES declarations: move to the end
...@@ -159,15 +151,12 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -159,15 +151,12 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
} else { } else {
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType); attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType);
} }
} }
} }
const values = {
table: this.quoteTable(tableName), let attributesClause = attrStr.join(', ');
attributes: attrStr.join(', ') const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', ');
},
pkString = primaryKeys.map(pk => { return this.quoteIdentifier(pk); }).join(', ');
if (options.uniqueKeys) { if (options.uniqueKeys) {
_.each(options.uniqueKeys, (columns, indexName) => { _.each(options.uniqueKeys, (columns, indexName) => {
...@@ -175,22 +164,22 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -175,22 +164,22 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
if (!_.isString(indexName)) { if (!_.isString(indexName)) {
indexName = 'uniq_' + tableName + '_' + columns.fields.join('_'); indexName = 'uniq_' + tableName + '_' + columns.fields.join('_');
} }
values.attributes += `, CONSTRAINT ${this.quoteIdentifier(indexName)} UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`; attributesClause += `, CONSTRAINT ${this.quoteIdentifier(indexName)} UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`;
} }
}); });
} }
if (pkString.length > 0) { if (pkString.length > 0) {
values.attributes += `, PRIMARY KEY (${pkString})`; attributesClause += `, PRIMARY KEY (${pkString})`;
} }
for (const fkey in foreignKeys) { for (const fkey in foreignKeys) {
if (foreignKeys.hasOwnProperty(fkey)) { if (foreignKeys.hasOwnProperty(fkey)) {
values.attributes += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey]; attributesClause += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey];
} }
} }
return _.template(query, this._templateSettings)(values).trim() + ';' + commentStr; return `${query(this.quoteTable(tableName), attributesClause)};${commentStr}`;
} }
describeTableQuery(tableName, schema) { describeTableQuery(tableName, schema) {
...@@ -234,11 +223,7 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -234,11 +223,7 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
} }
renameTableQuery(before, after) { renameTableQuery(before, after) {
const query = 'EXEC sp_rename <%= before %>, <%= after %>;'; return `EXEC sp_rename ${this.quoteTable(before)}, ${this.quoteTable(after)};`;
return _.template(query, this._templateSettings)({
before: this.quoteTable(before),
after: this.quoteTable(after)
});
} }
showTablesQuery() { showTablesQuery() {
...@@ -246,12 +231,8 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -246,12 +231,8 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
} }
dropTableQuery(tableName) { dropTableQuery(tableName) {
const query = "IF OBJECT_ID('<%= table %>', 'U') IS NOT NULL DROP TABLE <%= table %>"; const qouteTbl = this.quoteTable(tableName);
const values = { return `IF OBJECT_ID('${qouteTbl}', 'U') IS NOT NULL DROP TABLE ${qouteTbl};`;
table: this.quoteTable(tableName)
};
return _.template(query, this._templateSettings)(values).trim() + ';';
} }
addColumnQuery(table, key, dataType) { addColumnQuery(table, key, dataType) {
...@@ -259,45 +240,27 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -259,45 +240,27 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
// but instead we need to pass the key along as the field here // but instead we need to pass the key along as the field here
dataType.field = key; dataType.field = key;
const query = 'ALTER TABLE <%= table %> ADD <%= attribute %>;', const def = this.attributeToSQL(dataType, {
attribute = _.template('<%= key %> <%= definition %>', this._templateSettings)({
key: this.quoteIdentifier(key),
definition: this.attributeToSQL(dataType, {
context: 'addColumn' context: 'addColumn'
})
});
return _.template(query, this._templateSettings)({
table: this.quoteTable(table),
attribute
}); });
return `ALTER TABLE ${this.quoteTable(table)} ADD ${this.quoteIdentifier(key)} ${def};`;
} }
removeColumnQuery(tableName, attributeName) { removeColumnQuery(tableName, attributeName) {
const query = 'ALTER TABLE <%= tableName %> DROP COLUMN <%= attributeName %>;'; return `ALTER TABLE ${this.quoteTable(tableName)} DROP COLUMN ${this.quoteIdentifier(attributeName)};`;
return _.template(query, this._templateSettings)({
tableName: this.quoteTable(tableName),
attributeName: this.quoteIdentifier(attributeName)
});
} }
changeColumnQuery(tableName, attributes) { changeColumnQuery(tableName, attributes) {
const query = 'ALTER TABLE <%= tableName %> <%= query %>;';
const attrString = [], const attrString = [],
constraintString = []; constraintString = [];
for (const attributeName in attributes) { for (const attributeName in attributes) {
const quotedAttrName = this.quoteIdentifier(attributeName);
const definition = attributes[attributeName]; const definition = attributes[attributeName];
if (definition.match(/REFERENCES/)) { if (definition.includes('REFERENCES')) {
constraintString.push(_.template('FOREIGN KEY (<%= attrName %>) <%= definition %>', this._templateSettings)({ constraintString.push(`FOREIGN KEY (${quotedAttrName}) ${definition.replace(/.+?(?=REFERENCES)/, '')}`);
attrName: this.quoteIdentifier(attributeName),
definition: definition.replace(/.+?(?=REFERENCES)/, '')
}));
} else { } else {
attrString.push(_.template('<%= attrName %> <%= definition %>', this._templateSettings)({ attrString.push(`${quotedAttrName} ${definition}`);
attrName: this.quoteIdentifier(attributeName),
definition
}));
} }
} }
...@@ -310,39 +273,34 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -310,39 +273,34 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
finalQuery += 'ADD ' + constraintString.join(', '); finalQuery += 'ADD ' + constraintString.join(', ');
} }
return _.template(query, this._templateSettings)({ return `ALTER TABLE ${this.quoteTable(tableName)} ${finalQuery};`;
tableName: this.quoteTable(tableName),
query: finalQuery
});
} }
renameColumnQuery(tableName, attrBefore, attributes) { renameColumnQuery(tableName, attrBefore, attributes) {
const query = "EXEC sp_rename '<%= tableName %>.<%= before %>', '<%= after %>', 'COLUMN';", const newName = Object.keys(attributes)[0];
newName = Object.keys(attributes)[0]; return `EXEC sp_rename '${this.quoteTable(tableName)}.${attrBefore}', '${newName}', 'COLUMN';`;
return _.template(query, this._templateSettings)({
tableName: this.quoteTable(tableName),
before: attrBefore,
after: newName
});
} }
bulkInsertQuery(tableName, attrValueHashes, options, attributes) { bulkInsertQuery(tableName, attrValueHashes, options, attributes) {
const quotedTable = this.quoteTable(tableName);
options = options || {}; options = options || {};
attributes = attributes || {}; attributes = attributes || {};
const query = 'INSERT INTO <%= table %> (<%= attributes %>)<%= output %> VALUES <%= tuples %>;',
emptyQuery = 'INSERT INTO <%= table %><%= output %> DEFAULT VALUES', const tuples = [];
tuples = [], const allAttributes = [];
allAttributes = [], const allQueries = [];
allQueries = [];
let needIdentityInsertWrapper = false, let needIdentityInsertWrapper = false,
outputFragment; outputFragment = '';
if (options.returning) { if (options.returning) {
outputFragment = ' OUTPUT INSERTED.*'; outputFragment = ' OUTPUT INSERTED.*';
} }
const emptyQuery = `INSERT INTO ${quotedTable}${outputFragment} DEFAULT VALUES`;
attrValueHashes.forEach(attrValueHash => { attrValueHashes.forEach(attrValueHash => {
// special case for empty objects with primary keys // special case for empty objects with primary keys
const fields = Object.keys(attrValueHash); const fields = Object.keys(attrValueHash);
...@@ -375,27 +333,17 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -375,27 +333,17 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
')'); ')');
}); });
allQueries.push(query); const quotedAttributes = allAttributes.map(attr => this.quoteIdentifier(attr)).join(',');
allQueries.push(tupleStr => `INSERT INTO ${quotedTable} (${quotedAttributes})${outputFragment} VALUES ${tupleStr};`);
} }
const commands = []; const commands = [];
let offset = 0; let offset = 0;
const batch = Math.floor(250 / (allAttributes.length + 1)) + 1; const batch = Math.floor(250 / (allAttributes.length + 1)) + 1;
while (offset < Math.max(tuples.length, 1)) { while (offset < Math.max(tuples.length, 1)) {
const replacements = { const tupleStr = tuples.slice(offset, Math.min(tuples.length, offset + batch));
table: this.quoteTable(tableName), let generatedQuery = allQueries.map(v => typeof v === 'string' ? v : v(tupleStr)).join(';');
attributes: allAttributes.map(attr =>
this.quoteIdentifier(attr)).join(','),
tuples: tuples.slice(offset, Math.min(tuples.length, offset + batch)),
output: outputFragment
};
let generatedQuery = _.template(allQueries.join(';'), this._templateSettings)(replacements);
if (needIdentityInsertWrapper) { if (needIdentityInsertWrapper) {
generatedQuery = [ generatedQuery = `SET IDENTITY_INSERT ${quotedTable} ON; ${generatedQuery}; SET IDENTITY_INSERT ${quotedTable} OFF;`;
'SET IDENTITY_INSERT', this.quoteTable(tableName), 'ON;',
generatedQuery,
'SET IDENTITY_INSERT', this.quoteTable(tableName), 'OFF;'
].join(' ');
} }
commands.push(generatedQuery); commands.push(generatedQuery);
offset += batch; offset += batch;
...@@ -529,34 +477,23 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -529,34 +477,23 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
deleteQuery(tableName, where, options = {}, model) { deleteQuery(tableName, where, options = {}, model) {
const table = this.quoteTable(tableName); const table = this.quoteTable(tableName);
const query = 'DELETE<%= limit %> FROM <%= table %><%= where %>; SELECT @@ROWCOUNT AS AFFECTEDROWS;';
where = this.getWhereConditions(where, null, model, options);
let whereClause = this.getWhereConditions(where, null, model, options);
let limit = ''; let limit = '';
if (options.limit) { if (options.limit) {
limit = ' TOP(' + this.escape(options.limit) + ')'; limit = ' TOP(' + this.escape(options.limit) + ')';
} }
const replacements = { if (whereClause) {
limit, whereClause = ' WHERE ' + whereClause;
table,
where
};
if (replacements.where) {
replacements.where = ' WHERE ' + replacements.where;
} }
return _.template(query, this._templateSettings)(replacements); return `DELETE${limit} FROM ${table}${whereClause}; SELECT @@ROWCOUNT AS AFFECTEDROWS;`;
} }
showIndexesQuery(tableName) { showIndexesQuery(tableName) {
const sql = "EXEC sys.sp_helpindex @objname = N'<%= tableName %>';"; return `EXEC sys.sp_helpindex @objname = N'${this.quoteTable(tableName)}';`;
return _.template(sql, this._templateSettings)({
tableName: this.quoteTable(tableName)
});
} }
showConstraintsQuery(tableName) { showConstraintsQuery(tableName) {
...@@ -564,19 +501,13 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -564,19 +501,13 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
} }
removeIndexQuery(tableName, indexNameOrAttributes) { removeIndexQuery(tableName, indexNameOrAttributes) {
const sql = 'DROP INDEX <%= indexName %> ON <%= tableName %>';
let indexName = indexNameOrAttributes; let indexName = indexNameOrAttributes;
if (typeof indexName !== 'string') { if (typeof indexName !== 'string') {
indexName = Utils.underscore(tableName + '_' + indexNameOrAttributes.join('_')); indexName = Utils.underscore(tableName + '_' + indexNameOrAttributes.join('_'));
} }
const values = { return `DROP INDEX ${this.quoteIdentifiers(indexName)} ON ${this.quoteIdentifiers(tableName)}`;
tableName: this.quoteIdentifiers(tableName),
indexName: this.quoteIdentifiers(indexName)
};
return _.template(sql, this._templateSettings)(values);
} }
attributeToSQL(attribute) { attributeToSQL(attribute) {
...@@ -793,29 +724,19 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator { ...@@ -793,29 +724,19 @@ class MSSQLQueryGenerator extends AbstractQueryGenerator {
} }
dropForeignKeyQuery(tableName, foreignKey) { dropForeignKeyQuery(tableName, foreignKey) {
return _.template('ALTER TABLE <%= table %> DROP <%= key %>', this._templateSettings)({ return `ALTER TABLE ${this.quoteTable(tableName)} DROP ${this.quoteIdentifier(foreignKey)}`;
table: this.quoteTable(tableName),
key: this.quoteIdentifier(foreignKey)
});
} }
getDefaultConstraintQuery(tableName, attributeName) { getDefaultConstraintQuery(tableName, attributeName) {
const sql = 'SELECT name FROM sys.default_constraints ' + const quotedTable = this.quoteTable(tableName);
"WHERE PARENT_OBJECT_ID = OBJECT_ID('<%= table %>', 'U') " + return 'SELECT name FROM sys.default_constraints ' +
"AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = ('<%= column %>') " + `WHERE PARENT_OBJECT_ID = OBJECT_ID('${quotedTable}', 'U') ` +
"AND object_id = OBJECT_ID('<%= table %>', 'U'));"; `AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = ('${attributeName}') ` +
return _.template(sql, this._templateSettings)({ `AND object_id = OBJECT_ID('${quotedTable}', 'U'));`;
table: this.quoteTable(tableName),
column: attributeName
});
} }
dropConstraintQuery(tableName, constraintName) { dropConstraintQuery(tableName, constraintName) {
const sql = 'ALTER TABLE <%= table %> DROP CONSTRAINT <%= constraint %>;'; return `ALTER TABLE ${this.quoteTable(tableName)} DROP CONSTRAINT ${this.quoteIdentifier(constraintName)};`;
return _.template(sql, this._templateSettings)({
table: this.quoteTable(tableName),
constraint: this.quoteIdentifier(constraintName)
});
} }
setIsolationLevelQuery() { setIsolationLevelQuery() {
......
...@@ -22,25 +22,15 @@ class MySQLQueryGenerator extends AbstractQueryGenerator { ...@@ -22,25 +22,15 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
collate: null collate: null
}, options || {}); }, options || {});
const query = 'CREATE DATABASE IF NOT EXISTS <%= database %><%= charset %><%= collation %>'; const database = this.quoteIdentifier(databaseName);
const charset = options.charset ? ' DEFAULT CHARACTER SET ' + this.escape(options.charset) : '';
const collate = options.collate ? ' DEFAULT COLLATE ' + this.escape(options.collate) : '';
const values = { return `CREATE DATABASE IF NOT EXISTS ${database}${charset}${collate}`.trim() + ';';
database: this.quoteIdentifier(databaseName),
charset: options.charset ? ' DEFAULT CHARACTER SET ' + this.escape(options.charset) : '',
collation: options.collate ? ' DEFAULT COLLATE ' + this.escape(options.collate) : ''
};
return _.template(query, this._templateSettings)(values).trim() + ';';
} }
dropDatabaseQuery(databaseName) { dropDatabaseQuery(databaseName) {
const query = 'DROP DATABASE IF EXISTS <%= database %>'; return `DROP DATABASE IF EXISTS ${this.quoteIdentifier(databaseName).trim()};`;
const values = {
database: this.quoteIdentifier(databaseName)
};
return _.template(query, this._templateSettings)(values).trim() + ';';
} }
createSchema() { createSchema() {
...@@ -62,7 +52,6 @@ class MySQLQueryGenerator extends AbstractQueryGenerator { ...@@ -62,7 +52,6 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
rowFormat: null rowFormat: null
}, options || {}); }, options || {});
const query = 'CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %><%= comment %><%= charset %><%= collation %><%= initialAutoIncrement %><%= rowFormat %>';
const primaryKeys = []; const primaryKeys = [];
const foreignKeys = {}; const foreignKeys = {};
const attrStr = []; const attrStr = [];
...@@ -78,10 +67,10 @@ class MySQLQueryGenerator extends AbstractQueryGenerator { ...@@ -78,10 +67,10 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
if (dataType.includes('REFERENCES')) { if (dataType.includes('REFERENCES')) {
// MySQL doesn't support inline REFERENCES declarations: move to the end // MySQL doesn't support inline REFERENCES declarations: move to the end
match = dataType.match(/^(.+) (REFERENCES.*)$/); match = dataType.match(/^(.+) (REFERENCES.*)$/);
attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1].replace(/PRIMARY KEY/, '')); attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1].replace('PRIMARY KEY', ''));
foreignKeys[attr] = match[2]; foreignKeys[attr] = match[2];
} else { } else {
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace(/PRIMARY KEY/, '')); attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace('PRIMARY KEY', ''));
} }
} else if (dataType.includes('REFERENCES')) { } else if (dataType.includes('REFERENCES')) {
// MySQL doesn't support inline REFERENCES declarations: move to the end // MySQL doesn't support inline REFERENCES declarations: move to the end
...@@ -94,16 +83,14 @@ class MySQLQueryGenerator extends AbstractQueryGenerator { ...@@ -94,16 +83,14 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
} }
} }
const values = { const table = this.quoteTable(tableName);
table: this.quoteTable(tableName), let attributesClause = attrStr.join(', ');
attributes: attrStr.join(', '), const comment = options.comment && _.isString(options.comment) ? ' COMMENT ' + this.escape(options.comment) : '';
comment: options.comment && _.isString(options.comment) ? ' COMMENT ' + this.escape(options.comment) : '', const engine = options.engine;
engine: options.engine, const charset = options.charset ? ' DEFAULT CHARSET=' + options.charset : '';
charset: options.charset ? ' DEFAULT CHARSET=' + options.charset : '', const collation = options.collate ? ' COLLATE ' + options.collate : '';
collation: options.collate ? ' COLLATE ' + options.collate : '', const rowFormat = options.rowFormat ? ' ROW_FORMAT=' + options.rowFormat : '';
rowFormat: options.rowFormat ? ' ROW_FORMAT=' + options.rowFormat : '', const initialAutoIncrement = options.initialAutoIncrement ? ' AUTO_INCREMENT=' + options.initialAutoIncrement : '';
initialAutoIncrement: options.initialAutoIncrement ? ' AUTO_INCREMENT=' + options.initialAutoIncrement : ''
};
const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', '); const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', ');
if (options.uniqueKeys) { if (options.uniqueKeys) {
...@@ -112,22 +99,22 @@ class MySQLQueryGenerator extends AbstractQueryGenerator { ...@@ -112,22 +99,22 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
if (!_.isString(indexName)) { if (!_.isString(indexName)) {
indexName = 'uniq_' + tableName + '_' + columns.fields.join('_'); indexName = 'uniq_' + tableName + '_' + columns.fields.join('_');
} }
values.attributes += `, UNIQUE ${this.quoteIdentifier(indexName)} (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`; attributesClause += `, UNIQUE ${this.quoteIdentifier(indexName)} (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`;
} }
}); });
} }
if (pkString.length > 0) { if (pkString.length > 0) {
values.attributes += `, PRIMARY KEY (${pkString})`; attributesClause += `, PRIMARY KEY (${pkString})`;
} }
for (const fkey in foreignKeys) { for (const fkey in foreignKeys) {
if (foreignKeys.hasOwnProperty(fkey)) { if (foreignKeys.hasOwnProperty(fkey)) {
values.attributes += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey]; attributesClause += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey];
} }
} }
return _.template(query, this._templateSettings)(values).trim() + ';'; return `CREATE TABLE IF NOT EXISTS ${table} (${attributesClause}) ENGINE=${engine}${comment}${charset}${collation}${initialAutoIncrement}${rowFormat};`;
} }
...@@ -167,7 +154,7 @@ class MySQLQueryGenerator extends AbstractQueryGenerator { ...@@ -167,7 +154,7 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
for (const attributeName in attributes) { for (const attributeName in attributes) {
let definition = attributes[attributeName]; let definition = attributes[attributeName];
if (definition.match(/REFERENCES/)) { if (definition.includes('REFERENCES')) {
const attrName = this.quoteIdentifier(attributeName); const attrName = this.quoteIdentifier(attributeName);
definition = definition.replace(/.+?(?=REFERENCES)/, ''); definition = definition.replace(/.+?(?=REFERENCES)/, '');
constraintString.push(`FOREIGN KEY (${attrName}) ${definition}`); constraintString.push(`FOREIGN KEY (${attrName}) ${definition}`);
......
...@@ -64,8 +64,10 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -64,8 +64,10 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
let comments = ''; let comments = '';
let columnComments = ''; let columnComments = '';
const quotedTable = this.quoteTable(tableName);
if (options.comment && _.isString(options.comment)) { if (options.comment && _.isString(options.comment)) {
comments += '; COMMENT ON TABLE <%= table %> IS ' + this.escape(options.comment); comments += `; COMMENT ON TABLE ${quotedTable} IS ` + this.escape(options.comment);
} }
for (const attr in attributes) { for (const attr in attributes) {
...@@ -74,7 +76,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -74,7 +76,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
if (i !== -1) { if (i !== -1) {
// Move comment to a separate query // Move comment to a separate query
const escapedCommentText = this.escape(attributes[attr].substring(i + 8)); const escapedCommentText = this.escape(attributes[attr].substring(i + 8));
columnComments += `; COMMENT ON COLUMN <%= table %>.${quotedAttr} IS ${escapedCommentText}`; columnComments += `; COMMENT ON COLUMN ${quotedTable}.${quotedAttr} IS ${escapedCommentText}`;
attributes[attr] = attributes[attr].substring(0, i); attributes[attr] = attributes[attr].substring(0, i);
} }
...@@ -82,17 +84,13 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -82,17 +84,13 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
attrStr.push(quotedAttr + ' ' + dataType); attrStr.push(quotedAttr + ' ' + dataType);
} }
const values = {
table: this.quoteTable(tableName), let attributesClause = attrStr.join(', ');
attributes: attrStr.join(', '),
comments: _.template(comments, this._templateSettings)({ table: this.quoteTable(tableName) }),
columnComments: _.template(columnComments, this._templateSettings)({ table: this.quoteTable(tableName) })
};
if (options.uniqueKeys) { if (options.uniqueKeys) {
_.each(options.uniqueKeys, columns => { _.each(options.uniqueKeys, columns => {
if (columns.customIndex) { if (columns.customIndex) {
values.attributes += `, UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`; attributesClause += `, UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`;
} }
}); });
} }
...@@ -105,10 +103,10 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -105,10 +103,10 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
}, []).join(','); }, []).join(',');
if (pks.length > 0) { if (pks.length > 0) {
values.attributes += `, PRIMARY KEY (${pks})`; attributesClause += `, PRIMARY KEY (${pks})`;
} }
return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0') ? 'IF NOT EXISTS ' : ''}${values.table} (${values.attributes})${values.comments}${values.columnComments};`; return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0') ? 'IF NOT EXISTS ' : ''}${quotedTable} (${attributesClause})${comments}${columnComments};`;
} }
dropTableQuery(tableName, options) { dropTableQuery(tableName, options) {
...@@ -267,7 +265,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -267,7 +265,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
} }
changeColumnQuery(tableName, attributes) { changeColumnQuery(tableName, attributes) {
const query = 'ALTER TABLE <%= tableName %> ALTER COLUMN <%= query %>;'; const query = subQuery => `ALTER TABLE ${this.quoteTable(tableName)} ALTER COLUMN ${subQuery};`;
const sql = []; const sql = [];
for (const attributeName in attributes) { for (const attributeName in attributes) {
...@@ -275,34 +273,22 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -275,34 +273,22 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
let attrSql = ''; let attrSql = '';
if (definition.includes('NOT NULL')) { if (definition.includes('NOT NULL')) {
attrSql += _.template(query, this._templateSettings)({ attrSql += query(this.quoteIdentifier(attributeName) + ' SET NOT NULL');
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' SET NOT NULL'
});
definition = definition.replace('NOT NULL', '').trim(); definition = definition.replace('NOT NULL', '').trim();
} else if (!definition.match(/REFERENCES/)) { } else if (!definition.includes('REFERENCES')) {
attrSql += _.template(query, this._templateSettings)({ attrSql += query(this.quoteIdentifier(attributeName) + ' DROP NOT NULL');
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' DROP NOT NULL'
});
} }
if (definition.includes('DEFAULT')) { if (definition.includes('DEFAULT')) {
attrSql += _.template(query, this._templateSettings)({ attrSql += query(this.quoteIdentifier(attributeName) + ' SET DEFAULT ' + definition.match(/DEFAULT ([^;]+)/)[1]);
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' SET DEFAULT ' + definition.match(/DEFAULT ([^;]+)/)[1]
});
definition = definition.replace(/(DEFAULT[^;]+)/, '').trim(); definition = definition.replace(/(DEFAULT[^;]+)/, '').trim();
} else if (!definition.match(/REFERENCES/)) { } else if (!definition.includes('REFERENCES')) {
attrSql += _.template(query, this._templateSettings)({ attrSql += query(this.quoteIdentifier(attributeName) + ' DROP DEFAULT');
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' DROP DEFAULT'
});
} }
if (attributes[attributeName].match(/^ENUM\(/)) { if (attributes[attributeName].startsWith('ENUM(')) {
attrSql += this.pgEnum(tableName, attributeName, attributes[attributeName]); attrSql += this.pgEnum(tableName, attributeName, attributes[attributeName]);
definition = definition.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attributeName, { schema: false })); definition = definition.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attributeName, { schema: false }));
definition += ' USING (' + this.quoteIdentifier(attributeName) + '::' + this.pgEnumName(tableName, attributeName) + ')'; definition += ' USING (' + this.quoteIdentifier(attributeName) + '::' + this.pgEnumName(tableName, attributeName) + ')';
...@@ -310,24 +296,14 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -310,24 +296,14 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
if (definition.match(/UNIQUE;*$/)) { if (definition.match(/UNIQUE;*$/)) {
definition = definition.replace(/UNIQUE;*$/, ''); definition = definition.replace(/UNIQUE;*$/, '');
attrSql += query('ADD UNIQUE (' + this.quoteIdentifier(attributeName) + ')').replace('ALTER COLUMN', '');
attrSql += _.template(query.replace('ALTER COLUMN', ''), this._templateSettings)({
tableName: this.quoteTable(tableName),
query: 'ADD UNIQUE (' + this.quoteIdentifier(attributeName) + ')'
});
} }
if (definition.match(/REFERENCES/)) { if (definition.includes('REFERENCES')) {
definition = definition.replace(/.+?(?=REFERENCES)/, ''); definition = definition.replace(/.+?(?=REFERENCES)/, '');
attrSql += _.template(query.replace('ALTER COLUMN', ''), this._templateSettings)({ attrSql += query('ADD FOREIGN KEY (' + this.quoteIdentifier(attributeName) + ') ' + definition).replace('ALTER COLUMN', '');
tableName: this.quoteTable(tableName),
query: 'ADD FOREIGN KEY (' + this.quoteIdentifier(attributeName) + ') ' + definition
});
} else { } else {
attrSql += _.template(query, this._templateSettings)({ attrSql += query(this.quoteIdentifier(attributeName) + ' TYPE ' + definition);
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' TYPE ' + definition
});
} }
sql.push(attrSql); sql.push(attrSql);
...@@ -341,10 +317,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -341,10 +317,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
const attrString = []; const attrString = [];
for (const attributeName in attributes) { for (const attributeName in attributes) {
attrString.push(_.template('<%= before %> TO <%= after %>', this._templateSettings)({ attrString.push(`${this.quoteIdentifier(attrBefore)} TO ${this.quoteIdentifier(attributeName)}`);
before: this.quoteIdentifier(attrBefore),
after: this.quoteIdentifier(attributeName)
}));
} }
return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${attrString.join(', ')};`; return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${attrString.join(', ')};`;
...@@ -395,13 +368,15 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -395,13 +368,15 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
} }
deleteQuery(tableName, where, options = {}, model) { deleteQuery(tableName, where, options = {}, model) {
const replacements = { const table = this.quoteTable(tableName);
table: this.quoteTable(tableName), let whereClause = this.getWhereConditions(where, null, model, options);
where: this.getWhereConditions(where, null, model, options), const limit = options.limit ? ' LIMIT ' + this.escape(options.limit) : '';
limit: options.limit ? ' LIMIT ' + this.escape(options.limit) : '' let primaryKeys = '';
}; let primaryKeysSelection = '';
let query; if (whereClause) {
whereClause = ' WHERE ' + whereClause;
}
if (options.limit) { if (options.limit) {
if (!model) { if (!model) {
...@@ -410,19 +385,12 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -410,19 +385,12 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
const pks = _.values(model.primaryKeys).map(pk => this.quoteIdentifier(pk.field)).join(','); const pks = _.values(model.primaryKeys).map(pk => this.quoteIdentifier(pk.field)).join(',');
replacements.primaryKeys = model.primaryKeyAttributes.length > 1 ? '(' + pks + ')' : pks; primaryKeys = model.primaryKeyAttributes.length > 1 ? '(' + pks + ')' : pks;
replacements.primaryKeysSelection = pks; primaryKeysSelection = pks;
query = 'DELETE FROM <%= table %> WHERE <%= primaryKeys %> IN (SELECT <%= primaryKeysSelection %> FROM <%= table %><%= where %><%= limit %>)'; return `DELETE FROM ${table} WHERE ${primaryKeys} IN (SELECT ${primaryKeysSelection} FROM ${table}${whereClause}${limit})`;
} else {
query = 'DELETE FROM <%= table %><%= where %>';
}
if (replacements.where) {
replacements.where = ' WHERE ' + replacements.where;
} }
return `DELETE FROM ${table}${whereClause}`;
return _.template(query, this._templateSettings)(replacements);
} }
showIndexesQuery(tableName) { showIndexesQuery(tableName) {
...@@ -834,23 +802,23 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -834,23 +802,23 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
dataTypeMapping(tableName, attr, dataType) { dataTypeMapping(tableName, attr, dataType) {
if (dataType.includes('PRIMARY KEY')) { if (dataType.includes('PRIMARY KEY')) {
dataType = dataType.replace(/PRIMARY KEY/, ''); dataType = dataType.replace('PRIMARY KEY', '');
} }
if (dataType.includes('SERIAL')) { if (dataType.includes('SERIAL')) {
if (dataType.includes('BIGINT')) { if (dataType.includes('BIGINT')) {
dataType = dataType.replace(/SERIAL/, 'BIGSERIAL'); dataType = dataType.replace('SERIAL', 'BIGSERIAL');
dataType = dataType.replace(/BIGINT/, ''); dataType = dataType.replace('BIGINT', '');
} else if (dataType.includes('SMALLINT')) { } else if (dataType.includes('SMALLINT')) {
dataType = dataType.replace(/SERIAL/, 'SMALLSERIAL'); dataType = dataType.replace('SERIAL', 'SMALLSERIAL');
dataType = dataType.replace(/SMALLINT/, ''); dataType = dataType.replace('SMALLINT', '');
} else { } else {
dataType = dataType.replace(/INTEGER/, ''); dataType = dataType.replace('INTEGER', '');
} }
dataType = dataType.replace(/NOT NULL/, ''); dataType = dataType.replace('NOT NULL', '');
} }
if (dataType.match(/^ENUM\(/)) { if (dataType.startsWith('ENUM(')) {
dataType = dataType.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attr)); dataType = dataType.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attr));
} }
......
...@@ -45,7 +45,7 @@ class SQLiteQueryGenerator extends MySqlQueryGenerator { ...@@ -45,7 +45,7 @@ class SQLiteQueryGenerator extends MySqlQueryGenerator {
if (needsMultiplePrimaryKeys) { if (needsMultiplePrimaryKeys) {
primaryKeys.push(attr); primaryKeys.push(attr);
dataTypeString = dataType.replace(/PRIMARY KEY/, 'NOT NULL'); dataTypeString = dataType.replace('PRIMARY KEY', 'NOT NULL');
} }
} }
attrArray.push(this.quoteIdentifier(attr) + ' ' + dataTypeString); attrArray.push(this.quoteIdentifier(attr) + ' ' + dataTypeString);
......
...@@ -368,8 +368,7 @@ class Query extends AbstractQuery { ...@@ -368,8 +368,7 @@ class Query extends AbstractQuery {
switch (err.code) { switch (err.code) {
case 'SQLITE_CONSTRAINT': { case 'SQLITE_CONSTRAINT': {
let match = err.message.match(/FOREIGN KEY constraint failed/); if (err.message.includes('FOREIGN KEY constraint failed')) {
if (match !== null) {
return new sequelizeErrors.ForeignKeyConstraintError({ return new sequelizeErrors.ForeignKeyConstraintError({
parent: err parent: err
}); });
...@@ -378,7 +377,7 @@ class Query extends AbstractQuery { ...@@ -378,7 +377,7 @@ class Query extends AbstractQuery {
let fields = []; let fields = [];
// Sqlite pre 2.2 behavior - Error: SQLITE_CONSTRAINT: columns x, y are not unique // Sqlite pre 2.2 behavior - Error: SQLITE_CONSTRAINT: columns x, y are not unique
match = err.message.match(/columns (.*?) are/); let match = err.message.match(/columns (.*?) are/);
if (match !== null && match.length >= 2) { if (match !== null && match.length >= 2) {
fields = match[1].split(', '); fields = match[1].split(', ');
} else { } else {
......
'use strict'; 'use strict';
const url = require('url'); const url = require('url');
const Path = require('path'); const path = require('path');
const retry = require('retry-as-promised'); const retry = require('retry-as-promised');
const clsBluebird = require('cls-bluebird'); const clsBluebird = require('cls-bluebird');
const _ = require('lodash'); const _ = require('lodash');
...@@ -111,8 +111,8 @@ class Sequelize { ...@@ -111,8 +111,8 @@ class Sequelize {
options.host = urlParts.hostname; options.host = urlParts.hostname;
if (options.dialect === 'sqlite' && urlParts.pathname && !urlParts.pathname.startsWith('/:memory')) { if (options.dialect === 'sqlite' && urlParts.pathname && !urlParts.pathname.startsWith('/:memory')) {
const path = Path.join(options.host, urlParts.pathname); const storagePath = path.join(options.host, urlParts.pathname);
options.storage = options.storage || path; options.storage = path.resolve(options.storage || storagePath);
} }
if (urlParts.pathname) { if (urlParts.pathname) {
...@@ -384,30 +384,30 @@ class Sequelize { ...@@ -384,30 +384,30 @@ class Sequelize {
* *
* @tutorial https://github.com/sequelize/express-example * @tutorial https://github.com/sequelize/express-example
* *
* @param {string} path The path to the file that holds the model you want to import. If the part is relative, it will be resolved relatively to the calling file * @param {string} importPath The path to the file that holds the model you want to import. If the part is relative, it will be resolved relatively to the calling file
* *
* @returns {Model} Imported model, returned from cache if was already imported * @returns {Model} Imported model, returned from cache if was already imported
*/ */
import(path) { import(importPath) {
// is it a relative path? // is it a relative path?
if (Path.normalize(path) !== Path.resolve(path)) { if (path.normalize(importPath) !== path.resolve(importPath)) {
// make path relative to the caller // make path relative to the caller
const callerFilename = Utils.stack()[1].getFileName(); const callerFilename = Utils.stack()[1].getFileName();
const callerPath = Path.dirname(callerFilename); const callerPath = path.dirname(callerFilename);
path = Path.resolve(callerPath, path); importPath = path.resolve(callerPath, importPath);
} }
if (!this.importCache[path]) { if (!this.importCache[importPath]) {
let defineCall = arguments.length > 1 ? arguments[1] : require(path); let defineCall = arguments.length > 1 ? arguments[1] : require(importPath);
if (typeof defineCall === 'object') { if (typeof defineCall === 'object') {
// ES6 module compatibility // ES6 module compatibility
defineCall = defineCall.default; defineCall = defineCall.default;
} }
this.importCache[path] = defineCall(this, DataTypes); this.importCache[importPath] = defineCall(this, DataTypes);
} }
return this.importCache[path]; return this.importCache[importPath];
} }
/** /**
......
...@@ -66,7 +66,7 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -66,7 +66,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}); });
it('should be able to unscope destroy', function() { it('should be able to unscope destroy', function() {
return this.ScopeMe.unscoped().destroy({ where: {}}).then(() => { return this.ScopeMe.unscoped().destroy({ where: {} }).then(() => {
return expect(this.ScopeMe.unscoped().findAll()).to.eventually.have.length(0); return expect(this.ScopeMe.unscoped().findAll()).to.eventually.have.length(0);
}); });
}); });
......
...@@ -56,27 +56,27 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -56,27 +56,27 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
it('should work with connection strings (1)', () => { it('should work with connection strings (1)', () => {
const sequelize = new Sequelize('sqlite://test.sqlite'); // eslint-disable-line new Sequelize('sqlite://test.sqlite');
}); });
it('should work with connection strings (2)', () => { it('should work with connection strings (2)', () => {
const sequelize = new Sequelize('sqlite://test.sqlite/'); // eslint-disable-line new Sequelize('sqlite://test.sqlite/');
}); });
it('should work with connection strings (3)', () => { it('should work with connection strings (3)', () => {
const sequelize = new Sequelize('sqlite://test.sqlite/lol?reconnect=true'); // eslint-disable-line new Sequelize('sqlite://test.sqlite/lol?reconnect=true');
}); });
} }
if (dialect === 'postgres') { if (dialect === 'postgres') {
const getConnectionUri = _.template('<%= protocol %>://<%= username %>:<%= password %>@<%= host %><% if(port) { %>:<%= port %><% } %>/<%= database %>'); const getConnectionUri = o => `${o.protocol}://${o.username}:${o.password}@${o.host}${o.port ? `:${o.port}` : ''}/${o.database}`;
it('should work with connection strings (postgres protocol)', () => { it('should work with connection strings (postgres protocol)', () => {
const connectionUri = getConnectionUri(Object.assign(config[dialect], {protocol: 'postgres'})); const connectionUri = getConnectionUri(Object.assign(config[dialect], {protocol: 'postgres'}));
// postgres://... // postgres://...
const sequelize = new Sequelize(connectionUri); // eslint-disable-line new Sequelize(connectionUri);
}); });
it('should work with connection strings (postgresql protocol)', () => { it('should work with connection strings (postgresql protocol)', () => {
const connectionUri = getConnectionUri(Object.assign(config[dialect], {protocol: 'postgresql'})); const connectionUri = getConnectionUri(Object.assign(config[dialect], {protocol: 'postgresql'}));
// postgresql://... // postgresql://...
const sequelize = new Sequelize(connectionUri); // eslint-disable-line new Sequelize(connectionUri);
}); });
} }
}); });
...@@ -122,11 +122,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -122,11 +122,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
.authenticate() .authenticate()
.catch(err => { .catch(err => {
expect( expect(
err.message.match(/connect ECONNREFUSED/) || err.message.includes('connect ECONNREFUSED') ||
err.message.match(/invalid port number/) || err.message.includes('invalid port number') ||
err.message.match(/should be >=? 0 and < 65536/) || err.message.match(/should be >=? 0 and < 65536/) ||
err.message.match(/Login failed for user/) || err.message.includes('Login failed for user') ||
err.message.match(/Port must be > 0 and < 65536/) err.message.includes('Port must be > 0 and < 65536')
).to.be.ok; ).to.be.ok;
}); });
}); });
......
...@@ -4,7 +4,8 @@ const chai = require('chai'), ...@@ -4,7 +4,8 @@ const chai = require('chai'),
expect = chai.expect, expect = chai.expect,
Support = require('./support'), Support = require('./support'),
Sequelize = Support.Sequelize, Sequelize = Support.Sequelize,
dialect = Support.getTestDialect(); dialect = Support.getTestDialect(),
path = require('path');
describe('Sequelize', () => { describe('Sequelize', () => {
describe('dialect is required', () => { describe('dialect is required', () => {
...@@ -76,21 +77,21 @@ describe('Sequelize', () => { ...@@ -76,21 +77,21 @@ describe('Sequelize', () => {
const sequelize = new Sequelize('sqlite:subfolder/dbname.db'); const sequelize = new Sequelize('sqlite:subfolder/dbname.db');
const options = sequelize.options; const options = sequelize.options;
expect(options.dialect).to.equal('sqlite'); expect(options.dialect).to.equal('sqlite');
expect(options.storage).to.equal('subfolder/dbname.db'); expect(options.storage).to.equal(path.resolve('subfolder', 'dbname.db'));
}); });
it('should accept absolute paths for sqlite', () => { it('should accept absolute paths for sqlite', () => {
const sequelize = new Sequelize('sqlite:/home/abs/dbname.db'); const sequelize = new Sequelize('sqlite:/home/abs/dbname.db');
const options = sequelize.options; const options = sequelize.options;
expect(options.dialect).to.equal('sqlite'); expect(options.dialect).to.equal('sqlite');
expect(options.storage).to.equal('/home/abs/dbname.db'); expect(options.storage).to.equal(path.resolve('/home/abs/dbname.db'));
}); });
it('should prefer storage in options object', () => { it('should prefer storage in options object', () => {
const sequelize = new Sequelize('sqlite:/home/abs/dbname.db', {storage: '/completely/different/path.db'}); const sequelize = new Sequelize('sqlite:/home/abs/dbname.db', {storage: '/completely/different/path.db'});
const options = sequelize.options; const options = sequelize.options;
expect(options.dialect).to.equal('sqlite'); expect(options.dialect).to.equal('sqlite');
expect(options.storage).to.equal('/completely/different/path.db'); expect(options.storage).to.equal(path.resolve('/completely/different/path.db'));
}); });
it('should be able to use :memory:', () => { it('should be able to use :memory:', () => {
...@@ -166,7 +167,7 @@ describe('Sequelize', () => { ...@@ -166,7 +167,7 @@ describe('Sequelize', () => {
storage: '/completely/different/path.db', storage: '/completely/different/path.db',
dialectOptions: { dialectOptions: {
supportBigNumbers: true, supportBigNumbers: true,
application_name: 'server', application_name: 'server' // eslint-disable-line
} }
}); });
......
...@@ -245,7 +245,7 @@ if (current.dialect.name === 'mssql') { ...@@ -245,7 +245,7 @@ if (current.dialect.name === 'mssql') {
{ {
title: 'Should use the plus operator', title: 'Should use the plus operator',
arguments: ['+', 'myTable', { foo: 'bar' }, {}, {}], arguments: ['+', 'myTable', { foo: 'bar' }, {}, {}],
expectation: 'UPDATE [myTable] SET [foo]=[foo]+ N\'bar\' OUTPUT INSERTED.* ' expectation: 'UPDATE [myTable] SET [foo]=[foo]+ N\'bar\' OUTPUT INSERTED.*'
}, },
{ {
title: 'Should use the plus operator with where clause', title: 'Should use the plus operator with where clause',
...@@ -255,17 +255,17 @@ if (current.dialect.name === 'mssql') { ...@@ -255,17 +255,17 @@ if (current.dialect.name === 'mssql') {
{ {
title: 'Should use the plus operator without returning clause', title: 'Should use the plus operator without returning clause',
arguments: ['+', 'myTable', { foo: 'bar' }, {}, { returning: false }], arguments: ['+', 'myTable', { foo: 'bar' }, {}, { returning: false }],
expectation: 'UPDATE [myTable] SET [foo]=[foo]+ N\'bar\' ' expectation: 'UPDATE [myTable] SET [foo]=[foo]+ N\'bar\''
}, },
{ {
title: 'Should use the minus operator', title: 'Should use the minus operator',
arguments: ['-', 'myTable', { foo: 'bar' }, {}, {}], arguments: ['-', 'myTable', { foo: 'bar' }, {}, {}],
expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\' OUTPUT INSERTED.* ' expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\' OUTPUT INSERTED.*'
}, },
{ {
title: 'Should use the minus operator with negative value', title: 'Should use the minus operator with negative value',
arguments: ['-', 'myTable', { foo: -1 }, {}, {}], arguments: ['-', 'myTable', { foo: -1 }, {}, {}],
expectation: 'UPDATE [myTable] SET [foo]=[foo]- -1 OUTPUT INSERTED.* ' expectation: 'UPDATE [myTable] SET [foo]=[foo]- -1 OUTPUT INSERTED.*'
}, },
{ {
title: 'Should use the minus operator with where clause', title: 'Should use the minus operator with where clause',
...@@ -275,7 +275,7 @@ if (current.dialect.name === 'mssql') { ...@@ -275,7 +275,7 @@ if (current.dialect.name === 'mssql') {
{ {
title: 'Should use the minus operator without returning clause', title: 'Should use the minus operator without returning clause',
arguments: ['-', 'myTable', { foo: 'bar' }, {}, { returning: false }], arguments: ['-', 'myTable', { foo: 'bar' }, {}, { returning: false }],
expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\' ' expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\''
} }
].forEach(test => { ].forEach(test => {
it(test.title, function() { it(test.title, function() {
......
...@@ -39,7 +39,7 @@ if (dialect === 'mysql') { ...@@ -39,7 +39,7 @@ if (dialect === 'mysql') {
{ {
title: 'Should use the plus operator', title: 'Should use the plus operator',
arguments: ['+', 'myTable', { foo: 'bar' }, {}, {}], arguments: ['+', 'myTable', { foo: 'bar' }, {}, {}],
expectation: 'UPDATE `myTable` SET `foo`=`foo`+ \'bar\' ' expectation: 'UPDATE `myTable` SET `foo`=`foo`+ \'bar\''
}, },
{ {
title: 'Should use the plus operator with where clause', title: 'Should use the plus operator with where clause',
...@@ -49,12 +49,12 @@ if (dialect === 'mysql') { ...@@ -49,12 +49,12 @@ if (dialect === 'mysql') {
{ {
title: 'Should use the minus operator', title: 'Should use the minus operator',
arguments: ['-', 'myTable', { foo: 'bar' }, {}, {}], arguments: ['-', 'myTable', { foo: 'bar' }, {}, {}],
expectation: 'UPDATE `myTable` SET `foo`=`foo`- \'bar\' ' expectation: 'UPDATE `myTable` SET `foo`=`foo`- \'bar\''
}, },
{ {
title: 'Should use the minus operator with negative value', title: 'Should use the minus operator with negative value',
arguments: ['-', 'myTable', { foo: -1 }, {}, {}], arguments: ['-', 'myTable', { foo: -1 }, {}, {}],
expectation: 'UPDATE `myTable` SET `foo`=`foo`- -1 ' expectation: 'UPDATE `myTable` SET `foo`=`foo`- -1'
}, },
{ {
title: 'Should use the minus operator with where clause', title: 'Should use the minus operator with where clause',
......
...@@ -11,7 +11,7 @@ const chai = require('chai'), ...@@ -11,7 +11,7 @@ const chai = require('chai'),
current = Support.sequelize, current = Support.sequelize,
_ = require('lodash'); _ = require('lodash');
if (dialect.match(/^postgres/)) { if (dialect.startsWith('postgres')) {
describe('[POSTGRES Specific] QueryGenerator', () => { describe('[POSTGRES Specific] QueryGenerator', () => {
const suites = { const suites = {
createDatabaseQuery: [ createDatabaseQuery: [
...@@ -64,7 +64,7 @@ if (dialect.match(/^postgres/)) { ...@@ -64,7 +64,7 @@ if (dialect.match(/^postgres/)) {
{ {
title: 'Should use the plus operator without returning clause', title: 'Should use the plus operator without returning clause',
arguments: ['+', 'myTable', { foo: 'bar' }, {}, { returning: false }], arguments: ['+', 'myTable', { foo: 'bar' }, {}, { returning: false }],
expectation: 'UPDATE "myTable" SET "foo"="foo"+ \'bar\' ' expectation: 'UPDATE "myTable" SET "foo"="foo"+ \'bar\''
}, },
{ {
title: 'Should use the minus operator', title: 'Should use the minus operator',
...@@ -84,7 +84,7 @@ if (dialect.match(/^postgres/)) { ...@@ -84,7 +84,7 @@ if (dialect.match(/^postgres/)) {
{ {
title: 'Should use the minus operator without returning clause', title: 'Should use the minus operator without returning clause',
arguments: ['-', 'myTable', { foo: 'bar' }, {}, { returning: false }], arguments: ['-', 'myTable', { foo: 'bar' }, {}, { returning: false }],
expectation: 'UPDATE "myTable" SET "foo"="foo"- \'bar\' ' expectation: 'UPDATE "myTable" SET "foo"="foo"- \'bar\''
} }
], ],
attributesToSQL: [ attributesToSQL: [
......
...@@ -24,7 +24,7 @@ if (dialect === 'sqlite') { ...@@ -24,7 +24,7 @@ if (dialect === 'sqlite') {
{ {
title: 'Should use the plus operator', title: 'Should use the plus operator',
arguments: ['+', 'myTable', { foo: 'bar' }, {}], arguments: ['+', 'myTable', { foo: 'bar' }, {}],
expectation: 'UPDATE `myTable` SET `foo`=`foo`+ \'bar\' ' expectation: 'UPDATE `myTable` SET `foo`=`foo`+ \'bar\''
}, },
{ {
title: 'Should use the plus operator with where clause', title: 'Should use the plus operator with where clause',
...@@ -34,12 +34,12 @@ if (dialect === 'sqlite') { ...@@ -34,12 +34,12 @@ if (dialect === 'sqlite') {
{ {
title: 'Should use the minus operator', title: 'Should use the minus operator',
arguments: ['-', 'myTable', { foo: 'bar' }], arguments: ['-', 'myTable', { foo: 'bar' }],
expectation: 'UPDATE `myTable` SET `foo`=`foo`- \'bar\' ' expectation: 'UPDATE `myTable` SET `foo`=`foo`- \'bar\''
}, },
{ {
title: 'Should use the minus operator with negative value', title: 'Should use the minus operator with negative value',
arguments: ['-', 'myTable', { foo: -1 }], arguments: ['-', 'myTable', { foo: -1 }],
expectation: 'UPDATE `myTable` SET `foo`=`foo`- -1 ' expectation: 'UPDATE `myTable` SET `foo`=`foo`- -1'
}, },
{ {
title: 'Should use the minus operator with where clause', title: 'Should use the minus operator with where clause',
......
...@@ -28,7 +28,7 @@ describe(Support.getTestDialectTeaser('SQL'), () => { ...@@ -28,7 +28,7 @@ describe(Support.getTestDialectTeaser('SQL'), () => {
expectsql(sql.updateQuery(User.tableName, {user_name: 'triggertest'}, {id: 2}, options, User.rawAttributes), expectsql(sql.updateQuery(User.tableName, {user_name: 'triggertest'}, {id: 2}, options, User.rawAttributes),
{ {
query: { query: {
mssql: 'declare @tmp table ([id] INTEGER,[user_name] NVARCHAR(255));UPDATE [users] SET [user_name]=$1 OUTPUT INSERTED.[id],INSERTED.[user_name] into @tmp WHERE [id] = $2;select * from @tmp', mssql: 'declare @tmp table ([id] INTEGER,[user_name] NVARCHAR(255)); UPDATE [users] SET [user_name]=$1 OUTPUT INSERTED.[id],INSERTED.[user_name] into @tmp WHERE [id] = $2;select * from @tmp',
postgres: 'UPDATE "users" SET "user_name"=$1 WHERE "id" = $2 RETURNING *', postgres: 'UPDATE "users" SET "user_name"=$1 WHERE "id" = $2 RETURNING *',
default: 'UPDATE `users` SET `user_name`=$1 WHERE `id` = $2' default: 'UPDATE `users` SET `user_name`=$1 WHERE `id` = $2'
}, },
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!