Skip to content

Commit

Permalink
meta: make and accept a dialect (#15072)
Browse files Browse the repository at this point in the history
  • Loading branch information
WikiRik committed Oct 1, 2022
1 parent 6c80eac commit 0f4914f
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 35 deletions.
6 changes: 6 additions & 0 deletions src/dialects/abstract/index.ts
Expand Up @@ -126,6 +126,11 @@ export type DialectSupports = {
* enables the ability to use backslash escapes inside of the string.
*/
escapeStringConstants: boolean,

/**
* Whether this dialect supports date & time values with a precision down to at least the millisecond.
*/
milliseconds: boolean,
};

export abstract class AbstractDialect {
Expand Down Expand Up @@ -217,6 +222,7 @@ export abstract class AbstractDialect {
indexHints: false,
searchPath: false,
escapeStringConstants: false,
milliseconds: true,
};

declare readonly defaultVersion: string;
Expand Down
3 changes: 2 additions & 1 deletion src/dialects/db2/query-generator.js
Expand Up @@ -912,7 +912,8 @@ export class Db2QueryGenerator extends AbstractQueryGenerator {
for (const key in rawAttributes) {
if (rawAttributes[key].unique && dataValues[key] === undefined) {
if (rawAttributes[key].type instanceof DataTypes.DATE) {
dataValues[key] = Utils.now('db2');
// TODO: refactor this to this.dialect when https://github.com/sequelize/sequelize/pull/15069 has been merged
dataValues[key] = Utils.now(this._dialect);
} else if (rawAttributes[key].type instanceof DataTypes.STRING) {
dataValues[key] = `unique${uniqno++}`;
} else if (rawAttributes[key].type instanceof DataTypes.INTEGER) {
Expand Down
1 change: 1 addition & 0 deletions src/dialects/snowflake/index.js
Expand Up @@ -43,6 +43,7 @@ export class SnowflakeDialect extends AbstractDialect {
REGEXP: true,
schemas: true,
databases: true,
milliseconds: false,
});

constructor(sequelize) {
Expand Down
24 changes: 12 additions & 12 deletions src/model.js
Expand Up @@ -169,23 +169,23 @@ export class Model {
}

if (this.constructor._timestampAttributes.createdAt && defaults[this.constructor._timestampAttributes.createdAt]) {
this.dataValues[this.constructor._timestampAttributes.createdAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.createdAt], this.sequelize.options.dialect);
this.dataValues[this.constructor._timestampAttributes.createdAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.createdAt], this.sequelize.dialect);
delete defaults[this.constructor._timestampAttributes.createdAt];
}

if (this.constructor._timestampAttributes.updatedAt && defaults[this.constructor._timestampAttributes.updatedAt]) {
this.dataValues[this.constructor._timestampAttributes.updatedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.updatedAt], this.sequelize.options.dialect);
this.dataValues[this.constructor._timestampAttributes.updatedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.updatedAt], this.sequelize.dialect);
delete defaults[this.constructor._timestampAttributes.updatedAt];
}

if (this.constructor._timestampAttributes.deletedAt && defaults[this.constructor._timestampAttributes.deletedAt]) {
this.dataValues[this.constructor._timestampAttributes.deletedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.deletedAt], this.sequelize.options.dialect);
this.dataValues[this.constructor._timestampAttributes.deletedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.deletedAt], this.sequelize.dialect);
delete defaults[this.constructor._timestampAttributes.deletedAt];
}

for (key in defaults) {
if (values[key] === undefined) {
this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.options.dialect), { raw: true });
this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.dialect), { raw: true });
delete values[key];
}
}
Expand Down Expand Up @@ -1145,7 +1145,7 @@ Specify a different name for either index to resolve this issue.`);
}

if (Object.prototype.hasOwnProperty.call(definition, 'defaultValue')) {
this._defaultValues[name] = () => Utils.toDefaultValue(definition.defaultValue, this.sequelize.options.dialect);
this._defaultValues[name] = () => Utils.toDefaultValue(definition.defaultValue, this.sequelize.dialect);
}

if (Object.prototype.hasOwnProperty.call(definition, 'unique') && definition.unique) {
Expand Down Expand Up @@ -2509,7 +2509,7 @@ Specify a different name for either index to resolve this issue.`);
const updatedDataValues = _.pick(instance.dataValues, changed);
const insertValues = Utils.mapValueFieldNames(instance.dataValues, Object.keys(instance.rawAttributes), this);
const updateValues = Utils.mapValueFieldNames(updatedDataValues, options.fields, this);
const now = Utils.now(this.sequelize.options.dialect);
const now = Utils.now(this.sequelize.dialect);

// Attach createdAt
if (createdAtAttr && !insertValues[createdAtAttr]) {
Expand Down Expand Up @@ -2578,7 +2578,7 @@ Specify a different name for either index to resolve this issue.`);
}

const dialect = this.sequelize.options.dialect;
const now = Utils.now(this.sequelize.options.dialect);
const now = Utils.now(this.sequelize.dialect);
options = Utils.cloneDeep(options);

// Add CLS transaction
Expand Down Expand Up @@ -2983,7 +2983,7 @@ Specify a different name for either index to resolve this issue.`);
[field]: Object.prototype.hasOwnProperty.call(deletedAtAttribute, 'defaultValue') ? deletedAtAttribute.defaultValue : null,
};

attrValueHash[field] = Utils.now(this.sequelize.options.dialect);
attrValueHash[field] = Utils.now(this.sequelize.dialect);
result = await this.queryInterface.bulkUpdate(this.getTableName(options), attrValueHash, Object.assign(where, options.where), options, this.rawAttributes);
} else {
result = await this.queryInterface.bulkDelete(this.getTableName(options), options.where, options, this);
Expand Down Expand Up @@ -3118,7 +3118,7 @@ Specify a different name for either index to resolve this issue.`);
}

if (this._timestampAttributes.updatedAt && !options.silent) {
values[this._timestampAttributes.updatedAt] = this._getDefaultTimestamp(this._timestampAttributes.updatedAt) || Utils.now(this.sequelize.options.dialect);
values[this._timestampAttributes.updatedAt] = this._getDefaultTimestamp(this._timestampAttributes.updatedAt) || Utils.now(this.sequelize.dialect);
}

options.model = this;
Expand Down Expand Up @@ -3275,7 +3275,7 @@ Specify a different name for either index to resolve this issue.`);

static _getDefaultTimestamp(attr) {
if (Boolean(this.rawAttributes[attr]) && Boolean(this.rawAttributes[attr].defaultValue)) {
return Utils.toDefaultValue(this.rawAttributes[attr].defaultValue, this.sequelize.options.dialect);
return Utils.toDefaultValue(this.rawAttributes[attr].defaultValue, this.sequelize.dialect);
}

}
Expand Down Expand Up @@ -3443,7 +3443,7 @@ Instead of specifying a Model, either:
const updatedAtAttr = this._timestampAttributes.updatedAt;
if (!options.silent && updatedAtAttr && !incrementAmountsByField[updatedAtAttr]) {
const attrName = this.rawAttributes[updatedAtAttr].field || updatedAtAttr;
extraAttributesToBeUpdated[attrName] = this._getDefaultTimestamp(updatedAtAttr) || Utils.now(this.sequelize.options.dialect);
extraAttributesToBeUpdated[attrName] = this._getDefaultTimestamp(updatedAtAttr) || Utils.now(this.sequelize.dialect);
}

const tableName = this.getTableName(options);
Expand Down Expand Up @@ -3979,7 +3979,7 @@ Instead of specifying a Model, either:
const versionAttr = this.constructor._versionAttribute;
const hook = this.isNewRecord ? 'Create' : 'Update';
const wasNewRecord = this.isNewRecord;
const now = Utils.now(this.sequelize.options.dialect);
const now = Utils.now(this.sequelize.dialect);
let updatedAtAttr = this.constructor._timestampAttributes.updatedAt;

if (updatedAtAttr && options.fields.length > 0 && !options.fields.includes(updatedAtAttr)) {
Expand Down
23 changes: 7 additions & 16 deletions src/utils/dialect.ts
@@ -1,31 +1,22 @@
import isPlainObject from 'lodash/isPlainObject';
// eslint-disable-next-line import/order -- caused by temporarily mixing require with import
/* eslint-disable import/order -- caused by temporarily mixing require with import */
import { v1 as uuidv1, v4 as uuidv4 } from 'uuid';

const DataTypes = require('../data-types');
import type { AbstractDialect } from '../dialects/abstract';
/* eslint-enable import/order */

const dialectsSupportingMilliseconds = new Set([
'mariadb',
'mysql',
'postgres',
'sqlite',
'mssql',
'db2',
'ibmi',
]);
const DataTypes = require('../data-types');

// TODO: instead of receiving a dialect *name* here, require the actual AbstractDialect subclass
// and add a flag on AbstractDialect.supports to determine if the date should include milliseconds.
export function now(dialect: string): Date {
export function now(dialect: AbstractDialect): Date {
const d = new Date();
if (!dialectsSupportingMilliseconds.has(dialect)) {
if (!dialect.supports.milliseconds) {
d.setMilliseconds(0);
}

return d;
}

export function toDefaultValue(value: unknown, dialect: string): unknown {
export function toDefaultValue(value: unknown, dialect: AbstractDialect): unknown {
if (typeof value === 'function') {
const tmp = value();
if (tmp instanceof DataTypes.ABSTRACT) {
Expand Down
14 changes: 8 additions & 6 deletions test/unit/utils.test.js
Expand Up @@ -6,6 +6,8 @@ const expect = chai.expect;
const Support = require('./support');
const { DataTypes, Op, Utils } = require('@sequelize/core');

const dialect = Support.sequelize.dialect;

describe(Support.getTestDialectTeaser('Utils'), () => {
describe('merge', () => {
it('does not clone sequelize models', () => {
Expand Down Expand Up @@ -34,22 +36,22 @@ describe(Support.getTestDialectTeaser('Utils'), () => {

describe('toDefaultValue', () => {
it('return plain data types', () => {
expect(Utils.toDefaultValue(DataTypes.UUIDV4)).to.equal('UUIDV4');
expect(Utils.toDefaultValue(DataTypes.UUIDV4, dialect)).to.equal('UUIDV4');
});
it('return uuid v1', () => {
expect(/^[\da-z-]{36}$/.test(Utils.toDefaultValue(DataTypes.UUIDV1()))).to.be.equal(true);
expect(/^[\da-z-]{36}$/.test(Utils.toDefaultValue(DataTypes.UUIDV1(), dialect))).to.be.equal(true);
});
it('return uuid v4', () => {
expect(/^[\da-z-]{36}/.test(Utils.toDefaultValue(DataTypes.UUIDV4()))).to.be.equal(true);
expect(/^[\da-z-]{36}/.test(Utils.toDefaultValue(DataTypes.UUIDV4(), dialect))).to.be.equal(true);
});
it('return now', () => {
expect(Object.prototype.toString.call(Utils.toDefaultValue(DataTypes.NOW()))).to.be.equal('[object Date]');
expect(Object.prototype.toString.call(Utils.toDefaultValue(DataTypes.NOW(), dialect))).to.be.equal('[object Date]');
});
it('return plain string', () => {
expect(Utils.toDefaultValue('Test')).to.equal('Test');
expect(Utils.toDefaultValue('Test', dialect)).to.equal('Test');
});
it('return plain object', () => {
chai.assert.deepEqual({}, Utils.toDefaultValue({}));
chai.assert.deepEqual({}, Utils.toDefaultValue({}, dialect));
});
});

Expand Down

0 comments on commit 0f4914f

Please sign in to comment.