Skip to content

Commit 912ec79

Browse files
committed
Merge pull request sequelize#3733 from BridgeAR/testcase
Fix regression in util.toDefaultValue not returning the data types. Closes sequelize#3324, sequelize#3002 and sequelize#3410
2 parents cc6ee29 + 8b9c50f commit 912ec79

File tree

10 files changed

+100
-33
lines changed

10 files changed

+100
-33
lines changed

changelog.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
# 3.1.0
55

6-
- [BUG] Fix an issue with the build in isIP validator returning false negatives [#3756](https://github.com/sequelize/sequelize/pull/3756)
76
- [ADDED] It is now possible to defer constraints in PostgreSQL by added a property `deferrable` to the `references` object of a field.
7+
- [BUG] Fix an issue with the build in isIP validator returning false negatives [#3756](https://github.com/sequelize/sequelize/pull/3756)
8+
- [BUG] Fix regression in util.toDefaultValue not returning the data types [#3733](https://github.com/sequelize/sequelize/pull/3733)
89

910
# 3.0.1
1011

lib/associations/mixin.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ Mixin.belongsTo = singleLinked(BelongsTo);
222222
*
223223
* Similarily, when fetching through a join table with custom attributes, these attributes will be available as an object with the name of the through model.
224224
* ```js
225-
* user.getProjects().success(function (projects) {
225+
* user.getProjects().then(function (projects) {
226226
* var p1 = projects[0]
227227
* p1.userprojects.started // Is this project started yet?
228228
* })

lib/dialects/abstract/query-generator.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ var Utils = require('../../utils')
66
, DataTypes = require('../../data-types')
77
, _ = require('lodash')
88
, util = require('util')
9-
, Dottie = require('dottie');
9+
, Dottie = require('dottie')
10+
, uuid = require('node-uuid');
1011

1112
module.exports = (function() {
1213
var QueryGenerator = {
@@ -212,7 +213,7 @@ module.exports = (function() {
212213
//valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc() RETURNS SETOF <%= table %> AS $body$ BEGIN RETURN QUERY ' + valueQuery + '; EXCEPTION ' + options.exception + ' END; $body$ LANGUAGE plpgsql; SELECT * FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();';
213214

214215
// >= 9.2 - Use a UUID but prefix with 'func_' (numbers first not allowed)
215-
var delimiter = '$func_' + Utils.generateUUID().replace(/-/g, '') + '$';
216+
var delimiter = '$func_' + uuid.v4().replace(/-/g, '') + '$';
216217

217218
options.exception = 'WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL;';
218219
valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response <%= table %>, OUT sequelize_caught_exception text) RETURNS RECORD AS ' + delimiter +

lib/instance.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,15 @@ module.exports = (function() {
576576

577577
var values = Utils.mapValueFieldNames(this.dataValues, options.fields, this.Model)
578578
, query = null
579-
, args = [];
579+
, args = []
580+
, now = Utils.now(this.sequelize.options.dialect);
580581

581582
if (updatedAtAttr && !options.silent) {
582-
self.dataValues[updatedAtAttr] = values[self.Model.rawAttributes[updatedAtAttr].field || updatedAtAttr] = self.Model.__getTimestamp(updatedAtAttr);
583+
self.dataValues[updatedAtAttr] = values[self.Model.rawAttributes[updatedAtAttr].field || updatedAtAttr] = self.Model.$getDefaultTimestamp(updatedAtAttr) || now;
583584
}
584585

585586
if (self.isNewRecord && createdAtAttr && !values[createdAtAttr]) {
586-
self.dataValues[createdAtAttr] = values[self.Model.rawAttributes[createdAtAttr].field || createdAtAttr] = self.Model.__getTimestamp(createdAtAttr);
587+
self.dataValues[createdAtAttr] = values[self.Model.rawAttributes[createdAtAttr].field || createdAtAttr] = self.Model.$getDefaultTimestamp(createdAtAttr) || now;
587588
}
588589

589590
if (self.isNewRecord) {
@@ -881,7 +882,7 @@ module.exports = (function() {
881882
}
882883

883884
if (updatedAtAttr && !values[updatedAtAttr]) {
884-
options.attributes[updatedAtAttr] = this.Model.__getTimestamp(updatedAtAttr);
885+
options.attributes[updatedAtAttr] = this.Model.$getDefaultTimestamp(updatedAtAttr) || Utils.now(this.sequelize.options.dialect);
885886
}
886887

887888
Object.keys(values).forEach(function(attr) {

lib/model.js

+11-9
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ module.exports = (function() {
327327
if (definition.hasOwnProperty('defaultValue')) {
328328
if (typeof definition.defaultValue === 'function' && (
329329
definition.defaultValue === DataTypes.NOW ||
330-
definition.defaultValue === DataTypes.UUIDV4 ||
330+
definition.defaultValue === DataTypes.UUIDV1 ||
331331
definition.defaultValue === DataTypes.UUIDV4
332332
)) {
333333
definition.defaultValue = new definition.defaultValue();
@@ -909,7 +909,8 @@ module.exports = (function() {
909909
* where: ...,
910910
* limit: 12,
911911
* offset: 12
912-
* }).success(function (result) {
912+
* }).then(function (result) {
913+
* ...
913914
* })
914915
* ```
915916
* In the above example, `result.rows` will contain rows 13 through 24, while `result.count` will return the total number of rows that matched your query.
@@ -1148,8 +1149,8 @@ module.exports = (function() {
11481149
* @param {Object} options
11491150
* @param {Object} options.where where A hash of search attributes.
11501151
* @param {Object} [options.defaults] Default values to use if creating a new instance
1151-
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
11521152
*
1153+
* @see {Model#findAll} for a specification of find and options
11531154
* @return {Promise<Instance,created>}
11541155
*/
11551156
Model.prototype.findOrCreate = function(options) {
@@ -1257,11 +1258,13 @@ module.exports = (function() {
12571258
// Map field names
12581259
values = Utils.mapValueFieldNames(instance.dataValues, options.fields, this);
12591260

1261+
var now = Utils.now(this.sequelize.options.dialect);
1262+
12601263
if (createdAtAttr && !values[createdAtAttr]) {
1261-
values[createdAtAttr] = this.__getTimestamp(createdAtAttr);
1264+
values[createdAtAttr] = this.$getDefaultTimestamp(createdAtAttr) || now;
12621265
}
12631266
if (updatedAtAttr && !values[updatedAtAttr]) {
1264-
values[updatedAtAttr] = this.__getTimestamp(updatedAtAttr);
1267+
values[updatedAtAttr] = this.$getDefaultTimestamp(updatedAtAttr) || now;
12651268
}
12661269

12671270
// Build adds a null value for the primary key, if none was given by the user.
@@ -1633,7 +1636,7 @@ module.exports = (function() {
16331636
}
16341637

16351638
if (this._timestampAttributes.updatedAt) {
1636-
values[this._timestampAttributes.updatedAt] = this.__getTimestamp(this._timestampAttributes.updatedAt);
1639+
values[this._timestampAttributes.updatedAt] = this.$getDefaultTimestamp(this._timestampAttributes.updatedAt) || Utils.now(this.sequelize.options.dialect);
16371640
}
16381641

16391642
var instances
@@ -1789,12 +1792,11 @@ module.exports = (function() {
17891792
return this.QueryInterface.describeTable(this.tableName, schema || this.options.schema || undefined);
17901793
};
17911794

1792-
Model.prototype.__getTimestamp = function(attr) {
1795+
Model.prototype.$getDefaultTimestamp = function(attr) {
17931796
if (!!this.rawAttributes[attr] && !!this.rawAttributes[attr].defaultValue) {
17941797
return Utils.toDefaultValue(this.rawAttributes[attr].defaultValue);
1795-
} else {
1796-
return Utils.now(this.sequelize.options.dialect);
17971798
}
1799+
return undefined;
17981800
};
17991801

18001802
// Inject current scope into options. Includes should have been conformed (conformOptions) before calling this

lib/transaction.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

3-
var Utils = require('./utils');
3+
var Utils = require('./utils')
4+
, uuid = require('node-uuid');
45

56
/**
67
* The transaction object is used to identify a running transaction. It is created by calling `Sequelize.transaction()`.
@@ -22,15 +23,15 @@ var Transaction = module.exports = function(sequelize, options) {
2223
autocommit: true,
2324
isolationLevel: sequelize.options.isolationLevel
2425
}, options || {});
25-
this.id = this.options.transaction ? this.options.transaction.id : Utils.generateUUID();
26+
this.id = this.options.transaction ? this.options.transaction.id : uuid.v4();
2627

2728
if (this.options.transaction) {
2829
this.id = this.options.transaction.id;
2930
this.options.transaction.savepoints.push(this);
3031
this.name = this.id + '-savepoint-' + this.options.transaction.savepoints.length;
3132
this.parent = this.options.transaction;
3233
} else {
33-
this.id = this.name = Utils.generateUUID();
34+
this.id = this.name = uuid.v4();
3435
}
3536
};
3637

lib/utils.js

+9-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ var DataTypes = require('./data-types')
55
, lodash = require('lodash')
66
, ParameterValidator = require('./utils/parameter-validator')
77
, inflection = require('inflection')
8-
, _ = require('lodash')
98
, dottie = require('dottie')
109
, uuid = require('node-uuid')
1110
, deprecate = require('depd')('Utils');
@@ -139,7 +138,7 @@ var Utils = module.exports = {
139138
delete attributes[attribute];
140139
}
141140

142-
if (_.isPlainObject(attributes[attribute])) {
141+
if (lodash.isPlainObject(attributes[attribute])) {
143142
attributes[attribute] = Utils.mapOptionFieldNames({
144143
where: attributes[attribute]
145144
}, Model).where;
@@ -220,8 +219,13 @@ var Utils = module.exports = {
220219
},
221220

222221
toDefaultValue: function(value) {
223-
if (lodash.isFunction(value)) {
224-
return value();
222+
if (typeof value === 'function') {
223+
var tmp = value();
224+
if (tmp instanceof DataTypes.ABSTRACT) {
225+
return tmp.toSql();
226+
} else {
227+
return tmp;
228+
}
225229
} else if (value instanceof DataTypes.UUIDV1) {
226230
return uuid.v1();
227231
} else if (value instanceof DataTypes.UUIDV4) {
@@ -392,19 +396,12 @@ var Utils = module.exports = {
392396
this.logic = logic;
393397
},
394398

395-
generateUUID: function() {
396-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
397-
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
398-
return v.toString(16);
399-
});
400-
},
401-
402399
validateParameter: function(value, expectation, options) {
403400
return ParameterValidator.check(value, expectation, options);
404401
},
405402

406403
formatReferences: function (obj) {
407-
if (!_.isPlainObject(obj.references)) {
404+
if (!lodash.isPlainObject(obj.references)) {
408405
deprecate('Non-object references property found. Support for that will be removed in version 4. Expected { references: { model: "value", key: "key" } } instead of { references: "value", referencesKey: "key" }.');
409406
obj.references = { model: obj.references, key: obj.referencesKey, deferrable: obj.referencesDeferrable };
410407
obj.referencesKey = undefined;

test/integration/model/json.test.js

+35-1
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,46 @@ describe(Support.getTestDialectTeaser('Model'), function() {
1919
type: DataTypes.JSONB,
2020
field: 'event_data',
2121
index: true
22-
}
22+
},
23+
json: DataTypes.JSON
2324
});
2425

2526
return this.Event.sync({force: true});
2627
});
2728

29+
if (current.dialect.supports.lock) {
30+
it('findOrCreate supports transactions, json and locks', function() {
31+
var self = this;
32+
return current.transaction().then(function(t) {
33+
return self.Event.findOrCreate({
34+
where: {
35+
json: { some: { input: 'Hello' } }
36+
},
37+
defaults: {
38+
json: { some: { input: 'Hello' }, input: [1, 2, 3] },
39+
data: { some: { input: 'There' }, input: [4, 5, 6] }
40+
},
41+
transaction: t,
42+
lock: t.LOCK.UPDATE,
43+
logging: function (sql) {
44+
if (sql.indexOf('SELECT') !== -1 && sql.indexOf('CREATE') === -1) {
45+
expect(sql.indexOf('FOR UPDATE')).not.to.be.equal(-1);
46+
}
47+
}
48+
}).then(function() {
49+
return self.Event.count().then(function(count) {
50+
expect(count).to.equal(0);
51+
return t.commit().then(function() {
52+
return self.Event.count().then(function(count) {
53+
expect(count).to.equal(1);
54+
});
55+
});
56+
});
57+
});
58+
});
59+
});
60+
}
61+
2862
it('should create an instance with JSONB data', function () {
2963
return this.Event.create({
3064
data: {

test/unit/sql/data-types.test.js

+8
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
8888
mysql: 'CHAR(36) BINARY',
8989
sqlite: 'UUID'
9090
});
91+
92+
testsql('UUIDV1', DataTypes.UUIDV1, {
93+
default: 'UUIDV1'
94+
});
95+
96+
testsql('UUIDV4', DataTypes.UUIDV4, {
97+
default: 'UUIDV4'
98+
});
9199
});
92100

93101
suite('NOW', function () {

test/unit/utils.js

+22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ var chai = require('chai')
99
// Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation
1010

1111
suite(Support.getTestDialectTeaser('Utils'), function() {
12+
13+
suite('toDefaultValue', function () {
14+
test('return plain data types', function () {
15+
expect(Utils.toDefaultValue(DataTypes.UUIDV4)).to.equal('UUIDV4');
16+
});
17+
test('return uuid v1', function () {
18+
expect(/^[a-z0-9\-]{36}$/.test(Utils.toDefaultValue(DataTypes.UUIDV1()))).to.be.equal(true);
19+
});
20+
test('return uuid v4', function () {
21+
expect(/^[a-z0-9\-]{36}/.test(Utils.toDefaultValue(DataTypes.UUIDV4()))).to.be.equal(true);
22+
});
23+
test('return now', function () {
24+
expect(Object.prototype.toString.call(Utils.toDefaultValue(DataTypes.NOW()))).to.be.equal('[object Date]');
25+
});
26+
test('return plain string', function () {
27+
expect(Utils.toDefaultValue('Test')).to.equal('Test');
28+
});
29+
test('return plain object', function () {
30+
chai.assert.deepEqual({}, Utils.toDefaultValue({}));
31+
});
32+
});
33+
1234
suite('mapOptionFieldNames', function () {
1335
test('plain where', function () {
1436
expect(Utils.mapOptionFieldNames({

0 commit comments

Comments
 (0)