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

Commit 719f95b7 by Sushant Committed by GitHub

v5-beta (#9212)

1 parent 3fd065ca
---
verifyConditions:
- "@semantic-release/npm"
- "@semantic-release/github"
publish:
- "@semantic-release/npm"
- "@semantic-release/github"
prepare:
- "@semantic-release/npm"
...@@ -7,6 +7,7 @@ branches: ...@@ -7,6 +7,7 @@ branches:
only: only:
- master - master
- v3 - v3
- v5
- /^greenkeeper/.*$/ - /^greenkeeper/.*$/
except: except:
- /^v\d+\.\d+\.\d+$/ - /^v\d+\.\d+\.\d+$/
...@@ -43,50 +44,42 @@ before_script: ...@@ -43,50 +44,42 @@ before_script:
- "if [ $POSTGRES_VER ]; then docker run --link ${POSTGRES_VER}:db -e CHECK_PORT=5432 -e CHECK_HOST=db --net sequelize_default giorgos/takis; fi" - "if [ $POSTGRES_VER ]; then docker run --link ${POSTGRES_VER}:db -e CHECK_PORT=5432 -e CHECK_HOST=db --net sequelize_default giorgos/takis; fi"
script: script:
- 'if [ "$INSTALL_MODULE" ]; then npm install ${INSTALL_MODULE}; fi'
- npm run lint - npm run lint
- "if [ $COVERAGE ]; then npm run cover && bash <(curl -s https://codecov.io/bash) -f coverage/lcov.info; else npm run test; fi" - "if [ $COVERAGE ]; then npm run cover && bash <(curl -s https://codecov.io/bash) -f coverage/lcov.info; else npm run test; fi"
jobs: jobs:
include: include:
- stage: test - stage: test
node_js: '4' node_js: '6'
env: DIALECT=sqlite COVERAGE=true env: DIALECT=sqlite COVERAGE=true
- stage: test - stage: test
node_js: '4' node_js: '6'
sudo: required sudo: required
env: MYSQL_VER=mysql-57 DIALECT=mysql COVERAGE=true env: MYSQL_VER=mysql-57 DIALECT=mysql COVERAGE=true
- stage: test - stage: test
node_js: '4' node_js: '6'
sudo: required sudo: required
env: POSTGRES_VER=postgres-95 DIALECT=postgres COVERAGE=true env: POSTGRES_VER=postgres-95 DIALECT=postgres COVERAGE=true
- stage: test - stage: test
node_js: '4' node_js: '6'
sudo: required sudo: required
env: POSTGRES_VER=postgres-95 DIALECT=postgres-native COVERAGE=true env: POSTGRES_VER=postgres-95 DIALECT=postgres-native COVERAGE=true
- stage: test - stage: test
node_js: '8' node_js: '8'
env: DIALECT=sqlite env: DIALECT=sqlite
- stage: test # - stage: release
node_js: '4' # node_js: '8'
sudo: required # script:
env: POSTGRES_VER=postgres-95 DIALECT=postgres COVERAGE=true INSTALL_MODULE="pg@6.x pg-native@1.x" # - npm run semantic-release
- stage: test # before_deploy:
node_js: '6' # - npm run docs
env: DIALECT=sqlite # deploy:
- stage: release # provider: surge
node_js: '8' # project: ./esdoc/
script: # domain: docs.sequelizejs.com
- npm run semantic-release # skip_cleanup: true
before_deploy:
- npm run docs
deploy:
provider: surge
project: ./esdoc/
domain: docs.sequelizejs.com
skip_cleanup: true
stages: stages:
- test - test
- name: release # - name: release
if: branch = master AND type = push AND fork = false # if: branch = master AND type = push AND fork = false
...@@ -13,7 +13,7 @@ cache: ...@@ -13,7 +13,7 @@ cache:
environment: environment:
matrix: matrix:
- {NODE_VERSION: 4, DIALECT: mssql, COVERAGE: true} - {NODE_VERSION: 6, DIALECT: mssql, COVERAGE: true}
install: install:
- ps: Install-Product node $env:NODE_VERSION x64 - ps: Install-Product node $env:NODE_VERSION x64
...@@ -43,3 +43,4 @@ after_test: ...@@ -43,3 +43,4 @@ after_test:
branches: branches:
only: only:
- master - master
- v5
# Upgrade to v5
Sequelize v5 is the next major release after v4
## Breaking Changes
### Support for Node 6 and up
Sequelize v5 will only support Node 6 and up [#9015](https://github.com/sequelize/sequelize/issues/9015)
__DEV__: _Done, no deprecation_
### Secure Operators
With v4 you started to get a deprecation warning `String based operators are now deprecated`. Also concept of operators was introduced. These operators are Symbols which prevent hash injection attacks.
Please check these threads to know more
- (Issue) https://github.com/sequelize/sequelize/issues/7310
- (Fix) https://github.com/sequelize/sequelize/pull/8240
- (Explanation) https://github.com/sequelize/sequelize/issues/8417#issuecomment-334056048
- (Official Docs) http://docs.sequelizejs.com/manual/tutorial/querying.html#operators-security
With v5
- Operators are now enabled by default.
- You can still use string operators by passing an operators map in `operatorsAliases`
- Op.$raw is removed
__DEV__: _Incomplete, deprecated_
### Model
**Attributes**
`Model.attributes` now removed, use `Model.rawAttributes`. [#5320](https://github.com/sequelize/sequelize/issues/5320)
__Note__: _Please dont confuse this with `options.attributes`, they are still valid_
__DEV__: _Done, no deprecation_
**Paranoid Mode**
With v5 if `deletedAt` is set, record will be considered as deleted. So `paranoid` option will only use `deletedAt` as flag.
In v4 it used to compare current time with `deletedAt`. [#8496](https://github.com/sequelize/sequelize/issues/8496)
__DEV__: _Done, no deprecation_
'use strict'; 'use strict';
const Utils = require('../../utils');
const SqlString = require('../../sql-string');
const Model = require('../../model');
const DataTypes = require('../../data-types');
const util = require('util'); const util = require('util');
const _ = require('lodash'); const _ = require('lodash');
const Dottie = require('dottie'); const Dottie = require('dottie');
const uuid = require('uuid');
const semver = require('semver');
const Utils = require('../../utils');
const SqlString = require('../../sql-string');
const DataTypes = require('../../data-types');
const Model = require('../../model');
const Association = require('../../associations/base'); const Association = require('../../associations/base');
const BelongsTo = require('../../associations/belongs-to'); const BelongsTo = require('../../associations/belongs-to');
const BelongsToMany = require('../../associations/belongs-to-many'); const BelongsToMany = require('../../associations/belongs-to-many');
const HasMany = require('../../associations/has-many'); const HasMany = require('../../associations/has-many');
const Op = require('../../operators'); const Op = require('../../operators');
const uuid = require('uuid');
const semver = require('semver');
const QueryGenerator = { const QuoteHelper = require('./query-generator/helpers/quote');
_templateSettings: require('lodash').runInContext().templateSettings,
options: {}, /**
* Abstract Query Generator
*
* @private
*/
class QueryGenerator {
constructor(options) {
if (!options.sequelize) throw new Error('QueryGenerator initialized without options.sequelize');
if (!options._dialect) throw new Error('QueryGenerator initialized without options._dialect');
this.sequelize = options.sequelize;
this.options = options.sequelize.options;
// dialect name
this.dialect = options._dialect.name;
this._dialect = options._dialect;
// template config
this._templateSettings = require('lodash').runInContext().templateSettings;
}
extractTableDetails(tableName, options) { extractTableDetails(tableName, options) {
options = options || {}; options = options || {};
...@@ -27,11 +47,10 @@ const QueryGenerator = { ...@@ -27,11 +47,10 @@ const QueryGenerator = {
tableName: _.isPlainObject(tableName) ? tableName.tableName : tableName, tableName: _.isPlainObject(tableName) ? tableName.tableName : tableName,
delimiter: tableName.delimiter || options.delimiter || '.' delimiter: tableName.delimiter || options.delimiter || '.'
}; };
}, }
addSchema(param) { addSchema(param) {
const self = this; const self = this;
if (!param._schema) return param.tableName || param; if (!param._schema) return param.tableName || param;
return { return {
...@@ -44,11 +63,11 @@ const QueryGenerator = { ...@@ -44,11 +63,11 @@ const QueryGenerator = {
return self.quoteTable(this); return self.quoteTable(this);
} }
}; };
}, }
dropSchema(tableName, options) { dropSchema(tableName, options) {
return this.dropTableQuery(tableName, options); return this.dropTableQuery(tableName, options);
}, }
describeTableQuery(tableName, schema, schemaDelimiter) { describeTableQuery(tableName, schema, schemaDelimiter) {
const table = this.quoteTable( const table = this.quoteTable(
...@@ -59,20 +78,26 @@ const QueryGenerator = { ...@@ -59,20 +78,26 @@ const QueryGenerator = {
}) })
); );
return 'DESCRIBE ' + table + ';'; return `DESCRIBE ${table};`;
}, }
dropTableQuery(tableName) { dropTableQuery(tableName) {
return `DROP TABLE IF EXISTS ${this.quoteTable(tableName)};`; return `DROP TABLE IF EXISTS ${this.quoteTable(tableName)};`;
}, }
renameTableQuery(before, after) { renameTableQuery(before, after) {
return `ALTER TABLE ${this.quoteTable(before)} RENAME TO ${this.quoteTable(after)};`; return `ALTER TABLE ${this.quoteTable(before)} RENAME TO ${this.quoteTable(after)};`;
}, }
/* /**
Returns an insert into command. Parameters: table name + hash of attribute-value-pairs. * Returns an insert into command
@private *
* @param {String} table
* @param {Object} valueHash attribute value pairs
* @param {Object} modelAttributes
* @param {Object} [options]
*
* @private
*/ */
insertQuery(table, valueHash, modelAttributes, options) { insertQuery(table, valueHash, modelAttributes, options) {
options = options || {}; options = options || {};
...@@ -210,12 +235,17 @@ const QueryGenerator = { ...@@ -210,12 +235,17 @@ const QueryGenerator = {
} }
return _.template(query, this._templateSettings)(replacements); return _.template(query, this._templateSettings)(replacements);
}, }
/* /**
Returns an insert into command for multiple values. * Returns an insert into command for multiple values.
Parameters: table name + list of hashes of attribute-value-pairs. *
@private * @param {String} tableName
* @param {Object} fieldValueHashes
* @param {Object} options
* @param {Object} fieldMappedAttributes
*
* @private
*/ */
bulkInsertQuery(tableName, fieldValueHashes, options, fieldMappedAttributes) { bulkInsertQuery(tableName, fieldValueHashes, options, fieldMappedAttributes) {
options = options || {}; options = options || {};
...@@ -273,18 +303,19 @@ const QueryGenerator = { ...@@ -273,18 +303,19 @@ const QueryGenerator = {
}; };
return _.template(query, this._templateSettings)(replacements); return _.template(query, this._templateSettings)(replacements);
}, }
/* /**
Returns an update query. * Returns an update query
Parameters: *
- tableName -> Name of the table * @param {String} tableName
- values -> A hash with attribute-value-pairs * @param {Object} attrValueHash
- where -> A hash with conditions (e.g. {name: 'foo'}) * @param {Object} where A hash with conditions (e.g. {name: 'foo'}) OR an ID as integer
OR an ID as integer OR a string with conditions (e.g. 'name="foo"'). If you use a string, you have to escape it on your own.
OR a string with conditions (e.g. 'name="foo"'). * @param {Object} options
If you use a string, you have to escape it on your own. * @param {Object} attributes
@private *
* @private
*/ */
updateQuery(tableName, attrValueHash, where, options, attributes) { updateQuery(tableName, attrValueHash, where, options, attributes) {
options = options || {}; options = options || {};
...@@ -380,19 +411,18 @@ const QueryGenerator = { ...@@ -380,19 +411,18 @@ const QueryGenerator = {
} }
return _.template(query, this._templateSettings)(replacements).trim(); return _.template(query, this._templateSettings)(replacements).trim();
}, }
/* /**
Returns an update query. * Returns an update query using arithmetic operator
Parameters: *
- operator -> String with the arithmetic operator (e.g. '+' or '-') * @param {String} operator String with the arithmetic operator (e.g. '+' or '-')
- tableName -> Name of the table * @param {String} tableName Name of the table
- values -> A hash with attribute-value-pairs * @param {Object} attrValueHash A hash with attribute-value-pairs
- where -> A hash with conditions (e.g. {name: 'foo'}) * @param {Object} where A hash with conditions (e.g. {name: 'foo'}) OR an ID as integer
OR an ID as integer OR a string with conditions (e.g. 'name="foo"'). If you use a string, you have to escape it on your own.
OR a string with conditions (e.g. 'name="foo"'). * @param {Object} options
If you use a string, you have to escape it on your own. * @param {Object} attributes
@private
*/ */
arithmeticQuery(operator, tableName, attrValueHash, where, options, attributes) { arithmeticQuery(operator, tableName, attrValueHash, where, options, attributes) {
options = options || {}; options = options || {};
...@@ -432,7 +462,7 @@ const QueryGenerator = { ...@@ -432,7 +462,7 @@ const QueryGenerator = {
}; };
return _.template(query, this._templateSettings)(replacements); return _.template(query, this._templateSettings)(replacements);
}, }
nameIndexes(indexes, rawTablename) { nameIndexes(indexes, rawTablename) {
if (typeof rawTablename === 'object') { if (typeof rawTablename === 'object') {
...@@ -448,7 +478,7 @@ const QueryGenerator = { ...@@ -448,7 +478,7 @@ const QueryGenerator = {
return index; return index;
}); });
}, }
/* /*
Returns an add index query. Returns an add index query.
...@@ -576,7 +606,7 @@ const QueryGenerator = { ...@@ -576,7 +606,7 @@ const QueryGenerator = {
); );
return _.compact(ind).join(' '); return _.compact(ind).join(' ');
}, }
addConstraintQuery(tableName, options) { addConstraintQuery(tableName, options) {
options = options || {}; options = options || {};
...@@ -589,7 +619,7 @@ const QueryGenerator = { ...@@ -589,7 +619,7 @@ const QueryGenerator = {
} }
return `ALTER TABLE ${tableName} ADD ${constraintSnippet};`; return `ALTER TABLE ${tableName} ADD ${constraintSnippet};`;
}, }
getConstraintSnippet(tableName, options) { getConstraintSnippet(tableName, options) {
let constraintSnippet, constraintName; let constraintSnippet, constraintName;
...@@ -663,46 +693,12 @@ const QueryGenerator = { ...@@ -663,46 +693,12 @@ const QueryGenerator = {
default: throw new Error(`${options.type} is invalid.`); default: throw new Error(`${options.type} is invalid.`);
} }
return constraintSnippet; return constraintSnippet;
}, }
removeConstraintQuery(tableName, constraintName) { removeConstraintQuery(tableName, constraintName) {
return `ALTER TABLE ${this.quoteIdentifiers(tableName)} DROP CONSTRAINT ${this.quoteIdentifiers(constraintName)}`; return `ALTER TABLE ${this.quoteIdentifiers(tableName)} DROP CONSTRAINT ${this.quoteIdentifiers(constraintName)}`;
},
quoteTable(param, as) {
let table = '';
if (as === true) {
as = param.as || param.name || param;
}
if (_.isObject(param)) {
if (this._dialect.supports.schemas) {
if (param.schema) {
table += this.quoteIdentifier(param.schema) + '.';
}
table += this.quoteIdentifier(param.tableName);
} else {
if (param.schema) {
table += param.schema + (param.delimiter || '.');
} }
table += param.tableName;
table = this.quoteIdentifier(table);
}
} else {
table = this.quoteIdentifier(param);
}
if (as) {
table += ' AS ' + this.quoteIdentifier(as);
}
return table;
},
/* /*
Quote an object based on its type. This is a more general version of quoteIdentifiers Quote an object based on its type. This is a more general version of quoteIdentifiers
Strings: should proxy to quoteIdentifiers Strings: should proxy to quoteIdentifiers
...@@ -879,20 +875,79 @@ const QueryGenerator = { ...@@ -879,20 +875,79 @@ const QueryGenerator = {
} else { } else {
throw new Error('Unknown structure passed to order / group: ' + util.inspect(collection)); throw new Error('Unknown structure passed to order / group: ' + util.inspect(collection));
} }
}, }
/* /**
Split an identifier into .-separated tokens and quote each part * Split a list of identifiers by "." and quote each part
@private *
* @param {String} dialect Dialect name
* @param {String} identifiers
* @param {Object} [options]
* @param {Boolean} [options.force=false]
* @param {Boolean} [options.quoteIdentifiers=true]
*
* @returns {String}
*/ */
quoteIdentifier(identifier, force) {
return QuoteHelper.quoteIdentifier(this.dialect, identifier, {
force,
quoteIdentifiers: this.options.quoteIdentifiers
});
}
quoteIdentifiers(identifiers) { quoteIdentifiers(identifiers) {
if (identifiers.indexOf('.') !== -1) { if (identifiers.indexOf('.') !== -1) {
identifiers = identifiers.split('.'); identifiers = identifiers.split('.');
return this.quoteIdentifier(identifiers.slice(0, identifiers.length - 1).join('.')) + '.' + this.quoteIdentifier(identifiers[identifiers.length - 1]);
} else { const head = identifiers.slice(0, identifiers.length - 1).join('.');
const tail = identifiers[identifiers.length - 1];
return this.quoteIdentifier(head) + '.' + this.quoteIdentifier(tail);
}
return this.quoteIdentifier(identifiers); return this.quoteIdentifier(identifiers);
} }
},
/**
* Quote table name with optional alias and schema attribution
*
* @param {String|Object} param table string or object
* @param {String|Boolean} alias alias name
*
* @returns {String}
*/
quoteTable(param, alias) {
let table = '';
if (alias === true) {
alias = param.as || param.name || param;
}
if (_.isObject(param)) {
if (this._dialect.supports.schemas) {
if (param.schema) {
table += this.quoteIdentifier(param.schema) + '.';
}
table += this.quoteIdentifier(param.tableName);
} else {
if (param.schema) {
table += param.schema + (param.delimiter || '.');
}
table += param.tableName;
table = this.quoteIdentifier(table);
}
} else {
table = this.quoteIdentifier(param);
}
if (alias) {
table += ' AS ' + this.quoteIdentifier(alias);
}
return table;
}
/* /*
Escape a value (e.g. a string, number or date) Escape a value (e.g. a string, number or date)
...@@ -932,7 +987,62 @@ const QueryGenerator = { ...@@ -932,7 +987,62 @@ const QueryGenerator = {
} }
return SqlString.escape(value, this.options.timezone, this.dialect); return SqlString.escape(value, this.options.timezone, this.dialect);
}, }
isIdentifierQuoted(identifier) {
return QuoteHelper.isIdentifierQuoted(identifier);
}
/**
* Generates an SQL query that extract JSON property of given path.
*
* @param {String} column The JSON column
* @param {String|Array<String>} [path] The path to extract (optional)
* @returns {String} The generated sql query
* @private
*/
jsonPathExtractionQuery(column, path) {
let paths = _.toPath(path);
let pathStr;
let quotedColumn;
switch (this.dialect) {
case 'mysql':
/**
* Sub paths need to be quoted as ECMAScript identifiers
* https://bugs.mysql.com/bug.php?id=81896
*/
paths = paths.map(subPath => Utils.addTicks(subPath, '"'));
pathStr = `${['$'].concat(paths).join('.')}`;
quotedColumn = this.isIdentifierQuoted(column)
? column
: this.quoteIdentifier(column);
return `(${quotedColumn}->>'${pathStr}')`;
case 'sqlite':
pathStr = this.escape(['$']
.concat(paths)
.join('.')
.replace(/\.(\d+)(?:(?=\.)|$)/g, (_, digit) => `[${digit}]`));
quotedColumn = this.isIdentifierQuoted(column)
? column
: this.quoteIdentifier(column);
return `json_extract(${quotedColumn}, ${pathStr})`;
case 'postgres':
pathStr = this.escape(`{${paths.join(',')}}`);
quotedColumn = this.isIdentifierQuoted(column)
? column
: this.quoteIdentifier(column);
return `(${quotedColumn}#>>${pathStr})`;
default:
throw new Error(`Unsupported ${this.dialect} for JSON operations`);
}
}
/* /*
Returns a query for selecting elements in the table <tableName>. Returns a query for selecting elements in the table <tableName>.
...@@ -1220,7 +1330,7 @@ const QueryGenerator = { ...@@ -1220,7 +1330,7 @@ const QueryGenerator = {
} }
return `${query};`; return `${query};`;
}, }
escapeAttributes(attributes, options, mainTableAs) { escapeAttributes(attributes, options, mainTableAs) {
return attributes && attributes.map(attr => { return attributes && attributes.map(attr => {
...@@ -1255,7 +1365,7 @@ const QueryGenerator = { ...@@ -1255,7 +1365,7 @@ const QueryGenerator = {
return attr; return attr;
}); });
}, }
generateInclude(include, parentTableName, topLevelInfo) { generateInclude(include, parentTableName, topLevelInfo) {
const joinQueries = { const joinQueries = {
...@@ -1400,7 +1510,7 @@ const QueryGenerator = { ...@@ -1400,7 +1510,7 @@ const QueryGenerator = {
subQuery: joinQueries.subQuery.join(''), subQuery: joinQueries.subQuery.join(''),
attributes attributes
}; };
}, }
generateJoin(include, topLevelInfo) { generateJoin(include, topLevelInfo) {
const association = include.association; const association = include.association;
...@@ -1479,7 +1589,7 @@ const QueryGenerator = { ...@@ -1479,7 +1589,7 @@ const QueryGenerator = {
subQuery: [] subQuery: []
} }
}; };
}, }
generateThroughJoin(include, includeAs, parentTableName, topLevelInfo) { generateThroughJoin(include, includeAs, parentTableName, topLevelInfo) {
const through = include.through; const through = include.through;
...@@ -1582,7 +1692,7 @@ const QueryGenerator = { ...@@ -1582,7 +1692,7 @@ const QueryGenerator = {
condition: joinCondition, condition: joinCondition,
attributes attributes
}; };
}, }
/* /*
* Generates subQueryFilter - a select nested in the where clause of the subQuery. * Generates subQueryFilter - a select nested in the where clause of the subQuery.
...@@ -1683,7 +1793,7 @@ const QueryGenerator = { ...@@ -1683,7 +1793,7 @@ const QueryGenerator = {
')', ')',
'IS NOT NULL' 'IS NOT NULL'
].join(' ')); ].join(' '));
}, }
/* /*
* For a given include hierarchy creates a copy of it where only the required includes * For a given include hierarchy creates a copy of it where only the required includes
...@@ -1699,7 +1809,7 @@ const QueryGenerator = { ...@@ -1699,7 +1809,7 @@ const QueryGenerator = {
} }
return copy; return copy;
}, }
getQueryOrders(options, model, subQuery) { getQueryOrders(options, model, subQuery) {
const mainQueryOrder = []; const mainQueryOrder = [];
...@@ -1746,7 +1856,7 @@ const QueryGenerator = { ...@@ -1746,7 +1856,7 @@ const QueryGenerator = {
} }
return {mainQueryOrder, subQueryOrder}; return {mainQueryOrder, subQueryOrder};
}, }
selectFromTableFragment(options, model, attributes, tables, mainTableAs) { selectFromTableFragment(options, model, attributes, tables, mainTableAs) {
let fragment = 'SELECT ' + attributes.join(', ') + ' FROM ' + tables; let fragment = 'SELECT ' + attributes.join(', ') + ' FROM ' + tables;
...@@ -1756,112 +1866,8 @@ const QueryGenerator = { ...@@ -1756,112 +1866,8 @@ const QueryGenerator = {
} }
return fragment; return fragment;
},
/**
* Returns a query that starts a transaction.
*
* @param {Boolean} value A boolean that states whether autocommit shall be done or not.
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
setAutocommitQuery(value, options) {
if (options.parent) {
return;
}
// no query when value is not explicitly set
if (typeof value === 'undefined' || value === null) {
return;
}
return 'SET autocommit = ' + (value ? 1 : 0) + ';';
},
/**
* Returns a query that sets the transaction isolation level.
*
* @param {String} value The isolation level.
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
setIsolationLevelQuery(value, options) {
if (options.parent) {
return;
}
return 'SET SESSION TRANSACTION ISOLATION LEVEL ' + value + ';';
},
generateTransactionId() {
return uuid.v4();
},
/**
* Returns a query that starts a transaction.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
startTransactionQuery(transaction) {
if (transaction.parent) {
// force quoting of savepoint identifiers for postgres
return 'SAVEPOINT ' + this.quoteIdentifier(transaction.name, true) + ';';
}
return 'START TRANSACTION;';
},
/**
* Returns a query that defers the constraints. Only works for postgres.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
deferConstraintsQuery() {},
setConstraintQuery() {},
setDeferredQuery() {},
setImmediateQuery() {},
/**
* Returns a query that commits a transaction.
*
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
commitTransactionQuery(transaction) {
if (transaction.parent) {
return;
} }
return 'COMMIT;';
},
/**
* Returns a query that rollbacks a transaction.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
rollbackTransactionQuery(transaction) {
if (transaction.parent) {
// force quoting of savepoint identifiers for postgres
return 'ROLLBACK TO SAVEPOINT ' + this.quoteIdentifier(transaction.name, true) + ';';
}
return 'ROLLBACK;';
},
/** /**
* Returns an SQL fragment for adding result constraints * Returns an SQL fragment for adding result constraints
* *
...@@ -1886,7 +1892,7 @@ const QueryGenerator = { ...@@ -1886,7 +1892,7 @@ const QueryGenerator = {
/* eslint-enable */ /* eslint-enable */
return fragment; return fragment;
}, }
handleSequelizeMethod(smth, tableName, factory, options, prepend) { handleSequelizeMethod(smth, tableName, factory, options, prepend) {
let result; let result;
...@@ -1958,7 +1964,7 @@ const QueryGenerator = { ...@@ -1958,7 +1964,7 @@ const QueryGenerator = {
} }
return result; return result;
}, }
whereQuery(where, options) { whereQuery(where, options) {
const query = this.whereItemsQuery(where, options); const query = this.whereItemsQuery(where, options);
...@@ -1966,7 +1972,7 @@ const QueryGenerator = { ...@@ -1966,7 +1972,7 @@ const QueryGenerator = {
return 'WHERE '+query; return 'WHERE '+query;
} }
return ''; return '';
}, }
whereItemsQuery(where, options, binding) { whereItemsQuery(where, options, binding) {
if ( if (
...@@ -1997,55 +2003,7 @@ const QueryGenerator = { ...@@ -1997,55 +2003,7 @@ const QueryGenerator = {
} }
return items.length && items.filter(item => item && item.length).join(binding) || ''; return items.length && items.filter(item => item && item.length).join(binding) || '';
},
OperatorMap: {
[Op.eq]: '=',
[Op.ne]: '!=',
[Op.gte]: '>=',
[Op.gt]: '>',
[Op.lte]: '<=',
[Op.lt]: '<',
[Op.not]: 'IS NOT',
[Op.is]: 'IS',
[Op.in]: 'IN',
[Op.notIn]: 'NOT IN',
[Op.like]: 'LIKE',
[Op.notLike]: 'NOT LIKE',
[Op.iLike]: 'ILIKE',
[Op.notILike]: 'NOT ILIKE',
[Op.regexp]: '~',
[Op.notRegexp]: '!~',
[Op.iRegexp]: '~*',
[Op.notIRegexp]: '!~*',
[Op.between]: 'BETWEEN',
[Op.notBetween]: 'NOT BETWEEN',
[Op.overlap]: '&&',
[Op.contains]: '@>',
[Op.contained]: '<@',
[Op.adjacent]: '-|-',
[Op.strictLeft]: '<<',
[Op.strictRight]: '>>',
[Op.noExtendRight]: '&<',
[Op.noExtendLeft]: '&>',
[Op.any]: 'ANY',
[Op.all]: 'ALL',
[Op.and]: ' AND ',
[Op.or]: ' OR ',
[Op.col]: 'COL',
[Op.placeholder]: '$$PLACEHOLDER$$',
[Op.raw]: 'DEPRECATED' //kept here since we still throw an explicit error if operator being used remove by v5,
},
OperatorsAliasMap: {},
setOperatorsAliases(aliases) {
if (!aliases || _.isEmpty(aliases)) {
this.OperatorsAliasMap = false;
} else {
this.OperatorsAliasMap = _.assign({}, aliases);
} }
},
whereItemQuery(key, value, options) { whereItemQuery(key, value, options) {
options = options || {}; options = options || {};
...@@ -2138,7 +2096,7 @@ const QueryGenerator = { ...@@ -2138,7 +2096,7 @@ const QueryGenerator = {
} }
return this._joinKeyValue(key, this.escape(value, field), this.OperatorMap[Op.eq], options.prefix); return this._joinKeyValue(key, this.escape(value, field), this.OperatorMap[Op.eq], options.prefix);
}, }
_findField(key, options) { _findField(key, options) {
if (options.field) { if (options.field) {
...@@ -2152,32 +2110,7 @@ const QueryGenerator = { ...@@ -2152,32 +2110,7 @@ const QueryGenerator = {
if (options.model && options.model.fieldRawAttributesMap && options.model.fieldRawAttributesMap[key]) { if (options.model && options.model.fieldRawAttributesMap && options.model.fieldRawAttributesMap[key]) {
return options.model.fieldRawAttributesMap[key]; return options.model.fieldRawAttributesMap[key];
} }
},
_replaceAliases(orig) {
const obj = {};
if (!this.OperatorsAliasMap) {
return orig;
}
Utils.getOperators(orig).forEach(op => {
const item = orig[op];
if (_.isPlainObject(item)) {
obj[op] = this._replaceAliases(item);
} else {
obj[op] = item;
}
});
_.forOwn(orig, (item, prop) => {
prop = this.OperatorsAliasMap[prop] || prop;
if (_.isPlainObject(item)) {
item = this._replaceAliases(item);
} }
obj[prop] = item;
});
return obj;
},
// OR/AND/NOT grouping logic // OR/AND/NOT grouping logic
_whereGroupBind(key, value, options) { _whereGroupBind(key, value, options) {
...@@ -2204,7 +2137,7 @@ const QueryGenerator = { ...@@ -2204,7 +2137,7 @@ const QueryGenerator = {
} }
return value ? outerBinding + '('+value+')' : undefined; return value ? outerBinding + '('+value+')' : undefined;
}, }
_whereBind(binding, key, value, options) { _whereBind(binding, key, value, options) {
if (_.isPlainObject(value)) { if (_.isPlainObject(value)) {
...@@ -2219,7 +2152,7 @@ const QueryGenerator = { ...@@ -2219,7 +2152,7 @@ const QueryGenerator = {
value = value.filter(item => item && item.length); value = value.filter(item => item && item.length);
return value.length ? '('+value.join(binding)+')' : undefined; return value.length ? '('+value.join(binding)+')' : undefined;
}, }
_whereJSON(key, value, options) { _whereJSON(key, value, options) {
const items = []; const items = [];
...@@ -2244,8 +2177,7 @@ const QueryGenerator = { ...@@ -2244,8 +2177,7 @@ const QueryGenerator = {
const result = items.join(this.OperatorMap[Op.and]); const result = items.join(this.OperatorMap[Op.and]);
return items.length > 1 ? '('+result+')' : result; return items.length > 1 ? '('+result+')' : result;
}, }
_traverseJSON(items, baseKey, prop, item, path) { _traverseJSON(items, baseKey, prop, item, path) {
let cast; let cast;
...@@ -2272,11 +2204,11 @@ const QueryGenerator = { ...@@ -2272,11 +2204,11 @@ const QueryGenerator = {
item = this._toJSONValue(item); item = this._toJSONValue(item);
items.push(this.whereItemQuery(this._castKey(pathKey, item, cast), {[Op.eq]: item})); items.push(this.whereItemQuery(this._castKey(pathKey, item, cast), {[Op.eq]: item}));
}, }
_toJSONValue(value) { _toJSONValue(value) {
return value; return value;
}, }
_castKey(key, value, cast, json) { _castKey(key, value, cast, json) {
cast = cast || this._getJsonCast(Array.isArray(value) ? value[0] : value); cast = cast || this._getJsonCast(Array.isArray(value) ? value[0] : value);
...@@ -2285,7 +2217,7 @@ const QueryGenerator = { ...@@ -2285,7 +2217,7 @@ const QueryGenerator = {
} }
return new Utils.Literal(key); return new Utils.Literal(key);
}, }
_getJsonCast(value) { _getJsonCast(value) {
if (typeof value === 'number') { if (typeof value === 'number') {
...@@ -2298,7 +2230,7 @@ const QueryGenerator = { ...@@ -2298,7 +2230,7 @@ const QueryGenerator = {
return 'boolean'; return 'boolean';
} }
return; return;
}, }
_joinKeyValue(key, value, comparator, prefix) { _joinKeyValue(key, value, comparator, prefix) {
if (!key) { if (!key) {
...@@ -2309,7 +2241,7 @@ const QueryGenerator = { ...@@ -2309,7 +2241,7 @@ const QueryGenerator = {
} }
key = this._getSafeKey(key, prefix); key = this._getSafeKey(key, prefix);
return [key, value].join(' '+comparator+' '); return [key, value].join(' '+comparator+' ');
}, }
_getSafeKey(key, prefix) { _getSafeKey(key, prefix) {
if (key instanceof Utils.SequelizeMethod) { if (key instanceof Utils.SequelizeMethod) {
...@@ -2332,7 +2264,7 @@ const QueryGenerator = { ...@@ -2332,7 +2264,7 @@ const QueryGenerator = {
} }
return this._prefixKey(this.quoteIdentifier(key), prefix); return this._prefixKey(this.quoteIdentifier(key), prefix);
}, }
_prefixKey(key, prefix) { _prefixKey(key, prefix) {
if (prefix) { if (prefix) {
...@@ -2344,7 +2276,7 @@ const QueryGenerator = { ...@@ -2344,7 +2276,7 @@ const QueryGenerator = {
} }
return key; return key;
}, }
_whereParseSingleValueObject(key, field, prop, value, options) { _whereParseSingleValueObject(key, field, prop, value, options) {
if (prop === Op.not) { if (prop === Op.not) {
...@@ -2430,7 +2362,7 @@ const QueryGenerator = { ...@@ -2430,7 +2362,7 @@ const QueryGenerator = {
} }
return this._joinKeyValue(key, this.escape(value, field, escapeOptions), comparator, options.prefix); return this._joinKeyValue(key, this.escape(value, field, escapeOptions), comparator, options.prefix);
}, }
/* /*
Takes something and transforms it into values of a where condition. Takes something and transforms it into values of a where condition.
...@@ -2499,7 +2431,7 @@ const QueryGenerator = { ...@@ -2499,7 +2431,7 @@ const QueryGenerator = {
} }
return result ? result : '1=1'; return result ? result : '1=1';
}, }
// A recursive parser for nested where conditions // A recursive parser for nested where conditions
parseConditionObject(conditions, path) { parseConditionObject(conditions, path) {
...@@ -2512,15 +2444,14 @@ const QueryGenerator = { ...@@ -2512,15 +2444,14 @@ const QueryGenerator = {
} }
return result; return result;
}, []); }, []);
}, }
isIdentifierQuoted(string) {
return /^\s*(?:([`"'])(?:(?!\1).|\1{2})*\1\.?)+\s*$/i.test(string);
},
booleanValue(value) { booleanValue(value) {
return value; return value;
} }
}; }
_.assignIn(QueryGenerator.prototype, require('./query-generator/operators'));
_.assignIn(QueryGenerator.prototype, require('./query-generator/transaction'));
module.exports = QueryGenerator; module.exports = QueryGenerator;
/**
* Quote helpers implement quote ability for all dialects.
* These are basic block of query building
*
* Its better to implement all dialect implementation together here. Which will allow
* even abstract generator to use them by just specifying dialect type.
*
* Defining these helpers in each query dialect will leave
* code in dual dependency of abstract <-> specific dialect
*/
'use strict';
const Utils = require('../../../../utils');
/**
*
* @param {String} dialect Dialect name
* @param {String} identifier Identifier to quote
* @param {Object} [options]
* @param {Boolean} [options.force=false]
* @param {Boolean} [options.quoteIdentifiers=true]
*
* @returns {String}
*/
function quoteIdentifier(dialect, identifier, options) {
if (identifier === '*') return identifier;
options = Utils.defaults(options || {}, {
force: false,
quoteIdentifiers: true
});
switch (dialect) {
case 'sqlite':
return Utils.addTicks(Utils.removeTicks(identifier, '`'), '`');
case 'mysql':
return Utils.addTicks(Utils.removeTicks(identifier, '`'), '`');
case 'postgres':
if (
options.force !== true &&
options.quoteIdentifiers === false &&
identifier.indexOf('.') === -1 &&
identifier.indexOf('->') === -1
) {
// In Postgres, if tables or attributes are created double-quoted,
// they are also case sensitive. If they contain any uppercase
// characters, they must always be double-quoted. This makes it
// impossible to write queries in portable SQL if tables are created in
// this way. Hence, we strip quotes if we don't want case sensitivity.
return Utils.removeTicks(identifier, '"');
} else {
return Utils.addTicks(Utils.removeTicks(identifier, '"'), '"');
}
case 'mssql':
return '[' + identifier.replace(/[\[\]']+/g, '') + ']';
default:
throw new Error(`Dialect "${dialect}" is not supported`);
}
}
module.exports.quoteIdentifier = quoteIdentifier;
/**
* Test if a give string is already quoted
*
* @param {String} identifier
*
* @return Boolean
*/
function isIdentifierQuoted(identifier) {
return /^\s*(?:([`"'])(?:(?!\1).|\1{2})*\1\.?)+\s*$/i.test(identifier);
}
module.exports.isIdentifierQuoted = isIdentifierQuoted;
\ No newline at end of file
'use strict';
const _ = require('lodash');
const Op = require('../../../operators');
const Utils = require('../../../utils');
const OperatorHelpers = {
OperatorMap: {
[Op.eq]: '=',
[Op.ne]: '!=',
[Op.gte]: '>=',
[Op.gt]: '>',
[Op.lte]: '<=',
[Op.lt]: '<',
[Op.not]: 'IS NOT',
[Op.is]: 'IS',
[Op.in]: 'IN',
[Op.notIn]: 'NOT IN',
[Op.like]: 'LIKE',
[Op.notLike]: 'NOT LIKE',
[Op.iLike]: 'ILIKE',
[Op.notILike]: 'NOT ILIKE',
[Op.regexp]: '~',
[Op.notRegexp]: '!~',
[Op.iRegexp]: '~*',
[Op.notIRegexp]: '!~*',
[Op.between]: 'BETWEEN',
[Op.notBetween]: 'NOT BETWEEN',
[Op.overlap]: '&&',
[Op.contains]: '@>',
[Op.contained]: '<@',
[Op.adjacent]: '-|-',
[Op.strictLeft]: '<<',
[Op.strictRight]: '>>',
[Op.noExtendRight]: '&<',
[Op.noExtendLeft]: '&>',
[Op.any]: 'ANY',
[Op.all]: 'ALL',
[Op.and]: ' AND ',
[Op.or]: ' OR ',
[Op.col]: 'COL',
[Op.placeholder]: '$$PLACEHOLDER$$'
},
OperatorsAliasMap: {},
setOperatorsAliases(aliases) {
if (!aliases || _.isEmpty(aliases)) {
this.OperatorsAliasMap = false;
} else {
this.OperatorsAliasMap = _.assign({}, aliases);
}
},
_replaceAliases(orig) {
const obj = {};
if (!this.OperatorsAliasMap) {
return orig;
}
Utils.getOperators(orig).forEach(op => {
const item = orig[op];
if (_.isPlainObject(item)) {
obj[op] = this._replaceAliases(item);
} else {
obj[op] = item;
}
});
_.forOwn(orig, (item, prop) => {
prop = this.OperatorsAliasMap[prop] || prop;
if (_.isPlainObject(item)) {
item = this._replaceAliases(item);
}
obj[prop] = item;
});
return obj;
}
};
module.exports = OperatorHelpers;
\ No newline at end of file
'use strict';
const uuid = require('uuid');
const TransactionQueries = {
/**
* Returns a query that starts a transaction.
*
* @param {Boolean} value A boolean that states whether autocommit shall be done or not.
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
setAutocommitQuery(value, options) {
if (options.parent) {
return;
}
// no query when value is not explicitly set
if (typeof value === 'undefined' || value === null) {
return;
}
return `SET autocommit = ${(value ? 1 : 0)};`;
},
/**
* Returns a query that sets the transaction isolation level.
*
* @param {String} value The isolation level.
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
setIsolationLevelQuery(value, options) {
if (options.parent) {
return;
}
return `SET SESSION TRANSACTION ISOLATION LEVEL ${value};`;
},
generateTransactionId() {
return uuid.v4();
},
/**
* Returns a query that starts a transaction.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
startTransactionQuery(transaction) {
if (transaction.parent) {
// force quoting of savepoint identifiers for postgres
return `SAVEPOINT ${this.quoteIdentifier(transaction.name, true)};`;
}
return 'START TRANSACTION;';
},
/**
* Returns a query that defers the constraints. Only works for postgres.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
deferConstraintsQuery() {},
setConstraintQuery() {},
setDeferredQuery() {},
setImmediateQuery() {},
/**
* Returns a query that commits a transaction.
*
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
commitTransactionQuery(transaction) {
if (transaction.parent) {
return;
}
return 'COMMIT;';
},
/**
* Returns a query that rollbacks a transaction.
*
* @param {Transaction} transaction
* @param {Object} options An object with options.
* @return {String} The generated sql query.
* @private
*/
rollbackTransactionQuery(transaction) {
if (transaction.parent) {
// force quoting of savepoint identifiers for postgres
return `ROLLBACK TO SAVEPOINT ${this.quoteIdentifier(transaction.name, true)};`;
}
return 'ROLLBACK;';
}
};
module.exports = TransactionQueries;
\ No newline at end of file
...@@ -12,8 +12,7 @@ class MssqlDialect extends AbstractDialect { ...@@ -12,8 +12,7 @@ class MssqlDialect extends AbstractDialect {
super(); super();
this.sequelize = sequelize; this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize); this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, { this.QueryGenerator = new QueryGenerator({
options: sequelize.options,
_dialect: this, _dialect: this,
sequelize sequelize
}); });
......
...@@ -7,7 +7,6 @@ const TableHints = require('../../table-hints'); ...@@ -7,7 +7,6 @@ const TableHints = require('../../table-hints');
const AbstractQueryGenerator = require('../abstract/query-generator'); const AbstractQueryGenerator = require('../abstract/query-generator');
const randomBytes = require('crypto').randomBytes; const randomBytes = require('crypto').randomBytes;
const semver = require('semver'); const semver = require('semver');
const Op = require('../../operators'); const Op = require('../../operators');
/* istanbul ignore next */ /* istanbul ignore next */
...@@ -15,11 +14,7 @@ const throwMethodUndefined = function(methodName) { ...@@ -15,11 +14,7 @@ const throwMethodUndefined = function(methodName) {
throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.'); throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.');
}; };
const QueryGenerator = { class MSSQLQueryGenerator extends AbstractQueryGenerator {
__proto__: AbstractQueryGenerator,
options: {},
dialect: 'mssql',
createSchema(schema) { createSchema(schema) {
return [ return [
'IF NOT EXISTS (SELECT schema_name', 'IF NOT EXISTS (SELECT schema_name',
...@@ -31,7 +26,7 @@ const QueryGenerator = { ...@@ -31,7 +26,7 @@ const QueryGenerator = {
";'", ";'",
'END;' 'END;'
].join(' '); ].join(' ');
}, }
dropSchema(schema) { dropSchema(schema) {
// Mimics Postgres CASCADE, will drop objects belonging to the schema // Mimics Postgres CASCADE, will drop objects belonging to the schema
...@@ -66,7 +61,7 @@ const QueryGenerator = { ...@@ -66,7 +61,7 @@ const QueryGenerator = {
"EXEC sp_executesql N'DROP SCHEMA", this.quoteIdentifier(schema), ";'", "EXEC sp_executesql N'DROP SCHEMA", this.quoteIdentifier(schema), ";'",
'END;' 'END;'
].join(' '); ].join(' ');
}, }
showSchemasQuery() { showSchemasQuery() {
return [ return [
...@@ -75,7 +70,7 @@ const QueryGenerator = { ...@@ -75,7 +70,7 @@ const QueryGenerator = {
"'INFORMATION_SCHEMA', 'dbo', 'guest', 'sys', 'archive'", "'INFORMATION_SCHEMA', 'dbo', 'guest', 'sys', 'archive'",
')', 'AND', '"s"."name" NOT LIKE', "'db_%'" ')', 'AND', '"s"."name" NOT LIKE', "'db_%'"
].join(' '); ].join(' ');
}, }
versionQuery() { versionQuery() {
// Uses string manipulation to convert the MS Maj.Min.Patch.Build to semver Maj.Min.Patch // Uses string manipulation to convert the MS Maj.Min.Patch.Build to semver Maj.Min.Patch
...@@ -84,7 +79,7 @@ const QueryGenerator = { ...@@ -84,7 +79,7 @@ const QueryGenerator = {
"SET @ms_ver = REVERSE(CONVERT(NVARCHAR(20), SERVERPROPERTY('ProductVersion')));", "SET @ms_ver = REVERSE(CONVERT(NVARCHAR(20), SERVERPROPERTY('ProductVersion')));",
"SELECT REVERSE(SUBSTRING(@ms_ver, CHARINDEX('.', @ms_ver)+1, 20)) AS 'version'" "SELECT REVERSE(SUBSTRING(@ms_ver, CHARINDEX('.', @ms_ver)+1, 20)) AS 'version'"
].join(' '); ].join(' ');
}, }
createTableQuery(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
const query = "IF OBJECT_ID('<%= table %>', 'U') IS NULL CREATE TABLE <%= table %> (<%= attributes %>)", const query = "IF OBJECT_ID('<%= table %>', 'U') IS NULL CREATE TABLE <%= table %> (<%= attributes %>)",
...@@ -147,7 +142,7 @@ const QueryGenerator = { ...@@ -147,7 +142,7 @@ const QueryGenerator = {
} }
return _.template(query, this._templateSettings)(values).trim() + ';'; return _.template(query, this._templateSettings)(values).trim() + ';';
}, }
describeTableQuery(tableName, schema) { describeTableQuery(tableName, schema) {
let sql = [ let sql = [
...@@ -181,7 +176,7 @@ const QueryGenerator = { ...@@ -181,7 +176,7 @@ const QueryGenerator = {
} }
return sql; return sql;
}, }
renameTableQuery(before, after) { renameTableQuery(before, after) {
const query = 'EXEC sp_rename <%= before %>, <%= after %>;'; const query = 'EXEC sp_rename <%= before %>, <%= after %>;';
...@@ -189,11 +184,11 @@ const QueryGenerator = { ...@@ -189,11 +184,11 @@ const QueryGenerator = {
before: this.quoteTable(before), before: this.quoteTable(before),
after: this.quoteTable(after) after: this.quoteTable(after)
}); });
}, }
showTablesQuery() { showTablesQuery() {
return 'SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES;'; return 'SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES;';
}, }
dropTableQuery(tableName) { dropTableQuery(tableName) {
const query = "IF OBJECT_ID('<%= table %>', 'U') IS NOT NULL DROP TABLE <%= table %>"; const query = "IF OBJECT_ID('<%= table %>', 'U') IS NOT NULL DROP TABLE <%= table %>";
...@@ -202,7 +197,7 @@ const QueryGenerator = { ...@@ -202,7 +197,7 @@ const QueryGenerator = {
}; };
return _.template(query, this._templateSettings)(values).trim() + ';'; return _.template(query, this._templateSettings)(values).trim() + ';';
}, }
addColumnQuery(table, key, dataType) { addColumnQuery(table, key, dataType) {
// FIXME: attributeToSQL SHOULD be using attributes in addColumnQuery // FIXME: attributeToSQL SHOULD be using attributes in addColumnQuery
...@@ -221,7 +216,7 @@ const QueryGenerator = { ...@@ -221,7 +216,7 @@ const QueryGenerator = {
table: this.quoteTable(table), table: this.quoteTable(table),
attribute attribute
}); });
}, }
removeColumnQuery(tableName, attributeName) { removeColumnQuery(tableName, attributeName) {
const query = 'ALTER TABLE <%= tableName %> DROP COLUMN <%= attributeName %>;'; const query = 'ALTER TABLE <%= tableName %> DROP COLUMN <%= attributeName %>;';
...@@ -229,7 +224,7 @@ const QueryGenerator = { ...@@ -229,7 +224,7 @@ const QueryGenerator = {
tableName: this.quoteTable(tableName), tableName: this.quoteTable(tableName),
attributeName: this.quoteIdentifier(attributeName) attributeName: this.quoteIdentifier(attributeName)
}); });
}, }
changeColumnQuery(tableName, attributes) { changeColumnQuery(tableName, attributes) {
const query = 'ALTER TABLE <%= tableName %> <%= query %>;'; const query = 'ALTER TABLE <%= tableName %> <%= query %>;';
...@@ -265,7 +260,7 @@ const QueryGenerator = { ...@@ -265,7 +260,7 @@ const QueryGenerator = {
tableName: this.quoteTable(tableName), tableName: this.quoteTable(tableName),
query: finalQuery query: finalQuery
}); });
}, }
renameColumnQuery(tableName, attrBefore, attributes) { renameColumnQuery(tableName, attrBefore, attributes) {
const query = "EXEC sp_rename '<%= tableName %>.<%= before %>', '<%= after %>', 'COLUMN';", const query = "EXEC sp_rename '<%= tableName %>.<%= before %>', '<%= after %>', 'COLUMN';",
...@@ -276,7 +271,7 @@ const QueryGenerator = { ...@@ -276,7 +271,7 @@ const QueryGenerator = {
before: attrBefore, before: attrBefore,
after: newName after: newName
}); });
}, }
bulkInsertQuery(tableName, attrValueHashes, options, attributes) { bulkInsertQuery(tableName, attrValueHashes, options, attributes) {
options = options || {}; options = options || {};
...@@ -352,7 +347,7 @@ const QueryGenerator = { ...@@ -352,7 +347,7 @@ const QueryGenerator = {
offset += batch; offset += batch;
} }
return commands.join(';'); return commands.join(';');
}, }
updateQuery(tableName, attrValueHash, where, options, attributes) { updateQuery(tableName, attrValueHash, where, options, attributes) {
let sql = super.updateQuery(tableName, attrValueHash, where, options, attributes); let sql = super.updateQuery(tableName, attrValueHash, where, options, attributes);
...@@ -361,7 +356,7 @@ const QueryGenerator = { ...@@ -361,7 +356,7 @@ const QueryGenerator = {
sql = sql.replace('UPDATE', updateArgs); sql = sql.replace('UPDATE', updateArgs);
} }
return sql; return sql;
}, }
upsertQuery(tableName, insertValues, updateValues, where, model) { upsertQuery(tableName, insertValues, updateValues, where, model) {
const targetTableAlias = this.quoteTable(`${tableName}_target`); const targetTableAlias = this.quoteTable(`${tableName}_target`);
...@@ -478,7 +473,7 @@ const QueryGenerator = { ...@@ -478,7 +473,7 @@ const QueryGenerator = {
query = `SET IDENTITY_INSERT ${tableNameQuoted} ON; ${query} SET IDENTITY_INSERT ${tableNameQuoted} OFF;`; query = `SET IDENTITY_INSERT ${tableNameQuoted} ON; ${query} SET IDENTITY_INSERT ${tableNameQuoted} OFF;`;
} }
return query; return query;
}, }
deleteQuery(tableName, where, options) { deleteQuery(tableName, where, options) {
options = options || {}; options = options || {};
...@@ -513,18 +508,18 @@ const QueryGenerator = { ...@@ -513,18 +508,18 @@ const QueryGenerator = {
} }
return _.template(query, this._templateSettings)(replacements); return _.template(query, this._templateSettings)(replacements);
}, }
showIndexesQuery(tableName) { showIndexesQuery(tableName) {
const sql = "EXEC sys.sp_helpindex @objname = N'<%= tableName %>';"; const sql = "EXEC sys.sp_helpindex @objname = N'<%= tableName %>';";
return _.template(sql, this._templateSettings)({ return _.template(sql, this._templateSettings)({
tableName: this.quoteTable(tableName) tableName: this.quoteTable(tableName)
}); });
}, }
showConstraintsQuery(tableName) { showConstraintsQuery(tableName) {
return `EXEC sp_helpconstraint @objname = ${this.escape(this.quoteTable(tableName))};`; return `EXEC sp_helpconstraint @objname = ${this.escape(this.quoteTable(tableName))};`;
}, }
removeIndexQuery(tableName, indexNameOrAttributes) { removeIndexQuery(tableName, indexNameOrAttributes) {
const sql = 'DROP INDEX <%= indexName %> ON <%= tableName %>'; const sql = 'DROP INDEX <%= indexName %> ON <%= tableName %>';
...@@ -540,7 +535,7 @@ const QueryGenerator = { ...@@ -540,7 +535,7 @@ const QueryGenerator = {
}; };
return _.template(sql, this._templateSettings)(values); return _.template(sql, this._templateSettings)(values);
}, }
attributeToSQL(attribute) { attributeToSQL(attribute) {
if (!_.isPlainObject(attribute)) { if (!_.isPlainObject(attribute)) {
...@@ -618,7 +613,7 @@ const QueryGenerator = { ...@@ -618,7 +613,7 @@ const QueryGenerator = {
} }
return template; return template;
}, }
attributesToSQL(attributes, options) { attributesToSQL(attributes, options) {
const result = {}, const result = {},
...@@ -651,36 +646,31 @@ const QueryGenerator = { ...@@ -651,36 +646,31 @@ const QueryGenerator = {
} }
return result; return result;
}, }
createTrigger() { createTrigger() {
throwMethodUndefined('createTrigger'); throwMethodUndefined('createTrigger');
}, }
dropTrigger() { dropTrigger() {
throwMethodUndefined('dropTrigger'); throwMethodUndefined('dropTrigger');
}, }
renameTrigger() { renameTrigger() {
throwMethodUndefined('renameTrigger'); throwMethodUndefined('renameTrigger');
}, }
createFunction() { createFunction() {
throwMethodUndefined('createFunction'); throwMethodUndefined('createFunction');
}, }
dropFunction() { dropFunction() {
throwMethodUndefined('dropFunction'); throwMethodUndefined('dropFunction');
}, }
renameFunction() { renameFunction() {
throwMethodUndefined('renameFunction'); throwMethodUndefined('renameFunction');
}, }
quoteIdentifier(identifier) {
if (identifier === '*') return identifier;
return '[' + identifier.replace(/[\[\]']+/g, '') + ']';
},
/** /**
* Generate common SQL prefix for ForeignKeysQuery. * Generate common SQL prefix for ForeignKeysQuery.
...@@ -706,7 +696,7 @@ const QueryGenerator = { ...@@ -706,7 +696,7 @@ const QueryGenerator = {
'INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID ' + 'INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID ' +
'INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID ' + 'INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID ' +
'INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID'; 'INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID';
}, }
/** /**
* Generates an SQL query that returns all foreign keys details of a table. * Generates an SQL query that returns all foreign keys details of a table.
...@@ -723,7 +713,7 @@ const QueryGenerator = { ...@@ -723,7 +713,7 @@ const QueryGenerator = {
sql += ' AND SCHEMA_NAME(TB.SCHEMA_ID) =' + wrapSingleQuote(table.schema); sql += ' AND SCHEMA_NAME(TB.SCHEMA_ID) =' + wrapSingleQuote(table.schema);
} }
return sql; return sql;
}, }
getForeignKeyQuery(table, attributeName) { getForeignKeyQuery(table, attributeName) {
const tableName = table.tableName || table; const tableName = table.tableName || table;
...@@ -736,7 +726,7 @@ const QueryGenerator = { ...@@ -736,7 +726,7 @@ const QueryGenerator = {
} }
return sql; return sql;
}, }
getPrimaryKeyConstraintQuery(table, attributeName) { getPrimaryKeyConstraintQuery(table, attributeName) {
const tableName = wrapSingleQuote(table.tableName || table); const tableName = wrapSingleQuote(table.tableName || table);
...@@ -754,14 +744,14 @@ const QueryGenerator = { ...@@ -754,14 +744,14 @@ const QueryGenerator = {
`AND K.COLUMN_NAME = ${wrapSingleQuote(attributeName)}`, `AND K.COLUMN_NAME = ${wrapSingleQuote(attributeName)}`,
`AND K.TABLE_NAME = ${tableName};` `AND K.TABLE_NAME = ${tableName};`
].join(' '); ].join(' ');
}, }
dropForeignKeyQuery(tableName, foreignKey) { dropForeignKeyQuery(tableName, foreignKey) {
return _.template('ALTER TABLE <%= table %> DROP <%= key %>', this._templateSettings)({ return _.template('ALTER TABLE <%= table %> DROP <%= key %>', this._templateSettings)({
table: this.quoteTable(tableName), table: this.quoteTable(tableName),
key: this.quoteIdentifier(foreignKey) key: this.quoteIdentifier(foreignKey)
}); });
}, }
getDefaultConstraintQuery(tableName, attributeName) { getDefaultConstraintQuery(tableName, attributeName) {
const sql = 'SELECT name FROM SYS.DEFAULT_CONSTRAINTS ' + const sql = 'SELECT name FROM SYS.DEFAULT_CONSTRAINTS ' +
...@@ -772,7 +762,7 @@ const QueryGenerator = { ...@@ -772,7 +762,7 @@ const QueryGenerator = {
table: this.quoteTable(tableName), table: this.quoteTable(tableName),
column: attributeName column: attributeName
}); });
}, }
dropConstraintQuery(tableName, constraintName) { dropConstraintQuery(tableName, constraintName) {
const sql = 'ALTER TABLE <%= table %> DROP CONSTRAINT <%= constraint %>;'; const sql = 'ALTER TABLE <%= table %> DROP CONSTRAINT <%= constraint %>;';
...@@ -780,19 +770,19 @@ const QueryGenerator = { ...@@ -780,19 +770,19 @@ const QueryGenerator = {
table: this.quoteTable(tableName), table: this.quoteTable(tableName),
constraint: this.quoteIdentifier(constraintName) constraint: this.quoteIdentifier(constraintName)
}); });
}, }
setAutocommitQuery() { setAutocommitQuery() {
return ''; return '';
}, }
setIsolationLevelQuery() { setIsolationLevelQuery() {
}, }
generateTransactionId() { generateTransactionId() {
return randomBytes(10).toString('hex'); return randomBytes(10).toString('hex');
}, }
startTransactionQuery(transaction) { startTransactionQuery(transaction) {
if (transaction.parent) { if (transaction.parent) {
...@@ -800,7 +790,7 @@ const QueryGenerator = { ...@@ -800,7 +790,7 @@ const QueryGenerator = {
} }
return 'BEGIN TRANSACTION;'; return 'BEGIN TRANSACTION;';
}, }
commitTransactionQuery(transaction) { commitTransactionQuery(transaction) {
if (transaction.parent) { if (transaction.parent) {
...@@ -808,7 +798,7 @@ const QueryGenerator = { ...@@ -808,7 +798,7 @@ const QueryGenerator = {
} }
return 'COMMIT TRANSACTION;'; return 'COMMIT TRANSACTION;';
}, }
rollbackTransactionQuery(transaction) { rollbackTransactionQuery(transaction) {
if (transaction.parent) { if (transaction.parent) {
...@@ -816,7 +806,7 @@ const QueryGenerator = { ...@@ -816,7 +806,7 @@ const QueryGenerator = {
} }
return 'ROLLBACK TRANSACTION;'; return 'ROLLBACK TRANSACTION;';
}, }
selectFromTableFragment(options, model, attributes, tables, mainTableAs, where) { selectFromTableFragment(options, model, attributes, tables, mainTableAs, where) {
let topFragment = ''; let topFragment = '';
...@@ -874,7 +864,7 @@ const QueryGenerator = { ...@@ -874,7 +864,7 @@ const QueryGenerator = {
} }
return mainFragment; return mainFragment;
}, }
addLimitAndOffset(options, model) { addLimitAndOffset(options, model) {
// Skip handling of limit and offset as postfixes for older SQL Server versions // Skip handling of limit and offset as postfixes for older SQL Server versions
...@@ -907,7 +897,7 @@ const QueryGenerator = { ...@@ -907,7 +897,7 @@ const QueryGenerator = {
} }
return fragment; return fragment;
}, }
booleanValue(value) { booleanValue(value) {
return value ? 1 : 0; return value ? 1 : 0;
...@@ -919,4 +909,4 @@ function wrapSingleQuote(identifier) { ...@@ -919,4 +909,4 @@ function wrapSingleQuote(identifier) {
return Utils.addTicks(Utils.removeTicks(identifier, "'"), "'"); return Utils.addTicks(Utils.removeTicks(identifier, "'"), "'");
} }
module.exports = QueryGenerator; module.exports = MSSQLQueryGenerator;
\ No newline at end of file
...@@ -12,8 +12,7 @@ class MysqlDialect extends AbstractDialect { ...@@ -12,8 +12,7 @@ class MysqlDialect extends AbstractDialect {
super(); super();
this.sequelize = sequelize; this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize); this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, { this.QueryGenerator = new QueryGenerator({
options: sequelize.options,
_dialect: this, _dialect: this,
sequelize sequelize
}); });
......
...@@ -6,26 +6,27 @@ const AbstractQueryGenerator = require('../abstract/query-generator'); ...@@ -6,26 +6,27 @@ const AbstractQueryGenerator = require('../abstract/query-generator');
const util = require('util'); const util = require('util');
const Op = require('../../operators'); const Op = require('../../operators');
const QueryGenerator = { class MySQLQueryGenerator extends AbstractQueryGenerator {
__proto__: AbstractQueryGenerator, constructor(options) {
dialect: 'mysql', super(options);
OperatorMap: Object.assign({}, AbstractQueryGenerator.OperatorMap, { this.OperatorMap = Object.assign({}, this.OperatorMap, {
[Op.regexp]: 'REGEXP', [Op.regexp]: 'REGEXP',
[Op.notRegexp]: 'NOT REGEXP' [Op.notRegexp]: 'NOT REGEXP'
}), });
}
createSchema() { createSchema() {
return 'SHOW TABLES'; return 'SHOW TABLES';
}, }
showSchemasQuery() { showSchemasQuery() {
return 'SHOW TABLES'; return 'SHOW TABLES';
}, }
versionQuery() { versionQuery() {
return 'SELECT VERSION() as `version`'; return 'SELECT VERSION() as `version`';
}, }
createTableQuery(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
options = _.extend({ options = _.extend({
...@@ -100,11 +101,11 @@ const QueryGenerator = { ...@@ -100,11 +101,11 @@ const QueryGenerator = {
} }
return _.template(query, this._templateSettings)(values).trim() + ';'; return _.template(query, this._templateSettings)(values).trim() + ';';
}, }
showTablesQuery() { showTablesQuery() {
return 'SHOW TABLES;'; return 'SHOW TABLES;';
}, }
addColumnQuery(table, key, dataType) { addColumnQuery(table, key, dataType) {
const definition = this.attributeToSQL(dataType, { const definition = this.attributeToSQL(dataType, {
...@@ -114,11 +115,11 @@ const QueryGenerator = { ...@@ -114,11 +115,11 @@ const QueryGenerator = {
}); });
return `ALTER TABLE ${this.quoteTable(table)} ADD ${this.quoteIdentifier(key)} ${definition};`; return `ALTER TABLE ${this.quoteTable(table)} ADD ${this.quoteIdentifier(key)} ${definition};`;
}, }
removeColumnQuery(tableName, attributeName) { removeColumnQuery(tableName, attributeName) {
return `ALTER TABLE ${this.quoteTable(tableName)} DROP ${this.quoteIdentifier(attributeName)};`; return `ALTER TABLE ${this.quoteTable(tableName)} DROP ${this.quoteIdentifier(attributeName)};`;
}, }
changeColumnQuery(tableName, attributes) { changeColumnQuery(tableName, attributes) {
const attrString = []; const attrString = [];
...@@ -146,7 +147,7 @@ const QueryGenerator = { ...@@ -146,7 +147,7 @@ const QueryGenerator = {
} }
return `ALTER TABLE ${this.quoteTable(tableName)} ${finalQuery};`; return `ALTER TABLE ${this.quoteTable(tableName)} ${finalQuery};`;
}, }
renameColumnQuery(tableName, attrBefore, attributes) { renameColumnQuery(tableName, attrBefore, attributes) {
const attrString = []; const attrString = [];
...@@ -157,7 +158,7 @@ const QueryGenerator = { ...@@ -157,7 +158,7 @@ const QueryGenerator = {
} }
return `ALTER TABLE ${this.quoteTable(tableName)} CHANGE ${attrString.join(', ')};`; return `ALTER TABLE ${this.quoteTable(tableName)} CHANGE ${attrString.join(', ')};`;
}, }
handleSequelizeMethod(smth, tableName, factory, options, prepend) { handleSequelizeMethod(smth, tableName, factory, options, prepend) {
if (smth instanceof Utils.Json) { if (smth instanceof Utils.Json) {
...@@ -218,7 +219,7 @@ const QueryGenerator = { ...@@ -218,7 +219,7 @@ const QueryGenerator = {
} }
return super.handleSequelizeMethod(smth, tableName, factory, options, prepend); return super.handleSequelizeMethod(smth, tableName, factory, options, prepend);
}, }
_toJSONValue(value) { _toJSONValue(value) {
// true/false are stored as strings in mysql // true/false are stored as strings in mysql
...@@ -230,7 +231,7 @@ const QueryGenerator = { ...@@ -230,7 +231,7 @@ const QueryGenerator = {
return 'null'; return 'null';
} }
return value; return value;
}, }
upsertQuery(tableName, insertValues, updateValues, where, model, options) { upsertQuery(tableName, insertValues, updateValues, where, model, options) {
options.onDuplicate = 'UPDATE '; options.onDuplicate = 'UPDATE ';
...@@ -241,7 +242,7 @@ const QueryGenerator = { ...@@ -241,7 +242,7 @@ const QueryGenerator = {
}).join(', '); }).join(', ');
return this.insertQuery(tableName, insertValues, model.rawAttributes, options); return this.insertQuery(tableName, insertValues, model.rawAttributes, options);
}, }
deleteQuery(tableName, where, options, model) { deleteQuery(tableName, where, options, model) {
options = options || {}; options = options || {};
...@@ -268,11 +269,11 @@ const QueryGenerator = { ...@@ -268,11 +269,11 @@ const QueryGenerator = {
query += limit; query += limit;
return query; return query;
}, }
showIndexesQuery(tableName, options) { showIndexesQuery(tableName, options) {
return 'SHOW INDEX FROM ' + this.quoteTable(tableName) + ((options || {}).database ? ' FROM `' + options.database + '`' : ''); return 'SHOW INDEX FROM ' + this.quoteTable(tableName) + ((options || {}).database ? ' FROM `' + options.database + '`' : '');
}, }
showConstraintsQuery(table, constraintName) { showConstraintsQuery(table, constraintName) {
const tableName = table.tableName || table; const tableName = table.tableName || table;
...@@ -298,7 +299,7 @@ const QueryGenerator = { ...@@ -298,7 +299,7 @@ const QueryGenerator = {
} }
return sql + ';'; return sql + ';';
}, }
removeIndexQuery(tableName, indexNameOrAttributes) { removeIndexQuery(tableName, indexNameOrAttributes) {
let indexName = indexNameOrAttributes; let indexName = indexNameOrAttributes;
...@@ -308,7 +309,7 @@ const QueryGenerator = { ...@@ -308,7 +309,7 @@ const QueryGenerator = {
} }
return `DROP INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteTable(tableName)}`; return `DROP INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteTable(tableName)}`;
}, }
attributeToSQL(attribute, options) { attributeToSQL(attribute, options) {
if (!_.isPlainObject(attribute)) { if (!_.isPlainObject(attribute)) {
...@@ -375,7 +376,7 @@ const QueryGenerator = { ...@@ -375,7 +376,7 @@ const QueryGenerator = {
} }
return template; return template;
}, }
attributesToSQL(attributes, options) { attributesToSQL(attributes, options) {
const result = {}; const result = {};
...@@ -386,12 +387,7 @@ const QueryGenerator = { ...@@ -386,12 +387,7 @@ const QueryGenerator = {
} }
return result; return result;
}, }
quoteIdentifier(identifier) {
if (identifier === '*') return identifier;
return Utils.addTicks(Utils.removeTicks(identifier, '`'), '`');
},
/** /**
* Check whether the statmement is json function or simple path * Check whether the statmement is json function or simple path
...@@ -458,27 +454,7 @@ const QueryGenerator = { ...@@ -458,27 +454,7 @@ const QueryGenerator = {
// return true if the statement has valid json function // return true if the statement has valid json function
return hasJsonFunction; return hasJsonFunction;
}, }
/**
* Generates an SQL query that extract JSON property of given path.
*
* @param {String} column The JSON column
* @param {String|Array<String>} [path] The path to extract (optional)
* @returns {String} The generated sql query
* @private
*/
jsonPathExtractionQuery(column, path) {
/**
* Sub paths need to be quoted as ECMAScript identifiers
*
* https://bugs.mysql.com/bug.php?id=81896
*/
const paths = _.toPath(path).map(subPath => Utils.addTicks(subPath, '"'));
const pathStr = `${['$'].concat(paths).join('.')}`;
const quotedColumn = this.isIdentifierQuoted(column) ? column : this.quoteIdentifier(column);
return `(${quotedColumn}->>'${pathStr}')`;
},
/** /**
* Generates fields for getForeignKeysQuery * Generates fields for getForeignKeysQuery
...@@ -500,7 +476,7 @@ const QueryGenerator = { ...@@ -500,7 +476,7 @@ const QueryGenerator = {
'REFERENCED_TABLE_NAME as referencedTableName', 'REFERENCED_TABLE_NAME as referencedTableName',
'REFERENCED_COLUMN_NAME as referencedColumnName', 'REFERENCED_COLUMN_NAME as referencedColumnName',
].join(','); ].join(',');
}, }
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
...@@ -513,7 +489,7 @@ const QueryGenerator = { ...@@ -513,7 +489,7 @@ const QueryGenerator = {
getForeignKeysQuery(tableName, schemaName) { getForeignKeysQuery(tableName, schemaName) {
return 'SELECT ' + this._getForeignKeysQueryFields() + ' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = \'' + tableName + /* jshint ignore: line */ return 'SELECT ' + this._getForeignKeysQueryFields() + ' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = \'' + tableName + /* jshint ignore: line */
'\' AND CONSTRAINT_NAME!=\'PRIMARY\' AND CONSTRAINT_SCHEMA=\'' + schemaName + '\' AND REFERENCED_TABLE_NAME IS NOT NULL;'; /* jshint ignore: line */ '\' AND CONSTRAINT_NAME!=\'PRIMARY\' AND CONSTRAINT_SCHEMA=\'' + schemaName + '\' AND REFERENCED_TABLE_NAME IS NOT NULL;'; /* jshint ignore: line */
}, }
/** /**
* Generates an SQL query that returns the foreign key constraint of a given column. * Generates an SQL query that returns the foreign key constraint of a given column.
...@@ -537,7 +513,7 @@ const QueryGenerator = { ...@@ -537,7 +513,7 @@ const QueryGenerator = {
+ ' AND COLUMN_NAME = ' + wrapSingleQuote(columnName) + ' AND COLUMN_NAME = ' + wrapSingleQuote(columnName)
+ ' AND REFERENCED_TABLE_NAME IS NOT NULL' + ' AND REFERENCED_TABLE_NAME IS NOT NULL'
+ ')'; + ')';
}, }
/** /**
* Generates an SQL query that removes a foreign key from a table. * Generates an SQL query that removes a foreign key from a table.
...@@ -557,4 +533,4 @@ function wrapSingleQuote(identifier) { ...@@ -557,4 +533,4 @@ function wrapSingleQuote(identifier) {
return Utils.addTicks(identifier, '\''); return Utils.addTicks(identifier, '\'');
} }
module.exports = QueryGenerator; module.exports = MySQLQueryGenerator;
...@@ -12,8 +12,7 @@ class PostgresDialect extends AbstractDialect { ...@@ -12,8 +12,7 @@ class PostgresDialect extends AbstractDialect {
super(); super();
this.sequelize = sequelize; this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize); this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, { this.QueryGenerator = new QueryGenerator({
options: sequelize.options,
_dialect: this, _dialect: this,
sequelize sequelize
}); });
......
...@@ -7,14 +7,10 @@ const AbstractQueryGenerator = require('../abstract/query-generator'); ...@@ -7,14 +7,10 @@ const AbstractQueryGenerator = require('../abstract/query-generator');
const semver = require('semver'); const semver = require('semver');
const _ = require('lodash'); const _ = require('lodash');
const QueryGenerator = { class PostgresQueryGenerator extends AbstractQueryGenerator {
__proto__: AbstractQueryGenerator,
options: {},
dialect: 'postgres',
setSearchPath(searchPath) { setSearchPath(searchPath) {
return `SET search_path to ${searchPath};`; return `SET search_path to ${searchPath};`;
}, }
createSchema(schema) { createSchema(schema) {
const databaseVersion = _.get(this, 'sequelize.options.databaseVersion', 0); const databaseVersion = _.get(this, 'sequelize.options.databaseVersion', 0);
...@@ -24,19 +20,19 @@ const QueryGenerator = { ...@@ -24,19 +20,19 @@ const QueryGenerator = {
} }
return `CREATE SCHEMA ${schema};`; return `CREATE SCHEMA ${schema};`;
}, }
dropSchema(schema) { dropSchema(schema) {
return `DROP SCHEMA IF EXISTS ${schema} CASCADE;`; return `DROP SCHEMA IF EXISTS ${schema} CASCADE;`;
}, }
showSchemasQuery() { showSchemasQuery() {
return "SELECT schema_name FROM information_schema.schemata WHERE schema_name <> 'information_schema' AND schema_name != 'public' AND schema_name !~ E'^pg_';"; return "SELECT schema_name FROM information_schema.schemata WHERE schema_name <> 'information_schema' AND schema_name != 'public' AND schema_name !~ E'^pg_';";
}, }
versionQuery() { versionQuery() {
return 'SHOW SERVER_VERSION'; return 'SHOW SERVER_VERSION';
}, }
createTableQuery(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
options = _.extend({}, options || {}); options = _.extend({}, options || {});
...@@ -88,16 +84,16 @@ const QueryGenerator = { ...@@ -88,16 +84,16 @@ const QueryGenerator = {
} }
return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0') ? 'IF NOT EXISTS ' : ''}${values.table} (${values.attributes})${values.comments};`; return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0') ? 'IF NOT EXISTS ' : ''}${values.table} (${values.attributes})${values.comments};`;
}, }
dropTableQuery(tableName, options) { dropTableQuery(tableName, options) {
options = options || {}; options = options || {};
return `DROP TABLE IF EXISTS ${this.quoteTable(tableName)}${options.cascade ? ' CASCADE' : ''};`; return `DROP TABLE IF EXISTS ${this.quoteTable(tableName)}${options.cascade ? ' CASCADE' : ''};`;
}, }
showTablesQuery() { showTablesQuery() {
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type LIKE '%TABLE' AND table_name != 'spatial_ref_sys';"; return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type LIKE '%TABLE' AND table_name != 'spatial_ref_sys';";
}, }
describeTableQuery(tableName, schema) { describeTableQuery(tableName, schema) {
if (!schema) { if (!schema) {
...@@ -121,7 +117,7 @@ const QueryGenerator = { ...@@ -121,7 +117,7 @@ const QueryGenerator = {
'AND pk.table_name=c.table_name ' + 'AND pk.table_name=c.table_name ' +
'AND pk.column_name=c.column_name ' + 'AND pk.column_name=c.column_name ' +
`WHERE c.table_name = ${this.escape(tableName)} AND c.table_schema = ${this.escape(schema)} `; `WHERE c.table_name = ${this.escape(tableName)} AND c.table_schema = ${this.escape(schema)} `;
}, }
/** /**
* Check whether the statmement is json function or simple path * Check whether the statmement is json function or simple path
...@@ -188,22 +184,7 @@ const QueryGenerator = { ...@@ -188,22 +184,7 @@ const QueryGenerator = {
// return true if the statement has valid json function // return true if the statement has valid json function
return hasJsonFunction; return hasJsonFunction;
}, }
/**
* Generates an SQL query that extract JSON property of given path.
*
* @param {String} column The JSON column
* @param {String|Array<String>} [path] The path to extract (optional)
* @returns {String} The generated sql query
* @private
*/
jsonPathExtractionQuery(column, path) {
const paths = _.toPath(path);
const pathStr = this.escape(`{${paths.join(',')}}`);
const quotedColumn = this.isIdentifierQuoted(column) ? column : this.quoteIdentifier(column);
return `(${quotedColumn}#>>${pathStr})`;
},
handleSequelizeMethod(smth, tableName, factory, options, prepend) { handleSequelizeMethod(smth, tableName, factory, options, prepend) {
if (smth instanceof Utils.Json) { if (smth instanceof Utils.Json) {
...@@ -234,8 +215,8 @@ const QueryGenerator = { ...@@ -234,8 +215,8 @@ const QueryGenerator = {
return str; return str;
} }
} }
return AbstractQueryGenerator.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend); return super.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend);
}, }
addColumnQuery(table, key, dataType) { addColumnQuery(table, key, dataType) {
...@@ -251,13 +232,13 @@ const QueryGenerator = { ...@@ -251,13 +232,13 @@ const QueryGenerator = {
} }
return query; return query;
}, }
removeColumnQuery(tableName, attributeName) { removeColumnQuery(tableName, attributeName) {
const quotedTableName = this.quoteTable(this.extractTableDetails(tableName)); const quotedTableName = this.quoteTable(this.extractTableDetails(tableName));
const quotedAttributeName = this.quoteIdentifier(attributeName); const quotedAttributeName = this.quoteIdentifier(attributeName);
return `ALTER TABLE ${quotedTableName} DROP COLUMN ${quotedAttributeName};`; return `ALTER TABLE ${quotedTableName} DROP COLUMN ${quotedAttributeName};`;
}, }
changeColumnQuery(tableName, attributes) { changeColumnQuery(tableName, attributes) {
const query = 'ALTER TABLE <%= tableName %> ALTER COLUMN <%= query %>;'; const query = 'ALTER TABLE <%= tableName %> ALTER COLUMN <%= query %>;';
...@@ -327,7 +308,7 @@ const QueryGenerator = { ...@@ -327,7 +308,7 @@ const QueryGenerator = {
} }
return sql.join(''); return sql.join('');
}, }
renameColumnQuery(tableName, attrBefore, attributes) { renameColumnQuery(tableName, attrBefore, attributes) {
...@@ -341,7 +322,7 @@ const QueryGenerator = { ...@@ -341,7 +322,7 @@ const QueryGenerator = {
} }
return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${attrString.join(', ')};`; return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${attrString.join(', ')};`;
}, }
fn(fnName, tableName, parameters, body, returns, language) { fn(fnName, tableName, parameters, body, returns, language) {
fnName = fnName || 'testfunc'; fnName = fnName || 'testfunc';
...@@ -350,7 +331,7 @@ const QueryGenerator = { ...@@ -350,7 +331,7 @@ const QueryGenerator = {
parameters = parameters || ''; parameters = parameters || '';
return `CREATE OR REPLACE FUNCTION pg_temp.${fnName}(${parameters}) ${returns} AS $func$ BEGIN ${body} END; $func$ LANGUAGE ${language}; SELECT * FROM pg_temp.${fnName}();`; return `CREATE OR REPLACE FUNCTION pg_temp.${fnName}(${parameters}) ${returns} AS $func$ BEGIN ${body} END; $func$ LANGUAGE ${language}; SELECT * FROM pg_temp.${fnName}();`;
}, }
exceptionFn(fnName, tableName, parameters, main, then, when, returns, language) { exceptionFn(fnName, tableName, parameters, main, then, when, returns, language) {
when = when || 'unique_violation'; when = when || 'unique_violation';
...@@ -358,7 +339,7 @@ const QueryGenerator = { ...@@ -358,7 +339,7 @@ const QueryGenerator = {
const body = `${main} EXCEPTION WHEN ${when} THEN ${then};`; const body = `${main} EXCEPTION WHEN ${when} THEN ${then};`;
return this.fn(fnName, tableName, parameters, body, returns, language); return this.fn(fnName, tableName, parameters, body, returns, language);
}, }
upsertQuery(tableName, insertValues, updateValues, where, model, options) { upsertQuery(tableName, insertValues, updateValues, where, model, options) {
const primaryField = this.quoteIdentifier(model.primaryKeyField); const primaryField = this.quoteIdentifier(model.primaryKeyField);
...@@ -376,7 +357,7 @@ const QueryGenerator = { ...@@ -376,7 +357,7 @@ const QueryGenerator = {
`${insert} created := true;`, `${insert} created := true;`,
`${update}; created := false` `${update}; created := false`
); );
}, }
deleteQuery(tableName, where, options, model) { deleteQuery(tableName, where, options, model) {
let query; let query;
...@@ -429,7 +410,7 @@ const QueryGenerator = { ...@@ -429,7 +410,7 @@ const QueryGenerator = {
} }
return _.template(query, this._templateSettings)(replacements); return _.template(query, this._templateSettings)(replacements);
}, }
showIndexesQuery(tableName) { showIndexesQuery(tableName) {
let schemaJoin = ''; let schemaJoin = '';
...@@ -447,7 +428,7 @@ const QueryGenerator = { ...@@ -447,7 +428,7 @@ const QueryGenerator = {
'WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND ' + 'WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND ' +
`t.relkind = 'r' and t.relname = '${tableName}'${schemaWhere} ` + `t.relkind = 'r' and t.relname = '${tableName}'${schemaWhere} ` +
'GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;'; 'GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;';
}, }
showConstraintsQuery(tableName) { showConstraintsQuery(tableName) {
//Postgres converts camelCased alias to lowercase unless quoted //Postgres converts camelCased alias to lowercase unless quoted
...@@ -464,7 +445,7 @@ const QueryGenerator = { ...@@ -464,7 +445,7 @@ const QueryGenerator = {
'from INFORMATION_SCHEMA.table_constraints', 'from INFORMATION_SCHEMA.table_constraints',
`WHERE table_name='${tableName}';` `WHERE table_name='${tableName}';`
].join(' '); ].join(' ');
}, }
removeIndexQuery(tableName, indexNameOrAttributes) { removeIndexQuery(tableName, indexNameOrAttributes) {
let indexName = indexNameOrAttributes; let indexName = indexNameOrAttributes;
...@@ -474,7 +455,7 @@ const QueryGenerator = { ...@@ -474,7 +455,7 @@ const QueryGenerator = {
} }
return `DROP INDEX IF EXISTS ${this.quoteIdentifiers(indexName)}`; return `DROP INDEX IF EXISTS ${this.quoteIdentifiers(indexName)}`;
}, }
addLimitAndOffset(options) { addLimitAndOffset(options) {
let fragment = ''; let fragment = '';
...@@ -488,7 +469,7 @@ const QueryGenerator = { ...@@ -488,7 +469,7 @@ const QueryGenerator = {
/* eslint-enable */ /* eslint-enable */
return fragment; return fragment;
}, }
attributeToSQL(attribute) { attributeToSQL(attribute) {
if (!_.isPlainObject(attribute)) { if (!_.isPlainObject(attribute)) {
...@@ -573,11 +554,11 @@ const QueryGenerator = { ...@@ -573,11 +554,11 @@ const QueryGenerator = {
} }
return sql; return sql;
}, }
deferConstraintsQuery(options) { deferConstraintsQuery(options) {
return options.deferrable.toString(this); return options.deferrable.toString(this);
}, }
setConstraintQuery(columns, type) { setConstraintQuery(columns, type) {
let columnFragment = 'ALL'; let columnFragment = 'ALL';
...@@ -587,15 +568,15 @@ const QueryGenerator = { ...@@ -587,15 +568,15 @@ const QueryGenerator = {
} }
return 'SET CONSTRAINTS ' + columnFragment + ' ' + type; return 'SET CONSTRAINTS ' + columnFragment + ' ' + type;
}, }
setDeferredQuery(columns) { setDeferredQuery(columns) {
return this.setConstraintQuery(columns, 'DEFERRED'); return this.setConstraintQuery(columns, 'DEFERRED');
}, }
setImmediateQuery(columns) { setImmediateQuery(columns) {
return this.setConstraintQuery(columns, 'IMMEDIATE'); return this.setConstraintQuery(columns, 'IMMEDIATE');
}, }
attributesToSQL(attributes, options) { attributesToSQL(attributes, options) {
const result = {}; const result = {};
...@@ -606,7 +587,7 @@ const QueryGenerator = { ...@@ -606,7 +587,7 @@ const QueryGenerator = {
} }
return result; return result;
}, }
createTrigger(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) { createTrigger(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) {
...@@ -620,15 +601,15 @@ const QueryGenerator = { ...@@ -620,15 +601,15 @@ const QueryGenerator = {
+ `\tON ${tableName}\n` + `\tON ${tableName}\n`
+ `\t${expandedOptions}\n` + `\t${expandedOptions}\n`
+ `\tEXECUTE PROCEDURE ${functionName}(${paramList});`; + `\tEXECUTE PROCEDURE ${functionName}(${paramList});`;
}, }
dropTrigger(tableName, triggerName) { dropTrigger(tableName, triggerName) {
return `DROP TRIGGER ${triggerName} ON ${tableName} RESTRICT;`; return `DROP TRIGGER ${triggerName} ON ${tableName} RESTRICT;`;
}, }
renameTrigger(tableName, oldTriggerName, newTriggerName) { renameTrigger(tableName, oldTriggerName, newTriggerName) {
return `ALTER TRIGGER ${oldTriggerName} ON ${tableName} RENAME TO ${newTriggerName};`; return `ALTER TRIGGER ${oldTriggerName} ON ${tableName} RENAME TO ${newTriggerName};`;
}, }
createFunction(functionName, params, returnType, language, body, options) { createFunction(functionName, params, returnType, language, body, options) {
if (!functionName || !returnType || !language || !body) throw new Error('createFunction missing some parameters. Did you pass functionName, returnType, language and body?'); if (!functionName || !returnType || !language || !body) throw new Error('createFunction missing some parameters. Did you pass functionName, returnType, language and body?');
...@@ -643,19 +624,19 @@ const QueryGenerator = { ...@@ -643,19 +624,19 @@ const QueryGenerator = {
+ `\t${indentedBody}\n` + `\t${indentedBody}\n`
+ 'END;\n' + 'END;\n'
+ `$func$ language '${language}'${expandedOptions};`; + `$func$ language '${language}'${expandedOptions};`;
}, }
dropFunction(functionName, params) { dropFunction(functionName, params) {
if (!functionName) throw new Error('requires functionName'); if (!functionName) throw new Error('requires functionName');
// RESTRICT is (currently, as of 9.2) default but we'll be explicit // RESTRICT is (currently, as of 9.2) default but we'll be explicit
const paramList = this.expandFunctionParamList(params); const paramList = this.expandFunctionParamList(params);
return `DROP FUNCTION ${functionName}(${paramList}) RESTRICT;`; return `DROP FUNCTION ${functionName}(${paramList}) RESTRICT;`;
}, }
renameFunction(oldFunctionName, params, newFunctionName) { renameFunction(oldFunctionName, params, newFunctionName) {
const paramList = this.expandFunctionParamList(params); const paramList = this.expandFunctionParamList(params);
return `ALTER FUNCTION ${oldFunctionName}(${paramList}) RENAME TO ${newFunctionName};`; return `ALTER FUNCTION ${oldFunctionName}(${paramList}) RENAME TO ${newFunctionName};`;
}, }
databaseConnectionUri(config) { databaseConnectionUri(config) {
let uri = config.protocol + '://' + config.user + ':' + config.password + '@' + config.host; let uri = config.protocol + '://' + config.user + ':' + config.password + '@' + config.host;
...@@ -667,11 +648,11 @@ const QueryGenerator = { ...@@ -667,11 +648,11 @@ const QueryGenerator = {
uri += '?ssl=' + config.ssl; uri += '?ssl=' + config.ssl;
} }
return uri; return uri;
}, }
pgEscapeAndQuote(val) { pgEscapeAndQuote(val) {
return this.quoteIdentifier(Utils.removeTicks(this.escape(val), "'")); return this.quoteIdentifier(Utils.removeTicks(this.escape(val), "'"));
}, }
expandFunctionParamList(params) { expandFunctionParamList(params) {
if (_.isUndefined(params) || !_.isArray(params)) { if (_.isUndefined(params) || !_.isArray(params)) {
...@@ -695,12 +676,12 @@ const QueryGenerator = { ...@@ -695,12 +676,12 @@ const QueryGenerator = {
}); });
return paramList.join(', '); return paramList.join(', ');
}, }
expandOptions(options) { expandOptions(options) {
return _.isUndefined(options) || _.isEmpty(options) ? return _.isUndefined(options) || _.isEmpty(options) ?
'' : '\n\t' + options.join('\n\t'); '' : '\n\t' + options.join('\n\t');
}, }
decodeTriggerEventType(eventSpecifier) { decodeTriggerEventType(eventSpecifier) {
const EVENT_DECODER = { const EVENT_DECODER = {
...@@ -715,11 +696,11 @@ const QueryGenerator = { ...@@ -715,11 +696,11 @@ const QueryGenerator = {
} }
return EVENT_DECODER[eventSpecifier]; return EVENT_DECODER[eventSpecifier];
}, }
triggerEventTypeIsConstraint(eventSpecifier) { triggerEventTypeIsConstraint(eventSpecifier) {
return eventSpecifier === 'after_constraint' ? 'CONSTRAINT ' : ''; return eventSpecifier === 'after_constraint' ? 'CONSTRAINT ' : '';
}, }
expandTriggerEventSpec(fireOnSpec) { expandTriggerEventSpec(fireOnSpec) {
if (_.isEmpty(fireOnSpec)) { if (_.isEmpty(fireOnSpec)) {
...@@ -747,7 +728,7 @@ const QueryGenerator = { ...@@ -747,7 +728,7 @@ const QueryGenerator = {
return eventSpec; return eventSpec;
}).join(' OR '); }).join(' OR ');
}, }
pgEnumName(tableName, attr, options) { pgEnumName(tableName, attr, options) {
options = options || {}; options = options || {};
...@@ -761,7 +742,7 @@ const QueryGenerator = { ...@@ -761,7 +742,7 @@ const QueryGenerator = {
} }
return enumName; return enumName;
}, }
pgListEnums(tableName, attrName, options) { pgListEnums(tableName, attrName, options) {
let enumName = ''; let enumName = '';
...@@ -775,7 +756,7 @@ const QueryGenerator = { ...@@ -775,7 +756,7 @@ const QueryGenerator = {
'JOIN pg_enum e ON t.oid = e.enumtypid ' + 'JOIN pg_enum e ON t.oid = e.enumtypid ' +
'JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace ' + 'JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace ' +
`WHERE n.nspname = '${tableDetails.schema}'${enumName} GROUP BY 1`; `WHERE n.nspname = '${tableDetails.schema}'${enumName} GROUP BY 1`;
}, }
pgEnum(tableName, attr, dataType, options) { pgEnum(tableName, attr, dataType, options) {
const enumName = this.pgEnumName(tableName, attr, options); const enumName = this.pgEnumName(tableName, attr, options);
...@@ -792,7 +773,7 @@ const QueryGenerator = { ...@@ -792,7 +773,7 @@ const QueryGenerator = {
sql = this.pgEnumDrop(tableName, attr) + sql; sql = this.pgEnumDrop(tableName, attr) + sql;
} }
return sql; return sql;
}, }
pgEnumAdd(tableName, attr, value, options) { pgEnumAdd(tableName, attr, value, options) {
const enumName = this.pgEnumName(tableName, attr); const enumName = this.pgEnumName(tableName, attr);
...@@ -810,12 +791,12 @@ const QueryGenerator = { ...@@ -810,12 +791,12 @@ const QueryGenerator = {
} }
return sql; return sql;
}, }
pgEnumDrop(tableName, attr, enumName) { pgEnumDrop(tableName, attr, enumName) {
enumName = enumName || this.pgEnumName(tableName, attr); enumName = enumName || this.pgEnumName(tableName, attr);
return 'DROP TYPE IF EXISTS ' + enumName + '; '; return 'DROP TYPE IF EXISTS ' + enumName + '; ';
}, }
fromArray(text) { fromArray(text) {
text = text.replace(/^{/, '').replace(/}$/, ''); text = text.replace(/^{/, '').replace(/}$/, '');
...@@ -828,11 +809,11 @@ const QueryGenerator = { ...@@ -828,11 +809,11 @@ const QueryGenerator = {
matches = matches.map(m => m.replace(/",$/, '').replace(/,$/, '').replace(/(^"|"$)/, '')); matches = matches.map(m => m.replace(/",$/, '').replace(/,$/, '').replace(/(^"|"$)/, ''));
return matches.slice(0, -1); return matches.slice(0, -1);
}, }
padInt(i) { padInt(i) {
return i < 10 ? '0' + i.toString() : i.toString(); return i < 10 ? '0' + i.toString() : i.toString();
}, }
dataTypeMapping(tableName, attr, dataType) { dataTypeMapping(tableName, attr, dataType) {
if (_.includes(dataType, 'PRIMARY KEY')) { if (_.includes(dataType, 'PRIMARY KEY')) {
...@@ -854,21 +835,7 @@ const QueryGenerator = { ...@@ -854,21 +835,7 @@ const QueryGenerator = {
} }
return dataType; return dataType;
},
quoteIdentifier(identifier, force) {
if (identifier === '*') return identifier;
if (!force && this.options && this.options.quoteIdentifiers === false && identifier.indexOf('.') === -1 && identifier.indexOf('->') === -1) { // default is `true`
// In Postgres, if tables or attributes are created double-quoted,
// they are also case sensitive. If they contain any uppercase
// characters, they must always be double-quoted. This makes it
// impossible to write queries in portable SQL if tables are created in
// this way. Hence, we strip quotes if we don't want case sensitivity.
return Utils.removeTicks(identifier, '"');
} else {
return Utils.addTicks(Utils.removeTicks(identifier, '"'), '"');
} }
},
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
...@@ -880,7 +847,7 @@ const QueryGenerator = { ...@@ -880,7 +847,7 @@ const QueryGenerator = {
getForeignKeysQuery(tableName) { getForeignKeysQuery(tableName) {
return 'SELECT conname as constraint_name, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r ' + return 'SELECT conname as constraint_name, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r ' +
`WHERE r.conrelid = (SELECT oid FROM pg_class WHERE relname = '${tableName}' LIMIT 1) AND r.contype = 'f' ORDER BY 1;`; `WHERE r.conrelid = (SELECT oid FROM pg_class WHERE relname = '${tableName}' LIMIT 1) AND r.contype = 'f' ORDER BY 1;`;
}, }
/** /**
* Generate common SQL prefix for getForeignKeyReferencesQuery. * Generate common SQL prefix for getForeignKeyReferencesQuery.
...@@ -904,7 +871,7 @@ const QueryGenerator = { ...@@ -904,7 +871,7 @@ const QueryGenerator = {
'ON tc.constraint_name = kcu.constraint_name ' + 'ON tc.constraint_name = kcu.constraint_name ' +
'JOIN information_schema.constraint_column_usage AS ccu ' + 'JOIN information_schema.constraint_column_usage AS ccu ' +
'ON ccu.constraint_name = tc.constraint_name '; 'ON ccu.constraint_name = tc.constraint_name ';
}, }
/** /**
* Generates an SQL query that returns all foreign keys details of a table. * Generates an SQL query that returns all foreign keys details of a table.
...@@ -919,7 +886,7 @@ const QueryGenerator = { ...@@ -919,7 +886,7 @@ const QueryGenerator = {
`WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '${tableName}'` + `WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '${tableName}'` +
(catalogName ? ` AND tc.table_catalog = '${catalogName}'` : '') + (catalogName ? ` AND tc.table_catalog = '${catalogName}'` : '') +
(schemaName ? ` AND tc.table_schema = '${schemaName}'` : ''); (schemaName ? ` AND tc.table_schema = '${schemaName}'` : '');
}, }
getForeignKeyReferenceQuery(table, columnName) { getForeignKeyReferenceQuery(table, columnName) {
const tableName = table.tableName || table; const tableName = table.tableName || table;
...@@ -927,7 +894,7 @@ const QueryGenerator = { ...@@ -927,7 +894,7 @@ const QueryGenerator = {
return this._getForeignKeyReferencesQueryPrefix() + return this._getForeignKeyReferencesQueryPrefix() +
`WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name='${tableName}' AND kcu.column_name = '${columnName}'` + `WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name='${tableName}' AND kcu.column_name = '${columnName}'` +
(schema ? ` AND tc.table_schema = '${schema}'` : ''); (schema ? ` AND tc.table_schema = '${schema}'` : '');
}, }
/** /**
* Generates an SQL query that removes a foreign key from a table. * Generates an SQL query that removes a foreign key from a table.
...@@ -939,7 +906,7 @@ const QueryGenerator = { ...@@ -939,7 +906,7 @@ const QueryGenerator = {
*/ */
dropForeignKeyQuery(tableName, foreignKey) { dropForeignKeyQuery(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP CONSTRAINT ' + this.quoteIdentifier(foreignKey) + ';'; return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP CONSTRAINT ' + this.quoteIdentifier(foreignKey) + ';';
}, }
setAutocommitQuery(value, options) { setAutocommitQuery(value, options) {
if (options.parent) { if (options.parent) {
...@@ -957,8 +924,8 @@ const QueryGenerator = { ...@@ -957,8 +924,8 @@ const QueryGenerator = {
return; return;
} }
return AbstractQueryGenerator.setAutocommitQuery.call(this, value, options); return super.setAutocommitQuery.call(this, value, options);
} }
}; };
module.exports = QueryGenerator; module.exports = PostgresQueryGenerator;
...@@ -12,8 +12,7 @@ class SqliteDialect extends AbstractDialect { ...@@ -12,8 +12,7 @@ class SqliteDialect extends AbstractDialect {
super(); super();
this.sequelize = sequelize; this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize); this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, { this.QueryGenerator = new QueryGenerator({
options: sequelize.options,
_dialect: this, _dialect: this,
sequelize sequelize
}); });
......
...@@ -7,22 +7,18 @@ const _ = require('lodash'); ...@@ -7,22 +7,18 @@ const _ = require('lodash');
const MySqlQueryGenerator = require('../mysql/query-generator'); const MySqlQueryGenerator = require('../mysql/query-generator');
const AbstractQueryGenerator = require('../abstract/query-generator'); const AbstractQueryGenerator = require('../abstract/query-generator');
const QueryGenerator = { class SQLiteQueryGenerator extends MySqlQueryGenerator {
__proto__: MySqlQueryGenerator,
options: {},
dialect: 'sqlite',
createSchema() { createSchema() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
}, }
showSchemasQuery() { showSchemasQuery() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
}, }
versionQuery() { versionQuery() {
return 'SELECT sqlite_version() as `version`'; return 'SELECT sqlite_version() as `version`';
}, }
createTableQuery(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
options = options || {}; options = options || {};
...@@ -73,11 +69,11 @@ const QueryGenerator = { ...@@ -73,11 +69,11 @@ const QueryGenerator = {
const sql = `CREATE TABLE IF NOT EXISTS ${table} (${attrStr});`; const sql = `CREATE TABLE IF NOT EXISTS ${table} (${attrStr});`;
return this.replaceBooleanDefaults(sql); return this.replaceBooleanDefaults(sql);
}, }
booleanValue(value) { booleanValue(value) {
return value ? 1 : 0; return value ? 1 : 0;
}, }
/** /**
* Check whether the statmement is json function or simple path * Check whether the statmement is json function or simple path
...@@ -136,26 +132,7 @@ const QueryGenerator = { ...@@ -136,26 +132,7 @@ const QueryGenerator = {
// return true if the statement has valid json function // return true if the statement has valid json function
return hasJsonFunction; return hasJsonFunction;
}, }
/**
* Generates an SQL query that extract JSON property of given path.
*
* @param {String} column The JSON column
* @param {String|Array<String>} [path] The path to extract (optional)
* @returns {String} The generated sql query
* @private
*/
jsonPathExtractionQuery(column, path) {
const paths = _.toPath(path);
const pathStr = this.escape(['$']
.concat(paths)
.join('.')
.replace(/\.(\d+)(?:(?=\.)|$)/g, (_, digit) => `[${digit}]`));
const quotedColumn = this.isIdentifierQuoted(column) ? column : this.quoteIdentifier(column);
return `json_extract(${quotedColumn}, ${pathStr})`;
},
//sqlite can't cast to datetime so we need to convert date values to their ISO strings //sqlite can't cast to datetime so we need to convert date values to their ISO strings
_toJSONValue(value) { _toJSONValue(value) {
...@@ -165,7 +142,7 @@ const QueryGenerator = { ...@@ -165,7 +142,7 @@ const QueryGenerator = {
return value.map(val => val.toISOString()); return value.map(val => val.toISOString());
} }
return value; return value;
}, }
handleSequelizeMethod(smth, tableName, factory, options, prepend) { handleSequelizeMethod(smth, tableName, factory, options, prepend) {
...@@ -202,8 +179,8 @@ const QueryGenerator = { ...@@ -202,8 +179,8 @@ const QueryGenerator = {
} }
} }
return AbstractQueryGenerator.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend); return AbstractQueryGenerator.prototype.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend);
}, }
addColumnQuery(table, key, dataType) { addColumnQuery(table, key, dataType) {
const attributes = {}; const attributes = {};
...@@ -214,11 +191,11 @@ const QueryGenerator = { ...@@ -214,11 +191,11 @@ const QueryGenerator = {
const sql = `ALTER TABLE ${this.quoteTable(table)} ADD ${attribute};`; const sql = `ALTER TABLE ${this.quoteTable(table)} ADD ${attribute};`;
return this.replaceBooleanDefaults(sql); return this.replaceBooleanDefaults(sql);
}, }
showTablesQuery() { showTablesQuery() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
}, }
upsertQuery(tableName, insertValues, updateValues, where, model, options) { upsertQuery(tableName, insertValues, updateValues, where, model, options) {
options.ignoreDuplicates = true; options.ignoreDuplicates = true;
...@@ -226,7 +203,7 @@ const QueryGenerator = { ...@@ -226,7 +203,7 @@ const QueryGenerator = {
const sql = this.insertQuery(tableName, insertValues, model.rawAttributes, options) + ' ' + this.updateQuery(tableName, updateValues, where, options, model.rawAttributes); const sql = this.insertQuery(tableName, insertValues, model.rawAttributes, options) + ' ' + this.updateQuery(tableName, updateValues, where, options, model.rawAttributes);
return sql; return sql;
}, }
updateQuery(tableName, attrValueHash, where, options, attributes) { updateQuery(tableName, attrValueHash, where, options, attributes) {
options = options || {}; options = options || {};
...@@ -256,7 +233,7 @@ const QueryGenerator = { ...@@ -256,7 +233,7 @@ const QueryGenerator = {
} else { } else {
return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} ${this.whereQuery(where, options)}`; return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} ${this.whereQuery(where, options)}`;
} }
}, }
deleteQuery(tableName, where, options, model) { deleteQuery(tableName, where, options, model) {
options = options || {}; options = options || {};
...@@ -281,7 +258,7 @@ const QueryGenerator = { ...@@ -281,7 +258,7 @@ const QueryGenerator = {
} }
return `DELETE FROM ${this.quoteTable(tableName)} ${whereClause}`; return `DELETE FROM ${this.quoteTable(tableName)} ${whereClause}`;
}, }
attributesToSQL(attributes) { attributesToSQL(attributes) {
const result = {}; const result = {};
...@@ -345,11 +322,11 @@ const QueryGenerator = { ...@@ -345,11 +322,11 @@ const QueryGenerator = {
} }
return result; return result;
}, }
showIndexesQuery(tableName) { showIndexesQuery(tableName) {
return `PRAGMA INDEX_LIST(${this.quoteTable(tableName)})`; return `PRAGMA INDEX_LIST(${this.quoteTable(tableName)})`;
}, }
showConstraintsQuery(tableName, constraintName) { showConstraintsQuery(tableName, constraintName) {
let sql = `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}'`; let sql = `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}'`;
...@@ -359,7 +336,7 @@ const QueryGenerator = { ...@@ -359,7 +336,7 @@ const QueryGenerator = {
} }
return sql + ';'; return sql + ';';
}, }
removeIndexQuery(tableName, indexNameOrAttributes) { removeIndexQuery(tableName, indexNameOrAttributes) {
let indexName = indexNameOrAttributes; let indexName = indexNameOrAttributes;
...@@ -369,7 +346,7 @@ const QueryGenerator = { ...@@ -369,7 +346,7 @@ const QueryGenerator = {
} }
return `DROP INDEX IF EXISTS ${this.quoteIdentifier(indexName)}`; return `DROP INDEX IF EXISTS ${this.quoteIdentifier(indexName)}`;
}, }
describeTableQuery(tableName, schema, schemaDelimiter) { describeTableQuery(tableName, schema, schemaDelimiter) {
const table = { const table = {
...@@ -378,11 +355,11 @@ const QueryGenerator = { ...@@ -378,11 +355,11 @@ const QueryGenerator = {
tableName tableName
}; };
return `PRAGMA TABLE_INFO(${this.quoteTable(this.addSchema(table))});`; return `PRAGMA TABLE_INFO(${this.quoteTable(this.addSchema(table))});`;
}, }
describeCreateTableQuery(tableName) { describeCreateTableQuery(tableName) {
return `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}';`; return `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}';`;
}, }
removeColumnQuery(tableName, attributes) { removeColumnQuery(tableName, attributes) {
...@@ -409,7 +386,7 @@ const QueryGenerator = { ...@@ -409,7 +386,7 @@ const QueryGenerator = {
+ this.createTableQuery(tableName, attributes) + this.createTableQuery(tableName, attributes)
+ `INSERT INTO ${quotedTableName} SELECT ${attributeNames} FROM ${quotedBackupTableName};` + `INSERT INTO ${quotedTableName} SELECT ${attributeNames} FROM ${quotedBackupTableName};`
+ `DROP TABLE ${quotedBackupTableName};`; + `DROP TABLE ${quotedBackupTableName};`;
}, }
_alterConstraintQuery(tableName, attributes, createTableSql) { _alterConstraintQuery(tableName, attributes, createTableSql) {
let backupTableName; let backupTableName;
...@@ -432,7 +409,7 @@ const QueryGenerator = { ...@@ -432,7 +409,7 @@ const QueryGenerator = {
+ `INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};` + `INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};`
+ `DROP TABLE ${quotedTableName};` + `DROP TABLE ${quotedTableName};`
+ `ALTER TABLE ${quotedBackupTableName} RENAME TO ${quotedTableName};`; + `ALTER TABLE ${quotedBackupTableName} RENAME TO ${quotedTableName};`;
}, }
renameColumnQuery(tableName, attrNameBefore, attrNameAfter, attributes) { renameColumnQuery(tableName, attrNameBefore, attrNameAfter, attributes) {
...@@ -462,7 +439,7 @@ const QueryGenerator = { ...@@ -462,7 +439,7 @@ const QueryGenerator = {
+ this.createTableQuery(tableName, attributes) + this.createTableQuery(tableName, attributes)
+ `INSERT INTO ${quotedTableName} SELECT ${attributeNamesExport} FROM ${quotedBackupTableName};` + `INSERT INTO ${quotedTableName} SELECT ${attributeNamesExport} FROM ${quotedBackupTableName};`
+ `DROP TABLE ${quotedBackupTableName};`; + `DROP TABLE ${quotedBackupTableName};`;
}, }
startTransactionQuery(transaction) { startTransactionQuery(transaction) {
if (transaction.parent) { if (transaction.parent) {
...@@ -470,12 +447,12 @@ const QueryGenerator = { ...@@ -470,12 +447,12 @@ const QueryGenerator = {
} }
return 'BEGIN ' + transaction.options.type + ' TRANSACTION;'; return 'BEGIN ' + transaction.options.type + ' TRANSACTION;';
}, }
setAutocommitQuery() { setAutocommitQuery() {
// SQLite does not support SET autocommit // SQLite does not support SET autocommit
return null; return null;
}, }
setIsolationLevelQuery(value) { setIsolationLevelQuery(value) {
switch (value) { switch (value) {
...@@ -490,16 +467,11 @@ const QueryGenerator = { ...@@ -490,16 +467,11 @@ const QueryGenerator = {
default: default:
throw new Error('Unknown isolation level: ' + value); throw new Error('Unknown isolation level: ' + value);
} }
}, }
replaceBooleanDefaults(sql) { replaceBooleanDefaults(sql) {
return sql.replace(/DEFAULT '?false'?/g, 'DEFAULT 0').replace(/DEFAULT '?true'?/g, 'DEFAULT 1'); return sql.replace(/DEFAULT '?false'?/g, 'DEFAULT 0').replace(/DEFAULT '?true'?/g, 'DEFAULT 1');
}, }
quoteIdentifier(identifier) {
if (identifier === '*') return identifier;
return Utils.addTicks(Utils.removeTicks(identifier, '`'), '`');
},
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
...@@ -513,4 +485,4 @@ const QueryGenerator = { ...@@ -513,4 +485,4 @@ const QueryGenerator = {
} }
}; };
module.exports = QueryGenerator; module.exports = SQLiteQueryGenerator;
\ No newline at end of file
...@@ -77,15 +77,11 @@ class Model { ...@@ -77,15 +77,11 @@ class Model {
const deletedAtCol = model._timestampAttributes.deletedAt; const deletedAtCol = model._timestampAttributes.deletedAt;
const deletedAtAttribute = model.rawAttributes[deletedAtCol]; const deletedAtAttribute = model.rawAttributes[deletedAtCol];
const deletedAtObject = {}; const deletedAtObject = {};
const now = Utils.now(this.sequelize.options.dialect);
let deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null; let deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
deletedAtDefaultValue = deletedAtDefaultValue || { deletedAtDefaultValue = deletedAtDefaultValue || {
[Op.or]: {
[Op.gt]: now,
[Op.eq]: null [Op.eq]: null
}
}; };
deletedAtObject[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue; deletedAtObject[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue;
...@@ -1064,10 +1060,6 @@ class Model { ...@@ -1064,10 +1060,6 @@ class Model {
this._hasDefaultValues = !_.isEmpty(this._defaultValues); this._hasDefaultValues = !_.isEmpty(this._defaultValues);
// DEPRECATE: All code base is free from this.attributes now
// This should be removed in v5
this.attributes = this.rawAttributes;
this.tableAttributes = _.omit(this.rawAttributes, this._virtualAttributes); this.tableAttributes = _.omit(this.rawAttributes, this._virtualAttributes);
this.prototype._hasCustomGetters = Object.keys(this.prototype._customGetters).length; this.prototype._hasCustomGetters = Object.keys(this.prototype._customGetters).length;
...@@ -4204,9 +4196,6 @@ class Model { ...@@ -4204,9 +4196,6 @@ class Model {
// Aliases // Aliases
Model.prototype.updateAttributes = Model.prototype.update; Model.prototype.updateAttributes = Model.prototype.update;
Model._conformOptions = Model._conformOptions;
Model._validateIncludedElements = Model._validateIncludedElements;
Model._expandIncludeAll = Model._expandIncludeAll;
Model.findByPrimary = Model.findById; Model.findByPrimary = Model.findById;
Model.find = Model.findOne; Model.find = Model.findOne;
Model.findAndCountAll = Model.findAndCount; Model.findAndCountAll = Model.findAndCount;
......
...@@ -78,8 +78,7 @@ const Op = { ...@@ -78,8 +78,7 @@ const Op = {
values: Symbol.for('values'), values: Symbol.for('values'),
col: Symbol.for('col'), col: Symbol.for('col'),
placeholder: Symbol.for('placeholder'), placeholder: Symbol.for('placeholder'),
join: Symbol.for('join'), join: Symbol.for('join')
raw: Symbol.for('raw') //deprecated remove by v5.0
}; };
const Aliases = { const Aliases = {
...@@ -117,7 +116,6 @@ const Aliases = { ...@@ -117,7 +116,6 @@ const Aliases = {
$all: Op.all, $all: Op.all,
$values: Op.values, $values: Op.values,
$col: Op.col, $col: Op.col,
$raw: Op.raw //deprecated remove by v5.0
}; };
const LegacyAliases = { //deprecated remove by v5.0 const LegacyAliases = { //deprecated remove by v5.0
......
...@@ -4,29 +4,11 @@ ...@@ -4,29 +4,11 @@
"version": "0.0.0-development", "version": "0.0.0-development",
"author": "Sascha Depold <sascha@depold.com>", "author": "Sascha Depold <sascha@depold.com>",
"contributors": [ "contributors": [
{ "Sascha Depold <sascha@depold.com>",
"name": "Sascha Depold", "Jan Aagaard Meier <janzeh@gmail.com>",
"email": "sascha@depold.com" "Daniel Durante <me@danieldurante.com>",
}, "Mick Hansen <mick.kasper.hansen@gmail.com>",
{ "Sushant Dhiman <sushantdhiman@outlook.com>"
"name": "Jan Aagaard Meier",
"email": [
"janzeh@gmail.com",
"jmei@itu.dk"
]
},
{
"name": "Daniel Durante",
"email": "me@danieldurante.com"
},
{
"name": "Mick Hansen",
"email": "mick.kasper.hansen@gmail.com"
},
{
"name": "Sushant Dhiman",
"email": "sushantdhiman@outlook.com"
}
], ],
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -55,6 +37,8 @@ ...@@ -55,6 +37,8 @@
"wkx": "^0.4.1" "wkx": "^0.4.1"
}, },
"devDependencies": { "devDependencies": {
"@semantic-release/github": "^4.2.6",
"@semantic-release/npm": "^3.2.2",
"chai": "^4.1.2", "chai": "^4.1.2",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"chai-datetime": "^1.5.0", "chai-datetime": "^1.5.0",
...@@ -144,18 +128,12 @@ ...@@ -144,18 +128,12 @@
"semantic-release": "semantic-release" "semantic-release": "semantic-release"
}, },
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=6.0.0"
}, },
"license": "MIT", "license": "MIT",
"config": { "config": {
"commitizen": { "commitizen": {
"path": "./node_modules/cz-conventional-changelog" "path": "./node_modules/cz-conventional-changelog"
} }
},
"release": {
"verifyConditions": [
"@semantic-release/npm",
"@semantic-release/github"
]
} }
} }
...@@ -15,56 +15,56 @@ if (dialect === 'mysql') { ...@@ -15,56 +15,56 @@ if (dialect === 'mysql') {
username: { type: DataTypes.STRING, unique: true } username: { type: DataTypes.STRING, unique: true }
}, { timestamps: false }); }, { timestamps: false });
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.attributes)).to.deep.equal({username: 'VARCHAR(255) UNIQUE', id: 'INTEGER NOT NULL auto_increment PRIMARY KEY'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.rawAttributes)).to.deep.equal({username: 'VARCHAR(255) UNIQUE', id: 'INTEGER NOT NULL auto_increment PRIMARY KEY'});
}); });
it('handles extended attributes (default)', function() { it('handles extended attributes (default)', function() {
const User = this.sequelize.define('User' + config.rand(), { const User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, defaultValue: 'foo'} username: {type: DataTypes.STRING, defaultValue: 'foo'}
}, { timestamps: false }); }, { timestamps: false });
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.attributes)).to.deep.equal({username: "VARCHAR(255) DEFAULT 'foo'", id: 'INTEGER NOT NULL auto_increment PRIMARY KEY'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.rawAttributes)).to.deep.equal({username: "VARCHAR(255) DEFAULT 'foo'", id: 'INTEGER NOT NULL auto_increment PRIMARY KEY'});
}); });
it('handles extended attributes (null)', function() { it('handles extended attributes (null)', function() {
const User = this.sequelize.define('User' + config.rand(), { const User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, allowNull: false} username: {type: DataTypes.STRING, allowNull: false}
}, { timestamps: false }); }, { timestamps: false });
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.attributes)).to.deep.equal({username: 'VARCHAR(255) NOT NULL', id: 'INTEGER NOT NULL auto_increment PRIMARY KEY'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.rawAttributes)).to.deep.equal({username: 'VARCHAR(255) NOT NULL', id: 'INTEGER NOT NULL auto_increment PRIMARY KEY'});
}); });
it('handles extended attributes (primaryKey)', function() { it('handles extended attributes (primaryKey)', function() {
const User = this.sequelize.define('User' + config.rand(), { const User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, primaryKey: true} username: {type: DataTypes.STRING, primaryKey: true}
}, { timestamps: false }); }, { timestamps: false });
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.attributes)).to.deep.equal({username: 'VARCHAR(255) PRIMARY KEY'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.rawAttributes)).to.deep.equal({username: 'VARCHAR(255) PRIMARY KEY'});
}); });
it('adds timestamps', function() { it('adds timestamps', function() {
const User1 = this.sequelize.define('User' + config.rand(), {}); const User1 = this.sequelize.define('User' + config.rand(), {});
const User2 = this.sequelize.define('User' + config.rand(), {}, { timestamps: true }); const User2 = this.sequelize.define('User' + config.rand(), {}, { timestamps: true });
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User1.attributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', updatedAt: 'DATETIME NOT NULL', createdAt: 'DATETIME NOT NULL'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User1.rawAttributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', updatedAt: 'DATETIME NOT NULL', createdAt: 'DATETIME NOT NULL'});
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User2.attributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', updatedAt: 'DATETIME NOT NULL', createdAt: 'DATETIME NOT NULL'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User2.rawAttributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', updatedAt: 'DATETIME NOT NULL', createdAt: 'DATETIME NOT NULL'});
}); });
it('adds deletedAt if paranoid', function() { it('adds deletedAt if paranoid', function() {
const User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true }); const User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true });
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.attributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', deletedAt: 'DATETIME', updatedAt: 'DATETIME NOT NULL', createdAt: 'DATETIME NOT NULL'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.rawAttributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', deletedAt: 'DATETIME', updatedAt: 'DATETIME NOT NULL', createdAt: 'DATETIME NOT NULL'});
}); });
it('underscores timestamps if underscored', function() { it('underscores timestamps if underscored', function() {
const User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true, underscored: true }); const User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true, underscored: true });
expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.attributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', deleted_at: 'DATETIME', updated_at: 'DATETIME NOT NULL', created_at: 'DATETIME NOT NULL'}); expect(this.sequelize.getQueryInterface().QueryGenerator.attributesToSQL(User.rawAttributes)).to.deep.equal({id: 'INTEGER NOT NULL auto_increment PRIMARY KEY', deleted_at: 'DATETIME', updated_at: 'DATETIME NOT NULL', created_at: 'DATETIME NOT NULL'});
}); });
it('omits text fields with defaultValues', function() { it('omits text fields with defaultValues', function() {
const User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.TEXT, defaultValue: 'helloworld'}}); const User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.TEXT, defaultValue: 'helloworld'}});
expect(User.attributes.name.type.toString()).to.equal('TEXT'); expect(User.rawAttributes.name.type.toString()).to.equal('TEXT');
}); });
it('omits blobs fields with defaultValues', function() { it('omits blobs fields with defaultValues', function() {
const User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.STRING.BINARY, defaultValue: 'helloworld'}}); const User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.STRING.BINARY, defaultValue: 'helloworld'}});
expect(User.attributes.name.type.toString()).to.equal('VARCHAR(255) BINARY'); expect(User.rawAttributes.name.type.toString()).to.equal('VARCHAR(255) BINARY');
}); });
}); });
......
...@@ -2198,8 +2198,6 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2198,8 +2198,6 @@ describe(Support.getTestDialectTeaser('Model'), () => {
describe('schematic support', () => { describe('schematic support', () => {
beforeEach(function() { beforeEach(function() {
const self = this;
this.UserPublic = this.sequelize.define('UserPublic', { this.UserPublic = this.sequelize.define('UserPublic', {
age: Sequelize.INTEGER age: Sequelize.INTEGER
}); });
...@@ -2208,11 +2206,11 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2208,11 +2206,11 @@ describe(Support.getTestDialectTeaser('Model'), () => {
age: Sequelize.INTEGER age: Sequelize.INTEGER
}); });
return self.sequelize.dropAllSchemas().then(() => { return this.sequelize.dropAllSchemas().then(() => {
return self.sequelize.createSchema('schema_test').then(() => { return this.sequelize.createSchema('schema_test').then(() => {
return self.sequelize.createSchema('special').then(() => { return this.sequelize.createSchema('special').then(() => {
return self.UserSpecial.schema('special').sync({force: true}).then(UserSpecialSync => { return this.UserSpecial.schema('special').sync({force: true}).then(UserSpecialSync => {
self.UserSpecialSync = UserSpecialSync; this.UserSpecialSync = UserSpecialSync;
}); });
}); });
}); });
......
...@@ -5,7 +5,6 @@ const chai = require('chai'), ...@@ -5,7 +5,6 @@ const chai = require('chai'),
Sequelize = require('../../../index'), Sequelize = require('../../../index'),
Promise = Sequelize.Promise, Promise = Sequelize.Promise,
expect = chai.expect, expect = chai.expect,
moment = require('moment'),
Support = require(__dirname + '/../support'), Support = require(__dirname + '/../support'),
dialect = Support.getTestDialect(), dialect = Support.getTestDialect(),
DataTypes = require(__dirname + '/../../../lib/data-types'), DataTypes = require(__dirname + '/../../../lib/data-types'),
...@@ -991,31 +990,6 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -991,31 +990,6 @@ describe(Support.getTestDialectTeaser('Model'), () => {
})).to.eventually.be.equal(null); })).to.eventually.be.equal(null);
}); });
}); });
});
it('should find records where deletedAt set to future', function() {
const User = this.sequelize.define('paranoiduser', {
username: Sequelize.STRING
}, { paranoid: true });
return User.sync({ force: true }).then(() => {
return User.bulkCreate([
{username: 'Bob'},
{username: 'Tobi', deletedAt: moment().add(30, 'minutes').format()},
{username: 'Max', deletedAt: moment().add(30, 'days').format()},
{username: 'Tony', deletedAt: moment().subtract(30, 'days').format()}
]);
}).then(() => {
return User.find({ where: {username: 'Tobi'} });
}).then(tobi => {
expect(tobi).not.to.be.null;
}).then(() => {
return User.findAll();
}).then(users => {
expect(users.length).to.be.eql(3);
}); });
}); });
});
}); });
...@@ -214,7 +214,7 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -214,7 +214,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
expect(created).to.be.undefined; expect(created).to.be.undefined;
} else { } else {
expect(created).to.be.okay; expect(created).to.be.ok;
} }
}); });
}); });
......
...@@ -184,9 +184,11 @@ describe(Support.getTestDialectTeaser('Utils'), () => { ...@@ -184,9 +184,11 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
if (Support.getTestDialect() === 'postgres') { if (Support.getTestDialect() === 'postgres') {
describe('json', () => { describe('json', () => {
const queryGenerator = require('../../lib/dialects/postgres/query-generator.js'); beforeEach(function() {
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
});
it('successfully parses a complex nested condition hash', () => { it('successfully parses a complex nested condition hash', function() {
const conditions = { const conditions = {
metadata: { metadata: {
language: 'icelandic', language: 'icelandic',
...@@ -195,23 +197,23 @@ describe(Support.getTestDialectTeaser('Utils'), () => { ...@@ -195,23 +197,23 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
another_json_field: { x: 1 } another_json_field: { x: 1 }
}; };
const expected = '("metadata"#>>\'{language}\') = \'icelandic\' AND ("metadata"#>>\'{pg_rating,dk}\') = \'G\' AND ("another_json_field"#>>\'{x}\') = \'1\''; const expected = '("metadata"#>>\'{language}\') = \'icelandic\' AND ("metadata"#>>\'{pg_rating,dk}\') = \'G\' AND ("another_json_field"#>>\'{x}\') = \'1\'';
expect(queryGenerator.handleSequelizeMethod(new Utils.Json(conditions))).to.deep.equal(expected); expect(this.queryGenerator.handleSequelizeMethod(new Utils.Json(conditions))).to.deep.equal(expected);
}); });
it('successfully parses a string using dot notation', () => { it('successfully parses a string using dot notation', function() {
const path = 'metadata.pg_rating.dk'; const path = 'metadata.pg_rating.dk';
expect(queryGenerator.handleSequelizeMethod(new Utils.Json(path))).to.equal('("metadata"#>>\'{pg_rating,dk}\')'); expect(this.queryGenerator.handleSequelizeMethod(new Utils.Json(path))).to.equal('("metadata"#>>\'{pg_rating,dk}\')');
}); });
it('allows postgres json syntax', () => { it('allows postgres json syntax', function() {
const path = 'metadata->pg_rating->>dk'; const path = 'metadata->pg_rating->>dk';
expect(queryGenerator.handleSequelizeMethod(new Utils.Json(path))).to.equal(path); expect(this.queryGenerator.handleSequelizeMethod(new Utils.Json(path))).to.equal(path);
}); });
it('can take a value to compare against', () => { it('can take a value to compare against', function() {
const path = 'metadata.pg_rating.is'; const path = 'metadata.pg_rating.is';
const value = 'U'; const value = 'U';
expect(queryGenerator.handleSequelizeMethod(new Utils.Json(path, value))).to.equal('("metadata"#>>\'{pg_rating,is}\') = \'U\''); expect(this.queryGenerator.handleSequelizeMethod(new Utils.Json(path, value))).to.equal('("metadata"#>>\'{pg_rating,is}\') = \'U\'');
}); });
}); });
} }
......
...@@ -156,11 +156,18 @@ const Support = { ...@@ -156,11 +156,18 @@ const Support = {
}, },
getAbstractQueryGenerator(sequelize) { getAbstractQueryGenerator(sequelize) {
return Object.assign( class ModdedQueryGenerator extends AbstractQueryGenerator {
{}, quoteIdentifier(x) {
AbstractQueryGenerator, return x;
{options: sequelize.options, _dialect: sequelize.dialect, sequelize, quoteIdentifier(identifier) { return identifier; }} }
); }
const queryGenerator = new ModdedQueryGenerator({
sequelize,
_dialect: sequelize.dialect
});
return queryGenerator;
}, },
getTestDialect() { getTestDialect() {
......
...@@ -6,15 +6,6 @@ const chai = require('chai'), ...@@ -6,15 +6,6 @@ const chai = require('chai'),
getAbstractQueryGenerator = require(__dirname + '/../../support').getAbstractQueryGenerator; getAbstractQueryGenerator = require(__dirname + '/../../support').getAbstractQueryGenerator;
describe('QueryGenerator', () => { describe('QueryGenerator', () => {
describe('selectQuery', () => {
it('should generate correct query using array placeholder', function() {
const QG = getAbstractQueryGenerator(this.sequelize);
QG.selectQuery('foo', {where: {bar: {[Op.like]: {[Op.any]: ['a', 'b']}}}})
.should.be.equal('SELECT * FROM foo WHERE foo.bar LIKE ANY (\'a\', \'b\');');
});
});
describe('whereItemQuery', () => { describe('whereItemQuery', () => {
it('should generate correct query for Symbol operators', function() { it('should generate correct query for Symbol operators', function() {
const QG = getAbstractQueryGenerator(this.sequelize); const QG = getAbstractQueryGenerator(this.sequelize);
......
...@@ -3,53 +3,54 @@ ...@@ -3,53 +3,54 @@
const Support = require(__dirname + '/../../support'); const Support = require(__dirname + '/../../support');
const expectsql = Support.expectsql; const expectsql = Support.expectsql;
const current = Support.sequelize; const current = Support.sequelize;
const Operators = require('../../../../lib/operators');
const TableHints = require('../../../../lib/table-hints'); const TableHints = require('../../../../lib/table-hints');
const QueryGenerator = require('../../../../lib/dialects/mssql/query-generator'); const QueryGenerator = require('../../../../lib/dialects/mssql/query-generator');
const _ = require('lodash'); const _ = require('lodash');
if (current.dialect.name === 'mssql') { if (current.dialect.name === 'mssql') {
suite('[MSSQL Specific] QueryGenerator', () => { describe('[MSSQL Specific] QueryGenerator', () => {
// Dialect would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly before(function() {
QueryGenerator._dialect = current.dialect; this.queryGenerator = new QueryGenerator({
//Aliases might not be needed here since it doesn't seem like this test uses any operators sequelize: this.sequelize,
QueryGenerator.setOperatorsAliases(Operators.Aliases); _dialect: this.sequelize.dialect
});
test('getDefaultConstraintQuery', () => { });
expectsql(QueryGenerator.getDefaultConstraintQuery({tableName: 'myTable', schema: 'mySchema'}, 'myColumn'), {
it('getDefaultConstraintQuery', function() {
expectsql(this.queryGenerator.getDefaultConstraintQuery({tableName: 'myTable', schema: 'mySchema'}, 'myColumn'), {
mssql: "SELECT name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('[mySchema].[myTable]', 'U') AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = ('myColumn') AND object_id = OBJECT_ID('[mySchema].[myTable]', 'U'));" mssql: "SELECT name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('[mySchema].[myTable]', 'U') AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = ('myColumn') AND object_id = OBJECT_ID('[mySchema].[myTable]', 'U'));"
}); });
}); });
test('dropConstraintQuery', () => { it('dropConstraintQuery', function() {
expectsql(QueryGenerator.dropConstraintQuery({tableName: 'myTable', schema: 'mySchema'}, 'myConstraint'), { expectsql(this.queryGenerator.dropConstraintQuery({tableName: 'myTable', schema: 'mySchema'}, 'myConstraint'), {
mssql: 'ALTER TABLE [mySchema].[myTable] DROP CONSTRAINT [myConstraint];' mssql: 'ALTER TABLE [mySchema].[myTable] DROP CONSTRAINT [myConstraint];'
}); });
}); });
test('bulkInsertQuery', () => { it('bulkInsertQuery', function() {
//normal cases //normal cases
expectsql(QueryGenerator.bulkInsertQuery('myTable', [{ name: 'foo' }, {name: 'bar'}]), { expectsql(this.queryGenerator.bulkInsertQuery('myTable', [{ name: 'foo' }, {name: 'bar'}]), {
mssql: "INSERT INTO [myTable] ([name]) VALUES (N'foo'),(N'bar');" mssql: "INSERT INTO [myTable] ([name]) VALUES (N'foo'),(N'bar');"
}); });
expectsql(QueryGenerator.bulkInsertQuery('myTable', [{ username: 'username', firstName: 'firstName', lastName: 'lastName' }, { firstName: 'user1FirstName', lastName: 'user1LastName'}]), { expectsql(this.queryGenerator.bulkInsertQuery('myTable', [{ username: 'username', firstName: 'firstName', lastName: 'lastName' }, { firstName: 'user1FirstName', lastName: 'user1LastName'}]), {
mssql: "INSERT INTO [myTable] ([username],[firstName],[lastName]) VALUES (N'username',N'firstName',N'lastName'),(NULL,N'user1FirstName',N'user1LastName');" mssql: "INSERT INTO [myTable] ([username],[firstName],[lastName]) VALUES (N'username',N'firstName',N'lastName'),(NULL,N'user1FirstName',N'user1LastName');"
}); });
expectsql(QueryGenerator.bulkInsertQuery('myTable', [{ firstName: 'firstName', lastName: 'lastName' }, { firstName: 'user1FirstName', lastName: 'user1LastName'}]), { expectsql(this.queryGenerator.bulkInsertQuery('myTable', [{ firstName: 'firstName', lastName: 'lastName' }, { firstName: 'user1FirstName', lastName: 'user1LastName'}]), {
mssql: "INSERT INTO [myTable] ([firstName],[lastName]) VALUES (N'firstName',N'lastName'),(N'user1FirstName',N'user1LastName');" mssql: "INSERT INTO [myTable] ([firstName],[lastName]) VALUES (N'firstName',N'lastName'),(N'user1FirstName',N'user1LastName');"
}); });
//Bulk Insert With autogenerated primary key //Bulk Insert With autogenerated primary key
const attributes = { id: { autoIncrement: true }}; const attributes = { id: { autoIncrement: true }};
expectsql(QueryGenerator.bulkInsertQuery('myTable', [{ id: null }], {}, attributes), { expectsql(this.queryGenerator.bulkInsertQuery('myTable', [{ id: null }], {}, attributes), {
mssql: 'INSERT INTO [myTable] DEFAULT VALUES' mssql: 'INSERT INTO [myTable] DEFAULT VALUES'
}); });
}); });
test('selectFromTableFragment', () => { it('selectFromTableFragment', function() {
const modifiedGen = _.clone(QueryGenerator); const modifiedGen = _.cloneDeep(this.queryGenerator);
// Test newer versions first // Test newer versions first
// Should be all the same since handling is done in addLimitAndOffset // Should be all the same since handling is done in addLimitAndOffset
// for SQL Server 2012 and higher (>= v11.0.0) // for SQL Server 2012 and higher (>= v11.0.0)
...@@ -113,76 +114,76 @@ if (current.dialect.name === 'mssql') { ...@@ -113,76 +114,76 @@ if (current.dialect.name === 'mssql') {
}); });
}); });
test('getPrimaryKeyConstraintQuery', () => { it('getPrimaryKeyConstraintQuery', function() {
expectsql(QueryGenerator.getPrimaryKeyConstraintQuery('myTable', 'myColumnName'), { expectsql(this.queryGenerator.getPrimaryKeyConstraintQuery('myTable', 'myColumnName'), {
mssql: 'SELECT K.TABLE_NAME AS tableName, K.COLUMN_NAME AS columnName, K.CONSTRAINT_NAME AS constraintName FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS C JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS K ON C.TABLE_NAME = K.TABLE_NAME AND C.CONSTRAINT_CATALOG = K.CONSTRAINT_CATALOG AND C.CONSTRAINT_SCHEMA = K.CONSTRAINT_SCHEMA AND C.CONSTRAINT_NAME = K.CONSTRAINT_NAME WHERE C.CONSTRAINT_TYPE = \'PRIMARY KEY\' AND K.COLUMN_NAME = \'myColumnName\' AND K.TABLE_NAME = \'myTable\';' mssql: 'SELECT K.TABLE_NAME AS tableName, K.COLUMN_NAME AS columnName, K.CONSTRAINT_NAME AS constraintName FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS C JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS K ON C.TABLE_NAME = K.TABLE_NAME AND C.CONSTRAINT_CATALOG = K.CONSTRAINT_CATALOG AND C.CONSTRAINT_SCHEMA = K.CONSTRAINT_SCHEMA AND C.CONSTRAINT_NAME = K.CONSTRAINT_NAME WHERE C.CONSTRAINT_TYPE = \'PRIMARY KEY\' AND K.COLUMN_NAME = \'myColumnName\' AND K.TABLE_NAME = \'myTable\';'
}); });
}); });
test('createSchema', () => { it('createSchema', function() {
expectsql(QueryGenerator.createSchema('mySchema'), { expectsql(this.queryGenerator.createSchema('mySchema'), {
mssql: 'IF NOT EXISTS (SELECT schema_name FROM information_schema.schemata WHERE schema_name = \'mySchema\' ) BEGIN EXEC sp_executesql N\'CREATE SCHEMA [mySchema] ;\' END;' mssql: 'IF NOT EXISTS (SELECT schema_name FROM information_schema.schemata WHERE schema_name = \'mySchema\' ) BEGIN EXEC sp_executesql N\'CREATE SCHEMA [mySchema] ;\' END;'
}); });
}); });
test('dropSchema', () => { it('dropSchema', function() {
expectsql(QueryGenerator.dropSchema('mySchema'), { expectsql(this.queryGenerator.dropSchema('mySchema'), {
mssql: 'IF EXISTS (SELECT schema_name FROM information_schema.schemata WHERE schema_name = \'mySchema\' ) BEGIN DECLARE @id INT, @ms_sql NVARCHAR(2000); DECLARE @cascade TABLE ( id INT NOT NULL IDENTITY PRIMARY KEY, ms_sql NVARCHAR(2000) NOT NULL ); INSERT INTO @cascade ( ms_sql ) SELECT CASE WHEN o.type IN (\'F\',\'PK\') THEN N\'ALTER TABLE [\'+ s.name + N\'].[\' + p.name + N\'] DROP CONSTRAINT [\' + o.name + N\']\' ELSE N\'DROP TABLE [\'+ s.name + N\'].[\' + o.name + N\']\' END FROM sys.objects o JOIN sys.schemas s on o.schema_id = s.schema_id LEFT OUTER JOIN sys.objects p on o.parent_object_id = p.object_id WHERE o.type IN (\'F\', \'PK\', \'U\') AND s.name = \'mySchema\' ORDER BY o.type ASC; SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id; WHILE @id IS NOT NULL BEGIN BEGIN TRY EXEC sp_executesql @ms_sql; END TRY BEGIN CATCH BREAK; THROW; END CATCH; DELETE FROM @cascade WHERE id = @id; SELECT @id = NULL, @ms_sql = NULL; SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id; END EXEC sp_executesql N\'DROP SCHEMA [mySchema] ;\' END;' mssql: 'IF EXISTS (SELECT schema_name FROM information_schema.schemata WHERE schema_name = \'mySchema\' ) BEGIN DECLARE @id INT, @ms_sql NVARCHAR(2000); DECLARE @cascade TABLE ( id INT NOT NULL IDENTITY PRIMARY KEY, ms_sql NVARCHAR(2000) NOT NULL ); INSERT INTO @cascade ( ms_sql ) SELECT CASE WHEN o.type IN (\'F\',\'PK\') THEN N\'ALTER TABLE [\'+ s.name + N\'].[\' + p.name + N\'] DROP CONSTRAINT [\' + o.name + N\']\' ELSE N\'DROP TABLE [\'+ s.name + N\'].[\' + o.name + N\']\' END FROM sys.objects o JOIN sys.schemas s on o.schema_id = s.schema_id LEFT OUTER JOIN sys.objects p on o.parent_object_id = p.object_id WHERE o.type IN (\'F\', \'PK\', \'U\') AND s.name = \'mySchema\' ORDER BY o.type ASC; SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id; WHILE @id IS NOT NULL BEGIN BEGIN TRY EXEC sp_executesql @ms_sql; END TRY BEGIN CATCH BREAK; THROW; END CATCH; DELETE FROM @cascade WHERE id = @id; SELECT @id = NULL, @ms_sql = NULL; SELECT TOP 1 @id = id, @ms_sql = ms_sql FROM @cascade ORDER BY id; END EXEC sp_executesql N\'DROP SCHEMA [mySchema] ;\' END;'
}); });
}); });
test('showSchemasQuery', () => { it('showSchemasQuery', function() {
expectsql(QueryGenerator.showSchemasQuery(), { expectsql(this.queryGenerator.showSchemasQuery(), {
mssql: 'SELECT "name" as "schema_name" FROM sys.schemas as s WHERE "s"."name" NOT IN ( \'INFORMATION_SCHEMA\', \'dbo\', \'guest\', \'sys\', \'archive\' ) AND "s"."name" NOT LIKE \'db_%\'' mssql: 'SELECT "name" as "schema_name" FROM sys.schemas as s WHERE "s"."name" NOT IN ( \'INFORMATION_SCHEMA\', \'dbo\', \'guest\', \'sys\', \'archive\' ) AND "s"."name" NOT LIKE \'db_%\''
}); });
}); });
test('versionQuery', () => { it('versionQuery', function() {
expectsql(QueryGenerator.versionQuery(), { expectsql(this.queryGenerator.versionQuery(), {
mssql: "DECLARE @ms_ver NVARCHAR(20); SET @ms_ver = REVERSE(CONVERT(NVARCHAR(20), SERVERPROPERTY('ProductVersion'))); SELECT REVERSE(SUBSTRING(@ms_ver, CHARINDEX('.', @ms_ver)+1, 20)) AS 'version'" mssql: "DECLARE @ms_ver NVARCHAR(20); SET @ms_ver = REVERSE(CONVERT(NVARCHAR(20), SERVERPROPERTY('ProductVersion'))); SELECT REVERSE(SUBSTRING(@ms_ver, CHARINDEX('.', @ms_ver)+1, 20)) AS 'version'"
}); });
}); });
test('renameTableQuery', () => { it('renameTableQuery', function() {
expectsql(QueryGenerator.renameTableQuery('oldTableName', 'newTableName'), { expectsql(this.queryGenerator.renameTableQuery('oldTableName', 'newTableName'), {
mssql: 'EXEC sp_rename [oldTableName], [newTableName];' mssql: 'EXEC sp_rename [oldTableName], [newTableName];'
}); });
}); });
test('showTablesQuery', () => { it('showTablesQuery', function() {
expectsql(QueryGenerator.showTablesQuery(), { expectsql(this.queryGenerator.showTablesQuery(), {
mssql: 'SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES;' mssql: 'SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES;'
}); });
}); });
test('dropTableQuery', () => { it('dropTableQuery', function() {
expectsql(QueryGenerator.dropTableQuery('dirtyTable'), { expectsql(this.queryGenerator.dropTableQuery('dirtyTable'), {
mssql: "IF OBJECT_ID('[dirtyTable]', 'U') IS NOT NULL DROP TABLE [dirtyTable];" mssql: "IF OBJECT_ID('[dirtyTable]', 'U') IS NOT NULL DROP TABLE [dirtyTable];"
}); });
}); });
test('removeColumnQuery', () => { it('removeColumnQuery', function() {
expectsql(QueryGenerator.removeColumnQuery('myTable', 'myColumn'), { expectsql(this.queryGenerator.removeColumnQuery('myTable', 'myColumn'), {
mssql: 'ALTER TABLE [myTable] DROP COLUMN [myColumn];' mssql: 'ALTER TABLE [myTable] DROP COLUMN [myColumn];'
}); });
}); });
test('quoteIdentifier', () => { it('quoteIdentifier', function() {
expectsql(QueryGenerator.quoteIdentifier("'myTable'.'Test'"), { expectsql(this.queryGenerator.quoteIdentifier("'myTable'.'Test'"), {
mssql: '[myTable.Test]' mssql: '[myTable.Test]'
}); });
}); });
test('getForeignKeysQuery', () => { it('getForeignKeysQuery', function() {
expectsql(QueryGenerator.getForeignKeysQuery('myTable'), { expectsql(this.queryGenerator.getForeignKeysQuery('myTable'), {
mssql: "SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), referencedTableName = RTB.NAME, referencedColumnName = RCOL.NAME FROM SYS.FOREIGN_KEY_COLUMNS FKC INNER JOIN SYS.OBJECTS OBJ ON OBJ.OBJECT_ID = FKC.CONSTRAINT_OBJECT_ID INNER JOIN SYS.TABLES TB ON TB.OBJECT_ID = FKC.PARENT_OBJECT_ID INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID WHERE TB.NAME ='myTable'" mssql: "SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), referencedTableName = RTB.NAME, referencedColumnName = RCOL.NAME FROM SYS.FOREIGN_KEY_COLUMNS FKC INNER JOIN SYS.OBJECTS OBJ ON OBJ.OBJECT_ID = FKC.CONSTRAINT_OBJECT_ID INNER JOIN SYS.TABLES TB ON TB.OBJECT_ID = FKC.PARENT_OBJECT_ID INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID WHERE TB.NAME ='myTable'"
}); });
expectsql(QueryGenerator.getForeignKeysQuery('myTable', 'myDatabase'), { expectsql(this.queryGenerator.getForeignKeysQuery('myTable', 'myDatabase'), {
mssql: "SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, constraintCatalog = 'myDatabase', constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), tableCatalog = 'myDatabase', columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), referencedCatalog = 'myDatabase', referencedTableName = RTB.NAME, referencedColumnName = RCOL.NAME FROM SYS.FOREIGN_KEY_COLUMNS FKC INNER JOIN SYS.OBJECTS OBJ ON OBJ.OBJECT_ID = FKC.CONSTRAINT_OBJECT_ID INNER JOIN SYS.TABLES TB ON TB.OBJECT_ID = FKC.PARENT_OBJECT_ID INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID WHERE TB.NAME ='myTable'" mssql: "SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, constraintCatalog = 'myDatabase', constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), tableCatalog = 'myDatabase', columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), referencedCatalog = 'myDatabase', referencedTableName = RTB.NAME, referencedColumnName = RCOL.NAME FROM SYS.FOREIGN_KEY_COLUMNS FKC INNER JOIN SYS.OBJECTS OBJ ON OBJ.OBJECT_ID = FKC.CONSTRAINT_OBJECT_ID INNER JOIN SYS.TABLES TB ON TB.OBJECT_ID = FKC.PARENT_OBJECT_ID INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID WHERE TB.NAME ='myTable'"
}); });
expectsql(QueryGenerator.getForeignKeysQuery({ expectsql(this.queryGenerator.getForeignKeysQuery({
tableName: 'myTable', tableName: 'myTable',
schema: 'mySchema' schema: 'mySchema'
}, 'myDatabase'), { }, 'myDatabase'), {
...@@ -190,11 +191,11 @@ if (current.dialect.name === 'mssql') { ...@@ -190,11 +191,11 @@ if (current.dialect.name === 'mssql') {
}); });
}); });
test('getForeignKeyQuery', () => { it('getForeignKeyQuery', function() {
expectsql(QueryGenerator.getForeignKeyQuery('myTable', 'myColumn'), { expectsql(this.queryGenerator.getForeignKeyQuery('myTable', 'myColumn'), {
mssql: "SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), referencedTableName = RTB.NAME, referencedColumnName = RCOL.NAME FROM SYS.FOREIGN_KEY_COLUMNS FKC INNER JOIN SYS.OBJECTS OBJ ON OBJ.OBJECT_ID = FKC.CONSTRAINT_OBJECT_ID INNER JOIN SYS.TABLES TB ON TB.OBJECT_ID = FKC.PARENT_OBJECT_ID INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID WHERE TB.NAME ='myTable' AND COL.NAME ='myColumn'" mssql: "SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), referencedTableName = RTB.NAME, referencedColumnName = RCOL.NAME FROM SYS.FOREIGN_KEY_COLUMNS FKC INNER JOIN SYS.OBJECTS OBJ ON OBJ.OBJECT_ID = FKC.CONSTRAINT_OBJECT_ID INNER JOIN SYS.TABLES TB ON TB.OBJECT_ID = FKC.PARENT_OBJECT_ID INNER JOIN SYS.COLUMNS COL ON COL.COLUMN_ID = PARENT_COLUMN_ID AND COL.OBJECT_ID = TB.OBJECT_ID INNER JOIN SYS.TABLES RTB ON RTB.OBJECT_ID = FKC.REFERENCED_OBJECT_ID INNER JOIN SYS.COLUMNS RCOL ON RCOL.COLUMN_ID = REFERENCED_COLUMN_ID AND RCOL.OBJECT_ID = RTB.OBJECT_ID WHERE TB.NAME ='myTable' AND COL.NAME ='myColumn'"
}); });
expectsql(QueryGenerator.getForeignKeyQuery({ expectsql(this.queryGenerator.getForeignKeyQuery({
tableName: 'myTable', tableName: 'myTable',
schema: 'mySchema' schema: 'mySchema'
}, 'myColumn'), { }, 'myColumn'), {
...@@ -202,40 +203,52 @@ if (current.dialect.name === 'mssql') { ...@@ -202,40 +203,52 @@ if (current.dialect.name === 'mssql') {
}); });
}); });
test('dropForeignKeyQuery', () => { it('dropForeignKeyQuery', function() {
expectsql(QueryGenerator.dropForeignKeyQuery('myTable', 'myColumnKey'), { expectsql(this.queryGenerator.dropForeignKeyQuery('myTable', 'myColumnKey'), {
mssql: 'ALTER TABLE [myTable] DROP [myColumnKey]' mssql: 'ALTER TABLE [myTable] DROP [myColumnKey]'
}); });
}); });
test('arithmeticQuery', () => { describe('arithmeticQuery', () => {
[{ [
{
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]+ N\'bar\' OUTPUT INSERTED.* '
}, },
{ {
title: 'Should use the plus operator with where clause', title: 'Should use the plus operator with where clause',
arguments: ['+', 'myTable', { foo: 'bar' }, { bar: 'biz'}, {}], arguments: ['+', 'myTable', { foo: 'bar' }, { bar: 'biz'}, {}],
expectation: 'UPDATE myTable SET foo=foo+ \'bar\' WHERE bar = \'biz\'' expectation: 'UPDATE [myTable] SET [foo]=[foo]+ N\'bar\' OUTPUT INSERTED.* WHERE [bar] = N\'biz\''
},
{
title: 'Should use the plus operator without returning clause',
arguments: ['+', 'myTable', { foo: 'bar' }, {}, { returning: false }],
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- \'bar\' ' 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 ' 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',
arguments: ['-', 'myTable', { foo: 'bar' }, { bar: 'biz'}, {}], arguments: ['-', 'myTable', { foo: 'bar' }, { bar: 'biz'}, {}],
expectation: 'UPDATE myTable SET foo=foo- \'bar\' WHERE bar = \'biz\'' expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\' OUTPUT INSERTED.* WHERE [bar] = N\'biz\''
}].forEach(test => { },
it(test.title, () => { {
expectsql(QueryGenerator.arithmeticQuery.call(QueryGenerator, test.arguments), { title: 'Should use the minus operator without returning clause',
arguments: ['-', 'myTable', { foo: 'bar' }, {}, { returning: false }],
expectation: 'UPDATE [myTable] SET [foo]=[foo]- N\'bar\' '
}
].forEach(test => {
it(test.title, function() {
expectsql(this.queryGenerator.arithmeticQuery.apply(this.queryGenerator, test.arguments), {
mssql: test.expectation mssql: test.expectation
}); });
}); });
......
...@@ -608,20 +608,26 @@ if (dialect === 'mysql') { ...@@ -608,20 +608,26 @@ if (dialect === 'mysql') {
_.each(suites, (tests, suiteTitle) => { _.each(suites, (tests, suiteTitle) => {
describe(suiteTitle, () => { describe(suiteTitle, () => {
beforeEach(function() {
this.queryGenerator = new QueryGenerator({
sequelize: this.sequelize,
_dialect: this.sequelize.dialect
});
});
tests.forEach(test => { tests.forEach(test => {
const title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments); const title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
it(title, function() { it(title, function() {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
const context = test.context || {options: {}};
if (test.needsSequelize) { if (test.needsSequelize) {
if (_.isFunction(test.arguments[1])) test.arguments[1] = test.arguments[1](this.sequelize); if (_.isFunction(test.arguments[1])) test.arguments[1] = test.arguments[1](this.sequelize);
if (_.isFunction(test.arguments[2])) test.arguments[2] = test.arguments[2](this.sequelize); if (_.isFunction(test.arguments[2])) test.arguments[2] = test.arguments[2](this.sequelize);
} }
QueryGenerator.options = _.assign(context.options, { timezone: '+00:00' });
QueryGenerator._dialect = this.sequelize.dialect; // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
QueryGenerator.sequelize = this.sequelize; this.queryGenerator.options = Object.assign({}, this.queryGenerator.options, test.context && test.context.options || {});
QueryGenerator.setOperatorsAliases(Operators.LegacyAliases); this.queryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
const conditions = this.queryGenerator[suiteTitle].apply(this.queryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation); expect(conditions).to.deep.equal(test.expectation);
}); });
}); });
......
...@@ -984,27 +984,26 @@ if (dialect.match(/^postgres/)) { ...@@ -984,27 +984,26 @@ if (dialect.match(/^postgres/)) {
_.each(suites, (tests, suiteTitle) => { _.each(suites, (tests, suiteTitle) => {
describe(suiteTitle, () => { describe(suiteTitle, () => {
afterEach(function() { beforeEach(function() {
this.sequelize.options.quoteIdentifiers = true; this.queryGenerator = new QueryGenerator({
QueryGenerator.options.quoteIdentifiers = true; sequelize: this.sequelize,
_dialect: this.sequelize.dialect
});
}); });
tests.forEach(test => { tests.forEach(test => {
const title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments); const title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
it(title, function() { it(title, function() {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
const context = test.context || {options: {}};
if (test.needsSequelize) { if (test.needsSequelize) {
if (_.isFunction(test.arguments[1])) test.arguments[1] = test.arguments[1](this.sequelize); if (_.isFunction(test.arguments[1])) test.arguments[1] = test.arguments[1](this.sequelize);
if (_.isFunction(test.arguments[2])) test.arguments[2] = test.arguments[2](this.sequelize); if (_.isFunction(test.arguments[2])) test.arguments[2] = test.arguments[2](this.sequelize);
} }
QueryGenerator.options = _.assign(context.options, { timezone: '+00:00' }); // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
QueryGenerator._dialect = this.sequelize.dialect; this.queryGenerator.options = Object.assign({}, this.queryGenerator.options, test.context && test.context.options || {});
QueryGenerator.sequelize = this.sequelize; this.queryGenerator.setOperatorsAliases(Operators.LegacyAliases);
QueryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments); const conditions = this.queryGenerator[suiteTitle].apply(this.queryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation); expect(conditions).to.deep.equal(test.expectation);
}); });
}); });
......
...@@ -539,20 +539,26 @@ if (dialect === 'sqlite') { ...@@ -539,20 +539,26 @@ if (dialect === 'sqlite') {
_.each(suites, (tests, suiteTitle) => { _.each(suites, (tests, suiteTitle) => {
describe(suiteTitle, () => { describe(suiteTitle, () => {
beforeEach(function() {
this.queryGenerator = new QueryGenerator({
sequelize: this.sequelize,
_dialect: this.sequelize.dialect
});
});
tests.forEach(test => { tests.forEach(test => {
const title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments); const title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
it(title, function() { it(title, function() {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
const context = test.context || {options: {}};
if (test.needsSequelize) { if (test.needsSequelize) {
if (_.isFunction(test.arguments[1])) test.arguments[1] = test.arguments[1](this.sequelize); if (_.isFunction(test.arguments[1])) test.arguments[1] = test.arguments[1](this.sequelize);
if (_.isFunction(test.arguments[2])) test.arguments[2] = test.arguments[2](this.sequelize); if (_.isFunction(test.arguments[2])) test.arguments[2] = test.arguments[2](this.sequelize);
} }
QueryGenerator.options = _.assign(context.options, { timezone: '+00:00' });
QueryGenerator._dialect = this.sequelize.dialect; // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
QueryGenerator.sequelize = this.sequelize; this.queryGenerator.options = Object.assign({}, this.queryGenerator.options, test.context && test.context.options || {});
QueryGenerator.setOperatorsAliases(Operators.LegacyAliases); this.queryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
const conditions = this.queryGenerator[suiteTitle].apply(this.queryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation); expect(conditions).to.deep.equal(test.expectation);
}); });
}); });
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
const Support = require(__dirname + '/../support'), const Support = require(__dirname + '/../support'),
DataTypes = require(__dirname + '/../../../lib/data-types'), DataTypes = require(__dirname + '/../../../lib/data-types'),
util = require('util'), util = require('util'),
chai = require('chai'),
expect = chai.expect,
expectsql = Support.expectsql, expectsql = Support.expectsql,
current = Support.sequelize, current = Support.sequelize,
sql = current.dialect.QueryGenerator; sql = current.dialect.QueryGenerator;
...@@ -406,16 +404,6 @@ suite(Support.getTestDialectTeaser('SQL'), () => { ...@@ -406,16 +404,6 @@ suite(Support.getTestDialectTeaser('SQL'), () => {
}); });
}); });
suite('$raw', () => {
test('should fail on $raw', () => {
expect(() => {
sql.whereItemQuery('rank', {
$raw: 'AGHJZ'
});
}).to.throw(Error, 'The `$raw` where property is no longer supported. Use `sequelize.literal` instead.');
});
});
suite('$like', () => { suite('$like', () => {
testsql('username', { testsql('username', {
$like: '%swagger' $like: '%swagger'
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!