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

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:
only:
- master
- v3
- v5
- /^greenkeeper/.*$/
except:
- /^v\d+\.\d+\.\d+$/
......@@ -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"
script:
- 'if [ "$INSTALL_MODULE" ]; then npm install ${INSTALL_MODULE}; fi'
- 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"
jobs:
include:
- stage: test
node_js: '4'
node_js: '6'
env: DIALECT=sqlite COVERAGE=true
- stage: test
node_js: '4'
node_js: '6'
sudo: required
env: MYSQL_VER=mysql-57 DIALECT=mysql COVERAGE=true
- stage: test
node_js: '4'
node_js: '6'
sudo: required
env: POSTGRES_VER=postgres-95 DIALECT=postgres COVERAGE=true
- stage: test
node_js: '4'
node_js: '6'
sudo: required
env: POSTGRES_VER=postgres-95 DIALECT=postgres-native COVERAGE=true
- stage: test
node_js: '8'
env: DIALECT=sqlite
- stage: test
node_js: '4'
sudo: required
env: POSTGRES_VER=postgres-95 DIALECT=postgres COVERAGE=true INSTALL_MODULE="pg@6.x pg-native@1.x"
- stage: test
node_js: '6'
env: DIALECT=sqlite
- stage: release
node_js: '8'
script:
- npm run semantic-release
before_deploy:
- npm run docs
deploy:
provider: surge
project: ./esdoc/
domain: docs.sequelizejs.com
skip_cleanup: true
# - stage: release
# node_js: '8'
# script:
# - npm run semantic-release
# before_deploy:
# - npm run docs
# deploy:
# provider: surge
# project: ./esdoc/
# domain: docs.sequelizejs.com
# skip_cleanup: true
stages:
- test
- name: release
if: branch = master AND type = push AND fork = false
# - name: release
# if: branch = master AND type = push AND fork = false
......@@ -13,7 +13,7 @@ cache:
environment:
matrix:
- {NODE_VERSION: 4, DIALECT: mssql, COVERAGE: true}
- {NODE_VERSION: 6, DIALECT: mssql, COVERAGE: true}
install:
- ps: Install-Product node $env:NODE_VERSION x64
......@@ -43,3 +43,4 @@ after_test:
branches:
only:
- 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_
/**
* 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 {
super();
this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, {
options: sequelize.options,
this.QueryGenerator = new QueryGenerator({
_dialect: this,
sequelize
});
......
......@@ -7,7 +7,6 @@ const TableHints = require('../../table-hints');
const AbstractQueryGenerator = require('../abstract/query-generator');
const randomBytes = require('crypto').randomBytes;
const semver = require('semver');
const Op = require('../../operators');
/* istanbul ignore next */
......@@ -15,11 +14,7 @@ const throwMethodUndefined = function(methodName) {
throw new Error('The method "' + methodName + '" is not defined! Please add it to your sql dialect.');
};
const QueryGenerator = {
__proto__: AbstractQueryGenerator,
options: {},
dialect: 'mssql',
class MSSQLQueryGenerator extends AbstractQueryGenerator {
createSchema(schema) {
return [
'IF NOT EXISTS (SELECT schema_name',
......@@ -31,7 +26,7 @@ const QueryGenerator = {
";'",
'END;'
].join(' ');
},
}
dropSchema(schema) {
// Mimics Postgres CASCADE, will drop objects belonging to the schema
......@@ -66,7 +61,7 @@ const QueryGenerator = {
"EXEC sp_executesql N'DROP SCHEMA", this.quoteIdentifier(schema), ";'",
'END;'
].join(' ');
},
}
showSchemasQuery() {
return [
......@@ -75,7 +70,7 @@ const QueryGenerator = {
"'INFORMATION_SCHEMA', 'dbo', 'guest', 'sys', 'archive'",
')', 'AND', '"s"."name" NOT LIKE', "'db_%'"
].join(' ');
},
}
versionQuery() {
// Uses string manipulation to convert the MS Maj.Min.Patch.Build to semver Maj.Min.Patch
......@@ -84,7 +79,7 @@ const QueryGenerator = {
"SET @ms_ver = REVERSE(CONVERT(NVARCHAR(20), SERVERPROPERTY('ProductVersion')));",
"SELECT REVERSE(SUBSTRING(@ms_ver, CHARINDEX('.', @ms_ver)+1, 20)) AS 'version'"
].join(' ');
},
}
createTableQuery(tableName, attributes, options) {
const query = "IF OBJECT_ID('<%= table %>', 'U') IS NULL CREATE TABLE <%= table %> (<%= attributes %>)",
......@@ -147,7 +142,7 @@ const QueryGenerator = {
}
return _.template(query, this._templateSettings)(values).trim() + ';';
},
}
describeTableQuery(tableName, schema) {
let sql = [
......@@ -181,7 +176,7 @@ const QueryGenerator = {
}
return sql;
},
}
renameTableQuery(before, after) {
const query = 'EXEC sp_rename <%= before %>, <%= after %>;';
......@@ -189,11 +184,11 @@ const QueryGenerator = {
before: this.quoteTable(before),
after: this.quoteTable(after)
});
},
}
showTablesQuery() {
return 'SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES;';
},
}
dropTableQuery(tableName) {
const query = "IF OBJECT_ID('<%= table %>', 'U') IS NOT NULL DROP TABLE <%= table %>";
......@@ -202,7 +197,7 @@ const QueryGenerator = {
};
return _.template(query, this._templateSettings)(values).trim() + ';';
},
}
addColumnQuery(table, key, dataType) {
// FIXME: attributeToSQL SHOULD be using attributes in addColumnQuery
......@@ -221,7 +216,7 @@ const QueryGenerator = {
table: this.quoteTable(table),
attribute
});
},
}
removeColumnQuery(tableName, attributeName) {
const query = 'ALTER TABLE <%= tableName %> DROP COLUMN <%= attributeName %>;';
......@@ -229,7 +224,7 @@ const QueryGenerator = {
tableName: this.quoteTable(tableName),
attributeName: this.quoteIdentifier(attributeName)
});
},
}
changeColumnQuery(tableName, attributes) {
const query = 'ALTER TABLE <%= tableName %> <%= query %>;';
......@@ -265,7 +260,7 @@ const QueryGenerator = {
tableName: this.quoteTable(tableName),
query: finalQuery
});
},
}
renameColumnQuery(tableName, attrBefore, attributes) {
const query = "EXEC sp_rename '<%= tableName %>.<%= before %>', '<%= after %>', 'COLUMN';",
......@@ -276,7 +271,7 @@ const QueryGenerator = {
before: attrBefore,
after: newName
});
},
}
bulkInsertQuery(tableName, attrValueHashes, options, attributes) {
options = options || {};
......@@ -352,7 +347,7 @@ const QueryGenerator = {
offset += batch;
}
return commands.join(';');
},
}
updateQuery(tableName, attrValueHash, where, options, attributes) {
let sql = super.updateQuery(tableName, attrValueHash, where, options, attributes);
......@@ -361,7 +356,7 @@ const QueryGenerator = {
sql = sql.replace('UPDATE', updateArgs);
}
return sql;
},
}
upsertQuery(tableName, insertValues, updateValues, where, model) {
const targetTableAlias = this.quoteTable(`${tableName}_target`);
......@@ -478,7 +473,7 @@ const QueryGenerator = {
query = `SET IDENTITY_INSERT ${tableNameQuoted} ON; ${query} SET IDENTITY_INSERT ${tableNameQuoted} OFF;`;
}
return query;
},
}
deleteQuery(tableName, where, options) {
options = options || {};
......@@ -513,18 +508,18 @@ const QueryGenerator = {
}
return _.template(query, this._templateSettings)(replacements);
},
}
showIndexesQuery(tableName) {
const sql = "EXEC sys.sp_helpindex @objname = N'<%= tableName %>';";
return _.template(sql, this._templateSettings)({
tableName: this.quoteTable(tableName)
});
},
}
showConstraintsQuery(tableName) {
return `EXEC sp_helpconstraint @objname = ${this.escape(this.quoteTable(tableName))};`;
},
}
removeIndexQuery(tableName, indexNameOrAttributes) {
const sql = 'DROP INDEX <%= indexName %> ON <%= tableName %>';
......@@ -540,7 +535,7 @@ const QueryGenerator = {
};
return _.template(sql, this._templateSettings)(values);
},
}
attributeToSQL(attribute) {
if (!_.isPlainObject(attribute)) {
......@@ -618,7 +613,7 @@ const QueryGenerator = {
}
return template;
},
}
attributesToSQL(attributes, options) {
const result = {},
......@@ -651,36 +646,31 @@ const QueryGenerator = {
}
return result;
},
}
createTrigger() {
throwMethodUndefined('createTrigger');
},
}
dropTrigger() {
throwMethodUndefined('dropTrigger');
},
}
renameTrigger() {
throwMethodUndefined('renameTrigger');
},
}
createFunction() {
throwMethodUndefined('createFunction');
},
}
dropFunction() {
throwMethodUndefined('dropFunction');
},
}
renameFunction() {
throwMethodUndefined('renameFunction');
},
quoteIdentifier(identifier) {
if (identifier === '*') return identifier;
return '[' + identifier.replace(/[\[\]']+/g, '') + ']';
},
}
/**
* Generate common SQL prefix for ForeignKeysQuery.
......@@ -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.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';
},
}
/**
* Generates an SQL query that returns all foreign keys details of a table.
......@@ -723,7 +713,7 @@ const QueryGenerator = {
sql += ' AND SCHEMA_NAME(TB.SCHEMA_ID) =' + wrapSingleQuote(table.schema);
}
return sql;
},
}
getForeignKeyQuery(table, attributeName) {
const tableName = table.tableName || table;
......@@ -736,7 +726,7 @@ const QueryGenerator = {
}
return sql;
},
}
getPrimaryKeyConstraintQuery(table, attributeName) {
const tableName = wrapSingleQuote(table.tableName || table);
......@@ -754,14 +744,14 @@ const QueryGenerator = {
`AND K.COLUMN_NAME = ${wrapSingleQuote(attributeName)}`,
`AND K.TABLE_NAME = ${tableName};`
].join(' ');
},
}
dropForeignKeyQuery(tableName, foreignKey) {
return _.template('ALTER TABLE <%= table %> DROP <%= key %>', this._templateSettings)({
table: this.quoteTable(tableName),
key: this.quoteIdentifier(foreignKey)
});
},
}
getDefaultConstraintQuery(tableName, attributeName) {
const sql = 'SELECT name FROM SYS.DEFAULT_CONSTRAINTS ' +
......@@ -772,7 +762,7 @@ const QueryGenerator = {
table: this.quoteTable(tableName),
column: attributeName
});
},
}
dropConstraintQuery(tableName, constraintName) {
const sql = 'ALTER TABLE <%= table %> DROP CONSTRAINT <%= constraint %>;';
......@@ -780,19 +770,19 @@ const QueryGenerator = {
table: this.quoteTable(tableName),
constraint: this.quoteIdentifier(constraintName)
});
},
}
setAutocommitQuery() {
return '';
},
}
setIsolationLevelQuery() {
},
}
generateTransactionId() {
return randomBytes(10).toString('hex');
},
}
startTransactionQuery(transaction) {
if (transaction.parent) {
......@@ -800,7 +790,7 @@ const QueryGenerator = {
}
return 'BEGIN TRANSACTION;';
},
}
commitTransactionQuery(transaction) {
if (transaction.parent) {
......@@ -808,7 +798,7 @@ const QueryGenerator = {
}
return 'COMMIT TRANSACTION;';
},
}
rollbackTransactionQuery(transaction) {
if (transaction.parent) {
......@@ -816,7 +806,7 @@ const QueryGenerator = {
}
return 'ROLLBACK TRANSACTION;';
},
}
selectFromTableFragment(options, model, attributes, tables, mainTableAs, where) {
let topFragment = '';
......@@ -874,7 +864,7 @@ const QueryGenerator = {
}
return mainFragment;
},
}
addLimitAndOffset(options, model) {
// Skip handling of limit and offset as postfixes for older SQL Server versions
......@@ -907,7 +897,7 @@ const QueryGenerator = {
}
return fragment;
},
}
booleanValue(value) {
return value ? 1 : 0;
......@@ -919,4 +909,4 @@ function wrapSingleQuote(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 {
super();
this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, {
options: sequelize.options,
this.QueryGenerator = new QueryGenerator({
_dialect: this,
sequelize
});
......
......@@ -6,26 +6,27 @@ const AbstractQueryGenerator = require('../abstract/query-generator');
const util = require('util');
const Op = require('../../operators');
const QueryGenerator = {
__proto__: AbstractQueryGenerator,
dialect: 'mysql',
class MySQLQueryGenerator extends AbstractQueryGenerator {
constructor(options) {
super(options);
OperatorMap: Object.assign({}, AbstractQueryGenerator.OperatorMap, {
this.OperatorMap = Object.assign({}, this.OperatorMap, {
[Op.regexp]: 'REGEXP',
[Op.notRegexp]: 'NOT REGEXP'
}),
});
}
createSchema() {
return 'SHOW TABLES';
},
}
showSchemasQuery() {
return 'SHOW TABLES';
},
}
versionQuery() {
return 'SELECT VERSION() as `version`';
},
}
createTableQuery(tableName, attributes, options) {
options = _.extend({
......@@ -100,11 +101,11 @@ const QueryGenerator = {
}
return _.template(query, this._templateSettings)(values).trim() + ';';
},
}
showTablesQuery() {
return 'SHOW TABLES;';
},
}
addColumnQuery(table, key, dataType) {
const definition = this.attributeToSQL(dataType, {
......@@ -114,11 +115,11 @@ const QueryGenerator = {
});
return `ALTER TABLE ${this.quoteTable(table)} ADD ${this.quoteIdentifier(key)} ${definition};`;
},
}
removeColumnQuery(tableName, attributeName) {
return `ALTER TABLE ${this.quoteTable(tableName)} DROP ${this.quoteIdentifier(attributeName)};`;
},
}
changeColumnQuery(tableName, attributes) {
const attrString = [];
......@@ -146,7 +147,7 @@ const QueryGenerator = {
}
return `ALTER TABLE ${this.quoteTable(tableName)} ${finalQuery};`;
},
}
renameColumnQuery(tableName, attrBefore, attributes) {
const attrString = [];
......@@ -157,7 +158,7 @@ const QueryGenerator = {
}
return `ALTER TABLE ${this.quoteTable(tableName)} CHANGE ${attrString.join(', ')};`;
},
}
handleSequelizeMethod(smth, tableName, factory, options, prepend) {
if (smth instanceof Utils.Json) {
......@@ -218,7 +219,7 @@ const QueryGenerator = {
}
return super.handleSequelizeMethod(smth, tableName, factory, options, prepend);
},
}
_toJSONValue(value) {
// true/false are stored as strings in mysql
......@@ -230,7 +231,7 @@ const QueryGenerator = {
return 'null';
}
return value;
},
}
upsertQuery(tableName, insertValues, updateValues, where, model, options) {
options.onDuplicate = 'UPDATE ';
......@@ -241,7 +242,7 @@ const QueryGenerator = {
}).join(', ');
return this.insertQuery(tableName, insertValues, model.rawAttributes, options);
},
}
deleteQuery(tableName, where, options, model) {
options = options || {};
......@@ -268,11 +269,11 @@ const QueryGenerator = {
query += limit;
return query;
},
}
showIndexesQuery(tableName, options) {
return 'SHOW INDEX FROM ' + this.quoteTable(tableName) + ((options || {}).database ? ' FROM `' + options.database + '`' : '');
},
}
showConstraintsQuery(table, constraintName) {
const tableName = table.tableName || table;
......@@ -298,7 +299,7 @@ const QueryGenerator = {
}
return sql + ';';
},
}
removeIndexQuery(tableName, indexNameOrAttributes) {
let indexName = indexNameOrAttributes;
......@@ -308,7 +309,7 @@ const QueryGenerator = {
}
return `DROP INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteTable(tableName)}`;
},
}
attributeToSQL(attribute, options) {
if (!_.isPlainObject(attribute)) {
......@@ -375,7 +376,7 @@ const QueryGenerator = {
}
return template;
},
}
attributesToSQL(attributes, options) {
const result = {};
......@@ -386,12 +387,7 @@ const QueryGenerator = {
}
return result;
},
quoteIdentifier(identifier) {
if (identifier === '*') return identifier;
return Utils.addTicks(Utils.removeTicks(identifier, '`'), '`');
},
}
/**
* Check whether the statmement is json function or simple path
......@@ -458,27 +454,7 @@ const QueryGenerator = {
// return true if the statement has valid json function
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
......@@ -500,7 +476,7 @@ const QueryGenerator = {
'REFERENCED_TABLE_NAME as referencedTableName',
'REFERENCED_COLUMN_NAME as referencedColumnName',
].join(',');
},
}
/**
* Generates an SQL query that returns all foreign keys of a table.
......@@ -513,7 +489,7 @@ const QueryGenerator = {
getForeignKeysQuery(tableName, schemaName) {
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 */
},
}
/**
* Generates an SQL query that returns the foreign key constraint of a given column.
......@@ -537,7 +513,7 @@ const QueryGenerator = {
+ ' AND COLUMN_NAME = ' + wrapSingleQuote(columnName)
+ ' AND REFERENCED_TABLE_NAME IS NOT NULL'
+ ')';
},
}
/**
* Generates an SQL query that removes a foreign key from a table.
......@@ -557,4 +533,4 @@ function wrapSingleQuote(identifier) {
return Utils.addTicks(identifier, '\'');
}
module.exports = QueryGenerator;
module.exports = MySQLQueryGenerator;
......@@ -12,8 +12,7 @@ class PostgresDialect extends AbstractDialect {
super();
this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, {
options: sequelize.options,
this.QueryGenerator = new QueryGenerator({
_dialect: this,
sequelize
});
......
......@@ -12,8 +12,7 @@ class SqliteDialect extends AbstractDialect {
super();
this.sequelize = sequelize;
this.connectionManager = new ConnectionManager(this, sequelize);
this.QueryGenerator = _.extend({}, QueryGenerator, {
options: sequelize.options,
this.QueryGenerator = new QueryGenerator({
_dialect: this,
sequelize
});
......
......@@ -7,22 +7,18 @@ const _ = require('lodash');
const MySqlQueryGenerator = require('../mysql/query-generator');
const AbstractQueryGenerator = require('../abstract/query-generator');
const QueryGenerator = {
__proto__: MySqlQueryGenerator,
options: {},
dialect: 'sqlite',
class SQLiteQueryGenerator extends MySqlQueryGenerator {
createSchema() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
},
}
showSchemasQuery() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
},
}
versionQuery() {
return 'SELECT sqlite_version() as `version`';
},
}
createTableQuery(tableName, attributes, options) {
options = options || {};
......@@ -73,11 +69,11 @@ const QueryGenerator = {
const sql = `CREATE TABLE IF NOT EXISTS ${table} (${attrStr});`;
return this.replaceBooleanDefaults(sql);
},
}
booleanValue(value) {
return value ? 1 : 0;
},
}
/**
* Check whether the statmement is json function or simple path
......@@ -136,26 +132,7 @@ const QueryGenerator = {
// return true if the statement has valid json function
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
_toJSONValue(value) {
......@@ -165,7 +142,7 @@ const QueryGenerator = {
return value.map(val => val.toISOString());
}
return value;
},
}
handleSequelizeMethod(smth, tableName, factory, options, prepend) {
......@@ -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) {
const attributes = {};
......@@ -214,11 +191,11 @@ const QueryGenerator = {
const sql = `ALTER TABLE ${this.quoteTable(table)} ADD ${attribute};`;
return this.replaceBooleanDefaults(sql);
},
}
showTablesQuery() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
},
}
upsertQuery(tableName, insertValues, updateValues, where, model, options) {
options.ignoreDuplicates = true;
......@@ -226,7 +203,7 @@ const QueryGenerator = {
const sql = this.insertQuery(tableName, insertValues, model.rawAttributes, options) + ' ' + this.updateQuery(tableName, updateValues, where, options, model.rawAttributes);
return sql;
},
}
updateQuery(tableName, attrValueHash, where, options, attributes) {
options = options || {};
......@@ -256,7 +233,7 @@ const QueryGenerator = {
} else {
return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} ${this.whereQuery(where, options)}`;
}
},
}
deleteQuery(tableName, where, options, model) {
options = options || {};
......@@ -281,7 +258,7 @@ const QueryGenerator = {
}
return `DELETE FROM ${this.quoteTable(tableName)} ${whereClause}`;
},
}
attributesToSQL(attributes) {
const result = {};
......@@ -345,11 +322,11 @@ const QueryGenerator = {
}
return result;
},
}
showIndexesQuery(tableName) {
return `PRAGMA INDEX_LIST(${this.quoteTable(tableName)})`;
},
}
showConstraintsQuery(tableName, constraintName) {
let sql = `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}'`;
......@@ -359,7 +336,7 @@ const QueryGenerator = {
}
return sql + ';';
},
}
removeIndexQuery(tableName, indexNameOrAttributes) {
let indexName = indexNameOrAttributes;
......@@ -369,7 +346,7 @@ const QueryGenerator = {
}
return `DROP INDEX IF EXISTS ${this.quoteIdentifier(indexName)}`;
},
}
describeTableQuery(tableName, schema, schemaDelimiter) {
const table = {
......@@ -378,11 +355,11 @@ const QueryGenerator = {
tableName
};
return `PRAGMA TABLE_INFO(${this.quoteTable(this.addSchema(table))});`;
},
}
describeCreateTableQuery(tableName) {
return `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}';`;
},
}
removeColumnQuery(tableName, attributes) {
......@@ -409,7 +386,7 @@ const QueryGenerator = {
+ this.createTableQuery(tableName, attributes)
+ `INSERT INTO ${quotedTableName} SELECT ${attributeNames} FROM ${quotedBackupTableName};`
+ `DROP TABLE ${quotedBackupTableName};`;
},
}
_alterConstraintQuery(tableName, attributes, createTableSql) {
let backupTableName;
......@@ -432,7 +409,7 @@ const QueryGenerator = {
+ `INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};`
+ `DROP TABLE ${quotedTableName};`
+ `ALTER TABLE ${quotedBackupTableName} RENAME TO ${quotedTableName};`;
},
}
renameColumnQuery(tableName, attrNameBefore, attrNameAfter, attributes) {
......@@ -462,7 +439,7 @@ const QueryGenerator = {
+ this.createTableQuery(tableName, attributes)
+ `INSERT INTO ${quotedTableName} SELECT ${attributeNamesExport} FROM ${quotedBackupTableName};`
+ `DROP TABLE ${quotedBackupTableName};`;
},
}
startTransactionQuery(transaction) {
if (transaction.parent) {
......@@ -470,12 +447,12 @@ const QueryGenerator = {
}
return 'BEGIN ' + transaction.options.type + ' TRANSACTION;';
},
}
setAutocommitQuery() {
// SQLite does not support SET autocommit
return null;
},
}
setIsolationLevelQuery(value) {
switch (value) {
......@@ -490,16 +467,11 @@ const QueryGenerator = {
default:
throw new Error('Unknown isolation level: ' + value);
}
},
}
replaceBooleanDefaults(sql) {
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.
......@@ -513,4 +485,4 @@ const QueryGenerator = {
}
};
module.exports = QueryGenerator;
module.exports = SQLiteQueryGenerator;
\ No newline at end of file
......@@ -77,15 +77,11 @@ class Model {
const deletedAtCol = model._timestampAttributes.deletedAt;
const deletedAtAttribute = model.rawAttributes[deletedAtCol];
const deletedAtObject = {};
const now = Utils.now(this.sequelize.options.dialect);
let deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
deletedAtDefaultValue = deletedAtDefaultValue || {
[Op.or]: {
[Op.gt]: now,
[Op.eq]: null
}
};
deletedAtObject[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue;
......@@ -1064,10 +1060,6 @@ class Model {
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.prototype._hasCustomGetters = Object.keys(this.prototype._customGetters).length;
......@@ -4204,9 +4196,6 @@ class Model {
// Aliases
Model.prototype.updateAttributes = Model.prototype.update;
Model._conformOptions = Model._conformOptions;
Model._validateIncludedElements = Model._validateIncludedElements;
Model._expandIncludeAll = Model._expandIncludeAll;
Model.findByPrimary = Model.findById;
Model.find = Model.findOne;
Model.findAndCountAll = Model.findAndCount;
......
......@@ -78,8 +78,7 @@ const Op = {
values: Symbol.for('values'),
col: Symbol.for('col'),
placeholder: Symbol.for('placeholder'),
join: Symbol.for('join'),
raw: Symbol.for('raw') //deprecated remove by v5.0
join: Symbol.for('join')
};
const Aliases = {
......@@ -117,7 +116,6 @@ const Aliases = {
$all: Op.all,
$values: Op.values,
$col: Op.col,
$raw: Op.raw //deprecated remove by v5.0
};
const LegacyAliases = { //deprecated remove by v5.0
......
......@@ -4,29 +4,11 @@
"version": "0.0.0-development",
"author": "Sascha Depold <sascha@depold.com>",
"contributors": [
{
"name": "Sascha Depold",
"email": "sascha@depold.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"
}
"Sascha Depold <sascha@depold.com>",
"Jan Aagaard Meier <janzeh@gmail.com>",
"Daniel Durante <me@danieldurante.com>",
"Mick Hansen <mick.kasper.hansen@gmail.com>",
"Sushant Dhiman <sushantdhiman@outlook.com>"
],
"repository": {
"type": "git",
......@@ -55,6 +37,8 @@
"wkx": "^0.4.1"
},
"devDependencies": {
"@semantic-release/github": "^4.2.6",
"@semantic-release/npm": "^3.2.2",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"chai-datetime": "^1.5.0",
......@@ -144,18 +128,12 @@
"semantic-release": "semantic-release"
},
"engines": {
"node": ">=4.0.0"
"node": ">=6.0.0"
},
"license": "MIT",
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"release": {
"verifyConditions": [
"@semantic-release/npm",
"@semantic-release/github"
]
}
}
......@@ -15,56 +15,56 @@ if (dialect === 'mysql') {
username: { type: DataTypes.STRING, unique: true }
}, { 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() {
const User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, defaultValue: 'foo'}
}, { 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() {
const User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, allowNull: 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() {
const User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, primaryKey: true}
}, { 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() {
const User1 = this.sequelize.define('User' + config.rand(), {});
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(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(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.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() {
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() {
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() {
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() {
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'), () => {
describe('schematic support', () => {
beforeEach(function() {
const self = this;
this.UserPublic = this.sequelize.define('UserPublic', {
age: Sequelize.INTEGER
});
......@@ -2208,11 +2206,11 @@ describe(Support.getTestDialectTeaser('Model'), () => {
age: Sequelize.INTEGER
});
return self.sequelize.dropAllSchemas().then(() => {
return self.sequelize.createSchema('schema_test').then(() => {
return self.sequelize.createSchema('special').then(() => {
return self.UserSpecial.schema('special').sync({force: true}).then(UserSpecialSync => {
self.UserSpecialSync = UserSpecialSync;
return this.sequelize.dropAllSchemas().then(() => {
return this.sequelize.createSchema('schema_test').then(() => {
return this.sequelize.createSchema('special').then(() => {
return this.UserSpecial.schema('special').sync({force: true}).then(UserSpecialSync => {
this.UserSpecialSync = UserSpecialSync;
});
});
});
......
......@@ -5,7 +5,6 @@ const chai = require('chai'),
Sequelize = require('../../../index'),
Promise = Sequelize.Promise,
expect = chai.expect,
moment = require('moment'),
Support = require(__dirname + '/../support'),
dialect = Support.getTestDialect(),
DataTypes = require(__dirname + '/../../../lib/data-types'),
......@@ -991,31 +990,6 @@ describe(Support.getTestDialectTeaser('Model'), () => {
})).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'), () => {
if (dialect === 'sqlite') {
expect(created).to.be.undefined;
} else {
expect(created).to.be.okay;
expect(created).to.be.ok;
}
});
});
......
......@@ -184,9 +184,11 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
if (Support.getTestDialect() === 'postgres') {
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 = {
metadata: {
language: 'icelandic',
......@@ -195,23 +197,23 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
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';
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';
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 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 = {
},
getAbstractQueryGenerator(sequelize) {
return Object.assign(
{},
AbstractQueryGenerator,
{options: sequelize.options, _dialect: sequelize.dialect, sequelize, quoteIdentifier(identifier) { return identifier; }}
);
class ModdedQueryGenerator extends AbstractQueryGenerator {
quoteIdentifier(x) {
return x;
}
}
const queryGenerator = new ModdedQueryGenerator({
sequelize,
_dialect: sequelize.dialect
});
return queryGenerator;
},
getTestDialect() {
......
......@@ -6,15 +6,6 @@ const chai = require('chai'),
getAbstractQueryGenerator = require(__dirname + '/../../support').getAbstractQueryGenerator;
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', () => {
it('should generate correct query for Symbol operators', function() {
const QG = getAbstractQueryGenerator(this.sequelize);
......
......@@ -608,20 +608,26 @@ if (dialect === 'mysql') {
_.each(suites, (tests, suiteTitle) => {
describe(suiteTitle, () => {
beforeEach(function() {
this.queryGenerator = new QueryGenerator({
sequelize: this.sequelize,
_dialect: this.sequelize.dialect
});
});
tests.forEach(test => {
const title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
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 (_.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);
}
QueryGenerator.options = _.assign(context.options, { timezone: '+00:00' });
QueryGenerator._dialect = this.sequelize.dialect;
QueryGenerator.sequelize = this.sequelize;
QueryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
this.queryGenerator.options = Object.assign({}, this.queryGenerator.options, test.context && test.context.options || {});
this.queryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = this.queryGenerator[suiteTitle].apply(this.queryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation);
});
});
......
......@@ -984,27 +984,26 @@ if (dialect.match(/^postgres/)) {
_.each(suites, (tests, suiteTitle) => {
describe(suiteTitle, () => {
afterEach(function() {
this.sequelize.options.quoteIdentifiers = true;
QueryGenerator.options.quoteIdentifiers = true;
beforeEach(function() {
this.queryGenerator = new QueryGenerator({
sequelize: this.sequelize,
_dialect: this.sequelize.dialect
});
});
tests.forEach(test => {
const title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
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 (_.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);
}
QueryGenerator.options = _.assign(context.options, { timezone: '+00:00' });
QueryGenerator._dialect = this.sequelize.dialect;
QueryGenerator.sequelize = this.sequelize;
QueryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
this.queryGenerator.options = Object.assign({}, this.queryGenerator.options, test.context && test.context.options || {});
this.queryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = this.queryGenerator[suiteTitle].apply(this.queryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation);
});
});
......
......@@ -539,20 +539,26 @@ if (dialect === 'sqlite') {
_.each(suites, (tests, suiteTitle) => {
describe(suiteTitle, () => {
beforeEach(function() {
this.queryGenerator = new QueryGenerator({
sequelize: this.sequelize,
_dialect: this.sequelize.dialect
});
});
tests.forEach(test => {
const title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + JSON.stringify(test.arguments);
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 (_.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);
}
QueryGenerator.options = _.assign(context.options, { timezone: '+00:00' });
QueryGenerator._dialect = this.sequelize.dialect;
QueryGenerator.sequelize = this.sequelize;
QueryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments);
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
this.queryGenerator.options = Object.assign({}, this.queryGenerator.options, test.context && test.context.options || {});
this.queryGenerator.setOperatorsAliases(Operators.LegacyAliases);
const conditions = this.queryGenerator[suiteTitle].apply(this.queryGenerator, test.arguments);
expect(conditions).to.deep.equal(test.expectation);
});
});
......
......@@ -3,8 +3,6 @@
const Support = require(__dirname + '/../support'),
DataTypes = require(__dirname + '/../../../lib/data-types'),
util = require('util'),
chai = require('chai'),
expect = chai.expect,
expectsql = Support.expectsql,
current = Support.sequelize,
sql = current.dialect.QueryGenerator;
......@@ -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', () => {
testsql('username', {
$like: '%swagger'
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!