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

Commit be958ac1 by Simon Schick Committed by Sushant

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

1 parent d3451363
......@@ -108,10 +108,11 @@ class QueryGenerator {
const fields = [];
const values = [];
const bind = [];
const quotedTable = this.quoteTable(table);
const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
let query;
let valueQuery = '<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO <%= table %> (<%= attributes %>)<%= output %> VALUES (<%= values %>)<%= onConflictDoNothing %>';
let emptyQuery = '<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO <%= table %><%= output %><%= onConflictDoNothing %>';
let valueQuery = `<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO ${quotedTable} (<%= attributes %>)<%= output %> VALUES (<%= values %>)<%= onConflictDoNothing %>`;
let emptyQuery = `<%= tmpTable %>INSERT<%= ignoreDuplicates %> INTO ${quotedTable}<%= output %><%= onConflictDoNothing %>`;
let outputFragment;
let identityWrapperRequired = false;
let tmpTable = ''; //tmpTable declaration for trigger
......@@ -143,7 +144,6 @@ class QueryGenerator {
let tmpColumns = '';
let outputColumns = '';
tmpTable = 'declare @tmp table (<%= columns %>); ';
for (const modelKey in modelAttributes) {
const attribute = modelAttributes[modelKey];
......@@ -158,11 +158,7 @@ class QueryGenerator {
}
}
const replacement = {
columns: tmpColumns
};
tmpTable = _.template(tmpTable, this._templateSettings)(replacement).trim();
tmpTable = `declare @tmp table (${tmpColumns});`;
outputFragment = ' OUTPUT ' + outputColumns + ' into @tmp';
const selectFromTmp = ';select * from @tmp';
......@@ -187,12 +183,12 @@ class QueryGenerator {
const delimiter = '$func_' + uuidv4().replace(/-/g, '') + '$';
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 +
' LANGUAGE plpgsql; SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc()';
} else {
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 {
const replacements = {
ignoreDuplicates: options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '',
onConflictDoNothing: options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : '',
table: this.quoteTable(table),
attributes: fields.join(','),
output: outputFragment,
values: values.join(','),
......@@ -242,11 +237,7 @@ class QueryGenerator {
query = (replacements.attributes.length ? valueQuery : emptyQuery) + ';';
if (identityWrapperRequired && this._dialect.supports.autoIncrement.identityInsert) {
query = [
'SET IDENTITY_INSERT', this.quoteTable(table), 'ON;',
query,
'SET IDENTITY_INSERT', this.quoteTable(table), 'OFF;'
].join(' ');
query = `SET IDENTITY_INSERT ${quotedTable} ON; ${query} SET IDENTITY_INSERT ${quotedTable} OFF;`;
}
query = _.template(query, this._templateSettings)(replacements);
......@@ -272,7 +263,6 @@ class QueryGenerator {
options = options || {};
fieldMappedAttributes = fieldMappedAttributes || {};
const query = 'INSERT<%= ignoreDuplicates %> INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %><%= onDuplicateKeyUpdate %><%= onConflictDoNothing %><%= returning %>;';
const tuples = [];
const serials = {};
const allAttributes = [];
......@@ -314,17 +304,12 @@ class QueryGenerator {
}).join(',');
}
const replacements = {
ignoreDuplicates: options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '',
table: this.quoteTable(tableName),
attributes: allAttributes.map(attr => this.quoteIdentifier(attr)).join(','),
tuples: tuples.join(','),
onDuplicateKeyUpdate,
returning: this._dialect.supports.returnValues && options.returning ? ' RETURNING *' : '',
onConflictDoNothing: options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : ''
};
const ignoreDuplicates = options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '';
const attributes = allAttributes.map(attr => this.quoteIdentifier(attr)).join(',');
const returning = this._dialect.supports.returnValues && options.returning ? ' RETURNING *' : '';
const 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 {
const bind = [];
const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
const modelAttributeMap = {};
let query = '<%= tmpTable %>UPDATE <%= table %> SET <%= values %><%= output %> <%= where %>';
let outputFragment;
let outputFragment ='';
let tmpTable = ''; // tmpTable declaration for trigger
let selectFromTmp = ''; // Select statement for trigger
let suffix = '';
if (this._dialect.supports['LIMIT ON UPDATE'] && options.limit) {
if (this.dialect !== 'mssql') {
query += ' LIMIT ' + this.escape(options.limit) + ' ';
suffix = ' LIMIT ' + this.escape(options.limit) + ' ';
}
}
......@@ -366,7 +351,6 @@ class QueryGenerator {
//To capture output rows when there is a trigger on MSSQL DB
if (attributes && options.hasTrigger && this._dialect.supports.tmpTableTrigger) {
tmpTable = 'declare @tmp table (<%= columns %>); ';
let tmpColumns = '';
let outputColumns = '';
......@@ -383,20 +367,16 @@ class QueryGenerator {
}
}
const replacement ={
columns: tmpColumns
};
tmpTable = _.template(tmpTable, this._templateSettings)(replacement).trim();
tmpTable = `declare @tmp table (${tmpColumns}); `;
outputFragment = ' OUTPUT ' + outputColumns + ' into @tmp';
selectFromTmp = ';select * from @tmp';
query += selectFromTmp;
suffix += selectFromTmp;
}
} else if (this._dialect.supports.returnValues && options.returning) {
// ensure that the return output is properly mapped to model fields.
options.mapToModel = true;
query += ' RETURNING *';
suffix += ' RETURNING *';
}
}
......@@ -427,19 +407,12 @@ class QueryGenerator {
}
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) {
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
const result = { query };
if (options.bindParam !== false) {
......@@ -465,13 +438,13 @@ class QueryGenerator {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull);
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.returning) {
options.mapToModel = true;
query += ' RETURNING *';
returningFragment = 'RETURNING *';
} else if (this._dialect.supports.returnValues.output) {
outputFragment = ' OUTPUT INSERTED.*';
}
......@@ -488,14 +461,7 @@ class QueryGenerator {
values.push(this.quoteIdentifier(key) + '=' + this.escape(value));
}
const replacements = {
table: this.quoteTable(tableName),
values: values.join(','),
output: outputFragment,
where: this.whereQuery(where)
};
return _.template(query, this._templateSettings)(replacements);
return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')}${outputFragment} ${this.whereQuery(where)} ${returningFragment}`.trim();
}
/*
......@@ -1321,7 +1287,7 @@ class QueryGenerator {
mainQueryItems.push(' WHERE ' + options.where);
// Walk the main query to update all selects
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);
}
});
......
......@@ -22,25 +22,15 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
collate: null
}, 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 = {
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() + ';';
return `CREATE DATABASE IF NOT EXISTS ${database}${charset}${collate}`.trim() + ';';
}
dropDatabaseQuery(databaseName) {
const query = 'DROP DATABASE IF EXISTS <%= database %>';
const values = {
database: this.quoteIdentifier(databaseName)
};
return _.template(query, this._templateSettings)(values).trim() + ';';
return `DROP DATABASE IF EXISTS ${this.quoteIdentifier(databaseName).trim()};`;
}
createSchema() {
......@@ -62,7 +52,6 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
rowFormat: null
}, options || {});
const query = 'CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %><%= comment %><%= charset %><%= collation %><%= initialAutoIncrement %><%= rowFormat %>';
const primaryKeys = [];
const foreignKeys = {};
const attrStr = [];
......@@ -78,10 +67,10 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
if (dataType.includes('REFERENCES')) {
// MySQL doesn't support inline REFERENCES declarations: move to the end
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];
} else {
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace(/PRIMARY KEY/, ''));
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace('PRIMARY KEY', ''));
}
} else if (dataType.includes('REFERENCES')) {
// MySQL doesn't support inline REFERENCES declarations: move to the end
......@@ -94,16 +83,14 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
}
}
const values = {
table: this.quoteTable(tableName),
attributes: attrStr.join(', '),
comment: options.comment && _.isString(options.comment) ? ' COMMENT ' + this.escape(options.comment) : '',
engine: options.engine,
charset: options.charset ? ' DEFAULT CHARSET=' + options.charset : '',
collation: options.collate ? ' COLLATE ' + options.collate : '',
rowFormat: options.rowFormat ? ' ROW_FORMAT=' + options.rowFormat : '',
initialAutoIncrement: options.initialAutoIncrement ? ' AUTO_INCREMENT=' + options.initialAutoIncrement : ''
};
const table = this.quoteTable(tableName);
let attributesClause = attrStr.join(', ');
const comment = options.comment && _.isString(options.comment) ? ' COMMENT ' + this.escape(options.comment) : '';
const engine = options.engine;
const charset = options.charset ? ' DEFAULT CHARSET=' + options.charset : '';
const collation = options.collate ? ' COLLATE ' + options.collate : '';
const rowFormat = options.rowFormat ? ' ROW_FORMAT=' + options.rowFormat : '';
const initialAutoIncrement = options.initialAutoIncrement ? ' AUTO_INCREMENT=' + options.initialAutoIncrement : '';
const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', ');
if (options.uniqueKeys) {
......@@ -112,22 +99,22 @@ class MySQLQueryGenerator extends AbstractQueryGenerator {
if (!_.isString(indexName)) {
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) {
values.attributes += `, PRIMARY KEY (${pkString})`;
attributesClause += `, PRIMARY KEY (${pkString})`;
}
for (const fkey in foreignKeys) {
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 {
for (const attributeName in attributes) {
let definition = attributes[attributeName];
if (definition.match(/REFERENCES/)) {
if (definition.includes('REFERENCES')) {
const attrName = this.quoteIdentifier(attributeName);
definition = definition.replace(/.+?(?=REFERENCES)/, '');
constraintString.push(`FOREIGN KEY (${attrName}) ${definition}`);
......
......@@ -64,8 +64,10 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
let comments = '';
let columnComments = '';
const quotedTable = this.quoteTable(tableName);
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) {
......@@ -74,7 +76,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
if (i !== -1) {
// Move comment to a separate query
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);
}
......@@ -82,17 +84,13 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
attrStr.push(quotedAttr + ' ' + dataType);
}
const values = {
table: this.quoteTable(tableName),
attributes: attrStr.join(', '),
comments: _.template(comments, this._templateSettings)({ table: this.quoteTable(tableName) }),
columnComments: _.template(columnComments, this._templateSettings)({ table: this.quoteTable(tableName) })
};
let attributesClause = attrStr.join(', ');
if (options.uniqueKeys) {
_.each(options.uniqueKeys, columns => {
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 {
}, []).join(',');
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) {
......@@ -267,7 +265,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
}
changeColumnQuery(tableName, attributes) {
const query = 'ALTER TABLE <%= tableName %> ALTER COLUMN <%= query %>;';
const query = subQuery => `ALTER TABLE ${this.quoteTable(tableName)} ALTER COLUMN ${subQuery};`;
const sql = [];
for (const attributeName in attributes) {
......@@ -275,34 +273,22 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
let attrSql = '';
if (definition.includes('NOT NULL')) {
attrSql += _.template(query, this._templateSettings)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' SET NOT NULL'
});
attrSql += query(this.quoteIdentifier(attributeName) + ' SET NOT NULL');
definition = definition.replace('NOT NULL', '').trim();
} else if (!definition.match(/REFERENCES/)) {
attrSql += _.template(query, this._templateSettings)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' DROP NOT NULL'
});
} else if (!definition.includes('REFERENCES')) {
attrSql += query(this.quoteIdentifier(attributeName) + ' DROP NOT NULL');
}
if (definition.includes('DEFAULT')) {
attrSql += _.template(query, this._templateSettings)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' SET DEFAULT ' + definition.match(/DEFAULT ([^;]+)/)[1]
});
attrSql += query(this.quoteIdentifier(attributeName) + ' SET DEFAULT ' + definition.match(/DEFAULT ([^;]+)/)[1]);
definition = definition.replace(/(DEFAULT[^;]+)/, '').trim();
} else if (!definition.match(/REFERENCES/)) {
attrSql += _.template(query, this._templateSettings)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' DROP DEFAULT'
});
} else if (!definition.includes('REFERENCES')) {
attrSql += query(this.quoteIdentifier(attributeName) + ' DROP DEFAULT');
}
if (attributes[attributeName].match(/^ENUM\(/)) {
if (attributes[attributeName].startsWith('ENUM(')) {
attrSql += this.pgEnum(tableName, attributeName, attributes[attributeName]);
definition = definition.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attributeName, { schema: false }));
definition += ' USING (' + this.quoteIdentifier(attributeName) + '::' + this.pgEnumName(tableName, attributeName) + ')';
......@@ -310,24 +296,14 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
if (definition.match(/UNIQUE;*$/)) {
definition = definition.replace(/UNIQUE;*$/, '');
attrSql += _.template(query.replace('ALTER COLUMN', ''), this._templateSettings)({
tableName: this.quoteTable(tableName),
query: 'ADD UNIQUE (' + this.quoteIdentifier(attributeName) + ')'
});
attrSql += query('ADD UNIQUE (' + this.quoteIdentifier(attributeName) + ')').replace('ALTER COLUMN', '');
}
if (definition.match(/REFERENCES/)) {
if (definition.includes('REFERENCES')) {
definition = definition.replace(/.+?(?=REFERENCES)/, '');
attrSql += _.template(query.replace('ALTER COLUMN', ''), this._templateSettings)({
tableName: this.quoteTable(tableName),
query: 'ADD FOREIGN KEY (' + this.quoteIdentifier(attributeName) + ') ' + definition
});
attrSql += query('ADD FOREIGN KEY (' + this.quoteIdentifier(attributeName) + ') ' + definition).replace('ALTER COLUMN', '');
} else {
attrSql += _.template(query, this._templateSettings)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' TYPE ' + definition
});
attrSql += query(this.quoteIdentifier(attributeName) + ' TYPE ' + definition);
}
sql.push(attrSql);
......@@ -341,10 +317,7 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
const attrString = [];
for (const attributeName in attributes) {
attrString.push(_.template('<%= before %> TO <%= after %>', this._templateSettings)({
before: this.quoteIdentifier(attrBefore),
after: this.quoteIdentifier(attributeName)
}));
attrString.push(`${this.quoteIdentifier(attrBefore)} TO ${this.quoteIdentifier(attributeName)}`);
}
return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${attrString.join(', ')};`;
......@@ -395,13 +368,15 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
}
deleteQuery(tableName, where, options = {}, model) {
const replacements = {
table: this.quoteTable(tableName),
where: this.getWhereConditions(where, null, model, options),
limit: options.limit ? ' LIMIT ' + this.escape(options.limit) : ''
};
const table = this.quoteTable(tableName);
let whereClause = this.getWhereConditions(where, null, model, options);
const limit = options.limit ? ' LIMIT ' + this.escape(options.limit) : '';
let primaryKeys = '';
let primaryKeysSelection = '';
let query;
if (whereClause) {
whereClause = ' WHERE ' + whereClause;
}
if (options.limit) {
if (!model) {
......@@ -410,19 +385,12 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
const pks = _.values(model.primaryKeys).map(pk => this.quoteIdentifier(pk.field)).join(',');
replacements.primaryKeys = model.primaryKeyAttributes.length > 1 ? '(' + pks + ')' : pks;
replacements.primaryKeysSelection = pks;
primaryKeys = model.primaryKeyAttributes.length > 1 ? '(' + pks + ')' : pks;
primaryKeysSelection = pks;
query = 'DELETE FROM <%= table %> WHERE <%= primaryKeys %> IN (SELECT <%= primaryKeysSelection %> FROM <%= table %><%= where %><%= limit %>)';
} else {
query = 'DELETE FROM <%= table %><%= where %>';
return `DELETE FROM ${table} WHERE ${primaryKeys} IN (SELECT ${primaryKeysSelection} FROM ${table}${whereClause}${limit})`;
}
if (replacements.where) {
replacements.where = ' WHERE ' + replacements.where;
}
return _.template(query, this._templateSettings)(replacements);
return `DELETE FROM ${table}${whereClause}`;
}
showIndexesQuery(tableName) {
......@@ -834,23 +802,23 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
dataTypeMapping(tableName, attr, dataType) {
if (dataType.includes('PRIMARY KEY')) {
dataType = dataType.replace(/PRIMARY KEY/, '');
dataType = dataType.replace('PRIMARY KEY', '');
}
if (dataType.includes('SERIAL')) {
if (dataType.includes('BIGINT')) {
dataType = dataType.replace(/SERIAL/, 'BIGSERIAL');
dataType = dataType.replace(/BIGINT/, '');
dataType = dataType.replace('SERIAL', 'BIGSERIAL');
dataType = dataType.replace('BIGINT', '');
} else if (dataType.includes('SMALLINT')) {
dataType = dataType.replace(/SERIAL/, 'SMALLSERIAL');
dataType = dataType.replace(/SMALLINT/, '');
dataType = dataType.replace('SERIAL', 'SMALLSERIAL');
dataType = dataType.replace('SMALLINT', '');
} 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));
}
......
......@@ -45,7 +45,7 @@ class SQLiteQueryGenerator extends MySqlQueryGenerator {
if (needsMultiplePrimaryKeys) {
primaryKeys.push(attr);
dataTypeString = dataType.replace(/PRIMARY KEY/, 'NOT NULL');
dataTypeString = dataType.replace('PRIMARY KEY', 'NOT NULL');
}
}
attrArray.push(this.quoteIdentifier(attr) + ' ' + dataTypeString);
......
......@@ -368,8 +368,7 @@ class Query extends AbstractQuery {
switch (err.code) {
case 'SQLITE_CONSTRAINT': {
let match = err.message.match(/FOREIGN KEY constraint failed/);
if (match !== null) {
if (err.message.includes('FOREIGN KEY constraint failed')) {
return new sequelizeErrors.ForeignKeyConstraintError({
parent: err
});
......@@ -378,7 +377,7 @@ class Query extends AbstractQuery {
let fields = [];
// 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) {
fields = match[1].split(', ');
} else {
......
'use strict';
const url = require('url');
const Path = require('path');
const path = require('path');
const retry = require('retry-as-promised');
const clsBluebird = require('cls-bluebird');
const _ = require('lodash');
......@@ -111,8 +111,8 @@ class Sequelize {
options.host = urlParts.hostname;
if (options.dialect === 'sqlite' && urlParts.pathname && !urlParts.pathname.startsWith('/:memory')) {
const path = Path.join(options.host, urlParts.pathname);
options.storage = options.storage || path;
const storagePath = path.join(options.host, urlParts.pathname);
options.storage = path.resolve(options.storage || storagePath);
}
if (urlParts.pathname) {
......@@ -384,30 +384,30 @@ class Sequelize {
*
* @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
*/
import(path) {
import(importPath) {
// 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
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]) {
let defineCall = arguments.length > 1 ? arguments[1] : require(path);
if (!this.importCache[importPath]) {
let defineCall = arguments.length > 1 ? arguments[1] : require(importPath);
if (typeof defineCall === 'object') {
// ES6 module compatibility
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'), () => {
});
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);
});
});
......
......@@ -56,27 +56,27 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
if (dialect === 'sqlite') {
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)', () => {
const sequelize = new Sequelize('sqlite://test.sqlite/'); // eslint-disable-line
new Sequelize('sqlite://test.sqlite/');
});
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') {
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)', () => {
const connectionUri = getConnectionUri(Object.assign(config[dialect], {protocol: 'postgres'}));
// postgres://...
const sequelize = new Sequelize(connectionUri); // eslint-disable-line
new Sequelize(connectionUri);
});
it('should work with connection strings (postgresql protocol)', () => {
const connectionUri = getConnectionUri(Object.assign(config[dialect], {protocol: 'postgresql'}));
// postgresql://...
const sequelize = new Sequelize(connectionUri); // eslint-disable-line
new Sequelize(connectionUri);
});
}
});
......@@ -122,11 +122,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
.authenticate()
.catch(err => {
expect(
err.message.match(/connect ECONNREFUSED/) ||
err.message.match(/invalid port number/) ||
err.message.includes('connect ECONNREFUSED') ||
err.message.includes('invalid port number') ||
err.message.match(/should be >=? 0 and < 65536/) ||
err.message.match(/Login failed for user/) ||
err.message.match(/Port must be > 0 and < 65536/)
err.message.includes('Login failed for user') ||
err.message.includes('Port must be > 0 and < 65536')
).to.be.ok;
});
});
......
......@@ -4,7 +4,8 @@ const chai = require('chai'),
expect = chai.expect,
Support = require('./support'),
Sequelize = Support.Sequelize,
dialect = Support.getTestDialect();
dialect = Support.getTestDialect(),
path = require('path');
describe('Sequelize', () => {
describe('dialect is required', () => {
......@@ -76,21 +77,21 @@ describe('Sequelize', () => {
const sequelize = new Sequelize('sqlite:subfolder/dbname.db');
const options = sequelize.options;
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', () => {
const sequelize = new Sequelize('sqlite:/home/abs/dbname.db');
const options = sequelize.options;
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', () => {
const sequelize = new Sequelize('sqlite:/home/abs/dbname.db', {storage: '/completely/different/path.db'});
const options = sequelize.options;
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:', () => {
......@@ -166,7 +167,7 @@ describe('Sequelize', () => {
storage: '/completely/different/path.db',
dialectOptions: {
supportBigNumbers: true,
application_name: 'server',
application_name: 'server' // eslint-disable-line
}
});
......
......@@ -245,7 +245,7 @@ if (current.dialect.name === 'mssql') {
{
title: 'Should use the plus operator',
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',
......@@ -255,17 +255,17 @@ if (current.dialect.name === 'mssql') {
{
title: 'Should use the plus operator without returning clause',
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',
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',
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',
......@@ -275,7 +275,7 @@ if (current.dialect.name === 'mssql') {
{
title: 'Should use the minus operator without returning clause',
arguments: ['-', 'myTable', { foo: 'bar' }, {}, { returning: false }],
expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\' '
expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\''
}
].forEach(test => {
it(test.title, function() {
......
......@@ -39,7 +39,7 @@ if (dialect === 'mysql') {
{
title: 'Should use the plus operator',
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',
......@@ -49,12 +49,12 @@ if (dialect === 'mysql') {
{
title: 'Should use the minus operator',
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',
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',
......
......@@ -11,7 +11,7 @@ const chai = require('chai'),
current = Support.sequelize,
_ = require('lodash');
if (dialect.match(/^postgres/)) {
if (dialect.startsWith('postgres')) {
describe('[POSTGRES Specific] QueryGenerator', () => {
const suites = {
createDatabaseQuery: [
......@@ -64,7 +64,7 @@ if (dialect.match(/^postgres/)) {
{
title: 'Should use the plus operator without returning clause',
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',
......@@ -84,7 +84,7 @@ if (dialect.match(/^postgres/)) {
{
title: 'Should use the minus operator without returning clause',
arguments: ['-', 'myTable', { foo: 'bar' }, {}, { returning: false }],
expectation: 'UPDATE "myTable" SET "foo"="foo"- \'bar\' '
expectation: 'UPDATE "myTable" SET "foo"="foo"- \'bar\''
}
],
attributesToSQL: [
......
......@@ -24,7 +24,7 @@ if (dialect === 'sqlite') {
{
title: 'Should use the plus operator',
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',
......@@ -34,12 +34,12 @@ if (dialect === 'sqlite') {
{
title: 'Should use the minus operator',
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',
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',
......
......@@ -28,7 +28,7 @@ describe(Support.getTestDialectTeaser('SQL'), () => {
expectsql(sql.updateQuery(User.tableName, {user_name: 'triggertest'}, {id: 2}, options, User.rawAttributes),
{
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 *',
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!