@creditiq/sequelize
Advanced tools
Comparing version 6.6.6 to 6.20.1
10
index.js
'use strict'; | ||
// TODO [>=7]: remove me. I've been moved to 'exports' in package.json | ||
/** | ||
* The entry point. | ||
* A Sequelize module that contains the sequelize entry point. | ||
* | ||
* @module Sequelize | ||
* @module sequelize | ||
*/ | ||
module.exports = require('./lib/sequelize'); | ||
/** Exports the sequelize entry point. */ | ||
module.exports = require('./lib'); |
@@ -1,94 +0,7 @@ | ||
'use strict'; | ||
const { AssociationError } = require('./../errors'); | ||
/** | ||
* Creating associations in sequelize is done by calling one of the belongsTo / hasOne / hasMany / belongsToMany functions on a model (the source), and providing another model as the first argument to the function (the target). | ||
* | ||
* * hasOne - adds a foreign key to the target and singular association mixins to the source. | ||
* * belongsTo - add a foreign key and singular association mixins to the source. | ||
* * hasMany - adds a foreign key to target and plural association mixins to the source. | ||
* * belongsToMany - creates an N:M association with a join table and adds plural association mixins to the source. The junction table is created with sourceId and targetId. | ||
* | ||
* Creating an association will add a foreign key constraint to the attributes. All associations use `CASCADE` on update and `SET NULL` on delete, except for n:m, which also uses `CASCADE` on delete. | ||
* | ||
* When creating associations, you can provide an alias, via the `as` option. This is useful if the same model is associated twice, or you want your association to be called something other than the name of the target model. | ||
* | ||
* As an example, consider the case where users have many pictures, one of which is their profile picture. All pictures have a `userId`, but in addition the user model also has a `profilePictureId`, to be able to easily load the user's profile picture. | ||
* | ||
* ```js | ||
* User.hasMany(Picture) | ||
* User.belongsTo(Picture, { as: 'ProfilePicture', constraints: false }) | ||
* | ||
* user.getPictures() // gets you all pictures | ||
* user.getProfilePicture() // gets you only the profile picture | ||
* | ||
* User.findAll({ | ||
* where: ..., | ||
* include: [ | ||
* { model: Picture }, // load all pictures | ||
* { model: Picture, as: 'ProfilePicture' }, // load the profile picture. | ||
* // Notice that the spelling must be the exact same as the one in the association | ||
* ] | ||
* }) | ||
* ``` | ||
* To get full control over the foreign key column added by sequelize, you can use the `foreignKey` option. It can either be a string, that specifies the name, or and object type definition, | ||
* equivalent to those passed to `sequelize.define`. | ||
* | ||
* ```js | ||
* User.hasMany(Picture, { foreignKey: 'uid' }) | ||
* ``` | ||
* | ||
* The foreign key column in Picture will now be called `uid` instead of the default `userId`. | ||
* | ||
* ```js | ||
* User.hasMany(Picture, { | ||
* foreignKey: { | ||
* name: 'uid', | ||
* allowNull: false | ||
* } | ||
* }) | ||
* ``` | ||
* | ||
* This specifies that the `uid` column cannot be null. In most cases this will already be covered by the foreign key constraints, which sequelize creates automatically, but can be useful in case where the foreign keys are disabled, e.g. due to circular references (see `constraints: false` below). | ||
* | ||
* When fetching associated models, you can limit your query to only load some models. These queries are written in the same way as queries to `find`/`findAll`. To only get pictures in JPG, you can do: | ||
* | ||
* ```js | ||
* user.getPictures({ | ||
* where: { | ||
* format: 'jpg' | ||
* } | ||
* }) | ||
* ``` | ||
* | ||
* There are several ways to update and add new associations. Continuing with our example of users and pictures: | ||
* ```js | ||
* user.addPicture(p) // Add a single picture | ||
* user.setPictures([p1, p2]) // Associate user with ONLY these two picture, all other associations will be deleted | ||
* user.addPictures([p1, p2]) // Associate user with these two pictures, but don't touch any current associations | ||
* ``` | ||
* | ||
* You don't have to pass in a complete object to the association functions, if your associated model has a single primary key: | ||
* | ||
* ```js | ||
* user.addPicture(req.query.pid) // Here pid is just an integer, representing the primary key of the picture | ||
* ``` | ||
* | ||
* In the example above we have specified that a user belongs to his profile picture. Conceptually, this might not make sense, but since we want to add the foreign key to the user model this is the way to do it. | ||
* | ||
* Note how we also specified `constraints: false` for profile picture. This is because we add a foreign key from user to picture (profilePictureId), and from picture to user (userId). If we were to add foreign keys to both, it would create a cyclic dependency, and sequelize would not know which table to create first, since user depends on picture, and picture depends on user. These kinds of problems are detected by sequelize before the models are synced to the database, and you will get an error along the lines of `Error: Cyclic dependency found. 'users' is dependent of itself`. If you encounter this, you should either disable some constraints, or rethink your associations completely. | ||
*/ | ||
"use strict"; | ||
const { AssociationError } = require("./../errors"); | ||
class Association { | ||
constructor(source, target, options = {}) { | ||
/** | ||
* @type {Model} | ||
*/ | ||
this.source = source; | ||
/** | ||
* @type {Model} | ||
*/ | ||
this.target = target; | ||
this.options = options; | ||
@@ -98,25 +11,7 @@ this.scope = options.scope; | ||
this.as = options.as; | ||
/** | ||
* The type of the association. One of `HasMany`, `BelongsTo`, `HasOne`, `BelongsToMany` | ||
* | ||
* @type {string} | ||
*/ | ||
this.associationType = ''; | ||
this.associationType = ""; | ||
if (source.hasAlias(options.as)) { | ||
throw new AssociationError(`You have used the alias ${options.as} in two separate associations. ` + | ||
'Aliased associations must have unique aliases.' | ||
); | ||
throw new AssociationError(`You have used the alias ${options.as} in two separate associations. Aliased associations must have unique aliases.`); | ||
} | ||
} | ||
/** | ||
* Normalize input | ||
* | ||
* @param {Array|string} input it may be array or single obj, instance or primary key | ||
* | ||
* @private | ||
* @returns {Array} built objects | ||
*/ | ||
toInstanceArray(input) { | ||
@@ -126,18 +21,15 @@ if (!Array.isArray(input)) { | ||
} | ||
return input.map(element => { | ||
if (element instanceof this.target) return element; | ||
return input.map((element) => { | ||
if (element instanceof this.target) | ||
return element; | ||
const tmpInstance = {}; | ||
tmpInstance[this.target.primaryKeyAttribute] = element; | ||
return this.target.build(tmpInstance, { isNewRecord: false }); | ||
}); | ||
} | ||
[Symbol.for('nodejs.util.inspect.custom')]() { | ||
[Symbol.for("nodejs.util.inspect.custom")]() { | ||
return this.as; | ||
} | ||
} | ||
module.exports = Association; | ||
//# sourceMappingURL=base.js.map |
@@ -1,66 +0,37 @@ | ||
'use strict'; | ||
const Utils = require('./../utils'); | ||
const Helpers = require('./helpers'); | ||
const _ = require('lodash'); | ||
const Association = require('./base'); | ||
const BelongsTo = require('./belongs-to'); | ||
const HasMany = require('./has-many'); | ||
const HasOne = require('./has-one'); | ||
const AssociationError = require('../errors').AssociationError; | ||
const EmptyResultError = require('../errors').EmptyResultError; | ||
const Op = require('../operators'); | ||
/** | ||
* Many-to-many association with a join table. | ||
* | ||
* When the join table has additional attributes, these can be passed in the options object: | ||
* | ||
* ```js | ||
* UserProject = sequelize.define('user_project', { | ||
* role: Sequelize.STRING | ||
* }); | ||
* User.belongsToMany(Project, { through: UserProject }); | ||
* Project.belongsToMany(User, { through: UserProject }); | ||
* // through is required! | ||
* | ||
* user.addProject(project, { through: { role: 'manager' }}); | ||
* ``` | ||
* | ||
* All methods allow you to pass either a persisted instance, its primary key, or a mixture: | ||
* | ||
* ```js | ||
* const project = await Project.create({ id: 11 }); | ||
* await user.addProjects([project, 12]); | ||
* ``` | ||
* | ||
* If you want to set several target instances, but with different attributes you have to set the attributes on the instance, using a property with the name of the through model: | ||
* | ||
* ```js | ||
* p1.UserProjects = { | ||
* started: true | ||
* } | ||
* user.setProjects([p1, p2], { through: { started: false }}) // The default value is false, but p1 overrides that. | ||
* ``` | ||
* | ||
* Similarly, when fetching through a join table with custom attributes, these attributes will be available as an object with the name of the through model. | ||
* ```js | ||
* const projects = await user.getProjects(); | ||
* const p1 = projects[0]; | ||
* p1.UserProjects.started // Is this project started yet? | ||
* }) | ||
* ``` | ||
* | ||
* In the API reference below, add the name of the association to the method, e.g. for `User.belongsToMany(Project)` the getter will be `user.getProjects()`. | ||
* | ||
* @see {@link Model.belongsToMany} | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const Utils = require("./../utils"); | ||
const Helpers = require("./helpers"); | ||
const _ = require("lodash"); | ||
const Association = require("./base"); | ||
const BelongsTo = require("./belongs-to"); | ||
const HasMany = require("./has-many"); | ||
const HasOne = require("./has-one"); | ||
const AssociationError = require("../errors").AssociationError; | ||
const EmptyResultError = require("../errors").EmptyResultError; | ||
const Op = require("../operators"); | ||
class BelongsToMany extends Association { | ||
constructor(source, target, options) { | ||
super(source, target, options); | ||
if (this.options.through === undefined || this.options.through === true || this.options.through === null) { | ||
if (this.options.through === void 0 || this.options.through === true || this.options.through === null) { | ||
throw new AssociationError(`${source.name}.belongsToMany(${target.name}) requires through option, pass either a string or a model`); | ||
} | ||
if (!this.options.through.model) { | ||
@@ -71,17 +42,13 @@ this.options.through = { | ||
} | ||
this.associationType = 'BelongsToMany'; | ||
this.associationType = "BelongsToMany"; | ||
this.targetAssociation = null; | ||
this.sequelize = source.sequelize; | ||
this.through = { ...this.options.through }; | ||
this.through = __spreadValues({}, this.options.through); | ||
this.isMultiAssociation = true; | ||
this.doubleLinked = false; | ||
if (!this.as && this.isSelfAssociation) { | ||
throw new AssociationError('\'as\' must be defined for many-to-many self-associations'); | ||
throw new AssociationError("'as' must be defined for many-to-many self-associations"); | ||
} | ||
if (this.as) { | ||
this.isAliased = true; | ||
if (_.isPlainObject(this.as)) { | ||
@@ -100,22 +67,11 @@ this.options.name = this.as; | ||
} | ||
this.combinedTableName = Utils.combineTableNames( | ||
this.source.tableName, | ||
this.isSelfAssociation ? this.as || this.target.tableName : this.target.tableName | ||
); | ||
/* | ||
* If self association, this is the target association - Unless we find a pairing association | ||
*/ | ||
this.combinedTableName = Utils.combineTableNames(this.source.tableName, this.isSelfAssociation ? this.as || this.target.tableName : this.target.tableName); | ||
if (this.isSelfAssociation) { | ||
this.targetAssociation = this; | ||
} | ||
/* | ||
* Find paired association (if exists) | ||
*/ | ||
_.each(this.target.associations, association => { | ||
if (association.associationType !== 'BelongsToMany') return; | ||
if (association.target !== this.source) return; | ||
_.each(this.target.associations, (association) => { | ||
if (association.associationType !== "BelongsToMany") | ||
return; | ||
if (association.target !== this.source) | ||
return; | ||
if (this.options.through.model === association.options.through.model) { | ||
@@ -126,9 +82,4 @@ this.paired = association; | ||
}); | ||
/* | ||
* Default/generated source/target keys | ||
*/ | ||
this.sourceKey = this.options.sourceKey || this.source.primaryKeyAttribute; | ||
this.sourceKeyField = this.source.rawAttributes[this.sourceKey].field || this.sourceKey; | ||
if (this.options.targetKey) { | ||
@@ -142,12 +93,10 @@ this.targetKey = this.options.targetKey; | ||
} | ||
this._createForeignAndOtherKeys(); | ||
if (typeof this.through.model === 'string') { | ||
if (typeof this.through.model === "string") { | ||
if (!this.sequelize.isDefined(this.through.model)) { | ||
this.through.model = this.sequelize.define(this.through.model, {}, Object.assign(this.options, { | ||
tableName: this.through.model, | ||
indexes: [], //we don't want indexes here (as referenced in #2416) | ||
paranoid: this.through.paranoid ? this.through.paranoid : false, // Default to non-paranoid join (referenced in #11991) | ||
validate: {} // Don't propagate model-level validations | ||
indexes: [], | ||
paranoid: this.through.paranoid ? this.through.paranoid : false, | ||
validate: {} | ||
})); | ||
@@ -158,10 +107,11 @@ } else { | ||
} | ||
Object.assign(this.options, _.pick(this.through.model.options, [ | ||
'timestamps', 'createdAt', 'updatedAt', 'deletedAt', 'paranoid' | ||
"timestamps", | ||
"createdAt", | ||
"updatedAt", | ||
"deletedAt", | ||
"paranoid" | ||
])); | ||
if (this.paired) { | ||
let needInjectPaired = false; | ||
if (this.targetKeyDefault) { | ||
@@ -173,4 +123,2 @@ this.targetKey = this.paired.sourceKey; | ||
if (this.paired.targetKeyDefault) { | ||
// in this case paired.otherKey depends on paired.targetKey, | ||
// so cleanup previously wrong generated otherKey | ||
if (this.paired.targetKey !== this.sourceKey) { | ||
@@ -184,3 +132,2 @@ delete this.through.model.rawAttributes[this.paired.otherKey]; | ||
} | ||
if (this.otherKeyDefault) { | ||
@@ -190,4 +137,2 @@ this.otherKey = this.paired.foreignKey; | ||
if (this.paired.otherKeyDefault) { | ||
// If paired otherKey was inferred we should make sure to clean it up | ||
// before adding a new one that matches the foreignKey | ||
if (this.paired.otherKey !== this.foreignKey) { | ||
@@ -199,3 +144,2 @@ delete this.through.model.rawAttributes[this.paired.otherKey]; | ||
} | ||
if (needInjectPaired) { | ||
@@ -205,15 +149,9 @@ this.paired._injectAttributes(); | ||
} | ||
if (this.through) { | ||
this.throughModel = this.through.model; | ||
} | ||
this.options.tableName = this.combinedName = this.through.model === Object(this.through.model) ? this.through.model.tableName : this.through.model; | ||
this.associationAccessor = this.as; | ||
// Get singular and plural names, trying to uppercase the first letter, unless the model forbids it | ||
const plural = _.upperFirst(this.options.name.plural); | ||
const singular = _.upperFirst(this.options.name.singular); | ||
this.accessors = { | ||
@@ -232,7 +170,3 @@ get: `get${plural}`, | ||
} | ||
_createForeignAndOtherKeys() { | ||
/* | ||
* Default/generated foreign/other keys | ||
*/ | ||
if (_.isObject(this.options.foreignKey)) { | ||
@@ -243,10 +177,7 @@ this.foreignKeyAttribute = this.options.foreignKey; | ||
this.foreignKeyAttribute = {}; | ||
this.foreignKey = this.options.foreignKey || Utils.camelize( | ||
[ | ||
this.source.options.name.singular, | ||
this.sourceKey | ||
].join('_') | ||
); | ||
this.foreignKey = this.options.foreignKey || Utils.camelize([ | ||
this.source.options.name.singular, | ||
this.sourceKey | ||
].join("_")); | ||
} | ||
if (_.isObject(this.options.otherKey)) { | ||
@@ -259,29 +190,17 @@ this.otherKeyAttribute = this.options.otherKey; | ||
} | ||
this.otherKeyAttribute = {}; | ||
this.otherKey = this.options.otherKey || Utils.camelize( | ||
[ | ||
this.isSelfAssociation ? Utils.singularize(this.as) : this.target.options.name.singular, | ||
this.targetKey | ||
].join('_') | ||
); | ||
this.otherKey = this.options.otherKey || Utils.camelize([ | ||
this.isSelfAssociation ? Utils.singularize(this.as) : this.target.options.name.singular, | ||
this.targetKey | ||
].join("_")); | ||
} | ||
} | ||
// the id is in the target table | ||
// or in an extra table which connects two tables | ||
_injectAttributes() { | ||
this.identifier = this.foreignKey; | ||
this.foreignIdentifier = this.otherKey; | ||
// remove any PKs previously defined by sequelize | ||
// but ignore any keys that are part of this association (#5865) | ||
_.each(this.through.model.rawAttributes, (attribute, attributeName) => { | ||
if (attribute.primaryKey === true && attribute._autoGenerated === true) { | ||
if (attributeName === this.foreignKey || attributeName === this.otherKey) { | ||
// this key is still needed as it's part of the association | ||
// so just set primaryKey to false | ||
if ([this.foreignKey, this.otherKey].includes(attributeName)) { | ||
attribute.primaryKey = false; | ||
} | ||
else { | ||
} else { | ||
delete this.through.model.rawAttributes[attributeName]; | ||
@@ -292,3 +211,2 @@ } | ||
}); | ||
const sourceKey = this.source.rawAttributes[this.sourceKey]; | ||
@@ -300,5 +218,4 @@ const sourceKeyType = sourceKey.type; | ||
const targetKeyField = this.targetKeyField; | ||
const sourceAttribute = { type: sourceKeyType, ...this.foreignKeyAttribute }; | ||
const targetAttribute = { type: targetKeyType, ...this.otherKeyAttribute }; | ||
const sourceAttribute = __spreadValues({ type: sourceKeyType }, this.foreignKeyAttribute); | ||
const targetAttribute = __spreadValues({ type: targetKeyType }, this.otherKeyAttribute); | ||
if (this.primaryKeyDeleted === true) { | ||
@@ -308,10 +225,9 @@ targetAttribute.primaryKey = sourceAttribute.primaryKey = true; | ||
let uniqueKey; | ||
if (typeof this.options.uniqueKey === 'string' && this.options.uniqueKey !== '') { | ||
if (typeof this.options.uniqueKey === "string" && this.options.uniqueKey !== "") { | ||
uniqueKey = this.options.uniqueKey; | ||
} else { | ||
uniqueKey = [this.through.model.tableName, this.foreignKey, this.otherKey, 'unique'].join('_'); | ||
uniqueKey = [this.through.model.tableName, this.foreignKey, this.otherKey, "unique"].join("_"); | ||
} | ||
targetAttribute.unique = sourceAttribute.unique = uniqueKey; | ||
} | ||
if (!this.through.model.rawAttributes[this.foreignKey]) { | ||
@@ -322,3 +238,2 @@ this.through.model.rawAttributes[this.foreignKey] = { | ||
} | ||
if (!this.through.model.rawAttributes[this.otherKey]) { | ||
@@ -329,3 +244,2 @@ this.through.model.rawAttributes[this.otherKey] = { | ||
} | ||
if (this.options.constraints !== false) { | ||
@@ -336,9 +250,8 @@ sourceAttribute.references = { | ||
}; | ||
// For the source attribute the passed option is the priority | ||
sourceAttribute.onDelete = this.options.onDelete || this.through.model.rawAttributes[this.foreignKey].onDelete; | ||
sourceAttribute.onUpdate = this.options.onUpdate || this.through.model.rawAttributes[this.foreignKey].onUpdate; | ||
if (!sourceAttribute.onDelete) sourceAttribute.onDelete = 'CASCADE'; | ||
if (!sourceAttribute.onUpdate) sourceAttribute.onUpdate = 'CASCADE'; | ||
if (!sourceAttribute.onDelete) | ||
sourceAttribute.onDelete = "CASCADE"; | ||
if (!sourceAttribute.onUpdate) | ||
sourceAttribute.onUpdate = "CASCADE"; | ||
targetAttribute.references = { | ||
@@ -348,22 +261,20 @@ model: this.target.getTableName(), | ||
}; | ||
// But the for target attribute the previously defined option is the priority (since it could've been set by another belongsToMany call) | ||
targetAttribute.onDelete = this.through.model.rawAttributes[this.otherKey].onDelete || this.options.onDelete; | ||
targetAttribute.onUpdate = this.through.model.rawAttributes[this.otherKey].onUpdate || this.options.onUpdate; | ||
if (!targetAttribute.onDelete) targetAttribute.onDelete = 'CASCADE'; | ||
if (!targetAttribute.onUpdate) targetAttribute.onUpdate = 'CASCADE'; | ||
if (!targetAttribute.onDelete) | ||
targetAttribute.onDelete = "CASCADE"; | ||
if (!targetAttribute.onUpdate) | ||
targetAttribute.onUpdate = "CASCADE"; | ||
} | ||
Object.assign(this.through.model.rawAttributes[this.foreignKey], sourceAttribute); | ||
Object.assign(this.through.model.rawAttributes[this.otherKey], targetAttribute); | ||
this.through.model.refreshAttributes(); | ||
this.identifierField = this.through.model.rawAttributes[this.foreignKey].field || this.foreignKey; | ||
this.foreignIdentifierField = this.through.model.rawAttributes[this.otherKey].field || this.otherKey; | ||
if (this.options.sequelize.options.dialect === "db2" && this.source.rawAttributes[this.sourceKey].primaryKey !== true) { | ||
this.source.rawAttributes[this.sourceKey].unique = true; | ||
} | ||
if (this.paired && !this.paired.foreignIdentifierField) { | ||
this.paired.foreignIdentifierField = this.through.model.rawAttributes[this.paired.otherKey].field || this.paired.otherKey; | ||
} | ||
this.toSource = new BelongsTo(this.through.model, this.source, { | ||
@@ -380,3 +291,2 @@ foreignKey: this.foreignKey | ||
}); | ||
this.toTarget = new BelongsTo(this.through.model, this.target, { | ||
@@ -393,3 +303,2 @@ foreignKey: this.otherKey | ||
}); | ||
if (this.paired && this.paired.otherKeyDefault) { | ||
@@ -399,3 +308,2 @@ this.paired.toTarget = new BelongsTo(this.paired.through.model, this.paired.target, { | ||
}); | ||
this.paired.oneFromTarget = new HasOne(this.paired.target, this.paired.through.model, { | ||
@@ -407,47 +315,23 @@ foreignKey: this.paired.otherKey, | ||
} | ||
Helpers.checkNamingCollision(this); | ||
return this; | ||
} | ||
mixin(obj) { | ||
const methods = ['get', 'count', 'hasSingle', 'hasAll', 'set', 'add', 'addMultiple', 'remove', 'removeMultiple', 'create']; | ||
const methods = ["get", "count", "hasSingle", "hasAll", "set", "add", "addMultiple", "remove", "removeMultiple", "create"]; | ||
const aliases = { | ||
hasSingle: 'has', | ||
hasAll: 'has', | ||
addMultiple: 'add', | ||
removeMultiple: 'remove' | ||
hasSingle: "has", | ||
hasAll: "has", | ||
addMultiple: "add", | ||
removeMultiple: "remove" | ||
}; | ||
Helpers.mixinMethods(this, obj, methods, aliases); | ||
} | ||
/** | ||
* Get everything currently associated with this, using an optional where clause. | ||
* | ||
* @see | ||
* {@link Model} for a full explanation of options | ||
* | ||
* @param {Model} instance instance | ||
* @param {object} [options] find options | ||
* @param {object} [options.where] An optional where clause to limit the associated models | ||
* @param {string|boolean} [options.scope] Apply a scope on the related model, or remove its default scope by passing false | ||
* @param {string} [options.schema] Apply a schema on the related model | ||
* @param {object} [options.through.where] An optional where clause applied to through model (join table) | ||
* @param {boolean} [options.through.paranoid=true] If true, only non-deleted records will be returned from the join table. If false, both deleted and non-deleted records will be returned. Only applies if through model is paranoid | ||
* | ||
* @returns {Promise<Array<Model>>} | ||
*/ | ||
async get(instance, options) { | ||
options = Utils.cloneDeep(options) || {}; | ||
const through = this.through; | ||
let scopeWhere; | ||
let throughWhere; | ||
if (this.scope) { | ||
scopeWhere = { ...this.scope }; | ||
scopeWhere = __spreadValues({}, this.scope); | ||
} | ||
options.where = { | ||
@@ -459,12 +343,8 @@ [Op.and]: [ | ||
}; | ||
if (Object(through.model) === through.model) { | ||
throughWhere = {}; | ||
throughWhere[this.foreignKey] = instance.get(this.sourceKey); | ||
if (through.scope) { | ||
Object.assign(throughWhere, through.scope); | ||
} | ||
//If a user pass a where on the options through options, make an "and" with the current throughWhere | ||
if (options.through && options.through.where) { | ||
@@ -475,3 +355,2 @@ throughWhere = { | ||
} | ||
options.include = options.include || []; | ||
@@ -482,9 +361,8 @@ options.include.push({ | ||
required: true, | ||
paranoid: _.get(options.through, 'paranoid', true), | ||
paranoid: _.get(options.through, "paranoid", true), | ||
where: throughWhere | ||
}); | ||
} | ||
let model = this.target; | ||
if (Object.prototype.hasOwnProperty.call(options, 'scope')) { | ||
if (Object.prototype.hasOwnProperty.call(options, "scope")) { | ||
if (!options.scope) { | ||
@@ -496,26 +374,12 @@ model = model.unscoped(); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(options, 'schema')) { | ||
if (Object.prototype.hasOwnProperty.call(options, "schema")) { | ||
model = model.schema(options.schema, options.schemaDelimiter); | ||
} | ||
return model.findAll(options); | ||
} | ||
/** | ||
* Count everything currently associated with this, using an optional where clause. | ||
* | ||
* @param {Model} instance instance | ||
* @param {object} [options] find options | ||
* @param {object} [options.where] An optional where clause to limit the associated models | ||
* @param {string|boolean} [options.scope] Apply a scope on the related model, or remove its default scope by passing false | ||
* | ||
* @returns {Promise<number>} | ||
*/ | ||
async count(instance, options) { | ||
const sequelize = this.target.sequelize; | ||
options = Utils.cloneDeep(options); | ||
options.attributes = [ | ||
[sequelize.fn('COUNT', sequelize.col([this.target.name, this.targetKeyField].join('.'))), 'count'] | ||
[sequelize.fn("COUNT", sequelize.col([this.target.name, this.targetKeyField].join("."))), "count"] | ||
]; | ||
@@ -525,17 +389,5 @@ options.joinTableAttributes = []; | ||
options.plain = true; | ||
const result = await this.get(instance, options); | ||
return parseInt(result.count, 10); | ||
} | ||
/** | ||
* Check if one or more instance(s) are associated with this. If a list of instances is passed, the function returns true if _all_ instances are associated | ||
* | ||
* @param {Model} sourceInstance source instance to check for an association with | ||
* @param {Model|Model[]|string[]|string|number[]|number} [instances] Can be an array of instances or their primary keys | ||
* @param {object} [options] Options passed to getAssociations | ||
* | ||
* @returns {Promise<boolean>} | ||
*/ | ||
async has(sourceInstance, instances, options) { | ||
@@ -545,12 +397,10 @@ if (!Array.isArray(instances)) { | ||
} | ||
options = { | ||
raw: true, | ||
...options, | ||
options = __spreadProps(__spreadValues({ | ||
raw: true | ||
}, options), { | ||
scope: false, | ||
attributes: [this.targetKey], | ||
joinTableAttributes: [] | ||
}; | ||
const instancePrimaryKeys = instances.map(instance => { | ||
}); | ||
const instancePrimaryKeys = instances.map((instance) => { | ||
if (instance instanceof this.target) { | ||
@@ -563,3 +413,2 @@ return instance.where(); | ||
}); | ||
options.where = { | ||
@@ -571,24 +420,7 @@ [Op.and]: [ | ||
}; | ||
const associatedObjects = await this.get(sourceInstance, options); | ||
return _.differenceWith(instancePrimaryKeys, associatedObjects, | ||
(a, b) => _.isEqual(a[this.targetKey], b[this.targetKey])).length === 0; | ||
return _.differenceWith(instancePrimaryKeys, associatedObjects, (a, b) => _.isEqual(a[this.targetKey], b[this.targetKey])).length === 0; | ||
} | ||
/** | ||
* Set the associated models by passing an array of instances or their primary keys. | ||
* Everything that it not in the passed array will be un-associated. | ||
* | ||
* @param {Model} sourceInstance source instance to associate new instances with | ||
* @param {Model|Model[]|string[]|string|number[]|number} [newAssociatedObjects] A single instance or primary key, or a mixed array of persisted instances or primary keys | ||
* @param {object} [options] Options passed to `through.findAll`, `bulkCreate`, `update` and `destroy` | ||
* @param {object} [options.validate] Run validation for the join model | ||
* @param {object} [options.through] Additional attributes for the join table. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async set(sourceInstance, newAssociatedObjects, options) { | ||
options = options || {}; | ||
const sourceKey = this.sourceKey; | ||
@@ -598,3 +430,2 @@ const targetKey = this.targetKey; | ||
const foreignIdentifier = this.foreignIdentifier; | ||
if (newAssociatedObjects === null) { | ||
@@ -605,19 +436,12 @@ newAssociatedObjects = []; | ||
} | ||
const where = { | ||
[identifier]: sourceInstance.get(sourceKey), | ||
...this.through.scope | ||
}; | ||
const updateAssociations = currentRows => { | ||
const where = __spreadValues({ | ||
[identifier]: sourceInstance.get(sourceKey) | ||
}, this.through.scope); | ||
const updateAssociations = (currentRows) => { | ||
const obsoleteAssociations = []; | ||
const promises = []; | ||
const defaultAttributes = options.through || {}; | ||
const unassociatedObjects = newAssociatedObjects.filter(obj => | ||
!currentRows.some(currentRow => currentRow[foreignIdentifier] === obj.get(targetKey)) | ||
); | ||
const unassociatedObjects = newAssociatedObjects.filter((obj) => !currentRows.some((currentRow) => currentRow[foreignIdentifier] === obj.get(targetKey))); | ||
for (const currentRow of currentRows) { | ||
const newObj = newAssociatedObjects.find(obj => currentRow[foreignIdentifier] === obj.get(targetKey)); | ||
const newObj = newAssociatedObjects.find((obj) => currentRow[foreignIdentifier] === obj.get(targetKey)); | ||
if (!newObj) { | ||
@@ -627,80 +451,48 @@ obsoleteAssociations.push(currentRow); | ||
let throughAttributes = newObj[this.through.model.name]; | ||
// Quick-fix for subtle bug when using existing objects that might have the through model attached (not as an attribute object) | ||
if (throughAttributes instanceof this.through.model) { | ||
throughAttributes = {}; | ||
} | ||
const attributes = { ...defaultAttributes, ...throughAttributes }; | ||
const attributes = __spreadValues(__spreadValues({}, defaultAttributes), throughAttributes); | ||
if (Object.keys(attributes).length) { | ||
promises.push( | ||
this.through.model.update(attributes, Object.assign(options, { | ||
where: { | ||
[identifier]: sourceInstance.get(sourceKey), | ||
[foreignIdentifier]: newObj.get(targetKey) | ||
} | ||
promises.push(this.through.model.update(attributes, Object.assign(options, { | ||
where: { | ||
[identifier]: sourceInstance.get(sourceKey), | ||
[foreignIdentifier]: newObj.get(targetKey) | ||
} | ||
)) | ||
); | ||
}))); | ||
} | ||
} | ||
} | ||
if (obsoleteAssociations.length > 0) { | ||
promises.push( | ||
this.through.model.destroy({ | ||
...options, | ||
where: { | ||
[identifier]: sourceInstance.get(sourceKey), | ||
[foreignIdentifier]: obsoleteAssociations.map(obsoleteAssociation => obsoleteAssociation[foreignIdentifier]), | ||
...this.through.scope | ||
} | ||
}) | ||
); | ||
promises.push(this.through.model.destroy(__spreadProps(__spreadValues({}, options), { | ||
where: __spreadValues({ | ||
[identifier]: sourceInstance.get(sourceKey), | ||
[foreignIdentifier]: obsoleteAssociations.map((obsoleteAssociation) => obsoleteAssociation[foreignIdentifier]) | ||
}, this.through.scope) | ||
}))); | ||
} | ||
if (unassociatedObjects.length > 0) { | ||
const bulk = unassociatedObjects.map(unassociatedObject => { | ||
return { | ||
...defaultAttributes, | ||
...unassociatedObject[this.through.model.name], | ||
const bulk = unassociatedObjects.map((unassociatedObject) => { | ||
return __spreadValues(__spreadProps(__spreadValues(__spreadValues({}, defaultAttributes), unassociatedObject[this.through.model.name]), { | ||
[identifier]: sourceInstance.get(sourceKey), | ||
[foreignIdentifier]: unassociatedObject.get(targetKey), | ||
...this.through.scope | ||
}; | ||
[foreignIdentifier]: unassociatedObject.get(targetKey) | ||
}), this.through.scope); | ||
}); | ||
promises.push(this.through.model.bulkCreate(bulk, { validate: true, ...options })); | ||
promises.push(this.through.model.bulkCreate(bulk, __spreadValues({ validate: true }, options))); | ||
} | ||
return Promise.all(promises); | ||
}; | ||
try { | ||
const currentRows = await this.through.model.findAll({ ...options, where, raw: true }); | ||
const currentRows = await this.through.model.findAll(__spreadProps(__spreadValues({}, options), { where, raw: true })); | ||
return await updateAssociations(currentRows); | ||
} catch (error) { | ||
if (error instanceof EmptyResultError) return updateAssociations([]); | ||
if (error instanceof EmptyResultError) | ||
return updateAssociations([]); | ||
throw error; | ||
} | ||
} | ||
/** | ||
* Associate one or several rows with source instance. It will not un-associate any already associated instance | ||
* that may be missing from `newInstances`. | ||
* | ||
* @param {Model} sourceInstance source instance to associate new instances with | ||
* @param {Model|Model[]|string[]|string|number[]|number} [newInstances] A single instance or primary key, or a mixed array of persisted instances or primary keys | ||
* @param {object} [options] Options passed to `through.findAll`, `bulkCreate` and `update` | ||
* @param {object} [options.validate] Run validation for the join model. | ||
* @param {object} [options.through] Additional attributes for the join table. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async add(sourceInstance, newInstances, options) { | ||
// If newInstances is null or undefined, no-op | ||
if (!newInstances) return Promise.resolve(); | ||
options = { ...options }; | ||
if (!newInstances) | ||
return Promise.resolve(); | ||
options = __spreadValues({}, options); | ||
const association = this; | ||
@@ -712,12 +504,8 @@ const sourceKey = association.sourceKey; | ||
const defaultAttributes = options.through || {}; | ||
newInstances = association.toInstanceArray(newInstances); | ||
const where = { | ||
const where = __spreadValues({ | ||
[identifier]: sourceInstance.get(sourceKey), | ||
[foreignIdentifier]: newInstances.map(newInstance => newInstance.get(targetKey)), | ||
...association.through.scope | ||
}; | ||
const updateAssociations = currentRows => { | ||
[foreignIdentifier]: newInstances.map((newInstance) => newInstance.get(targetKey)) | ||
}, association.through.scope); | ||
const updateAssociations = (currentRows) => { | ||
const promises = []; | ||
@@ -727,4 +515,3 @@ const unassociatedObjects = []; | ||
for (const obj of newInstances) { | ||
const existingAssociation = currentRows && currentRows.find(current => current[foreignIdentifier] === obj.get(targetKey)); | ||
const existingAssociation = currentRows && currentRows.find((current) => current[foreignIdentifier] === obj.get(targetKey)); | ||
if (!existingAssociation) { | ||
@@ -734,5 +521,4 @@ unassociatedObjects.push(obj); | ||
const throughAttributes = obj[association.through.model.name]; | ||
const attributes = { ...defaultAttributes, ...throughAttributes }; | ||
if (Object.keys(attributes).some(attribute => attributes[attribute] !== existingAssociation[attribute])) { | ||
const attributes = __spreadValues(__spreadValues({}, defaultAttributes), throughAttributes); | ||
if (Object.keys(attributes).some((attribute) => attributes[attribute] !== existingAssociation[attribute])) { | ||
changedAssociations.push(obj); | ||
@@ -742,27 +528,19 @@ } | ||
} | ||
if (unassociatedObjects.length > 0) { | ||
const bulk = unassociatedObjects.map(unassociatedObject => { | ||
const bulk = unassociatedObjects.map((unassociatedObject) => { | ||
const throughAttributes = unassociatedObject[association.through.model.name]; | ||
const attributes = { ...defaultAttributes, ...throughAttributes }; | ||
const attributes = __spreadValues(__spreadValues({}, defaultAttributes), throughAttributes); | ||
attributes[identifier] = sourceInstance.get(sourceKey); | ||
attributes[foreignIdentifier] = unassociatedObject.get(targetKey); | ||
Object.assign(attributes, association.through.scope); | ||
return attributes; | ||
}); | ||
promises.push(association.through.model.bulkCreate(bulk, { validate: true, ...options })); | ||
promises.push(association.through.model.bulkCreate(bulk, __spreadValues({ validate: true }, options))); | ||
} | ||
for (const assoc of changedAssociations) { | ||
let throughAttributes = assoc[association.through.model.name]; | ||
const attributes = { ...defaultAttributes, ...throughAttributes }; | ||
// Quick-fix for subtle bug when using existing objects that might have the through model attached (not as an attribute object) | ||
const attributes = __spreadValues(__spreadValues({}, defaultAttributes), throughAttributes); | ||
if (throughAttributes instanceof association.through.model) { | ||
throughAttributes = {}; | ||
} | ||
promises.push(association.through.model.update(attributes, Object.assign(options, { where: { | ||
@@ -773,56 +551,28 @@ [identifier]: sourceInstance.get(sourceKey), | ||
} | ||
return Promise.all(promises); | ||
}; | ||
try { | ||
const currentRows = await association.through.model.findAll({ ...options, where, raw: true }); | ||
const currentRows = await association.through.model.findAll(__spreadProps(__spreadValues({}, options), { where, raw: true })); | ||
const [associations] = await updateAssociations(currentRows); | ||
return associations; | ||
} catch (error) { | ||
if (error instanceof EmptyResultError) return updateAssociations(); | ||
if (error instanceof EmptyResultError) | ||
return updateAssociations(); | ||
throw error; | ||
} | ||
} | ||
/** | ||
* Un-associate one or more instance(s). | ||
* | ||
* @param {Model} sourceInstance instance to un associate instances with | ||
* @param {Model|Model[]|string|string[]|number|number[]} [oldAssociatedObjects] Can be an Instance or its primary key, or a mixed array of instances and primary keys | ||
* @param {object} [options] Options passed to `through.destroy` | ||
* | ||
* @returns {Promise} | ||
*/ | ||
remove(sourceInstance, oldAssociatedObjects, options) { | ||
const association = this; | ||
options = options || {}; | ||
oldAssociatedObjects = association.toInstanceArray(oldAssociatedObjects); | ||
const where = { | ||
[association.identifier]: sourceInstance.get(association.sourceKey), | ||
[association.foreignIdentifier]: oldAssociatedObjects.map(newInstance => newInstance.get(association.targetKey)) | ||
[association.foreignIdentifier]: oldAssociatedObjects.map((newInstance) => newInstance.get(association.targetKey)) | ||
}; | ||
return association.through.model.destroy({ ...options, where }); | ||
return association.through.model.destroy(__spreadProps(__spreadValues({}, options), { where })); | ||
} | ||
/** | ||
* Create a new instance of the associated model and associate it with this. | ||
* | ||
* @param {Model} sourceInstance source instance | ||
* @param {object} [values] values for target model | ||
* @param {object} [options] Options passed to create and add | ||
* @param {object} [options.through] Additional attributes for the join table | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async create(sourceInstance, values, options) { | ||
const association = this; | ||
options = options || {}; | ||
values = values || {}; | ||
if (Array.isArray(options)) { | ||
@@ -833,3 +583,2 @@ options = { | ||
} | ||
if (association.scope) { | ||
@@ -841,25 +590,19 @@ Object.assign(values, association.scope); | ||
} | ||
// Create the related model instance | ||
const newAssociatedObject = await association.target.create(values, options); | ||
await sourceInstance[association.accessors.add](newAssociatedObject, _.omit(options, ['fields'])); | ||
await sourceInstance[association.accessors.add](newAssociatedObject, _.omit(options, ["fields"])); | ||
return newAssociatedObject; | ||
} | ||
verifyAssociationAlias(alias) { | ||
if (typeof alias === 'string') { | ||
if (typeof alias === "string") { | ||
return this.as === alias; | ||
} | ||
if (alias && alias.plural) { | ||
return this.as === alias.plural; | ||
} | ||
return !this.isAliased; | ||
} | ||
} | ||
module.exports = BelongsToMany; | ||
module.exports.BelongsToMany = BelongsToMany; | ||
module.exports.default = BelongsToMany; | ||
//# sourceMappingURL=belongs-to-many.js.map |
@@ -1,24 +0,29 @@ | ||
'use strict'; | ||
const Utils = require('./../utils'); | ||
const Helpers = require('./helpers'); | ||
const _ = require('lodash'); | ||
const Association = require('./base'); | ||
const Op = require('../operators'); | ||
/** | ||
* One-to-one association | ||
* | ||
* In the API reference below, add the name of the association to the method, e.g. for `User.belongsTo(Project)` the getter will be `user.getProject()`. | ||
* | ||
* @see {@link Model.belongsTo} | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const Utils = require("./../utils"); | ||
const Helpers = require("./helpers"); | ||
const _ = require("lodash"); | ||
const Association = require("./base"); | ||
const Op = require("../operators"); | ||
class BelongsTo extends Association { | ||
constructor(source, target, options) { | ||
super(source, target, options); | ||
this.associationType = 'BelongsTo'; | ||
this.associationType = "BelongsTo"; | ||
this.isSingleAssociation = true; | ||
this.foreignKeyAttribute = {}; | ||
if (this.as) { | ||
@@ -33,3 +38,2 @@ this.isAliased = true; | ||
} | ||
if (_.isObject(this.options.foreignKey)) { | ||
@@ -41,12 +45,8 @@ this.foreignKeyAttribute = this.options.foreignKey; | ||
} | ||
if (!this.foreignKey) { | ||
this.foreignKey = Utils.camelize( | ||
[ | ||
this.as, | ||
this.target.primaryKeyAttribute | ||
].join('_') | ||
); | ||
this.foreignKey = Utils.camelize([ | ||
this.as, | ||
this.target.primaryKeyAttribute | ||
].join("_")); | ||
} | ||
this.identifier = this.foreignKey; | ||
@@ -56,10 +56,5 @@ if (this.source.rawAttributes[this.identifier]) { | ||
} | ||
if ( | ||
this.options.targetKey | ||
&& !this.target.rawAttributes[this.options.targetKey] | ||
) { | ||
if (this.options.targetKey && !this.target.rawAttributes[this.options.targetKey]) { | ||
throw new Error(`Unknown attribute "${this.options.targetKey}" passed as targetKey, define this attribute on model "${this.target.name}" first`); | ||
} | ||
this.targetKey = this.options.targetKey || this.target.primaryKeyAttribute; | ||
@@ -69,9 +64,5 @@ this.targetKeyField = this.target.rawAttributes[this.targetKey].field || this.targetKey; | ||
this.targetIdentifier = this.targetKey; | ||
this.associationAccessor = this.as; | ||
this.options.useHooks = options.useHooks; | ||
// Get singular name, trying to uppercase the first letter, unless the model forbids it | ||
const singular = _.upperFirst(this.options.name.singular); | ||
this.accessors = { | ||
@@ -83,50 +74,25 @@ get: `get${singular}`, | ||
} | ||
// the id is in the source table | ||
_injectAttributes() { | ||
const newAttributes = { | ||
[this.foreignKey]: { | ||
[this.foreignKey]: __spreadValues({ | ||
type: this.options.keyType || this.target.rawAttributes[this.targetKey].type, | ||
allowNull: true, | ||
...this.foreignKeyAttribute | ||
} | ||
allowNull: true | ||
}, this.foreignKeyAttribute) | ||
}; | ||
if (this.options.constraints !== false) { | ||
const source = this.source.rawAttributes[this.foreignKey] || newAttributes[this.foreignKey]; | ||
this.options.onDelete = this.options.onDelete || (source.allowNull ? 'SET NULL' : 'NO ACTION'); | ||
this.options.onUpdate = this.options.onUpdate || 'CASCADE'; | ||
this.options.onDelete = this.options.onDelete || (source.allowNull ? "SET NULL" : "NO ACTION"); | ||
this.options.onUpdate = this.options.onUpdate || "CASCADE"; | ||
} | ||
Helpers.addForeignKeyConstraints(newAttributes[this.foreignKey], this.target, this.source, this.options, this.targetKeyField); | ||
Utils.mergeDefaults(this.source.rawAttributes, newAttributes); | ||
this.source.refreshAttributes(); | ||
this.identifierField = this.source.rawAttributes[this.foreignKey].field || this.foreignKey; | ||
Helpers.checkNamingCollision(this); | ||
return this; | ||
} | ||
mixin(obj) { | ||
const methods = ['get', 'set', 'create']; | ||
const methods = ["get", "set", "create"]; | ||
Helpers.mixinMethods(this, obj, methods); | ||
} | ||
/** | ||
* Get the associated instance. | ||
* | ||
* @param {Model|Array<Model>} instances source instances | ||
* @param {object} [options] find options | ||
* @param {string|boolean} [options.scope] Apply a scope on the related model, or remove its default scope by passing false. | ||
* @param {string} [options.schema] Apply a schema on the related model | ||
* | ||
* @see | ||
* {@link Model.findOne} for a full explanation of options | ||
* | ||
* @returns {Promise<Model>} | ||
*/ | ||
async get(instances, options) { | ||
@@ -136,6 +102,4 @@ const where = {}; | ||
let instance; | ||
options = Utils.cloneDeep(options); | ||
if (Object.prototype.hasOwnProperty.call(options, 'scope')) { | ||
if (Object.prototype.hasOwnProperty.call(options, "scope")) { | ||
if (!options.scope) { | ||
@@ -147,15 +111,12 @@ Target = Target.unscoped(); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(options, 'schema')) { | ||
if (Object.prototype.hasOwnProperty.call(options, "schema")) { | ||
Target = Target.schema(options.schema, options.schemaDelimiter); | ||
} | ||
if (!Array.isArray(instances)) { | ||
instance = instances; | ||
instances = undefined; | ||
instances = void 0; | ||
} | ||
if (instances) { | ||
where[this.targetKey] = { | ||
[Op.in]: instances.map(_instance => _instance.get(this.foreignKey)) | ||
[Op.in]: instances.map((_instance) => _instance.get(this.foreignKey)) | ||
}; | ||
@@ -169,7 +130,3 @@ } else { | ||
} | ||
options.where = options.where ? | ||
{ [Op.and]: [where, options.where] } : | ||
where; | ||
options.where = options.where ? { [Op.and]: [where, options.where] } : where; | ||
if (instances) { | ||
@@ -181,82 +138,44 @@ const results = await Target.findAll(options); | ||
} | ||
for (const _instance of results) { | ||
result[_instance.get(this.targetKey, { raw: true })] = _instance; | ||
} | ||
return result; | ||
} | ||
return Target.findOne(options); | ||
} | ||
/** | ||
* Set the associated model. | ||
* | ||
* @param {Model} sourceInstance the source instance | ||
* @param {?<Model>|string|number} [associatedInstance] An persisted instance or the primary key of an instance to associate with this. Pass `null` or `undefined` to remove the association. | ||
* @param {object} [options={}] options passed to `this.save` | ||
* @param {boolean} [options.save=true] Skip saving this after setting the foreign key if false. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async set(sourceInstance, associatedInstance, options = {}) { | ||
let value = associatedInstance; | ||
if (associatedInstance instanceof this.target) { | ||
value = associatedInstance[this.targetKey]; | ||
} | ||
sourceInstance.set(this.foreignKey, value); | ||
if (options.save === false) return; | ||
options = { | ||
if (options.save === false) | ||
return; | ||
options = __spreadValues({ | ||
fields: [this.foreignKey], | ||
allowNull: [this.foreignKey], | ||
association: true, | ||
...options | ||
}; | ||
// passes the changed field to save, so only that field get updated. | ||
association: true | ||
}, options); | ||
return await sourceInstance.save(options); | ||
} | ||
/** | ||
* Create a new instance of the associated model and associate it with this. | ||
* | ||
* @param {Model} sourceInstance the source instance | ||
* @param {object} [values={}] values to create associated model instance with | ||
* @param {object} [options={}] Options passed to `target.create` and setAssociation. | ||
* | ||
* @see | ||
* {@link Model#create} for a full explanation of options | ||
* | ||
* @returns {Promise<Model>} The created target model | ||
*/ | ||
async create(sourceInstance, values, options) { | ||
values = values || {}; | ||
options = options || {}; | ||
const newAssociatedObject = await this.target.create(values, options); | ||
await sourceInstance[this.accessors.set](newAssociatedObject, options); | ||
return newAssociatedObject; | ||
} | ||
verifyAssociationAlias(alias) { | ||
if (typeof alias === 'string') { | ||
if (typeof alias === "string") { | ||
return this.as === alias; | ||
} | ||
if (alias && alias.singular) { | ||
return this.as === alias.singular; | ||
} | ||
return !this.isAliased; | ||
} | ||
} | ||
module.exports = BelongsTo; | ||
module.exports.BelongsTo = BelongsTo; | ||
module.exports.default = BelongsTo; | ||
//# sourceMappingURL=belongs-to.js.map |
@@ -1,22 +0,30 @@ | ||
'use strict'; | ||
const Utils = require('./../utils'); | ||
const Helpers = require('./helpers'); | ||
const _ = require('lodash'); | ||
const Association = require('./base'); | ||
const Op = require('../operators'); | ||
/** | ||
* One-to-many association | ||
* | ||
* In the API reference below, add the name of the association to the method, e.g. for `User.hasMany(Project)` the getter will be `user.getProjects()`. | ||
* If the association is aliased, use the alias instead, e.g. `User.hasMany(Project, { as: 'jobs' })` will be `user.getJobs()`. | ||
* | ||
* @see {@link Model.hasMany} | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const Utils = require("./../utils"); | ||
const Helpers = require("./helpers"); | ||
const _ = require("lodash"); | ||
const Association = require("./base"); | ||
const Op = require("../operators"); | ||
class HasMany extends Association { | ||
constructor(source, target, options) { | ||
super(source, target, options); | ||
this.associationType = 'HasMany'; | ||
this.associationType = "HasMany"; | ||
this.targetAssociation = null; | ||
@@ -26,17 +34,10 @@ this.sequelize = source.sequelize; | ||
this.foreignKeyAttribute = {}; | ||
if (this.options.through) { | ||
throw new Error('N:M associations are not supported with hasMany. Use belongsToMany instead'); | ||
throw new Error("N:M associations are not supported with hasMany. Use belongsToMany instead"); | ||
} | ||
/* | ||
* If self association, this is the target association | ||
*/ | ||
if (this.isSelfAssociation) { | ||
this.targetAssociation = this; | ||
} | ||
if (this.as) { | ||
this.isAliased = true; | ||
if (_.isPlainObject(this.as)) { | ||
@@ -55,6 +56,2 @@ this.options.name = this.as; | ||
} | ||
/* | ||
* Foreign key setup | ||
*/ | ||
if (_.isObject(this.options.foreignKey)) { | ||
@@ -66,12 +63,8 @@ this.foreignKeyAttribute = this.options.foreignKey; | ||
} | ||
if (!this.foreignKey) { | ||
this.foreignKey = Utils.camelize( | ||
[ | ||
this.source.options.name.singular, | ||
this.source.primaryKeyAttribute | ||
].join('_') | ||
); | ||
this.foreignKey = Utils.camelize([ | ||
this.source.options.name.singular, | ||
this.source.primaryKeyAttribute | ||
].join("_")); | ||
} | ||
if (this.target.rawAttributes[this.foreignKey]) { | ||
@@ -81,8 +74,3 @@ this.identifierField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey; | ||
} | ||
/* | ||
* Source key setup | ||
*/ | ||
this.sourceKey = this.options.sourceKey || this.source.primaryKeyAttribute; | ||
if (this.source.rawAttributes[this.sourceKey]) { | ||
@@ -95,8 +83,4 @@ this.sourceKeyAttribute = this.sourceKey; | ||
} | ||
// Get singular and plural names | ||
// try to uppercase the first letter, unless the model forbids it | ||
const plural = _.upperFirst(this.options.name.plural); | ||
const singular = _.upperFirst(this.options.name.singular); | ||
this.associationAccessor = this.as; | ||
@@ -116,92 +100,56 @@ this.accessors = { | ||
} | ||
// the id is in the target table | ||
// or in an extra table which connects two tables | ||
_injectAttributes() { | ||
const newAttributes = { | ||
[this.foreignKey]: { | ||
[this.foreignKey]: __spreadValues({ | ||
type: this.options.keyType || this.source.rawAttributes[this.sourceKeyAttribute].type, | ||
allowNull: true, | ||
...this.foreignKeyAttribute | ||
} | ||
allowNull: true | ||
}, this.foreignKeyAttribute) | ||
}; | ||
// Create a new options object for use with addForeignKeyConstraints, to avoid polluting this.options in case it is later used for a n:m | ||
const constraintOptions = { ...this.options }; | ||
const constraintOptions = __spreadValues({}, this.options); | ||
if (this.options.constraints !== false) { | ||
const target = this.target.rawAttributes[this.foreignKey] || newAttributes[this.foreignKey]; | ||
constraintOptions.onDelete = constraintOptions.onDelete || (target.allowNull ? 'SET NULL' : 'CASCADE'); | ||
constraintOptions.onUpdate = constraintOptions.onUpdate || 'CASCADE'; | ||
constraintOptions.onDelete = constraintOptions.onDelete || (target.allowNull ? "SET NULL" : "CASCADE"); | ||
constraintOptions.onUpdate = constraintOptions.onUpdate || "CASCADE"; | ||
} | ||
Helpers.addForeignKeyConstraints(newAttributes[this.foreignKey], this.source, this.target, constraintOptions, this.sourceKeyField); | ||
Utils.mergeDefaults(this.target.rawAttributes, newAttributes); | ||
this.target.refreshAttributes(); | ||
this.source.refreshAttributes(); | ||
this.identifierField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey; | ||
this.foreignKeyField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey; | ||
this.sourceKeyField = this.source.rawAttributes[this.sourceKey].field || this.sourceKey; | ||
Helpers.checkNamingCollision(this); | ||
return this; | ||
} | ||
mixin(obj) { | ||
const methods = ['get', 'count', 'hasSingle', 'hasAll', 'set', 'add', 'addMultiple', 'remove', 'removeMultiple', 'create']; | ||
const methods = ["get", "count", "hasSingle", "hasAll", "set", "add", "addMultiple", "remove", "removeMultiple", "create"]; | ||
const aliases = { | ||
hasSingle: 'has', | ||
hasAll: 'has', | ||
addMultiple: 'add', | ||
removeMultiple: 'remove' | ||
hasSingle: "has", | ||
hasAll: "has", | ||
addMultiple: "add", | ||
removeMultiple: "remove" | ||
}; | ||
Helpers.mixinMethods(this, obj, methods, aliases); | ||
} | ||
/** | ||
* Get everything currently associated with this, using an optional where clause. | ||
* | ||
* @param {Model|Array<Model>} instances source instances | ||
* @param {object} [options] find options | ||
* @param {object} [options.where] An optional where clause to limit the associated models | ||
* @param {string|boolean} [options.scope] Apply a scope on the related model, or remove its default scope by passing false | ||
* @param {string} [options.schema] Apply a schema on the related model | ||
* | ||
* @see | ||
* {@link Model.findAll} for a full explanation of options | ||
* | ||
* @returns {Promise<Array<Model>>} | ||
*/ | ||
async get(instances, options = {}) { | ||
const where = {}; | ||
let Model = this.target; | ||
let instance; | ||
let values; | ||
if (!Array.isArray(instances)) { | ||
instance = instances; | ||
instances = undefined; | ||
instances = void 0; | ||
} | ||
options = { ...options }; | ||
options = __spreadValues({}, options); | ||
if (this.scope) { | ||
Object.assign(where, this.scope); | ||
} | ||
if (instances) { | ||
values = instances.map(_instance => _instance.get(this.sourceKey, { raw: true })); | ||
values = instances.map((_instance) => _instance.get(this.sourceKey, { raw: true })); | ||
if (options.limit && instances.length > 1) { | ||
options.groupedLimit = { | ||
limit: options.limit, | ||
on: this, // association | ||
on: this, | ||
values | ||
}; | ||
delete options.limit; | ||
@@ -217,8 +165,4 @@ } else { | ||
} | ||
options.where = options.where ? | ||
{ [Op.and]: [where, options.where] } : | ||
where; | ||
if (Object.prototype.hasOwnProperty.call(options, 'scope')) { | ||
options.where = options.where ? { [Op.and]: [where, options.where] } : where; | ||
if (Object.prototype.hasOwnProperty.call(options, "scope")) { | ||
if (!options.scope) { | ||
@@ -230,10 +174,8 @@ Model = Model.unscoped(); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(options, 'schema')) { | ||
if (Object.prototype.hasOwnProperty.call(options, "schema")) { | ||
Model = Model.schema(options.schema, options.schemaDelimiter); | ||
} | ||
const results = await Model.findAll(options); | ||
if (instance) return results; | ||
if (instance) | ||
return results; | ||
const result = {}; | ||
@@ -243,30 +185,13 @@ for (const _instance of instances) { | ||
} | ||
for (const _instance of results) { | ||
result[_instance.get(this.foreignKey, { raw: true })].push(_instance); | ||
} | ||
return result; | ||
} | ||
/** | ||
* Count everything currently associated with this, using an optional where clause. | ||
* | ||
* @param {Model} instance the source instance | ||
* @param {object} [options] find & count options | ||
* @param {object} [options.where] An optional where clause to limit the associated models | ||
* @param {string|boolean} [options.scope] Apply a scope on the related model, or remove its default scope by passing false | ||
* | ||
* @returns {Promise<number>} | ||
*/ | ||
async count(instance, options) { | ||
options = Utils.cloneDeep(options); | ||
options.attributes = [ | ||
[ | ||
this.sequelize.fn( | ||
'COUNT', | ||
this.sequelize.col(`${this.target.name}.${this.target.primaryKeyField}`) | ||
), | ||
'count' | ||
this.sequelize.fn("COUNT", this.sequelize.col(`${this.target.name}.${this.target.primaryKeyField}`)), | ||
"count" | ||
] | ||
@@ -276,32 +201,16 @@ ]; | ||
options.plain = true; | ||
const result = await this.get(instance, options); | ||
return parseInt(result.count, 10); | ||
} | ||
/** | ||
* Check if one or more rows are associated with `this`. | ||
* | ||
* @param {Model} sourceInstance the source instance | ||
* @param {Model|Model[]|string[]|string|number[]|number} [targetInstances] Can be an array of instances or their primary keys | ||
* @param {object} [options] Options passed to getAssociations | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async has(sourceInstance, targetInstances, options) { | ||
const where = {}; | ||
if (!Array.isArray(targetInstances)) { | ||
targetInstances = [targetInstances]; | ||
} | ||
options = { | ||
...options, | ||
options = __spreadProps(__spreadValues({}, options), { | ||
scope: false, | ||
attributes: [this.target.primaryKeyAttribute], | ||
raw: true | ||
}; | ||
where[Op.or] = targetInstances.map(instance => { | ||
}); | ||
where[Op.or] = targetInstances.map((instance) => { | ||
if (instance instanceof this.target) { | ||
@@ -314,3 +223,2 @@ return instance.where(); | ||
}); | ||
options.where = { | ||
@@ -322,18 +230,5 @@ [Op.and]: [ | ||
}; | ||
const associatedObjects = await this.get(sourceInstance, options); | ||
return associatedObjects.length === targetInstances.length; | ||
} | ||
/** | ||
* Set the associated models by passing an array of persisted instances or their primary keys. Everything that is not in the passed array will be un-associated | ||
* | ||
* @param {Model} sourceInstance source instance to associate new instances with | ||
* @param {Model|Model[]|string[]|string|number[]|number} [targetInstances] An array of persisted instances or primary key of instances to associate with this. Pass `null` or `undefined` to remove all associations. | ||
* @param {object} [options] Options passed to `target.findAll` and `update`. | ||
* @param {object} [options.validate] Run validation for the join model | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async set(sourceInstance, targetInstances, options) { | ||
@@ -345,104 +240,44 @@ if (targetInstances === null) { | ||
} | ||
const oldAssociations = await this.get(sourceInstance, { ...options, scope: false, raw: true }); | ||
const oldAssociations = await this.get(sourceInstance, __spreadProps(__spreadValues({}, options), { scope: false, raw: true })); | ||
const promises = []; | ||
const obsoleteAssociations = oldAssociations.filter(old => | ||
!targetInstances.find(obj => | ||
obj[this.target.primaryKeyAttribute] === old[this.target.primaryKeyAttribute] | ||
) | ||
); | ||
const unassociatedObjects = targetInstances.filter(obj => | ||
!oldAssociations.find(old => | ||
obj[this.target.primaryKeyAttribute] === old[this.target.primaryKeyAttribute] | ||
) | ||
); | ||
const obsoleteAssociations = oldAssociations.filter((old) => !targetInstances.find((obj) => obj[this.target.primaryKeyAttribute] === old[this.target.primaryKeyAttribute])); | ||
const unassociatedObjects = targetInstances.filter((obj) => !oldAssociations.find((old) => obj[this.target.primaryKeyAttribute] === old[this.target.primaryKeyAttribute])); | ||
let updateWhere; | ||
let update; | ||
if (obsoleteAssociations.length > 0) { | ||
update = {}; | ||
update[this.foreignKey] = null; | ||
updateWhere = { | ||
[this.target.primaryKeyAttribute]: obsoleteAssociations.map(associatedObject => | ||
associatedObject[this.target.primaryKeyAttribute] | ||
) | ||
[this.target.primaryKeyAttribute]: obsoleteAssociations.map((associatedObject) => associatedObject[this.target.primaryKeyAttribute]) | ||
}; | ||
promises.push(this.target.unscoped().update( | ||
update, | ||
{ | ||
...options, | ||
where: updateWhere | ||
} | ||
)); | ||
promises.push(this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), { | ||
where: updateWhere | ||
}))); | ||
} | ||
if (unassociatedObjects.length > 0) { | ||
updateWhere = {}; | ||
update = {}; | ||
update[this.foreignKey] = sourceInstance.get(this.sourceKey); | ||
Object.assign(update, this.scope); | ||
updateWhere[this.target.primaryKeyAttribute] = unassociatedObjects.map(unassociatedObject => | ||
unassociatedObject[this.target.primaryKeyAttribute] | ||
); | ||
promises.push(this.target.unscoped().update( | ||
update, | ||
{ | ||
...options, | ||
where: updateWhere | ||
} | ||
)); | ||
updateWhere[this.target.primaryKeyAttribute] = unassociatedObjects.map((unassociatedObject) => unassociatedObject[this.target.primaryKeyAttribute]); | ||
promises.push(this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), { | ||
where: updateWhere | ||
}))); | ||
} | ||
await Promise.all(promises); | ||
return sourceInstance; | ||
} | ||
/** | ||
* Associate one or more target rows with `this`. This method accepts a Model / string / number to associate a single row, | ||
* or a mixed array of Model / string / numbers to associate multiple rows. | ||
* | ||
* @param {Model} sourceInstance the source instance | ||
* @param {Model|Model[]|string[]|string|number[]|number} [targetInstances] A single instance or primary key, or a mixed array of persisted instances or primary keys | ||
* @param {object} [options] Options passed to `target.update`. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async add(sourceInstance, targetInstances, options = {}) { | ||
if (!targetInstances) return Promise.resolve(); | ||
if (!targetInstances) | ||
return Promise.resolve(); | ||
targetInstances = this.toInstanceArray(targetInstances); | ||
const update = { | ||
[this.foreignKey]: sourceInstance.get(this.sourceKey), | ||
...this.scope | ||
}; | ||
const update = __spreadValues({ | ||
[this.foreignKey]: sourceInstance.get(this.sourceKey) | ||
}, this.scope); | ||
const where = { | ||
[this.target.primaryKeyAttribute]: targetInstances.map(unassociatedObject => | ||
unassociatedObject.get(this.target.primaryKeyAttribute) | ||
) | ||
[this.target.primaryKeyAttribute]: targetInstances.map((unassociatedObject) => unassociatedObject.get(this.target.primaryKeyAttribute)) | ||
}; | ||
await this.target.unscoped().update(update, { ...options, where }); | ||
await this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), { where })); | ||
return sourceInstance; | ||
} | ||
/** | ||
* Un-associate one or several target rows. | ||
* | ||
* @param {Model} sourceInstance instance to un associate instances with | ||
* @param {Model|Model[]|string|string[]|number|number[]} [targetInstances] Can be an Instance or its primary key, or a mixed array of instances and primary keys | ||
* @param {object} [options] Options passed to `target.update` | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async remove(sourceInstance, targetInstances, options = {}) { | ||
@@ -452,26 +287,10 @@ const update = { | ||
}; | ||
targetInstances = this.toInstanceArray(targetInstances); | ||
const where = { | ||
[this.foreignKey]: sourceInstance.get(this.sourceKey), | ||
[this.target.primaryKeyAttribute]: targetInstances.map(targetInstance => | ||
targetInstance.get(this.target.primaryKeyAttribute) | ||
) | ||
[this.target.primaryKeyAttribute]: targetInstances.map((targetInstance) => targetInstance.get(this.target.primaryKeyAttribute)) | ||
}; | ||
await this.target.unscoped().update(update, { ...options, where }); | ||
await this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), { where })); | ||
return this; | ||
} | ||
/** | ||
* Create a new instance of the associated model and associate it with this. | ||
* | ||
* @param {Model} sourceInstance source instance | ||
* @param {object} [values] values for target model instance | ||
* @param {object} [options] Options passed to `target.create` | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async create(sourceInstance, values, options = {}) { | ||
@@ -483,34 +302,30 @@ if (Array.isArray(options)) { | ||
} | ||
if (values === undefined) { | ||
if (values === void 0) { | ||
values = {}; | ||
} | ||
if (this.scope) { | ||
for (const attribute of Object.keys(this.scope)) { | ||
values[attribute] = this.scope[attribute]; | ||
if (options.fields) options.fields.push(attribute); | ||
if (options.fields) | ||
options.fields.push(attribute); | ||
} | ||
} | ||
values[this.foreignKey] = sourceInstance.get(this.sourceKey); | ||
if (options.fields) options.fields.push(this.foreignKey); | ||
if (options.fields) | ||
options.fields.push(this.foreignKey); | ||
return await this.target.create(values, options); | ||
} | ||
verifyAssociationAlias(alias) { | ||
if (typeof alias === 'string') { | ||
if (typeof alias === "string") { | ||
return this.as === alias; | ||
} | ||
if (alias && alias.plural) { | ||
return this.as === alias.plural; | ||
} | ||
return !this.isAliased; | ||
} | ||
} | ||
module.exports = HasMany; | ||
module.exports.HasMany = HasMany; | ||
module.exports.default = HasMany; | ||
//# sourceMappingURL=has-many.js.map |
@@ -1,25 +0,32 @@ | ||
'use strict'; | ||
const Utils = require('./../utils'); | ||
const Helpers = require('./helpers'); | ||
const _ = require('lodash'); | ||
const Association = require('./base'); | ||
const Op = require('../operators'); | ||
/** | ||
* One-to-one association | ||
* | ||
* In the API reference below, add the name of the association to the method, e.g. for `User.hasOne(Project)` the getter will be `user.getProject()`. | ||
* This is almost the same as `belongsTo` with one exception - The foreign key will be defined on the target model. | ||
* | ||
* @see {@link Model.hasOne} | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const Utils = require("./../utils"); | ||
const Helpers = require("./helpers"); | ||
const _ = require("lodash"); | ||
const Association = require("./base"); | ||
const Op = require("../operators"); | ||
class HasOne extends Association { | ||
constructor(source, target, options) { | ||
super(source, target, options); | ||
this.associationType = 'HasOne'; | ||
this.associationType = "HasOne"; | ||
this.isSingleAssociation = true; | ||
this.foreignKeyAttribute = {}; | ||
if (this.as) { | ||
@@ -34,3 +41,2 @@ this.isAliased = true; | ||
} | ||
if (_.isObject(this.options.foreignKey)) { | ||
@@ -42,33 +48,20 @@ this.foreignKeyAttribute = this.options.foreignKey; | ||
} | ||
if (!this.foreignKey) { | ||
this.foreignKey = Utils.camelize( | ||
[ | ||
Utils.singularize(this.options.as || this.source.name), | ||
this.source.primaryKeyAttribute | ||
].join('_') | ||
); | ||
this.foreignKey = Utils.camelize([ | ||
Utils.singularize(this.options.as || this.source.name), | ||
this.source.primaryKeyAttribute | ||
].join("_")); | ||
} | ||
if ( | ||
this.options.sourceKey | ||
&& !this.source.rawAttributes[this.options.sourceKey] | ||
) { | ||
if (this.options.sourceKey && !this.source.rawAttributes[this.options.sourceKey]) { | ||
throw new Error(`Unknown attribute "${this.options.sourceKey}" passed as sourceKey, define this attribute on model "${this.source.name}" first`); | ||
} | ||
this.sourceKey = this.sourceKeyAttribute = this.options.sourceKey || this.source.primaryKeyAttribute; | ||
this.sourceKeyField = this.source.rawAttributes[this.sourceKey].field || this.sourceKey; | ||
this.sourceKeyIsPrimary = this.sourceKey === this.source.primaryKeyAttribute; | ||
this.associationAccessor = this.as; | ||
this.options.useHooks = options.useHooks; | ||
if (this.target.rawAttributes[this.foreignKey]) { | ||
this.identifierField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey; | ||
} | ||
// Get singular name, trying to uppercase the first letter, unless the model forbids it | ||
const singular = _.upperFirst(this.options.name.singular); | ||
this.accessors = { | ||
@@ -80,59 +73,31 @@ get: `get${singular}`, | ||
} | ||
// the id is in the target table | ||
_injectAttributes() { | ||
const newAttributes = { | ||
[this.foreignKey]: { | ||
[this.foreignKey]: __spreadValues({ | ||
type: this.options.keyType || this.source.rawAttributes[this.sourceKey].type, | ||
allowNull: true, | ||
...this.foreignKeyAttribute | ||
} | ||
allowNull: true | ||
}, this.foreignKeyAttribute) | ||
}; | ||
if (this.options.constraints !== false) { | ||
const target = this.target.rawAttributes[this.foreignKey] || newAttributes[this.foreignKey]; | ||
this.options.onDelete = this.options.onDelete || (target.allowNull ? 'SET NULL' : 'CASCADE'); | ||
this.options.onUpdate = this.options.onUpdate || 'CASCADE'; | ||
this.options.onDelete = this.options.onDelete || (target.allowNull ? "SET NULL" : "CASCADE"); | ||
this.options.onUpdate = this.options.onUpdate || "CASCADE"; | ||
} | ||
Helpers.addForeignKeyConstraints(newAttributes[this.foreignKey], this.source, this.target, this.options, this.sourceKeyField); | ||
Utils.mergeDefaults(this.target.rawAttributes, newAttributes); | ||
this.target.refreshAttributes(); | ||
this.identifierField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey; | ||
Helpers.checkNamingCollision(this); | ||
return this; | ||
} | ||
mixin(obj) { | ||
const methods = ['get', 'set', 'create']; | ||
const methods = ["get", "set", "create"]; | ||
Helpers.mixinMethods(this, obj, methods); | ||
} | ||
/** | ||
* Get the associated instance. | ||
* | ||
* @param {Model|Array<Model>} instances source instances | ||
* @param {object} [options] find options | ||
* @param {string|boolean} [options.scope] Apply a scope on the related model, or remove its default scope by passing false | ||
* @param {string} [options.schema] Apply a schema on the related model | ||
* | ||
* @see | ||
* {@link Model.findOne} for a full explanation of options | ||
* | ||
* @returns {Promise<Model>} | ||
*/ | ||
async get(instances, options) { | ||
const where = {}; | ||
let Target = this.target; | ||
let instance; | ||
options = Utils.cloneDeep(options); | ||
if (Object.prototype.hasOwnProperty.call(options, 'scope')) { | ||
if (Object.prototype.hasOwnProperty.call(options, "scope")) { | ||
if (!options.scope) { | ||
@@ -144,15 +109,12 @@ Target = Target.unscoped(); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(options, 'schema')) { | ||
if (Object.prototype.hasOwnProperty.call(options, "schema")) { | ||
Target = Target.schema(options.schema, options.schemaDelimiter); | ||
} | ||
if (!Array.isArray(instances)) { | ||
instance = instances; | ||
instances = undefined; | ||
instances = void 0; | ||
} | ||
if (instances) { | ||
where[this.foreignKey] = { | ||
[Op.in]: instances.map(_instance => _instance.get(this.sourceKey)) | ||
[Op.in]: instances.map((_instance) => _instance.get(this.sourceKey)) | ||
}; | ||
@@ -162,11 +124,6 @@ } else { | ||
} | ||
if (this.scope) { | ||
Object.assign(where, this.scope); | ||
} | ||
options.where = options.where ? | ||
{ [Op.and]: [where, options.where] } : | ||
where; | ||
options.where = options.where ? { [Op.and]: [where, options.where] } : where; | ||
if (instances) { | ||
@@ -178,40 +135,20 @@ const results = await Target.findAll(options); | ||
} | ||
for (const _instance of results) { | ||
result[_instance.get(this.foreignKey, { raw: true })] = _instance; | ||
} | ||
return result; | ||
} | ||
return Target.findOne(options); | ||
} | ||
/** | ||
* Set the associated model. | ||
* | ||
* @param {Model} sourceInstance the source instance | ||
* @param {?<Model>|string|number} [associatedInstance] An persisted instance or the primary key of an instance to associate with this. Pass `null` or `undefined` to remove the association. | ||
* @param {object} [options] Options passed to getAssociation and `target.save` | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async set(sourceInstance, associatedInstance, options) { | ||
options = { ...options, scope: false }; | ||
options = __spreadProps(__spreadValues({}, options), { scope: false }); | ||
const oldInstance = await sourceInstance[this.accessors.get](options); | ||
// TODO Use equals method once #5605 is resolved | ||
const alreadyAssociated = oldInstance && associatedInstance && this.target.primaryKeyAttributes.every(attribute => | ||
oldInstance.get(attribute, { raw: true }) === (associatedInstance.get ? associatedInstance.get(attribute, { raw: true }) : associatedInstance) | ||
); | ||
const alreadyAssociated = oldInstance && associatedInstance && this.target.primaryKeyAttributes.every((attribute) => oldInstance.get(attribute, { raw: true }) === (associatedInstance.get ? associatedInstance.get(attribute, { raw: true }) : associatedInstance)); | ||
if (oldInstance && !alreadyAssociated) { | ||
oldInstance[this.foreignKey] = null; | ||
await oldInstance.save({ | ||
...options, | ||
await oldInstance.save(__spreadProps(__spreadValues({}, options), { | ||
fields: [this.foreignKey], | ||
allowNull: [this.foreignKey], | ||
association: true | ||
}); | ||
})); | ||
} | ||
@@ -226,28 +163,11 @@ if (associatedInstance && !alreadyAssociated) { | ||
} | ||
Object.assign(associatedInstance, this.scope); | ||
associatedInstance.set(this.foreignKey, sourceInstance.get(this.sourceKeyAttribute)); | ||
return associatedInstance.save(options); | ||
} | ||
return null; | ||
} | ||
/** | ||
* Create a new instance of the associated model and associate it with this. | ||
* | ||
* @param {Model} sourceInstance the source instance | ||
* @param {object} [values={}] values to create associated model instance with | ||
* @param {object} [options] Options passed to `target.create` and setAssociation. | ||
* | ||
* @see | ||
* {@link Model#create} for a full explanation of options | ||
* | ||
* @returns {Promise<Model>} The created target model | ||
*/ | ||
async create(sourceInstance, values, options) { | ||
values = values || {}; | ||
options = options || {}; | ||
if (this.scope) { | ||
@@ -261,3 +181,2 @@ for (const attribute of Object.keys(this.scope)) { | ||
} | ||
values[this.foreignKey] = sourceInstance.get(this.sourceKeyAttribute); | ||
@@ -267,19 +186,15 @@ if (options.fields) { | ||
} | ||
return await this.target.create(values, options); | ||
} | ||
verifyAssociationAlias(alias) { | ||
if (typeof alias === 'string') { | ||
if (typeof alias === "string") { | ||
return this.as === alias; | ||
} | ||
if (alias && alias.singular) { | ||
return this.as === alias.singular; | ||
} | ||
return !this.isAliased; | ||
} | ||
} | ||
module.exports = HasOne; | ||
//# sourceMappingURL=has-one.js.map |
@@ -1,23 +0,11 @@ | ||
'use strict'; | ||
"use strict"; | ||
function checkNamingCollision(association) { | ||
if (Object.prototype.hasOwnProperty.call(association.source.rawAttributes, association.as)) { | ||
throw new Error( | ||
`Naming collision between attribute '${association.as}'` + | ||
` and association '${association.as}' on model ${association.source.name}` + | ||
'. To remedy this, change either foreignKey or as in your association definition' | ||
); | ||
throw new Error(`Naming collision between attribute '${association.as}' and association '${association.as}' on model ${association.source.name}. To remedy this, change either foreignKey or as in your association definition`); | ||
} | ||
} | ||
exports.checkNamingCollision = checkNamingCollision; | ||
function addForeignKeyConstraints(newAttribute, source, target, options, key) { | ||
// FK constraints are opt-in: users must either set `foreignKeyConstraints` | ||
// on the association, or request an `onDelete` or `onUpdate` behavior | ||
if (options.foreignKeyConstraint || options.onDelete || options.onUpdate) { | ||
// Find primary keys: composite keys not supported with this approach | ||
const primaryKeys = Object.keys(source.primaryKeys) | ||
.map(primaryKeyAttribute => source.rawAttributes[primaryKeyAttribute].field || primaryKeyAttribute); | ||
const primaryKeys = Object.keys(source.primaryKeys).map((primaryKeyAttribute) => source.rawAttributes[primaryKeyAttribute].field || primaryKeyAttribute); | ||
if (primaryKeys.length === 1 || !primaryKeys.includes(key)) { | ||
@@ -28,3 +16,2 @@ newAttribute.references = { | ||
}; | ||
newAttribute.onDelete = options.onDelete; | ||
@@ -36,22 +23,7 @@ newAttribute.onUpdate = options.onUpdate; | ||
exports.addForeignKeyConstraints = addForeignKeyConstraints; | ||
/** | ||
* Mixin (inject) association methods to model prototype | ||
* | ||
* @private | ||
* | ||
* @param {object} association instance | ||
* @param {object} obj Model prototype | ||
* @param {Array} methods Method names to inject | ||
* @param {object} aliases Mapping between model and association method names | ||
* | ||
*/ | ||
function mixinMethods(association, obj, methods, aliases) { | ||
aliases = aliases || {}; | ||
for (const method of methods) { | ||
// don't override custom methods | ||
if (!Object.prototype.hasOwnProperty.call(obj, association.accessors[method])) { | ||
const realMethod = aliases[method] || method; | ||
obj[association.accessors[method]] = function() { | ||
@@ -64,1 +36,2 @@ return association[realMethod](this, ...Array.from(arguments)); | ||
exports.mixinMethods = mixinMethods; | ||
//# sourceMappingURL=helpers.js.map |
@@ -1,12 +0,10 @@ | ||
'use strict'; | ||
const Association = require('./base'); | ||
Association.BelongsTo = require('./belongs-to'); | ||
Association.HasOne = require('./has-one'); | ||
Association.HasMany = require('./has-many'); | ||
Association.BelongsToMany = require('./belongs-to-many'); | ||
"use strict"; | ||
const Association = require("./base"); | ||
Association.BelongsTo = require("./belongs-to"); | ||
Association.HasOne = require("./has-one"); | ||
Association.HasMany = require("./has-many"); | ||
Association.BelongsToMany = require("./belongs-to-many"); | ||
module.exports = Association; | ||
module.exports.default = Association; | ||
module.exports.Association = Association; | ||
//# sourceMappingURL=index.js.map |
@@ -1,15 +0,10 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const HasOne = require('./has-one'); | ||
const HasMany = require('./has-many'); | ||
const BelongsToMany = require('./belongs-to-many'); | ||
const BelongsTo = require('./belongs-to'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const HasOne = require("./has-one"); | ||
const HasMany = require("./has-many"); | ||
const BelongsToMany = require("./belongs-to-many"); | ||
const BelongsTo = require("./belongs-to"); | ||
function isModel(model, sequelize) { | ||
return model | ||
&& model.prototype | ||
&& model.prototype instanceof sequelize.Sequelize.Model; | ||
return model && model.prototype && model.prototype instanceof sequelize.Sequelize.Model; | ||
} | ||
const Mixin = { | ||
@@ -20,29 +15,18 @@ hasMany(target, options = {}) { | ||
} | ||
const source = this; | ||
// Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option) | ||
options.hooks = options.hooks === undefined ? false : Boolean(options.hooks); | ||
options.hooks = options.hooks === void 0 ? false : Boolean(options.hooks); | ||
options.useHooks = options.hooks; | ||
Object.assign(options, _.omit(source.options, ['hooks'])); | ||
Object.assign(options, _.omit(source.options, ["hooks"])); | ||
if (options.useHooks) { | ||
this.runHooks('beforeAssociate', { source, target, type: HasMany }, options); | ||
this.runHooks("beforeAssociate", { source, target, type: HasMany }, options); | ||
} | ||
// the id is in the foreign table or in a connecting table | ||
const association = new HasMany(source, target, options); | ||
source.associations[association.associationAccessor] = association; | ||
association._injectAttributes(); | ||
association.mixin(source.prototype); | ||
if (options.useHooks) { | ||
this.runHooks('afterAssociate', { source, target, type: HasMany, association }, options); | ||
this.runHooks("afterAssociate", { source, target, type: HasMany, association }, options); | ||
} | ||
return association; | ||
}, | ||
belongsToMany(target, options = {}) { | ||
@@ -52,42 +36,28 @@ if (!isModel(target, this.sequelize)) { | ||
} | ||
const source = this; | ||
// Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option) | ||
options.hooks = options.hooks === undefined ? false : Boolean(options.hooks); | ||
options.hooks = options.hooks === void 0 ? false : Boolean(options.hooks); | ||
options.useHooks = options.hooks; | ||
options.timestamps = options.timestamps === undefined ? this.sequelize.options.timestamps : options.timestamps; | ||
Object.assign(options, _.omit(source.options, ['hooks', 'timestamps', 'scopes', 'defaultScope'])); | ||
options.timestamps = options.timestamps === void 0 ? this.sequelize.options.timestamps : options.timestamps; | ||
Object.assign(options, _.omit(source.options, ["hooks", "timestamps", "scopes", "defaultScope"])); | ||
if (options.useHooks) { | ||
this.runHooks('beforeAssociate', { source, target, type: BelongsToMany }, options); | ||
this.runHooks("beforeAssociate", { source, target, type: BelongsToMany }, options); | ||
} | ||
// the id is in the foreign table or in a connecting table | ||
const association = new BelongsToMany(source, target, options); | ||
source.associations[association.associationAccessor] = association; | ||
association._injectAttributes(); | ||
association.mixin(source.prototype); | ||
if (options.useHooks) { | ||
this.runHooks('afterAssociate', { source, target, type: BelongsToMany, association }, options); | ||
this.runHooks("afterAssociate", { source, target, type: BelongsToMany, association }, options); | ||
} | ||
return association; | ||
}, | ||
getAssociations(target) { | ||
return Object.values(this.associations).filter(association => association.target.name === target.name); | ||
return Object.values(this.associations).filter((association) => association.target.name === target.name); | ||
}, | ||
getAssociationForAlias(target, alias) { | ||
// Two associations cannot have the same alias, so we can use find instead of filter | ||
return this.getAssociations(target).find(association => association.verifyAssociationAlias(alias)) || null; | ||
return this.getAssociations(target).find((association) => association.verifyAssociationAlias(alias)) || null; | ||
} | ||
}; | ||
// The logic for hasOne and belongsTo is exactly the same | ||
function singleLinked(Type) { | ||
return function(target, options = {}) { | ||
// eslint-disable-next-line no-invalid-this | ||
const source = this; | ||
@@ -97,31 +67,22 @@ if (!isModel(target, source.sequelize)) { | ||
} | ||
// Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option) | ||
options.hooks = options.hooks === undefined ? false : Boolean(options.hooks); | ||
options.hooks = options.hooks === void 0 ? false : Boolean(options.hooks); | ||
options.useHooks = options.hooks; | ||
if (options.useHooks) { | ||
source.runHooks('beforeAssociate', { source, target, type: Type }, options); | ||
source.runHooks("beforeAssociate", { source, target, type: Type }, options); | ||
} | ||
// the id is in the foreign table | ||
const association = new Type(source, target, Object.assign(options, source.options)); | ||
source.associations[association.associationAccessor] = association; | ||
association._injectAttributes(); | ||
association.mixin(source.prototype); | ||
if (options.useHooks) { | ||
source.runHooks('afterAssociate', { source, target, type: Type, association }, options); | ||
source.runHooks("afterAssociate", { source, target, type: Type, association }, options); | ||
} | ||
return association; | ||
}; | ||
} | ||
Mixin.hasOne = singleLinked(HasOne); | ||
Mixin.belongsTo = singleLinked(BelongsTo); | ||
module.exports = Mixin; | ||
module.exports.Mixin = Mixin; | ||
module.exports.default = Mixin; | ||
//# sourceMappingURL=mixin.js.map |
@@ -1,15 +0,13 @@ | ||
'use strict'; | ||
const util = require('util'); | ||
const _ = require('lodash'); | ||
const wkx = require('wkx'); | ||
const sequelizeErrors = require('./errors'); | ||
const Validator = require('./utils/validator-extras').validator; | ||
const momentTz = require('moment-timezone'); | ||
const moment = require('moment'); | ||
const { logger } = require('./utils/logger'); | ||
"use strict"; | ||
const util = require("util"); | ||
const _ = require("lodash"); | ||
const wkx = require("wkx"); | ||
const sequelizeErrors = require("./errors"); | ||
const Validator = require("./utils/validator-extras").validator; | ||
const momentTz = require("moment-timezone"); | ||
const moment = require("moment"); | ||
const { logger } = require("./utils/logger"); | ||
const warnings = {}; | ||
const { classToInvokable } = require('./utils/class-to-invokable'); | ||
const { joinSQLFragments } = require('./utils/join-sql-fragments'); | ||
const { classToInvokable } = require("./utils/class-to-invokable"); | ||
const { joinSQLFragments } = require("./utils/join-sql-fragments"); | ||
class ABSTRACT { | ||
@@ -40,3 +38,4 @@ toString(options) { | ||
warnings[text] = true; | ||
logger.warn(`${text} \n>> Check: ${link}`); | ||
logger.warn(`${text} | ||
>> Check: ${link}`); | ||
} | ||
@@ -48,16 +47,7 @@ } | ||
} | ||
ABSTRACT.prototype.dialectTypes = ''; | ||
/** | ||
* STRING A variable length string | ||
*/ | ||
ABSTRACT.prototype.dialectTypes = ""; | ||
class STRING extends ABSTRACT { | ||
/** | ||
* @param {number} [length=255] length of string | ||
* @param {boolean} [binary=false] Is this binary? | ||
*/ | ||
constructor(length, binary) { | ||
super(); | ||
const options = typeof length === 'object' && length || { length, binary }; | ||
const options = typeof length === "object" && length || { length, binary }; | ||
this.options = options; | ||
@@ -70,15 +60,14 @@ this._binary = options.binary; | ||
`VARCHAR(${this._length})`, | ||
this._binary && 'BINARY' | ||
this._binary && "BINARY" | ||
]); | ||
} | ||
validate(value) { | ||
if (Object.prototype.toString.call(value) !== '[object String]') { | ||
if (this.options.binary && Buffer.isBuffer(value) || typeof value === 'number') { | ||
if (Object.prototype.toString.call(value) !== "[object String]") { | ||
if (this.options.binary && Buffer.isBuffer(value) || typeof value === "number") { | ||
return true; | ||
} | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid string", value)); | ||
} | ||
return true; | ||
} | ||
get BINARY() { | ||
@@ -89,3 +78,2 @@ this._binary = true; | ||
} | ||
static get BINARY() { | ||
@@ -95,13 +83,5 @@ return new this().BINARY; | ||
} | ||
/** | ||
* CHAR A fixed length string | ||
*/ | ||
class CHAR extends STRING { | ||
/** | ||
* @param {number} [length=255] length of string | ||
* @param {boolean} [binary=false] Is this binary? | ||
*/ | ||
constructor(length, binary) { | ||
super(typeof length === 'object' && length || { length, binary }); | ||
super(typeof length === "object" && length || { length, binary }); | ||
} | ||
@@ -111,28 +91,21 @@ toSql() { | ||
`CHAR(${this._length})`, | ||
this._binary && 'BINARY' | ||
this._binary && "BINARY" | ||
]); | ||
} | ||
} | ||
/** | ||
* Unlimited length TEXT column | ||
*/ | ||
class TEXT extends ABSTRACT { | ||
/** | ||
* @param {string} [length=''] could be tiny, medium, long. | ||
*/ | ||
constructor(length) { | ||
super(); | ||
const options = typeof length === 'object' && length || { length }; | ||
const options = typeof length === "object" && length || { length }; | ||
this.options = options; | ||
this._length = options.length || ''; | ||
this._length = options.length || ""; | ||
} | ||
toSql() { | ||
switch (this._length.toLowerCase()) { | ||
case 'tiny': | ||
return 'TINYTEXT'; | ||
case 'medium': | ||
return 'MEDIUMTEXT'; | ||
case 'long': | ||
return 'LONGTEXT'; | ||
case "tiny": | ||
return "TINYTEXT"; | ||
case "medium": | ||
return "MEDIUMTEXT"; | ||
case "long": | ||
return "LONGTEXT"; | ||
default: | ||
@@ -143,4 +116,4 @@ return this.key; | ||
validate(value) { | ||
if (typeof value !== 'string') { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); | ||
if (typeof value !== "string") { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid string", value)); | ||
} | ||
@@ -150,16 +123,9 @@ return true; | ||
} | ||
/** | ||
* An unlimited length case-insensitive text column. | ||
* Original case is preserved but acts case-insensitive when comparing values (such as when finding or unique constraints). | ||
* Only available in Postgres and SQLite. | ||
* | ||
*/ | ||
class CITEXT extends ABSTRACT { | ||
toSql() { | ||
return 'CITEXT'; | ||
return "CITEXT"; | ||
} | ||
validate(value) { | ||
if (typeof value !== 'string') { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); | ||
if (typeof value !== "string") { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid string", value)); | ||
} | ||
@@ -169,19 +135,6 @@ return true; | ||
} | ||
/** | ||
* Base number type which is used to build other types | ||
*/ | ||
class NUMBER extends ABSTRACT { | ||
/** | ||
* @param {object} options type options | ||
* @param {string|number} [options.length] length of type, like `INT(4)` | ||
* @param {boolean} [options.zerofill] Is zero filled? | ||
* @param {boolean} [options.unsigned] Is unsigned? | ||
* @param {string|number} [options.decimals] number of decimal points, used with length `FLOAT(5, 4)` | ||
* @param {string|number} [options.precision] defines precision for decimal type | ||
* @param {string|number} [options.scale] defines scale for decimal type | ||
*/ | ||
constructor(options = {}) { | ||
super(); | ||
if (typeof options === 'number') { | ||
if (typeof options === "number") { | ||
options = { | ||
@@ -203,12 +156,12 @@ length: options | ||
result += `(${this._length}`; | ||
if (typeof this._decimals === 'number') { | ||
if (typeof this._decimals === "number") { | ||
result += `,${this._decimals}`; | ||
} | ||
result += ')'; | ||
result += ")"; | ||
} | ||
if (this._unsigned) { | ||
result += ' UNSIGNED'; | ||
result += " UNSIGNED"; | ||
} | ||
if (this._zerofill) { | ||
result += ' ZEROFILL'; | ||
result += " ZEROFILL"; | ||
} | ||
@@ -224,6 +177,6 @@ return result; | ||
_stringify(number) { | ||
if (typeof number === 'number' || typeof number === 'boolean' || number === null || number === undefined) { | ||
if (typeof number === "number" || typeof number === "boolean" || number === null || number === void 0) { | ||
return number; | ||
} | ||
if (typeof number.toString === 'function') { | ||
if (typeof number.toString === "function") { | ||
return number.toString(); | ||
@@ -233,3 +186,2 @@ } | ||
} | ||
get UNSIGNED() { | ||
@@ -240,3 +192,2 @@ this._unsigned = true; | ||
} | ||
get ZEROFILL() { | ||
@@ -247,7 +198,5 @@ this._zerofill = true; | ||
} | ||
static get UNSIGNED() { | ||
return new this().UNSIGNED; | ||
} | ||
static get ZEROFILL() { | ||
@@ -257,6 +206,2 @@ return new this().ZEROFILL; | ||
} | ||
/** | ||
* A 32 bit integer | ||
*/ | ||
class INTEGER extends NUMBER { | ||
@@ -270,41 +215,17 @@ validate(value) { | ||
} | ||
/** | ||
* A 8 bit integer | ||
*/ | ||
class TINYINT extends INTEGER { | ||
} | ||
/** | ||
* A 16 bit integer | ||
*/ | ||
class SMALLINT extends INTEGER { | ||
} | ||
/** | ||
* A 24 bit integer | ||
*/ | ||
class MEDIUMINT extends INTEGER { | ||
} | ||
/** | ||
* A 64 bit integer | ||
*/ | ||
class BIGINT extends INTEGER { | ||
} | ||
/** | ||
* Floating point number (4-byte precision). | ||
*/ | ||
class FLOAT extends NUMBER { | ||
/** | ||
* @param {string|number} [length] length of type, like `FLOAT(4)` | ||
* @param {string|number} [decimals] number of decimal points, used with length `FLOAT(5, 4)` | ||
*/ | ||
constructor(length, decimals) { | ||
super(typeof length === 'object' && length || { length, decimals }); | ||
super(typeof length === "object" && length || { length, decimals }); | ||
} | ||
validate(value) { | ||
if (!Validator.isFloat(String(value))) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid float', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid float", value)); | ||
} | ||
@@ -314,49 +235,25 @@ return true; | ||
} | ||
/** | ||
* Floating point number (4-byte precision). | ||
*/ | ||
class REAL extends NUMBER { | ||
/** | ||
* @param {string|number} [length] length of type, like `REAL(4)` | ||
* @param {string|number} [decimals] number of decimal points, used with length `REAL(5, 4)` | ||
*/ | ||
constructor(length, decimals) { | ||
super(typeof length === 'object' && length || { length, decimals }); | ||
super(typeof length === "object" && length || { length, decimals }); | ||
} | ||
} | ||
/** | ||
* Floating point number (8-byte precision). | ||
*/ | ||
class DOUBLE extends NUMBER { | ||
/** | ||
* @param {string|number} [length] length of type, like `DOUBLE PRECISION(25)` | ||
* @param {string|number} [decimals] number of decimal points, used with length `DOUBLE PRECISION(25, 10)` | ||
*/ | ||
constructor(length, decimals) { | ||
super(typeof length === 'object' && length || { length, decimals }); | ||
super(typeof length === "object" && length || { length, decimals }); | ||
} | ||
} | ||
/** | ||
* Decimal type, variable precision, take length as specified by user | ||
*/ | ||
class DECIMAL extends NUMBER { | ||
/** | ||
* @param {string|number} [precision] defines precision | ||
* @param {string|number} [scale] defines scale | ||
*/ | ||
constructor(precision, scale) { | ||
super(typeof precision === 'object' && precision || { precision, scale }); | ||
super(typeof precision === "object" && precision || { precision, scale }); | ||
} | ||
toSql() { | ||
if (this._precision || this._scale) { | ||
return `DECIMAL(${[this._precision, this._scale].filter(_.identity).join(',')})`; | ||
return `DECIMAL(${[this._precision, this._scale].filter(_.identity).join(",")})`; | ||
} | ||
return 'DECIMAL'; | ||
return "DECIMAL"; | ||
} | ||
validate(value) { | ||
if (!Validator.isDecimal(String(value))) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid decimal', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid decimal", value)); | ||
} | ||
@@ -366,4 +263,2 @@ return true; | ||
} | ||
// TODO: Create intermediate class | ||
const protoExtensions = { | ||
@@ -373,9 +268,8 @@ escape: false, | ||
if (isNaN(value)) { | ||
return 'NaN'; | ||
return "NaN"; | ||
} | ||
if (!isFinite(value)) { | ||
const sign = value < 0 ? '-' : ''; | ||
const sign = value < 0 ? "-" : ""; | ||
return `${sign}Infinity`; | ||
} | ||
return value; | ||
@@ -390,17 +284,12 @@ }, | ||
}; | ||
for (const floating of [FLOAT, DOUBLE, REAL]) { | ||
Object.assign(floating.prototype, protoExtensions); | ||
} | ||
/** | ||
* A boolean / tinyint column, depending on dialect | ||
*/ | ||
class BOOLEAN extends ABSTRACT { | ||
toSql() { | ||
return 'TINYINT(1)'; | ||
return "TINYINT(1)"; | ||
} | ||
validate(value) { | ||
if (!Validator.isBoolean(String(value))) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid boolean', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid boolean", value)); | ||
} | ||
@@ -410,14 +299,11 @@ return true; | ||
_sanitize(value) { | ||
if (value !== null && value !== undefined) { | ||
if (value !== null && value !== void 0) { | ||
if (Buffer.isBuffer(value) && value.length === 1) { | ||
// Bit fields are returned as buffers | ||
value = value[0]; | ||
} | ||
const type = typeof value; | ||
if (type === 'string') { | ||
// Only take action on valid boolean strings. | ||
return value === 'true' ? true : value === 'false' ? false : value; | ||
if (type === "string") { | ||
return value === "true" ? true : value === "false" ? false : value; | ||
} | ||
if (type === 'number') { | ||
// Only take action on valid boolean integers. | ||
if (type === "number") { | ||
return value === 1 ? true : value === 0 ? false : value; | ||
@@ -429,35 +315,21 @@ } | ||
} | ||
BOOLEAN.parse = BOOLEAN.prototype._sanitize; | ||
/** | ||
* A time column | ||
* | ||
*/ | ||
class TIME extends ABSTRACT { | ||
toSql() { | ||
return 'TIME'; | ||
return "TIME"; | ||
} | ||
} | ||
/** | ||
* Date column with timezone, default is UTC | ||
*/ | ||
class DATE extends ABSTRACT { | ||
/** | ||
* @param {string|number} [length] precision to allow storing milliseconds | ||
*/ | ||
constructor(length) { | ||
super(); | ||
const options = typeof length === 'object' && length || { length }; | ||
const options = typeof length === "object" && length || { length }; | ||
this.options = options; | ||
this._length = options.length || ''; | ||
this._length = options.length || ""; | ||
} | ||
toSql() { | ||
return 'DATETIME'; | ||
return "DATETIME"; | ||
} | ||
validate(value) { | ||
if (!Validator.isDate(String(value))) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid date', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid date", value)); | ||
} | ||
@@ -473,8 +345,5 @@ return true; | ||
_isChanged(value, originalValue) { | ||
if (originalValue && !!value && | ||
(value === originalValue || | ||
value instanceof Date && originalValue instanceof Date && value.getTime() === originalValue.getTime())) { | ||
if (originalValue && !!value && (value === originalValue || value instanceof Date && originalValue instanceof Date && value.getTime() === originalValue.getTime())) { | ||
return false; | ||
} | ||
// not changed when set to same empty value | ||
if (!originalValue && !value && originalValue === value) { | ||
@@ -495,21 +364,18 @@ return false; | ||
_stringify(date, options) { | ||
date = this._applyTimezone(date, options); | ||
// Z here means current timezone, _not_ UTC | ||
return date.format('YYYY-MM-DD HH:mm:ss.SSS Z'); | ||
if (!moment.isMoment(date)) { | ||
date = this._applyTimezone(date, options); | ||
} | ||
return date.format("YYYY-MM-DD HH:mm:ss.SSS Z"); | ||
} | ||
} | ||
/** | ||
* A date only column (no timestamp) | ||
*/ | ||
class DATEONLY extends ABSTRACT { | ||
toSql() { | ||
return 'DATE'; | ||
return "DATE"; | ||
} | ||
_stringify(date) { | ||
return moment(date).format('YYYY-MM-DD'); | ||
return moment(date).format("YYYY-MM-DD"); | ||
} | ||
_sanitize(value, options) { | ||
if ((!options || options && !options.raw) && !!value) { | ||
return moment(value).format('YYYY-MM-DD'); | ||
return moment(value).format("YYYY-MM-DD"); | ||
} | ||
@@ -522,3 +388,2 @@ return value; | ||
} | ||
// not changed when set to same empty value | ||
if (!originalValue && !value && originalValue === value) { | ||
@@ -530,10 +395,6 @@ return false; | ||
} | ||
/** | ||
* A key / value store column. Only available in Postgres. | ||
*/ | ||
class HSTORE extends ABSTRACT { | ||
validate(value) { | ||
if (!_.isPlainObject(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid hstore', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid hstore", value)); | ||
} | ||
@@ -543,6 +404,2 @@ return true; | ||
} | ||
/** | ||
* A JSON string column. Available in MySQL, Postgres and SQLite | ||
*/ | ||
class JSONTYPE extends ABSTRACT { | ||
@@ -556,36 +413,21 @@ validate() { | ||
} | ||
/** | ||
* A binary storage JSON column. Only available in Postgres. | ||
*/ | ||
class JSONB extends JSONTYPE { | ||
} | ||
/** | ||
* A default value of the current timestamp | ||
*/ | ||
class NOW extends ABSTRACT { | ||
} | ||
/** | ||
* Binary storage | ||
*/ | ||
class BLOB extends ABSTRACT { | ||
/** | ||
* @param {string} [length=''] could be tiny, medium, long. | ||
*/ | ||
constructor(length) { | ||
super(); | ||
const options = typeof length === 'object' && length || { length }; | ||
const options = typeof length === "object" && length || { length }; | ||
this.options = options; | ||
this._length = options.length || ''; | ||
this._length = options.length || ""; | ||
} | ||
toSql() { | ||
switch (this._length.toLowerCase()) { | ||
case 'tiny': | ||
return 'TINYBLOB'; | ||
case 'medium': | ||
return 'MEDIUMBLOB'; | ||
case 'long': | ||
return 'LONGBLOB'; | ||
case "tiny": | ||
return "TINYBLOB"; | ||
case "medium": | ||
return "MEDIUMBLOB"; | ||
case "long": | ||
return "LONGBLOB"; | ||
default: | ||
@@ -596,4 +438,4 @@ return this.key; | ||
validate(value) { | ||
if (typeof value !== 'string' && !Buffer.isBuffer(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid blob', value)); | ||
if (typeof value !== "string" && !Buffer.isBuffer(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid blob", value)); | ||
} | ||
@@ -606,8 +448,7 @@ return true; | ||
value = Buffer.from(value); | ||
} | ||
else { | ||
} else { | ||
value = Buffer.from(value.toString()); | ||
} | ||
} | ||
const hex = value.toString('hex'); | ||
const hex = value.toString("hex"); | ||
return this._hexify(hex); | ||
@@ -622,4 +463,3 @@ } | ||
value = Buffer.from(value); | ||
} | ||
else { | ||
} else { | ||
value = Buffer.from(value.toString()); | ||
@@ -631,14 +471,4 @@ } | ||
} | ||
BLOB.prototype.escape = false; | ||
/** | ||
* Range types are data types representing a range of values of some element type (called the range's subtype). | ||
* Only available in Postgres. See [the Postgres documentation](http://www.postgresql.org/docs/9.4/static/rangetypes.html) for more details | ||
*/ | ||
class RANGE extends ABSTRACT { | ||
/** | ||
* @param {ABSTRACT} subtype A subtype for range, like RANGE(DATE) | ||
*/ | ||
constructor(subtype) { | ||
@@ -649,3 +479,3 @@ super(); | ||
options.subtype = new INTEGER(); | ||
if (typeof options.subtype === 'function') { | ||
if (typeof options.subtype === "function") { | ||
options.subtype = new options.subtype(); | ||
@@ -658,6 +488,6 @@ } | ||
if (!Array.isArray(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid range", value)); | ||
} | ||
if (value.length !== 2) { | ||
throw new sequelizeErrors.ValidationError('A range must be an array with two elements'); | ||
throw new sequelizeErrors.ValidationError("A range must be an array with two elements"); | ||
} | ||
@@ -667,11 +497,6 @@ return true; | ||
} | ||
/** | ||
* A column storing a unique universal identifier. | ||
* Use with `UUIDV1` or `UUIDV4` for default values. | ||
*/ | ||
class UUID extends ABSTRACT { | ||
validate(value, options) { | ||
if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value)); | ||
if (typeof value !== "string" || !Validator.isUUID(value) && (!options || !options.acceptStrings)) { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid uuid", value)); | ||
} | ||
@@ -681,10 +506,6 @@ return true; | ||
} | ||
/** | ||
* A default unique universal identifier generated following the UUID v1 standard | ||
*/ | ||
class UUIDV1 extends ABSTRACT { | ||
validate(value, options) { | ||
if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value)); | ||
if (typeof value !== "string" || !Validator.isUUID(value) && (!options || !options.acceptStrings)) { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid uuid", value)); | ||
} | ||
@@ -694,10 +515,6 @@ return true; | ||
} | ||
/** | ||
* A default unique universal identifier generated following the UUID v4 standard | ||
*/ | ||
class UUIDV4 extends ABSTRACT { | ||
validate(value, options) { | ||
if (typeof value !== 'string' || !Validator.isUUID(value, 4) && (!options || !options.acceptStrings)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuidv4', value)); | ||
if (typeof value !== "string" || !Validator.isUUID(value, 4) && (!options || !options.acceptStrings)) { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid uuidv4", value)); | ||
} | ||
@@ -707,51 +524,6 @@ return true; | ||
} | ||
/** | ||
* A virtual value that is not stored in the DB. This could for example be useful if you want to provide a default value in your model that is returned to the user but not stored in the DB. | ||
* | ||
* You could also use it to validate a value before permuting and storing it. VIRTUAL also takes a return type and dependency fields as arguments | ||
* If a virtual attribute is present in `attributes` it will automatically pull in the extra fields as well. | ||
* Return type is mostly useful for setups that rely on types like GraphQL. | ||
* | ||
* @example <caption>Checking password length before hashing it</caption> | ||
* sequelize.define('user', { | ||
* password_hash: DataTypes.STRING, | ||
* password: { | ||
* type: DataTypes.VIRTUAL, | ||
* set: function (val) { | ||
* // Remember to set the data value, otherwise it won't be validated | ||
* this.setDataValue('password', val); | ||
* this.setDataValue('password_hash', this.salt + val); | ||
* }, | ||
* validate: { | ||
* isLongEnough: function (val) { | ||
* if (val.length < 7) { | ||
* throw new Error("Please choose a longer password") | ||
* } | ||
* } | ||
* } | ||
* } | ||
* }) | ||
* | ||
* # In the above code the password is stored plainly in the password field so it can be validated, but is never stored in the DB. | ||
* | ||
* @example <caption>Virtual with dependency fields</caption> | ||
* { | ||
* active: { | ||
* type: new DataTypes.VIRTUAL(DataTypes.BOOLEAN, ['createdAt']), | ||
* get: function() { | ||
* return this.get('createdAt') > Date.now() - (7 * 24 * 60 * 60 * 1000) | ||
* } | ||
* } | ||
* } | ||
* | ||
*/ | ||
class VIRTUAL extends ABSTRACT { | ||
/** | ||
* @param {ABSTRACT} [ReturnType] return type for virtual type | ||
* @param {Array} [fields] array of fields this virtual type is dependent on | ||
*/ | ||
constructor(ReturnType, fields) { | ||
super(); | ||
if (typeof ReturnType === 'function') | ||
if (typeof ReturnType === "function") | ||
ReturnType = new ReturnType(); | ||
@@ -762,21 +534,7 @@ this.returnType = ReturnType; | ||
} | ||
/** | ||
* An enumeration, Postgres Only | ||
* | ||
* @example | ||
* DataTypes.ENUM('value', 'another value') | ||
* DataTypes.ENUM(['value', 'another value']) | ||
* DataTypes.ENUM({ | ||
* values: ['value', 'another value'] | ||
* }) | ||
*/ | ||
class ENUM extends ABSTRACT { | ||
/** | ||
* @param {...any|{ values: any[] }|any[]} args either array of values or options object with values array. It also supports variadic values | ||
*/ | ||
constructor(...args) { | ||
super(); | ||
const value = args[0]; | ||
const options = typeof value === 'object' && !Array.isArray(value) && value || { | ||
const options = typeof value === "object" && !Array.isArray(value) && value || { | ||
values: args.reduce((result, element) => { | ||
@@ -791,3 +549,3 @@ return result.concat(Array.isArray(element) ? element : [element]); | ||
if (!this.values.includes(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid choice in %j', value, this.values)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid choice in %j", value, this.values)); | ||
} | ||
@@ -797,13 +555,3 @@ return true; | ||
} | ||
/** | ||
* An array of `type`. Only available in Postgres. | ||
* | ||
* @example | ||
* DataTypes.ARRAY(DataTypes.DECIMAL) | ||
*/ | ||
class ARRAY extends ABSTRACT { | ||
/** | ||
* @param {ABSTRACT} type type of array values | ||
*/ | ||
constructor(type) { | ||
@@ -813,3 +561,3 @@ super(); | ||
this.options = options; | ||
this.type = typeof options.type === 'function' ? new options.type() : options.type; | ||
this.type = typeof options.type === "function" ? new options.type() : options.type; | ||
} | ||
@@ -821,3 +569,3 @@ toSql() { | ||
if (!Array.isArray(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid array', value)); | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid array", value)); | ||
} | ||
@@ -830,54 +578,3 @@ return true; | ||
} | ||
/** | ||
* A column storing Geometry information. | ||
* It is only available in PostgreSQL (with PostGIS), MariaDB or MySQL. | ||
* | ||
* GeoJSON is accepted as input and returned as output. | ||
* | ||
* In PostGIS, the GeoJSON is parsed using the PostGIS function `ST_GeomFromGeoJSON`. | ||
* In MySQL it is parsed using the function `GeomFromText`. | ||
* | ||
* Therefore, one can just follow the [GeoJSON spec](https://tools.ietf.org/html/rfc7946) for handling geometry objects. See the following examples: | ||
* | ||
* @example <caption>Defining a Geometry type attribute</caption> | ||
* DataTypes.GEOMETRY | ||
* DataTypes.GEOMETRY('POINT') | ||
* DataTypes.GEOMETRY('POINT', 4326) | ||
* | ||
* @example <caption>Create a new point</caption> | ||
* const point = { type: 'Point', coordinates: [39.807222,-76.984722]}; | ||
* | ||
* User.create({username: 'username', geometry: point }); | ||
* | ||
* @example <caption>Create a new linestring</caption> | ||
* const line = { type: 'LineString', 'coordinates': [ [100.0, 0.0], [101.0, 1.0] ] }; | ||
* | ||
* User.create({username: 'username', geometry: line }); | ||
* | ||
* @example <caption>Create a new polygon</caption> | ||
* const polygon = { type: 'Polygon', coordinates: [ | ||
* [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], | ||
* [100.0, 1.0], [100.0, 0.0] ] | ||
* ]}; | ||
* | ||
* User.create({username: 'username', geometry: polygon }); | ||
* | ||
* @example <caption>Create a new point with a custom SRID</caption> | ||
* const point = { | ||
* type: 'Point', | ||
* coordinates: [39.807222,-76.984722], | ||
* crs: { type: 'name', properties: { name: 'EPSG:4326'} } | ||
* }; | ||
* | ||
* User.create({username: 'username', geometry: point }) | ||
* | ||
* | ||
* @see {@link DataTypes.GEOGRAPHY} | ||
*/ | ||
class GEOMETRY extends ABSTRACT { | ||
/** | ||
* @param {string} [type] Type of geometry data | ||
* @param {string} [srid] SRID of type | ||
*/ | ||
constructor(type, srid) { | ||
@@ -891,37 +588,10 @@ super(); | ||
_stringify(value, options) { | ||
return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
return `ST_GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
} | ||
_bindParam(value, options) { | ||
return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
return `ST_GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
} | ||
} | ||
GEOMETRY.prototype.escape = false; | ||
/** | ||
* A geography datatype represents two dimensional spacial objects in an elliptic coord system. | ||
* | ||
* __The difference from geometry and geography type:__ | ||
* | ||
* PostGIS 1.5 introduced a new spatial type called geography, which uses geodetic measurement instead of Cartesian measurement. | ||
* Coordinate points in the geography type are always represented in WGS 84 lon lat degrees (SRID 4326), | ||
* but measurement functions and relationships ST_Distance, ST_DWithin, ST_Length, and ST_Area always return answers in meters or assume inputs in meters. | ||
* | ||
* __What is best to use? It depends:__ | ||
* | ||
* When choosing between the geometry and geography type for data storage, you should consider what you’ll be using it for. | ||
* If all you do are simple measurements and relationship checks on your data, and your data covers a fairly large area, then most likely you’ll be better off storing your data using the new geography type. | ||
* Although the new geography data type can cover the globe, the geometry type is far from obsolete. | ||
* The geometry type has a much richer set of functions than geography, relationship checks are generally faster, and it has wider support currently across desktop and web-mapping tools | ||
* | ||
* @example <caption>Defining a Geography type attribute</caption> | ||
* DataTypes.GEOGRAPHY | ||
* DataTypes.GEOGRAPHY('POINT') | ||
* DataTypes.GEOGRAPHY('POINT', 4326) | ||
*/ | ||
class GEOGRAPHY extends ABSTRACT { | ||
/** | ||
* @param {string} [type] Type of geography data | ||
* @param {string} [srid] SRID of type | ||
*/ | ||
constructor(type, srid) { | ||
@@ -935,21 +605,13 @@ super(); | ||
_stringify(value, options) { | ||
return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
return `ST_GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
} | ||
_bindParam(value, options) { | ||
return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
return `ST_GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`; | ||
} | ||
} | ||
GEOGRAPHY.prototype.escape = false; | ||
/** | ||
* The cidr type holds an IPv4 or IPv6 network specification. Takes 7 or 19 bytes. | ||
* | ||
* Only available for Postgres | ||
*/ | ||
class CIDR extends ABSTRACT { | ||
validate(value) { | ||
if (typeof value !== 'string' || !Validator.isIPRange(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid CIDR', value)); | ||
if (typeof value !== "string" || !Validator.isIPRange(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid CIDR", value)); | ||
} | ||
@@ -959,12 +621,6 @@ return true; | ||
} | ||
/** | ||
* The INET type holds an IPv4 or IPv6 host address, and optionally its subnet. Takes 7 or 19 bytes | ||
* | ||
* Only available for Postgres | ||
*/ | ||
class INET extends ABSTRACT { | ||
validate(value) { | ||
if (typeof value !== 'string' || !Validator.isIP(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid INET', value)); | ||
if (typeof value !== "string" || !Validator.isIP(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid INET", value)); | ||
} | ||
@@ -974,13 +630,6 @@ return true; | ||
} | ||
/** | ||
* The MACADDR type stores MAC addresses. Takes 6 bytes | ||
* | ||
* Only available for Postgres | ||
* | ||
*/ | ||
class MACADDR extends ABSTRACT { | ||
validate(value) { | ||
if (typeof value !== 'string' || !Validator.isMACAddress(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid MACADDR', value)); | ||
if (typeof value !== "string" || !Validator.isMACAddress(value)) { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid MACADDR", value)); | ||
} | ||
@@ -990,13 +639,6 @@ return true; | ||
} | ||
/** | ||
* The TSVECTOR type stores text search vectors. | ||
* | ||
* Only available for Postgres | ||
* | ||
*/ | ||
class TSVECTOR extends ABSTRACT { | ||
validate(value) { | ||
if (typeof value !== 'string') { | ||
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); | ||
if (typeof value !== "string") { | ||
throw new sequelizeErrors.ValidationError(util.format("%j is not a valid string", value)); | ||
} | ||
@@ -1006,48 +648,2 @@ return true; | ||
} | ||
/** | ||
* A convenience class holding commonly used data types. The data types are used when defining a new model using `Sequelize.define`, like this: | ||
* ```js | ||
* sequelize.define('model', { | ||
* column: DataTypes.INTEGER | ||
* }) | ||
* ``` | ||
* When defining a model you can just as easily pass a string as type, but often using the types defined here is beneficial. For example, using `DataTypes.BLOB`, mean | ||
* that that column will be returned as an instance of `Buffer` when being fetched by sequelize. | ||
* | ||
* To provide a length for the data type, you can invoke it like a function: `INTEGER(2)` | ||
* | ||
* Some data types have special properties that can be accessed in order to change the data type. | ||
* For example, to get an unsigned integer with zerofill you can do `DataTypes.INTEGER.UNSIGNED.ZEROFILL`. | ||
* The order you access the properties in do not matter, so `DataTypes.INTEGER.ZEROFILL.UNSIGNED` is fine as well. | ||
* | ||
* * All number types (`INTEGER`, `BIGINT`, `FLOAT`, `DOUBLE`, `REAL`, `DECIMAL`) expose the properties `UNSIGNED` and `ZEROFILL` | ||
* * The `CHAR` and `STRING` types expose the `BINARY` property | ||
* | ||
* Three of the values provided here (`NOW`, `UUIDV1` and `UUIDV4`) are special default values, that should not be used to define types. Instead they are used as shorthands for | ||
* defining default values. For example, to get a uuid field with a default value generated following v1 of the UUID standard: | ||
* ```js | ||
* sequelize.define('model', { | ||
* uuid: { | ||
* type: DataTypes.UUID, | ||
* defaultValue: DataTypes.UUIDV1, | ||
* primaryKey: true | ||
* } | ||
* }) | ||
* ``` | ||
* There may be times when you want to generate your own UUID conforming to some other algorithm. This is accomplished | ||
* using the defaultValue property as well, but instead of specifying one of the supplied UUID types, you return a value | ||
* from a function. | ||
* ```js | ||
* sequelize.define('model', { | ||
* uuid: { | ||
* type: DataTypes.UUID, | ||
* defaultValue: function() { | ||
* return generateMyId() | ||
* }, | ||
* primaryKey: true | ||
* } | ||
* }) | ||
* ``` | ||
*/ | ||
const DataTypes = module.exports = { | ||
@@ -1084,3 +680,3 @@ ABSTRACT, | ||
REAL, | ||
'DOUBLE PRECISION': DOUBLE, | ||
"DOUBLE PRECISION": DOUBLE, | ||
DOUBLE, | ||
@@ -1095,6 +691,4 @@ GEOMETRY, | ||
}; | ||
_.each(DataTypes, (dataType, name) => { | ||
// guard for aliases | ||
if (!Object.prototype.hasOwnProperty.call(dataType, 'key')) { | ||
if (!Object.prototype.hasOwnProperty.call(dataType, "key")) { | ||
dataType.types = {}; | ||
@@ -1104,12 +698,11 @@ dataType.key = dataType.prototype.key = name; | ||
}); | ||
const dialectMap = {}; | ||
dialectMap.postgres = require('./dialects/postgres/data-types')(DataTypes); | ||
dialectMap.mysql = require('./dialects/mysql/data-types')(DataTypes); | ||
dialectMap.mariadb = require('./dialects/mariadb/data-types')(DataTypes); | ||
dialectMap.sqlite = require('./dialects/sqlite/data-types')(DataTypes); | ||
dialectMap.mssql = require('./dialects/mssql/data-types')(DataTypes); | ||
dialectMap.postgres = require("./dialects/postgres/data-types")(DataTypes); | ||
dialectMap.mysql = require("./dialects/mysql/data-types")(DataTypes); | ||
dialectMap.mariadb = require("./dialects/mariadb/data-types")(DataTypes); | ||
dialectMap.sqlite = require("./dialects/sqlite/data-types")(DataTypes); | ||
dialectMap.mssql = require("./dialects/mssql/data-types")(DataTypes); | ||
dialectMap.db2 = require("./dialects/db2/data-types")(DataTypes); | ||
dialectMap.snowflake = require("./dialects/snowflake/data-types")(DataTypes); | ||
const dialectList = Object.values(dialectMap); | ||
for (const dataTypes of dialectList) { | ||
@@ -1122,4 +715,2 @@ _.each(dataTypes, (DataType, key) => { | ||
} | ||
// Wrap all data types to not require `new` | ||
for (const dataTypes of [DataTypes, ...dialectList]) { | ||
@@ -1130,3 +721,3 @@ _.each(dataTypes, (DataType, key) => { | ||
} | ||
Object.assign(DataTypes, dialectMap); | ||
//# sourceMappingURL=data-types.js.map |
@@ -1,5 +0,3 @@ | ||
'use strict'; | ||
const { classToInvokable } = require('./utils'); | ||
"use strict"; | ||
const { classToInvokable } = require("./utils"); | ||
class ABSTRACT { | ||
@@ -9,30 +7,24 @@ static toString(...args) { | ||
} | ||
toString(...args) { | ||
return this.toSql(...args); | ||
} | ||
toSql() { | ||
throw new Error('toSql implementation missing'); | ||
throw new Error("toSql implementation missing"); | ||
} | ||
} | ||
class INITIALLY_DEFERRED extends ABSTRACT { | ||
toSql() { | ||
return 'DEFERRABLE INITIALLY DEFERRED'; | ||
return "DEFERRABLE INITIALLY DEFERRED"; | ||
} | ||
} | ||
class INITIALLY_IMMEDIATE extends ABSTRACT { | ||
toSql() { | ||
return 'DEFERRABLE INITIALLY IMMEDIATE'; | ||
return "DEFERRABLE INITIALLY IMMEDIATE"; | ||
} | ||
} | ||
class NOT extends ABSTRACT { | ||
toSql() { | ||
return 'NOT DEFERRABLE'; | ||
return "NOT DEFERRABLE"; | ||
} | ||
} | ||
class SET_DEFERRED extends ABSTRACT { | ||
@@ -43,3 +35,2 @@ constructor(constraints) { | ||
} | ||
toSql(queryGenerator) { | ||
@@ -49,3 +40,2 @@ return queryGenerator.setDeferredQuery(this.constraints); | ||
} | ||
class SET_IMMEDIATE extends ABSTRACT { | ||
@@ -56,3 +46,2 @@ constructor(constraints) { | ||
} | ||
toSql(queryGenerator) { | ||
@@ -62,41 +51,2 @@ return queryGenerator.setImmediateQuery(this.constraints); | ||
} | ||
/** | ||
* A collection of properties related to deferrable constraints. It can be used to | ||
* make foreign key constraints deferrable and to set the constraints within a | ||
* transaction. This is only supported in PostgreSQL. | ||
* | ||
* The foreign keys can be configured like this. It will create a foreign key | ||
* that will check the constraints immediately when the data was inserted. | ||
* | ||
* ```js | ||
* sequelize.define('Model', { | ||
* foreign_id: { | ||
* type: Sequelize.INTEGER, | ||
* references: { | ||
* model: OtherModel, | ||
* key: 'id', | ||
* deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE | ||
* } | ||
* } | ||
* }); | ||
* ``` | ||
* | ||
* The constraints can be configured in a transaction like this. It will | ||
* trigger a query once the transaction has been started and set the constraints | ||
* to be checked at the very end of the transaction. | ||
* | ||
* ```js | ||
* sequelize.transaction({ | ||
* deferrable: Sequelize.Deferrable.SET_DEFERRED | ||
* }); | ||
* ``` | ||
* | ||
* @property INITIALLY_DEFERRED Use when declaring a constraint. Allow and enable by default this constraint's checks to be deferred at the end of transactions. | ||
* @property INITIALLY_IMMEDIATE Use when declaring a constraint. Allow the constraint's checks to be deferred at the end of transactions. | ||
* @property NOT Use when declaring a constraint. Set the constraint to not deferred. This is the default in PostgreSQL and makes it impossible to dynamically defer the constraints within a transaction. | ||
* @property SET_DEFERRED Use when declaring a transaction. Defer the deferrable checks involved in this transaction at commit. | ||
* @property SET_IMMEDIATE Use when declaring a transaction. Execute the deferrable checks involved in this transaction immediately. | ||
*/ | ||
const Deferrable = { | ||
@@ -109,3 +59,3 @@ INITIALLY_DEFERRED: classToInvokable(INITIALLY_DEFERRED), | ||
}; | ||
module.exports = Deferrable; | ||
//# sourceMappingURL=deferrable.js.map |
@@ -1,23 +0,12 @@ | ||
'use strict'; | ||
const { Pool, TimeoutError } = require('sequelize-pool'); | ||
const _ = require('lodash'); | ||
const semver = require('semver'); | ||
const errors = require('../../errors'); | ||
const { logger } = require('../../utils/logger'); | ||
const deprecations = require('../../utils/deprecations'); | ||
const debug = logger.debugContext('pool'); | ||
/** | ||
* Abstract Connection Manager | ||
* | ||
* Connection manager which handles pooling & replication. | ||
* Uses sequelize-pool for pooling | ||
* | ||
* @private | ||
*/ | ||
"use strict"; | ||
const { Pool, TimeoutError } = require("sequelize-pool"); | ||
const _ = require("lodash"); | ||
const semver = require("semver"); | ||
const errors = require("../../errors"); | ||
const { logger } = require("../../utils/logger"); | ||
const deprecations = require("../../utils/deprecations"); | ||
const debug = logger.debugContext("pool"); | ||
class ConnectionManager { | ||
constructor(dialect, sequelize) { | ||
const config = _.cloneDeep(sequelize.config); | ||
this.sequelize = sequelize; | ||
@@ -28,22 +17,18 @@ this.config = config; | ||
this.dialectName = this.sequelize.options.dialect; | ||
if (config.pool === false) { | ||
throw new Error('Support for pool:false was removed in v4.0'); | ||
throw new Error("Support for pool:false was removed in v4.0"); | ||
} | ||
config.pool = _.defaults(config.pool || {}, { | ||
max: 5, | ||
min: 0, | ||
idle: 10000, | ||
acquire: 60000, | ||
evict: 1000, | ||
idle: 1e4, | ||
acquire: 6e4, | ||
evict: 1e3, | ||
validate: this._validate.bind(this) | ||
}); | ||
this.initPools(); | ||
} | ||
refreshTypeParser(dataTypes) { | ||
_.each(dataTypes, dataType => { | ||
if (Object.prototype.hasOwnProperty.call(dataType, 'parse')) { | ||
_.each(dataTypes, (dataType) => { | ||
if (Object.prototype.hasOwnProperty.call(dataType, "parse")) { | ||
if (dataType.types[this.dialectName]) { | ||
@@ -57,12 +42,2 @@ this._refreshTypeParser(dataType); | ||
} | ||
/** | ||
* Try to load dialect module from various configured options. | ||
* Priority goes like dialectModulePath > dialectModule > require(default) | ||
* | ||
* @param {string} moduleName Name of dialect module to lookup | ||
* | ||
* @private | ||
* @returns {object} | ||
*/ | ||
_loadDialectModule(moduleName) { | ||
@@ -77,5 +52,4 @@ try { | ||
return require(moduleName); | ||
} catch (err) { | ||
if (err.code === 'MODULE_NOT_FOUND') { | ||
if (err.code === "MODULE_NOT_FOUND") { | ||
if (this.sequelize.config.dialectModulePath) { | ||
@@ -86,13 +60,5 @@ throw new Error(`Unable to find dialect at ${this.sequelize.config.dialectModulePath}`); | ||
} | ||
throw err; | ||
} | ||
} | ||
/** | ||
* Handler which executes on process exit or connection manager shutdown | ||
* | ||
* @private | ||
* @returns {Promise} | ||
*/ | ||
async _onProcessExit() { | ||
@@ -102,37 +68,21 @@ if (!this.pool) { | ||
} | ||
await this.pool.drain(); | ||
debug('connection drain due to process exit'); | ||
debug("connection drain due to process exit"); | ||
return await this.pool.destroyAllNow(); | ||
} | ||
/** | ||
* Drain the pool and close it permanently | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async close() { | ||
// Mark close of pool | ||
this.getConnection = async function getConnection() { | ||
throw new Error('ConnectionManager.getConnection was called after the connection manager was closed!'); | ||
throw new Error("ConnectionManager.getConnection was called after the connection manager was closed!"); | ||
}; | ||
return await this._onProcessExit(); | ||
} | ||
/** | ||
* Initialize connection pool. By default pool autostart is set to false, so no connection will be | ||
* be created unless `pool.acquire` is called. | ||
*/ | ||
initPools() { | ||
const config = this.config; | ||
if (!config.replication) { | ||
this.pool = new Pool({ | ||
name: 'sequelize', | ||
name: "sequelize", | ||
create: () => this._connect(config), | ||
destroy: async connection => { | ||
destroy: async (connection) => { | ||
const result = await this._disconnect(connection); | ||
debug('connection destroy'); | ||
debug("connection destroy"); | ||
return result; | ||
@@ -148,25 +98,14 @@ }, | ||
}); | ||
debug(`pool created with max/min: ${config.pool.max}/${config.pool.min}, no replication`); | ||
return; | ||
} | ||
if (!Array.isArray(config.replication.read)) { | ||
config.replication.read = [config.replication.read]; | ||
} | ||
// Map main connection config | ||
config.replication.write = _.defaults(config.replication.write, _.omit(config, 'replication')); | ||
// Apply defaults to each read config | ||
config.replication.read = config.replication.read.map(readConfig => | ||
_.defaults(readConfig, _.omit(this.config, 'replication')) | ||
); | ||
// custom pooling for replication (original author @janmeier) | ||
config.replication.write = _.defaults(config.replication.write, _.omit(config, "replication")); | ||
config.replication.read = config.replication.read.map((readConfig) => _.defaults(readConfig, _.omit(this.config, "replication"))); | ||
let reads = 0; | ||
this.pool = { | ||
release: client => { | ||
if (client.queryType === 'read') { | ||
release: (client) => { | ||
if (client.queryType === "read") { | ||
this.pool.read.release(client); | ||
@@ -178,4 +117,4 @@ } else { | ||
acquire: (queryType, useMaster) => { | ||
useMaster = useMaster === undefined ? false : useMaster; | ||
if (queryType === 'SELECT' && !useMaster) { | ||
useMaster = useMaster === void 0 ? false : useMaster; | ||
if (queryType === "SELECT" && !useMaster) { | ||
return this.pool.read.acquire(); | ||
@@ -185,5 +124,5 @@ } | ||
}, | ||
destroy: connection => { | ||
destroy: (connection) => { | ||
this.pool[connection.queryType].destroy(connection); | ||
debug('connection destroy'); | ||
debug("connection destroy"); | ||
}, | ||
@@ -195,4 +134,3 @@ destroyAllNow: async () => { | ||
]); | ||
debug('all connections destroyed'); | ||
debug("all connections destroyed"); | ||
}, | ||
@@ -204,11 +142,10 @@ drain: async () => Promise.all([ | ||
read: new Pool({ | ||
name: 'sequelize:read', | ||
name: "sequelize:read", | ||
create: async () => { | ||
// round robin config | ||
const nextRead = reads++ % config.replication.read.length; | ||
const connection = await this._connect(config.replication.read[nextRead]); | ||
connection.queryType = 'read'; | ||
connection.queryType = "read"; | ||
return connection; | ||
}, | ||
destroy: connection => this._disconnect(connection), | ||
destroy: (connection) => this._disconnect(connection), | ||
validate: config.pool.validate, | ||
@@ -223,9 +160,9 @@ max: config.pool.max, | ||
write: new Pool({ | ||
name: 'sequelize:write', | ||
name: "sequelize:write", | ||
create: async () => { | ||
const connection = await this._connect(config.replication.write); | ||
connection.queryType = 'write'; | ||
connection.queryType = "write"; | ||
return connection; | ||
}, | ||
destroy: connection => this._disconnect(connection), | ||
destroy: (connection) => this._disconnect(connection), | ||
validate: config.pool.validate, | ||
@@ -240,19 +177,6 @@ max: config.pool.max, | ||
}; | ||
debug(`pool created with max/min: ${config.pool.max}/${config.pool.min}, with replication`); | ||
} | ||
/** | ||
* Get connection from pool. It sets database version if it's not already set. | ||
* Call pool.acquire to get a connection | ||
* | ||
* @param {object} [options] Pool options | ||
* @param {string} [options.type] Set which replica to use. Available options are `read` and `write` | ||
* @param {boolean} [options.useMaster=false] Force master or write replica to get connection from | ||
* | ||
* @returns {Promise<Connection>} | ||
*/ | ||
async getConnection(options) { | ||
options = options || {}; | ||
if (this.sequelize.options.databaseVersion === 0) { | ||
@@ -264,17 +188,11 @@ if (!this.versionPromise) { | ||
const _options = {}; | ||
_options.transaction = { connection }; // Cheat .query to use our private connection | ||
_options.logging = () => {}; | ||
_options.transaction = { connection }; | ||
_options.logging = () => { | ||
}; | ||
_options.logging.__testLoggingFn = true; | ||
//connection might have set databaseVersion value at initialization, | ||
//avoiding a useless round trip | ||
if (this.sequelize.options.databaseVersion === 0) { | ||
const version = await this.sequelize.databaseVersion(_options); | ||
const parsedVersion = _.get(semver.coerce(version), 'version') || version; | ||
this.sequelize.options.databaseVersion = semver.valid(parsedVersion) | ||
? parsedVersion | ||
: this.dialect.defaultVersion; | ||
const parsedVersion = _.get(semver.coerce(version), "version") || version; | ||
this.sequelize.options.databaseVersion = semver.valid(parsedVersion) ? parsedVersion : this.dialect.defaultVersion; | ||
} | ||
if (semver.lt(this.sequelize.options.databaseVersion, this.dialect.defaultVersion)) { | ||
@@ -284,3 +202,2 @@ deprecations.unsupportedEngine(); | ||
} | ||
this.versionPromise = null; | ||
@@ -296,63 +213,32 @@ return await this._disconnect(connection); | ||
} | ||
let result; | ||
try { | ||
result = await this.pool.acquire(options.type, options.useMaster); | ||
} catch (error) { | ||
if (error instanceof TimeoutError) throw new errors.ConnectionAcquireTimeoutError(error); | ||
if (error instanceof TimeoutError) | ||
throw new errors.ConnectionAcquireTimeoutError(error); | ||
throw error; | ||
} | ||
debug('connection acquired'); | ||
debug("connection acquired"); | ||
return result; | ||
} | ||
/** | ||
* Release a pooled connection so it can be utilized by other connection requests | ||
* | ||
* @param {Connection} connection | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async releaseConnection(connection) { | ||
releaseConnection(connection) { | ||
this.pool.release(connection); | ||
debug('connection released'); | ||
debug("connection released"); | ||
} | ||
/** | ||
* Call dialect library to get connection | ||
* | ||
* @param {*} config Connection config | ||
* @private | ||
* @returns {Promise<Connection>} | ||
*/ | ||
async destroyConnection(connection) { | ||
await this.pool.destroy(connection); | ||
debug(`connection ${connection.uuid} destroyed`); | ||
} | ||
async _connect(config) { | ||
await this.sequelize.runHooks('beforeConnect', config); | ||
await this.sequelize.runHooks("beforeConnect", config); | ||
const connection = await this.dialect.connectionManager.connect(config); | ||
await this.sequelize.runHooks('afterConnect', connection, config); | ||
await this.sequelize.runHooks("afterConnect", connection, config); | ||
return connection; | ||
} | ||
/** | ||
* Call dialect library to disconnect a connection | ||
* | ||
* @param {Connection} connection | ||
* @private | ||
* @returns {Promise} | ||
*/ | ||
async _disconnect(connection) { | ||
await this.sequelize.runHooks('beforeDisconnect', connection); | ||
await this.sequelize.runHooks("beforeDisconnect", connection); | ||
await this.dialect.connectionManager.disconnect(connection); | ||
return this.sequelize.runHooks('afterDisconnect', connection); | ||
return this.sequelize.runHooks("afterDisconnect", connection); | ||
} | ||
/** | ||
* Determine if a connection is still valid or not | ||
* | ||
* @param {Connection} connection | ||
* | ||
* @returns {boolean} | ||
*/ | ||
_validate(connection) { | ||
@@ -362,9 +248,8 @@ if (!this.dialect.connectionManager.validate) { | ||
} | ||
return this.dialect.connectionManager.validate(connection); | ||
} | ||
} | ||
module.exports = ConnectionManager; | ||
module.exports.ConnectionManager = ConnectionManager; | ||
module.exports.default = ConnectionManager; | ||
//# sourceMappingURL=connection-manager.js.map |
@@ -1,30 +0,20 @@ | ||
'use strict'; | ||
class AbstractDialect {} | ||
"use strict"; | ||
class AbstractDialect { | ||
} | ||
AbstractDialect.prototype.supports = { | ||
'DEFAULT': true, | ||
'DEFAULT VALUES': false, | ||
'VALUES ()': false, | ||
'LIMIT ON UPDATE': false, | ||
'ORDER NULLS': false, | ||
'UNION': true, | ||
'UNION ALL': true, | ||
'RIGHT JOIN': true, | ||
/* does the dialect support returning values for inserted/updated fields */ | ||
"DEFAULT": true, | ||
"DEFAULT VALUES": false, | ||
"VALUES ()": false, | ||
"LIMIT ON UPDATE": false, | ||
"ON DUPLICATE KEY": true, | ||
"ORDER NULLS": false, | ||
"UNION": true, | ||
"UNION ALL": true, | ||
"RIGHT JOIN": true, | ||
returnValues: false, | ||
/* features specific to autoIncrement values */ | ||
autoIncrement: { | ||
/* does the dialect require modification of insert queries when inserting auto increment fields */ | ||
identityInsert: false, | ||
/* does the dialect support inserting default/null values for autoincrement fields */ | ||
defaultValue: true, | ||
/* does the dialect support updating autoincrement fields */ | ||
update: true | ||
}, | ||
/* Do we need to say DEFAULT for bulk insert */ | ||
bulkDefault: false, | ||
@@ -40,5 +30,6 @@ schemas: false, | ||
inserts: { | ||
ignoreDuplicates: '', /* dialect specific words for INSERT IGNORE or DO NOTHING */ | ||
updateOnDuplicate: false, /* whether dialect supports ON DUPLICATE KEY UPDATE */ | ||
onConflictDoNothing: '' /* dialect specific words for ON CONFLICT DO NOTHING */ | ||
ignoreDuplicates: "", | ||
updateOnDuplicate: false, | ||
onConflictDoNothing: "", | ||
conflictFields: false | ||
}, | ||
@@ -65,3 +56,2 @@ constraints: { | ||
}, | ||
joinTableDependent: true, | ||
groupedLimit: true, | ||
@@ -72,5 +62,5 @@ indexViaAlter: false, | ||
}; | ||
module.exports = AbstractDialect; | ||
module.exports.AbstractDialect = AbstractDialect; | ||
module.exports.default = AbstractDialect; | ||
//# sourceMappingURL=index.js.map |
@@ -1,51 +0,63 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Op = require('../../../operators'); | ||
const Utils = require('../../../utils'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
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.startsWith]: 'LIKE', | ||
[Op.endsWith]: 'LIKE', | ||
[Op.substring]: 'LIKE', | ||
[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.match]: '@@' | ||
[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.startsWith]: "LIKE", | ||
[Op.endsWith]: "LIKE", | ||
[Op.substring]: "LIKE", | ||
[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.match]: "@@" | ||
}, | ||
OperatorsAliasMap: {}, | ||
setOperatorsAliases(aliases) { | ||
@@ -55,6 +67,5 @@ if (!aliases || _.isEmpty(aliases)) { | ||
} else { | ||
this.OperatorsAliasMap = { ...aliases }; | ||
this.OperatorsAliasMap = __spreadValues({}, aliases); | ||
} | ||
}, | ||
_replaceAliases(orig) { | ||
@@ -65,4 +76,3 @@ const obj = {}; | ||
} | ||
Utils.getOperators(orig).forEach(op => { | ||
Utils.getOperators(orig).forEach((op) => { | ||
const item = orig[op]; | ||
@@ -75,3 +85,2 @@ if (_.isPlainObject(item)) { | ||
}); | ||
_.forOwn(orig, (item, prop) => { | ||
@@ -87,3 +96,3 @@ prop = this.OperatorsAliasMap[prop] || prop; | ||
}; | ||
module.exports = OperatorHelpers; | ||
//# sourceMappingURL=operators.js.map |
@@ -1,14 +0,4 @@ | ||
'use strict'; | ||
const uuidv4 = require('uuid').v4; | ||
"use strict"; | ||
const uuidv4 = require("uuid").v4; | ||
const TransactionQueries = { | ||
/** | ||
* Returns a query that sets the transaction isolation level. | ||
* | ||
* @param {string} value The isolation level. | ||
* @param {object} options An object with options. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
setIsolationLevelQuery(value, options) { | ||
@@ -18,39 +8,21 @@ if (options.parent) { | ||
} | ||
return `SET TRANSACTION ISOLATION LEVEL ${value};`; | ||
}, | ||
generateTransactionId() { | ||
return uuidv4(); | ||
}, | ||
/** | ||
* Returns a query that starts a transaction. | ||
* | ||
* @param {Transaction} transaction | ||
* @returns {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;'; | ||
return "START TRANSACTION;"; | ||
}, | ||
deferConstraintsQuery() {}, | ||
setConstraintQuery() {}, | ||
setDeferredQuery() {}, | ||
setImmediateQuery() {}, | ||
/** | ||
* Returns a query that commits a transaction. | ||
* | ||
* @param {Transaction} transaction An object with options. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
deferConstraintsQuery() { | ||
}, | ||
setConstraintQuery() { | ||
}, | ||
setDeferredQuery() { | ||
}, | ||
setImmediateQuery() { | ||
}, | ||
commitTransactionQuery(transaction) { | ||
@@ -60,23 +32,12 @@ if (transaction.parent) { | ||
} | ||
return 'COMMIT;'; | ||
return "COMMIT;"; | ||
}, | ||
/** | ||
* Returns a query that rollbacks a transaction. | ||
* | ||
* @param {Transaction} transaction | ||
* @returns {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;'; | ||
return "ROLLBACK;"; | ||
} | ||
}; | ||
module.exports = TransactionQueries; | ||
//# sourceMappingURL=transaction.js.map |
@@ -1,13 +0,26 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Utils = require('../../utils'); | ||
const DataTypes = require('../../data-types'); | ||
const Transaction = require('../../transaction'); | ||
const QueryTypes = require('../../query-types'); | ||
/** | ||
* The interface that Sequelize uses to talk to all databases | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const _ = require("lodash"); | ||
const Utils = require("../../utils"); | ||
const DataTypes = require("../../data-types"); | ||
const Transaction = require("../../transaction"); | ||
const QueryTypes = require("../../query-types"); | ||
class QueryInterface { | ||
@@ -18,16 +31,2 @@ constructor(sequelize, queryGenerator) { | ||
} | ||
/** | ||
* Create a database | ||
* | ||
* @param {string} database Database name to create | ||
* @param {object} [options] Query options | ||
* @param {string} [options.charset] Database default character set, MYSQL only | ||
* @param {string} [options.collate] Database default collation | ||
* @param {string} [options.encoding] Database default character set, PostgreSQL only | ||
* @param {string} [options.ctype] Database character classification, PostgreSQL only | ||
* @param {string} [options.template] The name of the template from which to create the new database, PostgreSQL only | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async createDatabase(database, options) { | ||
@@ -38,11 +37,2 @@ options = options || {}; | ||
} | ||
/** | ||
* Drop a database | ||
* | ||
* @param {string} database Database name to drop | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropDatabase(database, options) { | ||
@@ -53,11 +43,2 @@ options = options || {}; | ||
} | ||
/** | ||
* Create a schema | ||
* | ||
* @param {string} schema Schema name to create | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async createSchema(schema, options) { | ||
@@ -68,11 +49,2 @@ options = options || {}; | ||
} | ||
/** | ||
* Drop a schema | ||
* | ||
* @param {string} schema Schema name to drop | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropSchema(schema, options) { | ||
@@ -83,13 +55,4 @@ options = options || {}; | ||
} | ||
/** | ||
* Drop all schemas | ||
* | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropAllSchemas(options) { | ||
options = options || {}; | ||
if (!this.queryGenerator._dialect.supports.schemas) { | ||
@@ -99,103 +62,22 @@ return this.sequelize.drop(options); | ||
const schemas = await this.showAllSchemas(options); | ||
return Promise.all(schemas.map(schemaName => this.dropSchema(schemaName, options))); | ||
return Promise.all(schemas.map((schemaName) => this.dropSchema(schemaName, options))); | ||
} | ||
/** | ||
* Show all schemas | ||
* | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise<Array>} | ||
*/ | ||
async showAllSchemas(options) { | ||
options = { | ||
...options, | ||
options = __spreadProps(__spreadValues({}, options), { | ||
raw: true, | ||
type: this.sequelize.QueryTypes.SELECT | ||
}; | ||
}); | ||
const showSchemasSql = this.queryGenerator.showSchemasQuery(options); | ||
const schemaNames = await this.sequelize.query(showSchemasSql, options); | ||
return _.flatten(schemaNames.map(value => value.schema_name ? value.schema_name : value)); | ||
return _.flatten(schemaNames.map((value) => value.schema_name ? value.schema_name : value)); | ||
} | ||
/** | ||
* Return database version | ||
* | ||
* @param {object} [options] Query options | ||
* @param {QueryType} [options.type] Query type | ||
* | ||
* @returns {Promise} | ||
* @private | ||
*/ | ||
async databaseVersion(options) { | ||
return await this.sequelize.query( | ||
this.queryGenerator.versionQuery(), | ||
{ ...options, type: QueryTypes.VERSION } | ||
); | ||
return await this.sequelize.query(this.queryGenerator.versionQuery(), __spreadProps(__spreadValues({}, options), { type: QueryTypes.VERSION })); | ||
} | ||
/** | ||
* Create a table with given set of attributes | ||
* | ||
* ```js | ||
* queryInterface.createTable( | ||
* 'nameOfTheNewTable', | ||
* { | ||
* id: { | ||
* type: Sequelize.INTEGER, | ||
* primaryKey: true, | ||
* autoIncrement: true | ||
* }, | ||
* createdAt: { | ||
* type: Sequelize.DATE | ||
* }, | ||
* updatedAt: { | ||
* type: Sequelize.DATE | ||
* }, | ||
* attr1: Sequelize.STRING, | ||
* attr2: Sequelize.INTEGER, | ||
* attr3: { | ||
* type: Sequelize.BOOLEAN, | ||
* defaultValue: false, | ||
* allowNull: false | ||
* }, | ||
* //foreign key usage | ||
* attr4: { | ||
* type: Sequelize.INTEGER, | ||
* references: { | ||
* model: 'another_table_name', | ||
* key: 'id' | ||
* }, | ||
* onUpdate: 'cascade', | ||
* onDelete: 'cascade' | ||
* } | ||
* }, | ||
* { | ||
* engine: 'MYISAM', // default: 'InnoDB' | ||
* charset: 'latin1', // default: null | ||
* schema: 'public', // default: public, PostgreSQL only. | ||
* comment: 'my table', // comment for table | ||
* collate: 'latin1_danish_ci' // collation, MYSQL only | ||
* } | ||
* ) | ||
* ``` | ||
* | ||
* @param {string} tableName Name of table to create | ||
* @param {object} attributes Object representing a list of table attributes to create | ||
* @param {object} [options] create table and query options | ||
* @param {Model} [model] model class | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async createTable(tableName, attributes, options, model) { | ||
let sql = ''; | ||
options = { ...options }; | ||
let sql = ""; | ||
options = __spreadValues({}, options); | ||
if (options && options.uniqueKeys) { | ||
_.forOwn(options.uniqueKeys, uniqueKey => { | ||
if (uniqueKey.customIndex === undefined) { | ||
_.forOwn(options.uniqueKeys, (uniqueKey) => { | ||
if (uniqueKey.customIndex === void 0) { | ||
uniqueKey.customIndex = true; | ||
@@ -205,19 +87,8 @@ } | ||
} | ||
if (model) { | ||
options.uniqueKeys = options.uniqueKeys || model.uniqueKeys; | ||
} | ||
attributes = _.mapValues( | ||
attributes, | ||
attribute => this.sequelize.normalizeAttribute(attribute) | ||
); | ||
// Postgres requires special SQL commands for ENUM/ENUM[] | ||
attributes = _.mapValues(attributes, (attribute) => this.sequelize.normalizeAttribute(attribute)); | ||
await this.ensureEnums(tableName, attributes, options, model); | ||
if ( | ||
!tableName.schema && | ||
(options.schema || !!model && model._schema) | ||
) { | ||
if (!tableName.schema && (options.schema || !!model && model._schema)) { | ||
tableName = this.queryGenerator.addSchema({ | ||
@@ -228,51 +99,35 @@ tableName, | ||
} | ||
attributes = this.queryGenerator.attributesToSQL(attributes, { table: tableName, context: 'createTable' }); | ||
attributes = this.queryGenerator.attributesToSQL(attributes, { | ||
table: tableName, | ||
context: "createTable", | ||
withoutForeignKeyConstraints: options.withoutForeignKeyConstraints | ||
}); | ||
sql = this.queryGenerator.createTableQuery(tableName, attributes, options); | ||
return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Drop a table from database | ||
* | ||
* @param {string} tableName Table name to drop | ||
* @param {object} options Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async tableExists(tableName, options) { | ||
const sql = this.queryGenerator.tableExistsQuery(tableName); | ||
const out = await this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { | ||
type: QueryTypes.SHOWTABLES | ||
})); | ||
return out.length === 1; | ||
} | ||
async dropTable(tableName, options) { | ||
// if we're forcing we should be cascading unless explicitly stated otherwise | ||
options = { ...options }; | ||
options = __spreadValues({}, options); | ||
options.cascade = options.cascade || options.force || false; | ||
const sql = this.queryGenerator.dropTableQuery(tableName, options); | ||
await this.sequelize.query(sql, options); | ||
} | ||
async _dropAllTables(tableNames, skip, options) { | ||
for (const tableName of tableNames) { | ||
// if tableName is not in the Array of tables names then don't drop it | ||
if (!skip.includes(tableName.tableName || tableName)) { | ||
await this.dropTable(tableName, { ...options, cascade: true } ); | ||
await this.dropTable(tableName, __spreadProps(__spreadValues({}, options), { cascade: true })); | ||
} | ||
} | ||
} | ||
/** | ||
* Drop all tables from database | ||
* | ||
* @param {object} [options] query options | ||
* @param {Array} [options.skip] List of table to skip | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropAllTables(options) { | ||
options = options || {}; | ||
const skip = options.skip || []; | ||
const tableNames = await this.showAllTables(options); | ||
const foreignKeys = await this.getForeignKeysForTables(tableNames, options); | ||
for (const tableName of tableNames) { | ||
@@ -283,3 +138,2 @@ let normalizedTableName = tableName; | ||
} | ||
for (const foreignKey of foreignKeys[normalizedTableName]) { | ||
@@ -291,12 +145,2 @@ await this.sequelize.query(this.queryGenerator.dropForeignKeyQuery(tableName, foreignKey)); | ||
} | ||
/** | ||
* Rename a table | ||
* | ||
* @param {string} before Current name of table | ||
* @param {string} after New name from table | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async renameTable(before, after, options) { | ||
@@ -307,20 +151,7 @@ options = options || {}; | ||
} | ||
/** | ||
* Get all tables in current database | ||
* | ||
* @param {object} [options] Query options | ||
* @param {boolean} [options.raw=true] Run query in raw mode | ||
* @param {QueryType} [options.type=QueryType.SHOWTABLE] query type | ||
* | ||
* @returns {Promise<Array>} | ||
* @private | ||
*/ | ||
async showAllTables(options) { | ||
options = { | ||
...options, | ||
options = __spreadProps(__spreadValues({}, options), { | ||
raw: true, | ||
type: QueryTypes.SHOWTABLES | ||
}; | ||
}); | ||
const showTablesSql = this.queryGenerator.showTablesQuery(this.sequelize.config.database); | ||
@@ -330,89 +161,34 @@ const tableNames = await this.sequelize.query(showTablesSql, options); | ||
} | ||
/** | ||
* Describe a table structure | ||
* | ||
* This method returns an array of hashes containing information about all attributes in the table. | ||
* | ||
* ```js | ||
* { | ||
* name: { | ||
* type: 'VARCHAR(255)', // this will be 'CHARACTER VARYING' for pg! | ||
* allowNull: true, | ||
* defaultValue: null | ||
* }, | ||
* isBetaMember: { | ||
* type: 'TINYINT(1)', // this will be 'BOOLEAN' for pg! | ||
* allowNull: false, | ||
* defaultValue: false | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* @param {string} tableName table name | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise<object>} | ||
*/ | ||
async describeTable(tableName, options) { | ||
let schema = null; | ||
let schemaDelimiter = null; | ||
if (typeof options === 'string') { | ||
if (typeof options === "string") { | ||
schema = options; | ||
} else if (typeof options === 'object' && options !== null) { | ||
} else if (typeof options === "object" && options !== null) { | ||
schema = options.schema || null; | ||
schemaDelimiter = options.schemaDelimiter || null; | ||
} | ||
if (typeof tableName === 'object' && tableName !== null) { | ||
if (typeof tableName === "object" && tableName !== null) { | ||
schema = tableName.schema; | ||
tableName = tableName.tableName; | ||
} | ||
const sql = this.queryGenerator.describeTableQuery(tableName, schema, schemaDelimiter); | ||
options = { ...options, type: QueryTypes.DESCRIBE }; | ||
options = __spreadProps(__spreadValues({}, options), { type: QueryTypes.DESCRIBE }); | ||
try { | ||
const data = await this.sequelize.query(sql, options); | ||
/* | ||
* If no data is returned from the query, then the table name may be wrong. | ||
* Query generators that use information_schema for retrieving table info will just return an empty result set, | ||
* it will not throw an error like built-ins do (e.g. DESCRIBE on MySql). | ||
*/ | ||
if (_.isEmpty(data)) { | ||
throw new Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`); | ||
} | ||
return data; | ||
} catch (e) { | ||
if (e.original && e.original.code === 'ER_NO_SUCH_TABLE') { | ||
if (e.original && e.original.code === "ER_NO_SUCH_TABLE") { | ||
throw new Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`); | ||
} | ||
throw e; | ||
} | ||
} | ||
/** | ||
* Add a new column to a table | ||
* | ||
* ```js | ||
* queryInterface.addColumn('tableA', 'columnC', Sequelize.STRING, { | ||
* after: 'columnB' // after option is only supported by MySQL | ||
* }); | ||
* ``` | ||
* | ||
* @param {string} table Table to add column to | ||
* @param {string} key Column name | ||
* @param {object} attribute Attribute definition | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async addColumn(table, key, attribute, options) { | ||
if (!table || !key || !attribute) { | ||
throw new Error('addColumn takes at least 3 arguments (table, attribute name, attribute definition)'); | ||
throw new Error("addColumn takes at least 3 arguments (table, attribute name, attribute definition)"); | ||
} | ||
options = options || {}; | ||
@@ -422,14 +198,5 @@ attribute = this.sequelize.normalizeAttribute(attribute); | ||
} | ||
/** | ||
* Remove a column from a table | ||
* | ||
* @param {string} tableName Table to remove column from | ||
* @param {string} attributeName Column name to remove | ||
* @param {object} [options] Query options | ||
*/ | ||
async removeColumn(tableName, attributeName, options) { | ||
return this.sequelize.query(this.queryGenerator.removeColumnQuery(tableName, attributeName), options); | ||
} | ||
normalizeAttribute(dataTypeOrOptions) { | ||
@@ -442,36 +209,21 @@ let attribute; | ||
} | ||
return this.sequelize.normalizeAttribute(attribute); | ||
} | ||
/** | ||
* Change a column definition | ||
* | ||
* @param {string} tableName Table name to change from | ||
* @param {string} attributeName Column name | ||
* @param {object} dataTypeOrOptions Attribute definition for new column | ||
* @param {object} [options] Query options | ||
*/ | ||
quoteIdentifier(identifier2, force) { | ||
return this.queryGenerator.quoteIdentifier(identifier2, force); | ||
} | ||
quoteIdentifiers(identifiers) { | ||
return this.queryGenerator.quoteIdentifiers(identifiers); | ||
} | ||
async changeColumn(tableName, attributeName, dataTypeOrOptions, options) { | ||
options = options || {}; | ||
const query = this.queryGenerator.attributesToSQL({ | ||
[attributeName]: this.normalizeAttribute(dataTypeOrOptions) | ||
}, { | ||
context: 'changeColumn', | ||
context: "changeColumn", | ||
table: tableName | ||
}); | ||
const sql = this.queryGenerator.changeColumnQuery(tableName, query); | ||
return this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Rejects if the table doesn't have the specified column, otherwise returns the column description. | ||
* | ||
* @param {string} tableName | ||
* @param {string} columnName | ||
* @param {object} options | ||
* @private | ||
*/ | ||
async assertTableHasColumn(tableName, columnName, options) { | ||
@@ -484,19 +236,6 @@ const description = await this.describeTable(tableName, options); | ||
} | ||
/** | ||
* Rename a column | ||
* | ||
* @param {string} tableName Table name whose column to rename | ||
* @param {string} attrNameBefore Current column name | ||
* @param {string} attrNameAfter New column name | ||
* @param {object} [options] Query option | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async renameColumn(tableName, attrNameBefore, attrNameAfter, options) { | ||
options = options || {}; | ||
const data = (await this.assertTableHasColumn(tableName, attrNameBefore, options))[attrNameBefore]; | ||
const _options = {}; | ||
_options[attrNameAfter] = { | ||
@@ -508,36 +247,9 @@ attribute: attrNameAfter, | ||
}; | ||
// fix: a not-null column cannot have null as default value | ||
if (data.defaultValue === null && !data.allowNull) { | ||
delete _options[attrNameAfter].defaultValue; | ||
} | ||
const sql = this.queryGenerator.renameColumnQuery( | ||
tableName, | ||
attrNameBefore, | ||
this.queryGenerator.attributesToSQL(_options) | ||
); | ||
const sql = this.queryGenerator.renameColumnQuery(tableName, attrNameBefore, this.queryGenerator.attributesToSQL(_options)); | ||
return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Add an index to a column | ||
* | ||
* @param {string|object} tableName Table name to add index on, can be a object with schema | ||
* @param {Array} [attributes] Use options.fields instead, List of attributes to add index on | ||
* @param {object} options indexes options | ||
* @param {Array} options.fields List of attributes to add index on | ||
* @param {boolean} [options.concurrently] Pass CONCURRENT so other operations run while the index is created | ||
* @param {boolean} [options.unique] Create a unique index | ||
* @param {string} [options.using] Useful for GIN indexes | ||
* @param {string} [options.operator] Index operator | ||
* @param {string} [options.type] Type of index, available options are UNIQUE|FULLTEXT|SPATIAL | ||
* @param {string} [options.name] Name of the index. Default is <table>_<attr1>_<attr2> | ||
* @param {object} [options.where] Where condition on index, for partial indexes | ||
* @param {string} [rawTablename] table name, this is just for backward compatibiity | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async addIndex(tableName, attributes, options, rawTablename) { | ||
// Support for passing tableName, attributes, options or tableName, options (with a fields param which is the attributes) | ||
if (!Array.isArray(attributes)) { | ||
@@ -548,37 +260,14 @@ rawTablename = options; | ||
} | ||
if (!rawTablename) { | ||
// Map for backwards compat | ||
rawTablename = tableName; | ||
} | ||
options = Utils.cloneDeep(options); | ||
options.fields = attributes; | ||
const sql = this.queryGenerator.addIndexQuery(tableName, options, rawTablename); | ||
return await this.sequelize.query(sql, { ...options, supportsSearchPath: false }); | ||
return await this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { supportsSearchPath: false })); | ||
} | ||
/** | ||
* Show indexes on a table | ||
* | ||
* @param {string} tableName table name | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise<Array>} | ||
* @private | ||
*/ | ||
async showIndex(tableName, options) { | ||
const sql = this.queryGenerator.showIndexesQuery(tableName, options); | ||
return await this.sequelize.query(sql, { ...options, type: QueryTypes.SHOWINDEXES }); | ||
return await this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { type: QueryTypes.SHOWINDEXES })); | ||
} | ||
/** | ||
* Returns all foreign key constraints of requested tables | ||
* | ||
* @param {string[]} tableNames table names | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async getForeignKeysForTables(tableNames, options) { | ||
@@ -588,10 +277,5 @@ if (tableNames.length === 0) { | ||
} | ||
options = { ...options, type: QueryTypes.FOREIGNKEYS }; | ||
const results = await Promise.all(tableNames.map(tableName => | ||
this.sequelize.query(this.queryGenerator.getForeignKeysQuery(tableName, this.sequelize.config.database), options))); | ||
options = __spreadProps(__spreadValues({}, options), { type: QueryTypes.FOREIGNKEYS }); | ||
const results = await Promise.all(tableNames.map((tableName) => this.sequelize.query(this.queryGenerator.getForeignKeysQuery(tableName, this.sequelize.config.database), options))); | ||
const result = {}; | ||
tableNames.forEach((tableName, i) => { | ||
@@ -601,160 +285,37 @@ if (_.isObject(tableName)) { | ||
} | ||
result[tableName] = Array.isArray(results[i]) | ||
? results[i].map(r => r.constraint_name) | ||
: [results[i] && results[i].constraint_name]; | ||
result[tableName] = Array.isArray(results[i]) ? results[i].map((r) => r.constraint_name) : [results[i] && results[i].constraint_name]; | ||
result[tableName] = result[tableName].filter(_.identity); | ||
}); | ||
return result; | ||
} | ||
/** | ||
* Get foreign key references details for the table | ||
* | ||
* Those details contains constraintSchema, constraintName, constraintCatalog | ||
* tableCatalog, tableSchema, tableName, columnName, | ||
* referencedTableCatalog, referencedTableCatalog, referencedTableSchema, referencedTableName, referencedColumnName. | ||
* Remind: constraint informations won't return if it's sqlite. | ||
* | ||
* @param {string} tableName table name | ||
* @param {object} [options] Query options | ||
*/ | ||
async getForeignKeyReferencesForTable(tableName, options) { | ||
const queryOptions = { | ||
...options, | ||
const queryOptions = __spreadProps(__spreadValues({}, options), { | ||
type: QueryTypes.FOREIGNKEYS | ||
}; | ||
}); | ||
const query = this.queryGenerator.getForeignKeysQuery(tableName, this.sequelize.config.database); | ||
return this.sequelize.query(query, queryOptions); | ||
} | ||
/** | ||
* Remove an already existing index from a table | ||
* | ||
* @param {string} tableName Table name to drop index from | ||
* @param {string|string[]} indexNameOrAttributes Index name or list of attributes that in the index | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async removeIndex(tableName, indexNameOrAttributes, options) { | ||
options = options || {}; | ||
const sql = this.queryGenerator.removeIndexQuery(tableName, indexNameOrAttributes); | ||
const sql = this.queryGenerator.removeIndexQuery(tableName, indexNameOrAttributes, options); | ||
return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Add a constraint to a table | ||
* | ||
* Available constraints: | ||
* - UNIQUE | ||
* - DEFAULT (MSSQL only) | ||
* - CHECK (MySQL - Ignored by the database engine ) | ||
* - FOREIGN KEY | ||
* - PRIMARY KEY | ||
* | ||
* @example <caption>UNIQUE</caption> | ||
* queryInterface.addConstraint('Users', { | ||
* fields: ['email'], | ||
* type: 'unique', | ||
* name: 'custom_unique_constraint_name' | ||
* }); | ||
* | ||
* @example <caption>CHECK</caption> | ||
* queryInterface.addConstraint('Users', { | ||
* fields: ['roles'], | ||
* type: 'check', | ||
* where: { | ||
* roles: ['user', 'admin', 'moderator', 'guest'] | ||
* } | ||
* }); | ||
* | ||
* @example <caption>Default - MSSQL only</caption> | ||
* queryInterface.addConstraint('Users', { | ||
* fields: ['roles'], | ||
* type: 'default', | ||
* defaultValue: 'guest' | ||
* }); | ||
* | ||
* @example <caption>Primary Key</caption> | ||
* queryInterface.addConstraint('Users', { | ||
* fields: ['username'], | ||
* type: 'primary key', | ||
* name: 'custom_primary_constraint_name' | ||
* }); | ||
* | ||
* @example <caption>Foreign Key</caption> | ||
* queryInterface.addConstraint('Posts', { | ||
* fields: ['username'], | ||
* type: 'foreign key', | ||
* name: 'custom_fkey_constraint_name', | ||
* references: { //Required field | ||
* table: 'target_table_name', | ||
* field: 'target_column_name' | ||
* }, | ||
* onDelete: 'cascade', | ||
* onUpdate: 'cascade' | ||
* }); | ||
* | ||
* @example <caption>Composite Foreign Key</caption> | ||
* queryInterface.addConstraint('TableName', { | ||
* fields: ['source_column_name', 'other_source_column_name'], | ||
* type: 'foreign key', | ||
* name: 'custom_fkey_constraint_name', | ||
* references: { //Required field | ||
* table: 'target_table_name', | ||
* fields: ['target_column_name', 'other_target_column_name'] | ||
* }, | ||
* onDelete: 'cascade', | ||
* onUpdate: 'cascade' | ||
* }); | ||
* | ||
* @param {string} tableName Table name where you want to add a constraint | ||
* @param {object} options An object to define the constraint name, type etc | ||
* @param {string} options.type Type of constraint. One of the values in available constraints(case insensitive) | ||
* @param {Array} options.fields Array of column names to apply the constraint over | ||
* @param {string} [options.name] Name of the constraint. If not specified, sequelize automatically creates a named constraint based on constraint type, table & column names | ||
* @param {string} [options.defaultValue] The value for the default constraint | ||
* @param {object} [options.where] Where clause/expression for the CHECK constraint | ||
* @param {object} [options.references] Object specifying target table, column name to create foreign key constraint | ||
* @param {string} [options.references.table] Target table name | ||
* @param {string} [options.references.field] Target column name | ||
* @param {string} [options.references.fields] Target column names for a composite primary key. Must match the order of fields in options.fields. | ||
* @param {string} [options.deferrable] Sets the constraint to be deferred or immediately checked. See Sequelize.Deferrable. PostgreSQL Only | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async addConstraint(tableName, options) { | ||
if (!options.fields) { | ||
throw new Error('Fields must be specified through options.fields'); | ||
throw new Error("Fields must be specified through options.fields"); | ||
} | ||
if (!options.type) { | ||
throw new Error('Constraint type must be specified through options.type'); | ||
throw new Error("Constraint type must be specified through options.type"); | ||
} | ||
options = Utils.cloneDeep(options); | ||
const sql = this.queryGenerator.addConstraintQuery(tableName, options); | ||
return await this.sequelize.query(sql, options); | ||
} | ||
async showConstraint(tableName, constraintName, options) { | ||
const sql = this.queryGenerator.showConstraintsQuery(tableName, constraintName); | ||
return await this.sequelize.query(sql, { ...options, type: QueryTypes.SHOWCONSTRAINTS }); | ||
return await this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { type: QueryTypes.SHOWCONSTRAINTS })); | ||
} | ||
/** | ||
* Remove a constraint from a table | ||
* | ||
* @param {string} tableName Table name to drop constraint from | ||
* @param {string} constraintName Constraint name | ||
* @param {object} options Query options | ||
*/ | ||
async removeConstraint(tableName, constraintName, options) { | ||
return this.sequelize.query(this.queryGenerator.removeConstraintQuery(tableName, constraintName), options); | ||
} | ||
async insert(instance, tableName, values, options) { | ||
@@ -764,137 +325,60 @@ options = Utils.cloneDeep(options); | ||
const sql = this.queryGenerator.insertQuery(tableName, values, instance && instance.constructor.rawAttributes, options); | ||
options.type = QueryTypes.INSERT; | ||
options.instance = instance; | ||
const results = await this.sequelize.query(sql, options); | ||
if (instance) results[0].isNewRecord = false; | ||
if (instance) | ||
results[0].isNewRecord = false; | ||
return results; | ||
} | ||
/** | ||
* Upsert | ||
* | ||
* @param {string} tableName table to upsert on | ||
* @param {object} insertValues values to be inserted, mapped to field name | ||
* @param {object} updateValues values to be updated, mapped to field name | ||
* @param {object} where where conditions, which can be used for UPDATE part when INSERT fails | ||
* @param {object} options query options | ||
* | ||
* @returns {Promise<boolean,?number>} Resolves an array with <created, primaryKey> | ||
*/ | ||
async upsert(tableName, insertValues, updateValues, where, options) { | ||
options = { ...options }; | ||
options = __spreadValues({}, options); | ||
const model = options.model; | ||
const primaryKeys = Object.values(model.primaryKeys).map(item => item.field); | ||
const uniqueKeys = Object.values(model.uniqueKeys).filter(c => c.fields.length > 0).map(c => c.fields); | ||
const indexKeys = Object.values(model._indexes).filter(c => c.unique && c.fields.length > 0).map(c => c.fields); | ||
options.type = QueryTypes.UPSERT; | ||
options.updateOnDuplicate = Object.keys(updateValues); | ||
options.upsertKeys = []; | ||
// For fields in updateValues, try to find a constraint or unique index | ||
// that includes given field. Only first matching upsert key is used. | ||
for (const field of options.updateOnDuplicate) { | ||
const uniqueKey = uniqueKeys.find(fields => fields.includes(field)); | ||
if (uniqueKey) { | ||
options.upsertKeys = uniqueKey; | ||
break; | ||
options.upsertKeys = options.conflictFields || []; | ||
if (options.upsertKeys.length === 0) { | ||
const primaryKeys = Object.values(model.primaryKeys).map((item) => item.field); | ||
const uniqueKeys = Object.values(model.uniqueKeys).filter((c) => c.fields.length > 0).map((c) => c.fields); | ||
const indexKeys = Object.values(model._indexes).filter((c) => c.unique && c.fields.length > 0).map((c) => c.fields); | ||
for (const field of options.updateOnDuplicate) { | ||
const uniqueKey = uniqueKeys.find((fields) => fields.includes(field)); | ||
if (uniqueKey) { | ||
options.upsertKeys = uniqueKey; | ||
break; | ||
} | ||
const indexKey = indexKeys.find((fields) => fields.includes(field)); | ||
if (indexKey) { | ||
options.upsertKeys = indexKey; | ||
break; | ||
} | ||
} | ||
const indexKey = indexKeys.find(fields => fields.includes(field)); | ||
if (indexKey) { | ||
options.upsertKeys = indexKey; | ||
break; | ||
if (options.upsertKeys.length === 0 || _.intersection(options.updateOnDuplicate, primaryKeys).length) { | ||
options.upsertKeys = primaryKeys; | ||
} | ||
options.upsertKeys = _.uniq(options.upsertKeys); | ||
} | ||
// Always use PK, if no constraint available OR update data contains PK | ||
if ( | ||
options.upsertKeys.length === 0 | ||
|| _.intersection(options.updateOnDuplicate, primaryKeys).length | ||
) { | ||
options.upsertKeys = primaryKeys; | ||
} | ||
options.upsertKeys = _.uniq(options.upsertKeys); | ||
const sql = this.queryGenerator.insertQuery(tableName, insertValues, model.rawAttributes, options); | ||
return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Insert multiple records into a table | ||
* | ||
* @example | ||
* queryInterface.bulkInsert('roles', [{ | ||
* label: 'user', | ||
* createdAt: new Date(), | ||
* updatedAt: new Date() | ||
* }, { | ||
* label: 'admin', | ||
* createdAt: new Date(), | ||
* updatedAt: new Date() | ||
* }]); | ||
* | ||
* @param {string} tableName Table name to insert record to | ||
* @param {Array} records List of records to insert | ||
* @param {object} options Various options, please see Model.bulkCreate options | ||
* @param {object} attributes Various attributes mapped by field name | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async bulkInsert(tableName, records, options, attributes) { | ||
options = { ...options }; | ||
options = __spreadValues({}, options); | ||
options.type = QueryTypes.INSERT; | ||
const results = await this.sequelize.query( | ||
this.queryGenerator.bulkInsertQuery(tableName, records, options, attributes), | ||
options | ||
); | ||
const results = await this.sequelize.query(this.queryGenerator.bulkInsertQuery(tableName, records, options, attributes), options); | ||
return results[0]; | ||
} | ||
async update(instance, tableName, values, identifier, options) { | ||
options = { ...options }; | ||
async update(instance, tableName, values, identifier2, options) { | ||
options = __spreadValues({}, options); | ||
options.hasTrigger = instance && instance.constructor.options.hasTrigger; | ||
const sql = this.queryGenerator.updateQuery(tableName, values, identifier, options, instance.constructor.rawAttributes); | ||
const sql = this.queryGenerator.updateQuery(tableName, values, identifier2, options, instance.constructor.rawAttributes); | ||
options.type = QueryTypes.UPDATE; | ||
options.instance = instance; | ||
return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Update multiple records of a table | ||
* | ||
* @example | ||
* queryInterface.bulkUpdate('roles', { | ||
* label: 'admin', | ||
* }, { | ||
* userType: 3, | ||
* }, | ||
* ); | ||
* | ||
* @param {string} tableName Table name to update | ||
* @param {object} values Values to be inserted, mapped to field name | ||
* @param {object} identifier A hash with conditions OR an ID as integer OR a string with conditions | ||
* @param {object} [options] Various options, please see Model.bulkCreate options | ||
* @param {object} [attributes] Attributes on return objects if supported by SQL dialect | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async bulkUpdate(tableName, values, identifier, options, attributes) { | ||
async bulkUpdate(tableName, values, identifier2, options, attributes) { | ||
options = Utils.cloneDeep(options); | ||
if (typeof identifier === 'object') identifier = Utils.cloneDeep(identifier); | ||
const sql = this.queryGenerator.updateQuery(tableName, values, identifier, options, attributes); | ||
if (typeof identifier2 === "object") | ||
identifier2 = Utils.cloneDeep(identifier2); | ||
const sql = this.queryGenerator.updateQuery(tableName, values, identifier2, options, attributes); | ||
const table = _.isObject(tableName) ? tableName : { tableName }; | ||
const model = _.find(this.sequelize.modelManager.models, { tableName: table.tableName }); | ||
options.type = QueryTypes.BULKUPDATE; | ||
@@ -904,10 +388,6 @@ options.model = model; | ||
} | ||
async delete(instance, tableName, identifier, options) { | ||
async delete(instance, tableName, identifier2, options) { | ||
const cascades = []; | ||
const sql = this.queryGenerator.deleteQuery(tableName, identifier, {}, instance.constructor); | ||
options = { ...options }; | ||
// Check for a restrict field | ||
const sql = this.queryGenerator.deleteQuery(tableName, identifier2, {}, instance.constructor); | ||
options = __spreadValues({}, options); | ||
if (!!instance.constructor && !!instance.constructor.associations) { | ||
@@ -917,8 +397,5 @@ const keys = Object.keys(instance.constructor.associations); | ||
let association; | ||
for (let i = 0; i < length; i++) { | ||
association = instance.constructor.associations[keys[i]]; | ||
if (association.options && association.options.onDelete && | ||
association.options.onDelete.toLowerCase() === 'cascade' && | ||
association.options.useHooks === true) { | ||
if (association.options && association.options.onDelete && association.options.onDelete.toLowerCase() === "cascade" && association.options.useHooks === true) { | ||
cascades.push(association.accessors.get); | ||
@@ -928,9 +405,10 @@ } | ||
} | ||
for (const cascade of cascades) { | ||
let instances = await instance[cascade](options); | ||
// Check for hasOne relationship with non-existing associate ("has zero") | ||
if (!instances) continue; | ||
if (!Array.isArray(instances)) instances = [instances]; | ||
for (const _instance of instances) await _instance.destroy(options); | ||
if (!instances) | ||
continue; | ||
if (!Array.isArray(instances)) | ||
instances = [instances]; | ||
for (const _instance of instances) | ||
await _instance.destroy(options); | ||
} | ||
@@ -940,66 +418,30 @@ options.instance = instance; | ||
} | ||
/** | ||
* Delete multiple records from a table | ||
* | ||
* @param {string} tableName table name from where to delete records | ||
* @param {object} where where conditions to find records to delete | ||
* @param {object} [options] options | ||
* @param {boolean} [options.truncate] Use truncate table command | ||
* @param {boolean} [options.cascade=false] Only used in conjunction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE. | ||
* @param {boolean} [options.restartIdentity=false] Only used in conjunction with TRUNCATE. Automatically restart sequences owned by columns of the truncated table. | ||
* @param {Model} [model] Model | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async bulkDelete(tableName, where, options, model) { | ||
options = Utils.cloneDeep(options); | ||
options = _.defaults(options, { limit: null }); | ||
if (options.truncate === true) { | ||
return this.sequelize.query( | ||
this.queryGenerator.truncateTableQuery(tableName, options), | ||
options | ||
); | ||
return this.sequelize.query(this.queryGenerator.truncateTableQuery(tableName, options), options); | ||
} | ||
if (typeof identifier === 'object') where = Utils.cloneDeep(where); | ||
return await this.sequelize.query( | ||
this.queryGenerator.deleteQuery(tableName, where, options, model), | ||
options | ||
); | ||
if (typeof identifier === "object") | ||
where = Utils.cloneDeep(where); | ||
return await this.sequelize.query(this.queryGenerator.deleteQuery(tableName, where, options, model), options); | ||
} | ||
async select(model, tableName, optionsArg) { | ||
const options = { ...optionsArg, type: QueryTypes.SELECT, model }; | ||
return await this.sequelize.query( | ||
this.queryGenerator.selectQuery(tableName, options, model), | ||
options | ||
); | ||
const options = __spreadProps(__spreadValues({}, optionsArg), { type: QueryTypes.SELECT, model }); | ||
return await this.sequelize.query(this.queryGenerator.selectQuery(tableName, options, model), options); | ||
} | ||
async increment(model, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) { | ||
options = Utils.cloneDeep(options); | ||
const sql = this.queryGenerator.arithmeticQuery('+', tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options); | ||
const sql = this.queryGenerator.arithmeticQuery("+", tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options); | ||
options.type = QueryTypes.UPDATE; | ||
options.model = model; | ||
return await this.sequelize.query(sql, options); | ||
} | ||
async decrement(model, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) { | ||
options = Utils.cloneDeep(options); | ||
const sql = this.queryGenerator.arithmeticQuery('-', tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options); | ||
const sql = this.queryGenerator.arithmeticQuery("-", tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options); | ||
options.type = QueryTypes.UPDATE; | ||
options.model = model; | ||
return await this.sequelize.query(sql, options); | ||
} | ||
async rawSelect(tableName, options, attributeSelector, Model) { | ||
@@ -1012,9 +454,6 @@ options = Utils.cloneDeep(options); | ||
}); | ||
const sql = this.queryGenerator.selectQuery(tableName, options, Model); | ||
if (attributeSelector === undefined) { | ||
throw new Error('Please pass an attribute selector!'); | ||
if (attributeSelector === void 0) { | ||
throw new Error("Please pass an attribute selector!"); | ||
} | ||
const data = await this.sequelize.query(sql, options); | ||
@@ -1024,11 +463,7 @@ if (!options.plain) { | ||
} | ||
const result = data ? data[attributeSelector] : null; | ||
if (!options || !options.dataType) { | ||
return result; | ||
} | ||
const dataType = options.dataType; | ||
if (dataType instanceof DataTypes.DECIMAL || dataType instanceof DataTypes.FLOAT) { | ||
@@ -1040,3 +475,5 @@ if (result !== null) { | ||
if (dataType instanceof DataTypes.INTEGER || dataType instanceof DataTypes.BIGINT) { | ||
return parseInt(result, 10); | ||
if (result !== null) { | ||
return parseInt(result, 10); | ||
} | ||
} | ||
@@ -1050,13 +487,3 @@ if (dataType instanceof DataTypes.DATE) { | ||
} | ||
async createTrigger( | ||
tableName, | ||
triggerName, | ||
timingType, | ||
fireOnArray, | ||
functionName, | ||
functionParams, | ||
optionsArray, | ||
options | ||
) { | ||
async createTrigger(tableName, triggerName, timingType, fireOnArray, functionName, functionParams, optionsArray, options) { | ||
const sql = this.queryGenerator.createTrigger(tableName, triggerName, timingType, fireOnArray, functionName, functionParams, optionsArray); | ||
@@ -1068,7 +495,5 @@ options = options || {}; | ||
} | ||
async dropTrigger(tableName, triggerName, options) { | ||
const sql = this.queryGenerator.dropTrigger(tableName, triggerName); | ||
options = options || {}; | ||
if (sql) { | ||
@@ -1078,7 +503,5 @@ return await this.sequelize.query(sql, options); | ||
} | ||
async renameTrigger(tableName, oldTriggerName, newTriggerName, options) { | ||
const sql = this.queryGenerator.renameTrigger(tableName, oldTriggerName, newTriggerName); | ||
options = options || {}; | ||
if (sql) { | ||
@@ -1088,44 +511,5 @@ return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Create an SQL function | ||
* | ||
* @example | ||
* queryInterface.createFunction( | ||
* 'someFunction', | ||
* [ | ||
* {type: 'integer', name: 'param', direction: 'IN'} | ||
* ], | ||
* 'integer', | ||
* 'plpgsql', | ||
* 'RETURN param + 1;', | ||
* [ | ||
* 'IMMUTABLE', | ||
* 'LEAKPROOF' | ||
* ], | ||
* { | ||
* variables: | ||
* [ | ||
* {type: 'integer', name: 'myVar', default: 100} | ||
* ], | ||
* force: true | ||
* }; | ||
* ); | ||
* | ||
* @param {string} functionName Name of SQL function to create | ||
* @param {Array} params List of parameters declared for SQL function | ||
* @param {string} returnType SQL type of function returned value | ||
* @param {string} language The name of the language that the function is implemented in | ||
* @param {string} body Source code of function | ||
* @param {Array} optionsArray Extra-options for creation | ||
* @param {object} [options] query options | ||
* @param {boolean} options.force If force is true, any existing functions with the same parameters will be replaced. For postgres, this means using `CREATE OR REPLACE FUNCTION` instead of `CREATE FUNCTION`. Default is false | ||
* @param {Array<object>} options.variables List of declared variables. Each variable should be an object with string fields `type` and `name`, and optionally having a `default` field as well. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async createFunction(functionName, params, returnType, language, body, optionsArray, options) { | ||
const sql = this.queryGenerator.createFunction(functionName, params, returnType, language, body, optionsArray, options); | ||
options = options || {}; | ||
if (sql) { | ||
@@ -1135,25 +519,5 @@ return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Drop an SQL function | ||
* | ||
* @example | ||
* queryInterface.dropFunction( | ||
* 'someFunction', | ||
* [ | ||
* {type: 'varchar', name: 'param1', direction: 'IN'}, | ||
* {type: 'integer', name: 'param2', direction: 'INOUT'} | ||
* ] | ||
* ); | ||
* | ||
* @param {string} functionName Name of SQL function to drop | ||
* @param {Array} params List of parameters declared for SQL function | ||
* @param {object} [options] query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropFunction(functionName, params, options) { | ||
const sql = this.queryGenerator.dropFunction(functionName, params); | ||
options = options || {}; | ||
if (sql) { | ||
@@ -1163,27 +527,5 @@ return await this.sequelize.query(sql, options); | ||
} | ||
/** | ||
* Rename an SQL function | ||
* | ||
* @example | ||
* queryInterface.renameFunction( | ||
* 'fooFunction', | ||
* [ | ||
* {type: 'varchar', name: 'param1', direction: 'IN'}, | ||
* {type: 'integer', name: 'param2', direction: 'INOUT'} | ||
* ], | ||
* 'barFunction' | ||
* ); | ||
* | ||
* @param {string} oldFunctionName Current name of function | ||
* @param {Array} params List of parameters declared for SQL function | ||
* @param {string} newFunctionName New name of function | ||
* @param {object} [options] query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async renameFunction(oldFunctionName, params, newFunctionName, options) { | ||
const sql = this.queryGenerator.renameFunction(oldFunctionName, params, newFunctionName); | ||
options = options || {}; | ||
if (sql) { | ||
@@ -1193,50 +535,31 @@ return await this.sequelize.query(sql, options); | ||
} | ||
// Helper methods useful for querying | ||
/** | ||
* @private | ||
*/ | ||
ensureEnums() { | ||
// noop by default | ||
} | ||
async setIsolationLevel(transaction, value, options) { | ||
if (!transaction || !(transaction instanceof Transaction)) { | ||
throw new Error('Unable to set isolation level for a transaction without transaction object!'); | ||
throw new Error("Unable to set isolation level for a transaction without transaction object!"); | ||
} | ||
if (transaction.parent || !value) { | ||
// Not possible to set a separate isolation level for savepoints | ||
return; | ||
} | ||
options = { ...options, transaction: transaction.parent || transaction }; | ||
options = __spreadProps(__spreadValues({}, options), { transaction: transaction.parent || transaction }); | ||
const sql = this.queryGenerator.setIsolationLevelQuery(value, { | ||
parent: transaction.parent | ||
}); | ||
if (!sql) return; | ||
if (!sql) | ||
return; | ||
return await this.sequelize.query(sql, options); | ||
} | ||
async startTransaction(transaction, options) { | ||
if (!transaction || !(transaction instanceof Transaction)) { | ||
throw new Error('Unable to start a transaction without transaction object!'); | ||
throw new Error("Unable to start a transaction without transaction object!"); | ||
} | ||
options = { ...options, transaction: transaction.parent || transaction }; | ||
options.transaction.name = transaction.parent ? transaction.name : undefined; | ||
options = __spreadProps(__spreadValues({}, options), { transaction: transaction.parent || transaction }); | ||
options.transaction.name = transaction.parent ? transaction.name : void 0; | ||
const sql = this.queryGenerator.startTransactionQuery(transaction); | ||
return await this.sequelize.query(sql, options); | ||
} | ||
async deferConstraints(transaction, options) { | ||
options = { ...options, transaction: transaction.parent || transaction }; | ||
options = __spreadProps(__spreadValues({}, options), { transaction: transaction.parent || transaction }); | ||
const sql = this.queryGenerator.deferConstraintsQuery(options); | ||
if (sql) { | ||
@@ -1246,48 +569,36 @@ return await this.sequelize.query(sql, options); | ||
} | ||
async commitTransaction(transaction, options) { | ||
if (!transaction || !(transaction instanceof Transaction)) { | ||
throw new Error('Unable to commit a transaction without transaction object!'); | ||
throw new Error("Unable to commit a transaction without transaction object!"); | ||
} | ||
if (transaction.parent) { | ||
// Savepoints cannot be committed | ||
return; | ||
} | ||
options = { | ||
...options, | ||
options = __spreadProps(__spreadValues({}, options), { | ||
transaction: transaction.parent || transaction, | ||
supportsSearchPath: false, | ||
completesTransaction: true | ||
}; | ||
}); | ||
const sql = this.queryGenerator.commitTransactionQuery(transaction); | ||
const promise = this.sequelize.query(sql, options); | ||
transaction.finished = 'commit'; | ||
transaction.finished = "commit"; | ||
return await promise; | ||
} | ||
async rollbackTransaction(transaction, options) { | ||
if (!transaction || !(transaction instanceof Transaction)) { | ||
throw new Error('Unable to rollback a transaction without transaction object!'); | ||
throw new Error("Unable to rollback a transaction without transaction object!"); | ||
} | ||
options = { | ||
...options, | ||
options = __spreadProps(__spreadValues({}, options), { | ||
transaction: transaction.parent || transaction, | ||
supportsSearchPath: false, | ||
completesTransaction: true | ||
}; | ||
options.transaction.name = transaction.parent ? transaction.name : undefined; | ||
}); | ||
options.transaction.name = transaction.parent ? transaction.name : void 0; | ||
const sql = this.queryGenerator.rollbackTransactionQuery(transaction); | ||
const promise = this.sequelize.query(sql, options); | ||
transaction.finished = 'rollback'; | ||
transaction.finished = "rollback"; | ||
return await promise; | ||
} | ||
} | ||
exports.QueryInterface = QueryInterface; | ||
//# sourceMappingURL=query-interface.js.map |
@@ -1,12 +0,26 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const SqlString = require('../../sql-string'); | ||
const QueryTypes = require('../../query-types'); | ||
const Dot = require('dottie'); | ||
const deprecations = require('../../utils/deprecations'); | ||
const uuid = require('uuid').v4; | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const _ = require("lodash"); | ||
const SqlString = require("../../sql-string"); | ||
const QueryTypes = require("../../query-types"); | ||
const Dot = require("dottie"); | ||
const deprecations = require("../../utils/deprecations"); | ||
const uuid = require("uuid").v4; | ||
const { safeStringifyJson } = require("../../utils.js"); | ||
class AbstractQuery { | ||
constructor(connection, sequelize, options) { | ||
@@ -18,32 +32,12 @@ this.uuid = uuid(); | ||
this.sequelize = sequelize; | ||
this.options = { | ||
this.options = __spreadValues({ | ||
plain: false, | ||
raw: false, | ||
// eslint-disable-next-line no-console | ||
logging: console.log, | ||
...options | ||
}; | ||
logging: console.log | ||
}, options); | ||
this.checkLoggingOption(); | ||
if (options.rawErrors) { | ||
this.formatError = AbstractQuery.prototype.formatError; | ||
} | ||
} | ||
/** | ||
* rewrite query with parameters | ||
* | ||
* Examples: | ||
* | ||
* query.formatBindParameters('select $1 as foo', ['fooval']); | ||
* | ||
* query.formatBindParameters('select $foo as foo', { foo: 'fooval' }); | ||
* | ||
* Options | ||
* skipUnescape: bool, skip unescaping $$ | ||
* skipValueReplace: bool, do not replace (but do unescape $$). Check correct syntax and if all values are available | ||
* | ||
* @param {string} sql | ||
* @param {object|Array} values | ||
* @param {string} dialect | ||
* @param {Function} [replacementFunc] | ||
* @param {object} [options] | ||
* @private | ||
*/ | ||
static formatBindParameters(sql, values, dialect, replacementFunc, options) { | ||
@@ -53,23 +47,21 @@ if (!values) { | ||
} | ||
options = options || {}; | ||
if (typeof replacementFunc !== 'function') { | ||
if (typeof replacementFunc !== "function") { | ||
options = replacementFunc || {}; | ||
replacementFunc = undefined; | ||
replacementFunc = void 0; | ||
} | ||
if (!replacementFunc) { | ||
if (options.skipValueReplace) { | ||
replacementFunc = (match, key, values) => { | ||
if (values[key] !== undefined) { | ||
replacementFunc = (match, key, values2) => { | ||
if (values2[key] !== void 0) { | ||
return match; | ||
} | ||
return undefined; | ||
return void 0; | ||
}; | ||
} else { | ||
replacementFunc = (match, key, values, timeZone, dialect) => { | ||
if (values[key] !== undefined) { | ||
return SqlString.escape(values[key], timeZone, dialect); | ||
replacementFunc = (match, key, values2, timeZone2, dialect2) => { | ||
if (values2[key] !== void 0) { | ||
return SqlString.escape(values2[key], timeZone2, dialect2); | ||
} | ||
return undefined; | ||
return void 0; | ||
}; | ||
@@ -79,17 +71,15 @@ } | ||
const origReplacementFunc = replacementFunc; | ||
replacementFunc = (match, key, values, timeZone, dialect, options) => { | ||
if (origReplacementFunc(match, key, values, timeZone, dialect, options) !== undefined) { | ||
replacementFunc = (match, key, values2, timeZone2, dialect2, options2) => { | ||
if (origReplacementFunc(match, key, values2, timeZone2, dialect2, options2) !== void 0) { | ||
return match; | ||
} | ||
return undefined; | ||
return void 0; | ||
}; | ||
} | ||
const timeZone = null; | ||
const list = Array.isArray(values); | ||
sql = sql.replace(/\B\$(\$|\w+)/g, (match, key) => { | ||
if ('$' === key) { | ||
if (key === "$") { | ||
return options.skipUnescape ? match : key; | ||
} | ||
let replVal; | ||
@@ -104,3 +94,3 @@ if (list) { | ||
} | ||
if (replVal === undefined) { | ||
if (replVal === void 0) { | ||
throw new Error(`Named bind parameter "${match}" has no value in the given object.`); | ||
@@ -112,45 +102,23 @@ } | ||
} | ||
/** | ||
* Execute the passed sql query. | ||
* | ||
* Examples: | ||
* | ||
* query.run('SELECT 1') | ||
* | ||
* @private | ||
*/ | ||
formatError(error, errStack) { | ||
error.stack = errStack; | ||
return error; | ||
} | ||
run() { | ||
throw new Error('The run method wasn\'t overwritten!'); | ||
throw new Error("The run method wasn't overwritten!"); | ||
} | ||
/** | ||
* Check the logging option of the instance and print deprecation warnings. | ||
* | ||
* @private | ||
*/ | ||
checkLoggingOption() { | ||
if (this.options.logging === true) { | ||
deprecations.noTrueLogging(); | ||
// eslint-disable-next-line no-console | ||
this.options.logging = console.log; | ||
} | ||
} | ||
/** | ||
* Get the attributes of an insert query, which contains the just inserted id. | ||
* | ||
* @returns {string} The field name. | ||
* @private | ||
*/ | ||
getInsertIdField() { | ||
return 'insertId'; | ||
return "insertId"; | ||
} | ||
getUniqueConstraintErrorMessage(field) { | ||
let message = field ? `${field} must be unique` : 'Must be unique'; | ||
let message = field ? `${field} must be unique` : "Must be unique"; | ||
if (field && this.model) { | ||
for (const key of Object.keys(this.model.uniqueKeys)) { | ||
if (this.model.uniqueKeys[key].fields.includes(field.replace(/"/g, ''))) { | ||
if (this.model.uniqueKeys[key].fields.includes(field.replace(/"/g, ""))) { | ||
if (this.model.uniqueKeys[key].msg) { | ||
@@ -164,120 +132,85 @@ message = this.model.uniqueKeys[key].msg; | ||
} | ||
isRawQuery() { | ||
return this.options.type === QueryTypes.RAW; | ||
} | ||
isVersionQuery() { | ||
return this.options.type === QueryTypes.VERSION; | ||
} | ||
isUpsertQuery() { | ||
return this.options.type === QueryTypes.UPSERT; | ||
} | ||
isInsertQuery(results, metaData) { | ||
let result = true; | ||
if (this.options.type === QueryTypes.INSERT) { | ||
return true; | ||
} | ||
// is insert query if sql contains insert into | ||
result = result && this.sql.toLowerCase().startsWith('insert into'); | ||
// is insert query if no results are passed or if the result has the inserted id | ||
result = result && this.sql.toLowerCase().startsWith("insert into"); | ||
result = result && (!results || Object.prototype.hasOwnProperty.call(results, this.getInsertIdField())); | ||
// is insert query if no metadata are passed or if the metadata has the inserted id | ||
result = result && (!metaData || Object.prototype.hasOwnProperty.call(metaData, this.getInsertIdField())); | ||
return result; | ||
} | ||
handleInsertQuery(results, metaData) { | ||
if (this.instance) { | ||
// add the inserted row id to the instance | ||
const autoIncrementAttribute = this.model.autoIncrementAttribute; | ||
let id = null; | ||
id = id || results && results[this.getInsertIdField()]; | ||
id = id || metaData && metaData[this.getInsertIdField()]; | ||
this.instance[autoIncrementAttribute] = id; | ||
} | ||
} | ||
isShowTablesQuery() { | ||
return this.options.type === QueryTypes.SHOWTABLES; | ||
} | ||
handleShowTablesQuery(results) { | ||
return _.flatten(results.map(resultSet => Object.values(resultSet))); | ||
return _.flatten(results.map((resultSet) => Object.values(resultSet))); | ||
} | ||
isShowIndexesQuery() { | ||
return this.options.type === QueryTypes.SHOWINDEXES; | ||
} | ||
isShowConstraintsQuery() { | ||
return this.options.type === QueryTypes.SHOWCONSTRAINTS; | ||
} | ||
isDescribeQuery() { | ||
return this.options.type === QueryTypes.DESCRIBE; | ||
} | ||
isSelectQuery() { | ||
return this.options.type === QueryTypes.SELECT; | ||
} | ||
isBulkUpdateQuery() { | ||
return this.options.type === QueryTypes.BULKUPDATE; | ||
} | ||
isBulkDeleteQuery() { | ||
return this.options.type === QueryTypes.BULKDELETE; | ||
} | ||
isForeignKeysQuery() { | ||
return this.options.type === QueryTypes.FOREIGNKEYS; | ||
} | ||
isUpdateQuery() { | ||
return this.options.type === QueryTypes.UPDATE; | ||
} | ||
handleSelectQuery(results) { | ||
let result = null; | ||
// Map raw fields to names if a mapping is provided | ||
if (this.options.fieldMap) { | ||
const fieldMap = this.options.fieldMap; | ||
results = results.map(result => _.reduce(fieldMap, (result, name, field) => { | ||
if (result[field] !== undefined && name !== field) { | ||
result[name] = result[field]; | ||
delete result[field]; | ||
results = results.map((result2) => _.reduce(fieldMap, (result3, name, field) => { | ||
if (result3[field] !== void 0 && name !== field) { | ||
result3[name] = result3[field]; | ||
delete result3[field]; | ||
} | ||
return result; | ||
}, result)); | ||
return result3; | ||
}, result2)); | ||
} | ||
// Raw queries | ||
if (this.options.raw) { | ||
result = results.map(result => { | ||
result = results.map((result2) => { | ||
let o = {}; | ||
for (const key in result) { | ||
if (Object.prototype.hasOwnProperty.call(result, key)) { | ||
o[key] = result[key]; | ||
for (const key in result2) { | ||
if (Object.prototype.hasOwnProperty.call(result2, key)) { | ||
o[key] = result2[key]; | ||
} | ||
} | ||
if (this.options.nest) { | ||
o = Dot.transform(o); | ||
} | ||
return o; | ||
}); | ||
// Queries with include | ||
} else if (this.options.hasJoin === true) { | ||
@@ -291,3 +224,2 @@ results = AbstractQuery._groupJoinData(results, { | ||
}); | ||
result = this.model.bulkBuild(results, { | ||
@@ -302,3 +234,2 @@ isNewRecord: false, | ||
}); | ||
// Regular queries | ||
} else { | ||
@@ -311,4 +242,2 @@ result = this.model.bulkBuild(results, { | ||
} | ||
// return the first real model instance if options.plain is set (e.g. Model.find) | ||
if (this.options.plain) { | ||
@@ -319,23 +248,11 @@ result = result.length === 0 ? null : result[0]; | ||
} | ||
isShowOrDescribeQuery() { | ||
let result = false; | ||
result = result || this.sql.toLowerCase().startsWith('show'); | ||
result = result || this.sql.toLowerCase().startsWith('describe'); | ||
result = result || this.sql.toLowerCase().startsWith("show"); | ||
result = result || this.sql.toLowerCase().startsWith("describe"); | ||
return result; | ||
} | ||
isCallQuery() { | ||
return this.sql.toLowerCase().startsWith('call'); | ||
return this.sql.toLowerCase().startsWith("call"); | ||
} | ||
/** | ||
* @param {string} sql | ||
* @param {Function} debugContext | ||
* @param {Array|object} parameters | ||
* @protected | ||
* @returns {Function} A function to call after the query was completed. | ||
*/ | ||
_logQuery(sql, debugContext, parameters) { | ||
@@ -346,15 +263,14 @@ const { connection, options } = this; | ||
const startTime = Date.now(); | ||
let logParameter = ''; | ||
let logParameter = ""; | ||
if (logQueryParameters && parameters) { | ||
const delimiter = sql.endsWith(';') ? '' : ';'; | ||
const delimiter = sql.endsWith(";") ? "" : ";"; | ||
let paramStr; | ||
if (Array.isArray(parameters)) { | ||
paramStr = parameters.map(p=>JSON.stringify(p)).join(', '); | ||
paramStr = parameters.map((p) => safeStringifyJson(p)).join(", "); | ||
} else { | ||
paramStr = JSON.stringify(parameters); | ||
paramStr = safeStringifyJson(parameters); | ||
} | ||
logParameter = `${delimiter} ${paramStr}`; | ||
} | ||
const fmt = `(${connection.uuid || 'default'}): ${sql}${logParameter}`; | ||
const fmt = `(${connection.uuid || "default"}): ${sql}${logParameter}`; | ||
const msg = `Executing ${fmt}`; | ||
@@ -373,63 +289,6 @@ debugContext(msg); | ||
} | ||
/** | ||
* The function takes the result of the query execution and groups | ||
* the associated data by the callee. | ||
* | ||
* Example: | ||
* groupJoinData([ | ||
* { | ||
* some: 'data', | ||
* id: 1, | ||
* association: { foo: 'bar', id: 1 } | ||
* }, { | ||
* some: 'data', | ||
* id: 1, | ||
* association: { foo: 'bar', id: 2 } | ||
* }, { | ||
* some: 'data', | ||
* id: 1, | ||
* association: { foo: 'bar', id: 3 } | ||
* } | ||
* ]) | ||
* | ||
* Result: | ||
* Something like this: | ||
* | ||
* [ | ||
* { | ||
* some: 'data', | ||
* id: 1, | ||
* association: [ | ||
* { foo: 'bar', id: 1 }, | ||
* { foo: 'bar', id: 2 }, | ||
* { foo: 'bar', id: 3 } | ||
* ] | ||
* } | ||
* ] | ||
* | ||
* @param {Array} rows | ||
* @param {object} includeOptions | ||
* @param {object} options | ||
* @private | ||
*/ | ||
static _groupJoinData(rows, includeOptions, options) { | ||
/* | ||
* Assumptions | ||
* ID is not necessarily the first field | ||
* All fields for a level is grouped in the same set (i.e. Panel.id, Task.id, Panel.title is not possible) | ||
* Parent keys will be seen before any include/child keys | ||
* Previous set won't necessarily be parent set (one parent could have two children, one child would then be previous set for the other) | ||
*/ | ||
/* | ||
* Author (MH) comment: This code is an unreadable mess, but it's performant. | ||
* groupJoinData is a performance critical function so we prioritize perf over readability. | ||
*/ | ||
if (!rows.length) { | ||
return []; | ||
} | ||
// Generic looping | ||
let i; | ||
@@ -439,7 +298,5 @@ let length; | ||
let $length; | ||
// Row specific looping | ||
let rowsI; | ||
let row; | ||
const rowsLength = rows.length; | ||
// Key specific looping | ||
let keys; | ||
@@ -454,3 +311,2 @@ let key; | ||
const checkExisting = options.checkExisting; | ||
// If we don't have to deduplicate we can pre-allocate the resulting array | ||
let itemHash; | ||
@@ -462,6 +318,5 @@ let parentHash; | ||
const includeMap = {}; | ||
// Result variables for the respective functions | ||
let $keyPrefix; | ||
let $keyPrefixString; | ||
let $prevKeyPrefixString; // eslint-disable-line | ||
let $prevKeyPrefixString; | ||
let $prevKeyPrefix; | ||
@@ -471,5 +326,4 @@ let $lastKeyPrefix; | ||
let $parent; | ||
// Map each key to an include option | ||
let previousPiece; | ||
const buildIncludeMap = piece => { | ||
const buildIncludeMap = (piece) => { | ||
if (Object.prototype.hasOwnProperty.call($current.includeMap, piece)) { | ||
@@ -485,61 +339,48 @@ includeMap[key] = $current = $current.includeMap[piece]; | ||
}; | ||
// Calculate the string prefix of a key ('User.Results' for 'User.Results.id') | ||
const keyPrefixStringMemo = {}; | ||
const keyPrefixString = (key, memo) => { | ||
if (!Object.prototype.hasOwnProperty.call(memo, key)) { | ||
memo[key] = key.substr(0, key.lastIndexOf('.')); | ||
const keyPrefixString = (key2, memo) => { | ||
if (!Object.prototype.hasOwnProperty.call(memo, key2)) { | ||
memo[key2] = key2.substr(0, key2.lastIndexOf(".")); | ||
} | ||
return memo[key]; | ||
return memo[key2]; | ||
}; | ||
// Removes the prefix from a key ('id' for 'User.Results.id') | ||
const removeKeyPrefixMemo = {}; | ||
const removeKeyPrefix = key => { | ||
if (!Object.prototype.hasOwnProperty.call(removeKeyPrefixMemo, key)) { | ||
const index = key.lastIndexOf('.'); | ||
removeKeyPrefixMemo[key] = key.substr(index === -1 ? 0 : index + 1); | ||
const removeKeyPrefix = (key2) => { | ||
if (!Object.prototype.hasOwnProperty.call(removeKeyPrefixMemo, key2)) { | ||
const index = key2.lastIndexOf("."); | ||
removeKeyPrefixMemo[key2] = key2.substr(index === -1 ? 0 : index + 1); | ||
} | ||
return removeKeyPrefixMemo[key]; | ||
return removeKeyPrefixMemo[key2]; | ||
}; | ||
// Calculates the array prefix of a key (['User', 'Results'] for 'User.Results.id') | ||
const keyPrefixMemo = {}; | ||
const keyPrefix = key => { | ||
// We use a double memo and keyPrefixString so that different keys with the same prefix will receive the same array instead of differnet arrays with equal values | ||
if (!Object.prototype.hasOwnProperty.call(keyPrefixMemo, key)) { | ||
const prefixString = keyPrefixString(key, keyPrefixStringMemo); | ||
const keyPrefix = (key2) => { | ||
if (!Object.prototype.hasOwnProperty.call(keyPrefixMemo, key2)) { | ||
const prefixString = keyPrefixString(key2, keyPrefixStringMemo); | ||
if (!Object.prototype.hasOwnProperty.call(keyPrefixMemo, prefixString)) { | ||
keyPrefixMemo[prefixString] = prefixString ? prefixString.split('.') : []; | ||
keyPrefixMemo[prefixString] = prefixString ? prefixString.split(".") : []; | ||
} | ||
keyPrefixMemo[key] = keyPrefixMemo[prefixString]; | ||
keyPrefixMemo[key2] = keyPrefixMemo[prefixString]; | ||
} | ||
return keyPrefixMemo[key]; | ||
return keyPrefixMemo[key2]; | ||
}; | ||
// Calcuate the last item in the array prefix ('Results' for 'User.Results.id') | ||
const lastKeyPrefixMemo = {}; | ||
const lastKeyPrefix = key => { | ||
if (!Object.prototype.hasOwnProperty.call(lastKeyPrefixMemo, key)) { | ||
const prefix = keyPrefix(key); | ||
const length = prefix.length; | ||
lastKeyPrefixMemo[key] = !length ? '' : prefix[length - 1]; | ||
const lastKeyPrefix = (key2) => { | ||
if (!Object.prototype.hasOwnProperty.call(lastKeyPrefixMemo, key2)) { | ||
const prefix2 = keyPrefix(key2); | ||
const length2 = prefix2.length; | ||
lastKeyPrefixMemo[key2] = !length2 ? "" : prefix2[length2 - 1]; | ||
} | ||
return lastKeyPrefixMemo[key]; | ||
return lastKeyPrefixMemo[key2]; | ||
}; | ||
const getUniqueKeyAttributes = model => { | ||
let uniqueKeyAttributes = _.chain(model.uniqueKeys); | ||
uniqueKeyAttributes = uniqueKeyAttributes | ||
.result(`${uniqueKeyAttributes.findKey()}.fields`) | ||
.map(field => _.findKey(model.attributes, chr => chr.field === field)) | ||
.value(); | ||
return uniqueKeyAttributes; | ||
const getUniqueKeyAttributes = (model) => { | ||
let uniqueKeyAttributes2 = _.chain(model.uniqueKeys); | ||
uniqueKeyAttributes2 = uniqueKeyAttributes2.result(`${uniqueKeyAttributes2.findKey()}.fields`).map((field) => _.findKey(model.attributes, (chr) => chr.field === field)).value(); | ||
return uniqueKeyAttributes2; | ||
}; | ||
const stringify = obj => obj instanceof Buffer ? obj.toString('hex') : obj; | ||
const stringify = (obj) => obj instanceof Buffer ? obj.toString("hex") : obj; | ||
let primaryKeyAttributes; | ||
let uniqueKeyAttributes; | ||
let prefix; | ||
for (rowsI = 0; rowsI < rowsLength; rowsI++) { | ||
row = rows[rowsI]; | ||
// Keys are the same for all rows, so only need to compute them on the first row | ||
if (rowsI === 0) { | ||
@@ -549,18 +390,13 @@ keys = Object.keys(row); | ||
} | ||
if (checkExisting) { | ||
topExists = false; | ||
// Compute top level hash key (this is usually just the primary key values) | ||
$length = includeOptions.model.primaryKeyAttributes.length; | ||
topHash = ''; | ||
topHash = ""; | ||
if ($length === 1) { | ||
topHash = stringify(row[includeOptions.model.primaryKeyAttributes[0]]); | ||
} | ||
else if ($length > 1) { | ||
} else if ($length > 1) { | ||
for ($i = 0; $i < $length; $i++) { | ||
topHash += stringify(row[includeOptions.model.primaryKeyAttributes[$i]]); | ||
} | ||
} | ||
else if (!_.isEmpty(includeOptions.model.uniqueKeys)) { | ||
} else if (!_.isEmpty(includeOptions.model.uniqueKeys)) { | ||
uniqueKeyAttributes = getUniqueKeyAttributes(includeOptions.model); | ||
@@ -572,33 +408,22 @@ for ($i = 0; $i < uniqueKeyAttributes.length; $i++) { | ||
} | ||
topValues = values = {}; | ||
$prevKeyPrefix = undefined; | ||
$prevKeyPrefix = void 0; | ||
for (keyI = 0; keyI < keyLength; keyI++) { | ||
key = keys[keyI]; | ||
// The string prefix isn't actualy needed | ||
// We use it so keyPrefix for different keys will resolve to the same array if they have the same prefix | ||
// TODO: Find a better way? | ||
$keyPrefixString = keyPrefixString(key, keyPrefixStringMemo); | ||
$keyPrefix = keyPrefix(key); | ||
// On the first row we compute the includeMap | ||
if (rowsI === 0 && !Object.prototype.hasOwnProperty.call(includeMap, key)) { | ||
if (!$keyPrefix.length) { | ||
includeMap[key] = includeMap[''] = includeOptions; | ||
includeMap[key] = includeMap[""] = includeOptions; | ||
} else { | ||
$current = includeOptions; | ||
previousPiece = undefined; | ||
previousPiece = void 0; | ||
$keyPrefix.forEach(buildIncludeMap); | ||
} | ||
} | ||
// End of key set | ||
if ($prevKeyPrefix !== undefined && $prevKeyPrefix !== $keyPrefix) { | ||
if ($prevKeyPrefix !== void 0 && $prevKeyPrefix !== $keyPrefix) { | ||
if (checkExisting) { | ||
// Compute hash key for this set instance | ||
// TODO: Optimize | ||
length = $prevKeyPrefix.length; | ||
$parent = null; | ||
parentHash = null; | ||
if (length) { | ||
@@ -612,9 +437,7 @@ for (i = 0; i < length; i++) { | ||
itemHash += stringify(row[`${prefix}.${primaryKeyAttributes[0]}`]); | ||
} | ||
else if ($length > 1) { | ||
} else if ($length > 1) { | ||
for ($i = 0; $i < $length; $i++) { | ||
itemHash += stringify(row[`${prefix}.${primaryKeyAttributes[$i]}`]); | ||
} | ||
} | ||
else if (!_.isEmpty(includeMap[prefix].model.uniqueKeys)) { | ||
} else if (!_.isEmpty(includeMap[prefix].model.uniqueKeys)) { | ||
uniqueKeyAttributes = getUniqueKeyAttributes(includeMap[prefix].model); | ||
@@ -628,3 +451,2 @@ for ($i = 0; $i < uniqueKeyAttributes.length; $i++) { | ||
} | ||
itemHash = parentHash + itemHash; | ||
@@ -639,3 +461,2 @@ $parent = prefix; | ||
} | ||
if (itemHash === topHash) { | ||
@@ -650,3 +471,2 @@ if (!resultMap[itemHash]) { | ||
$lastKeyPrefix = lastKeyPrefix(prevKey); | ||
if (includeMap[prevKey].association.isSingleAssociation) { | ||
@@ -663,9 +483,4 @@ if ($parent) { | ||
} | ||
// Reset values | ||
values = {}; | ||
} else { | ||
// If checkExisting is false it's because there's only 1:1 associations in this query | ||
// However we still need to map onto the appropriate parent | ||
// For 1:1 we map forward, initializing the value object on the parent to be filled in the next iterations of the loop | ||
$current = topValues; | ||
@@ -683,4 +498,2 @@ length = $keyPrefix.length; | ||
} | ||
// End of iteration, set value and set prev values (for next iteration) | ||
values[removeKeyPrefix(key)] = row[key]; | ||
@@ -691,3 +504,2 @@ prevKey = key; | ||
} | ||
if (checkExisting) { | ||
@@ -697,3 +509,2 @@ length = $prevKeyPrefix.length; | ||
parentHash = null; | ||
if (length) { | ||
@@ -707,9 +518,7 @@ for (i = 0; i < length; i++) { | ||
itemHash += stringify(row[`${prefix}.${primaryKeyAttributes[0]}`]); | ||
} | ||
else if ($length > 0) { | ||
} else if ($length > 0) { | ||
for ($i = 0; $i < $length; $i++) { | ||
itemHash += stringify(row[`${prefix}.${primaryKeyAttributes[$i]}`]); | ||
} | ||
} | ||
else if (!_.isEmpty(includeMap[prefix].model.uniqueKeys)) { | ||
} else if (!_.isEmpty(includeMap[prefix].model.uniqueKeys)) { | ||
uniqueKeyAttributes = getUniqueKeyAttributes(includeMap[prefix].model); | ||
@@ -723,3 +532,2 @@ for ($i = 0; $i < uniqueKeyAttributes.length; $i++) { | ||
} | ||
itemHash = parentHash + itemHash; | ||
@@ -734,3 +542,2 @@ $parent = prefix; | ||
} | ||
if (itemHash === topHash) { | ||
@@ -745,3 +552,2 @@ if (!resultMap[itemHash]) { | ||
$lastKeyPrefix = lastKeyPrefix(prevKey); | ||
if (includeMap[prevKey].association.isSingleAssociation) { | ||
@@ -765,9 +571,8 @@ if ($parent) { | ||
} | ||
return results; | ||
} | ||
} | ||
module.exports = AbstractQuery; | ||
module.exports.AbstractQuery = AbstractQuery; | ||
module.exports.default = AbstractQuery; | ||
//# sourceMappingURL=query.js.map |
@@ -1,21 +0,26 @@ | ||
'use strict'; | ||
const semver = require('semver'); | ||
const AbstractConnectionManager = require('../abstract/connection-manager'); | ||
const SequelizeErrors = require('../../errors'); | ||
const { logger } = require('../../utils/logger'); | ||
const DataTypes = require('../../data-types').mariadb; | ||
const momentTz = require('moment-timezone'); | ||
const debug = logger.debugContext('connection:mariadb'); | ||
const parserStore = require('../parserStore')('mariadb'); | ||
/** | ||
* MariaDB Connection Manager | ||
* | ||
* Get connections, validate and disconnect them. | ||
* AbstractConnectionManager pooling use it to handle MariaDB specific connections | ||
* Use https://github.com/MariaDB/mariadb-connector-nodejs to connect with MariaDB server | ||
* | ||
* @private | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const semver = require("semver"); | ||
const AbstractConnectionManager = require("../abstract/connection-manager"); | ||
const SequelizeErrors = require("../../errors"); | ||
const { logger } = require("../../utils/logger"); | ||
const DataTypes = require("../../data-types").mariadb; | ||
const momentTz = require("moment-timezone"); | ||
const debug = logger.debugContext("connection:mariadb"); | ||
const parserStore = require("../parserStore")("mariadb"); | ||
class ConnectionManager extends AbstractConnectionManager { | ||
@@ -25,6 +30,5 @@ constructor(dialect, sequelize) { | ||
super(dialect, sequelize); | ||
this.lib = this._loadDialectModule('mariadb'); | ||
this.lib = this._loadDialectModule("mariadb"); | ||
this.refreshTypeParser(DataTypes); | ||
} | ||
static _typecast(field, next) { | ||
@@ -36,27 +40,12 @@ if (parserStore.get(field.type)) { | ||
} | ||
_refreshTypeParser(dataType) { | ||
parserStore.refresh(dataType); | ||
} | ||
_clearTypeParser() { | ||
parserStore.clear(); | ||
} | ||
/** | ||
* Connect with MariaDB database based on config, Handle any errors in connection | ||
* Set the pool handlers on connection.error | ||
* Also set proper timezone once connection is connected. | ||
* | ||
* @param {object} config | ||
* @returns {Promise<Connection>} | ||
* @private | ||
*/ | ||
async connect(config) { | ||
// Named timezone is not supported in mariadb, convert to offset | ||
let tzOffset = this.sequelize.options.timezone; | ||
tzOffset = /\//.test(tzOffset) ? momentTz.tz(tzOffset).format('Z') | ||
: tzOffset; | ||
const connectionConfig = { | ||
tzOffset = /\//.test(tzOffset) ? momentTz.tz(tzOffset).format("Z") : tzOffset; | ||
const connectionConfig = __spreadValues({ | ||
host: config.host, | ||
@@ -71,11 +60,7 @@ port: config.port, | ||
supportBigNumbers: true, | ||
foundRows: false, | ||
...config.dialectOptions | ||
}; | ||
foundRows: false | ||
}, config.dialectOptions); | ||
if (!this.sequelize.config.keepDefaultTimezone) { | ||
// set timezone for this connection | ||
if (connectionConfig.initSql) { | ||
if (!Array.isArray( | ||
connectionConfig.initSql)) { | ||
if (!Array.isArray(connectionConfig.initSql)) { | ||
connectionConfig.initSql = [connectionConfig.initSql]; | ||
@@ -88,14 +73,12 @@ } | ||
} | ||
try { | ||
const connection = await this.lib.createConnection(connectionConfig); | ||
this.sequelize.options.databaseVersion = semver.coerce(connection.serverVersion()).version; | ||
debug('connection acquired'); | ||
connection.on('error', error => { | ||
debug("connection acquired"); | ||
connection.on("error", (error) => { | ||
switch (error.code) { | ||
case 'ESOCKET': | ||
case 'ECONNRESET': | ||
case 'EPIPE': | ||
case 'PROTOCOL_CONNECTION_LOST': | ||
case "ESOCKET": | ||
case "ECONNRESET": | ||
case "EPIPE": | ||
case "PROTOCOL_CONNECTION_LOST": | ||
this.pool.destroy(connection); | ||
@@ -107,14 +90,14 @@ } | ||
switch (err.code) { | ||
case 'ECONNREFUSED': | ||
case "ECONNREFUSED": | ||
throw new SequelizeErrors.ConnectionRefusedError(err); | ||
case 'ER_ACCESS_DENIED_ERROR': | ||
case 'ER_ACCESS_DENIED_NO_PASSWORD_ERROR': | ||
case "ER_ACCESS_DENIED_ERROR": | ||
case "ER_ACCESS_DENIED_NO_PASSWORD_ERROR": | ||
throw new SequelizeErrors.AccessDeniedError(err); | ||
case 'ENOTFOUND': | ||
case "ENOTFOUND": | ||
throw new SequelizeErrors.HostNotFoundError(err); | ||
case 'EHOSTUNREACH': | ||
case 'ENETUNREACH': | ||
case 'EADDRNOTAVAIL': | ||
case "EHOSTUNREACH": | ||
case "ENETUNREACH": | ||
case "EADDRNOTAVAIL": | ||
throw new SequelizeErrors.HostNotReachableError(err); | ||
case 'EINVAL': | ||
case "EINVAL": | ||
throw new SequelizeErrors.InvalidConnectionError(err); | ||
@@ -126,7 +109,5 @@ default: | ||
} | ||
async disconnect(connection) { | ||
// Don't disconnect connections with CLOSED state | ||
if (!connection.isValid()) { | ||
debug('connection tried to disconnect but was already at CLOSED state'); | ||
debug("connection tried to disconnect but was already at CLOSED state"); | ||
return; | ||
@@ -136,3 +117,2 @@ } | ||
} | ||
validate(connection) { | ||
@@ -142,5 +122,5 @@ return connection && connection.isValid(); | ||
} | ||
module.exports = ConnectionManager; | ||
module.exports.ConnectionManager = ConnectionManager; | ||
module.exports.default = ConnectionManager; | ||
//# sourceMappingURL=connection-manager.js.map |
@@ -1,39 +0,29 @@ | ||
'use strict'; | ||
const wkx = require('wkx'); | ||
const _ = require('lodash'); | ||
const moment = require('moment-timezone'); | ||
module.exports = BaseTypes => { | ||
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://mariadb.com/kb/en/library/resultset/#field-types'; | ||
/** | ||
* types: [buffer_type, ...] | ||
* | ||
* @see documentation : https://mariadb.com/kb/en/library/resultset/#field-types | ||
* @see connector implementation : https://github.com/MariaDB/mariadb-connector-nodejs/blob/master/lib/const/field-type.js | ||
*/ | ||
BaseTypes.DATE.types.mariadb = ['DATETIME']; | ||
BaseTypes.STRING.types.mariadb = ['VAR_STRING']; | ||
BaseTypes.CHAR.types.mariadb = ['STRING']; | ||
BaseTypes.TEXT.types.mariadb = ['BLOB']; | ||
BaseTypes.TINYINT.types.mariadb = ['TINY']; | ||
BaseTypes.SMALLINT.types.mariadb = ['SHORT']; | ||
BaseTypes.MEDIUMINT.types.mariadb = ['INT24']; | ||
BaseTypes.INTEGER.types.mariadb = ['LONG']; | ||
BaseTypes.BIGINT.types.mariadb = ['LONGLONG']; | ||
BaseTypes.FLOAT.types.mariadb = ['FLOAT']; | ||
BaseTypes.TIME.types.mariadb = ['TIME']; | ||
BaseTypes.DATEONLY.types.mariadb = ['DATE']; | ||
BaseTypes.BOOLEAN.types.mariadb = ['TINY']; | ||
BaseTypes.BLOB.types.mariadb = ['TINYBLOB', 'BLOB', 'LONGBLOB']; | ||
BaseTypes.DECIMAL.types.mariadb = ['NEWDECIMAL']; | ||
"use strict"; | ||
const wkx = require("wkx"); | ||
const _ = require("lodash"); | ||
const momentTz = require("moment-timezone"); | ||
const moment = require("moment"); | ||
module.exports = (BaseTypes) => { | ||
BaseTypes.ABSTRACT.prototype.dialectTypes = "https://mariadb.com/kb/en/library/resultset/#field-types"; | ||
BaseTypes.DATE.types.mariadb = ["DATETIME"]; | ||
BaseTypes.STRING.types.mariadb = ["VAR_STRING"]; | ||
BaseTypes.CHAR.types.mariadb = ["STRING"]; | ||
BaseTypes.TEXT.types.mariadb = ["BLOB"]; | ||
BaseTypes.TINYINT.types.mariadb = ["TINY"]; | ||
BaseTypes.SMALLINT.types.mariadb = ["SHORT"]; | ||
BaseTypes.MEDIUMINT.types.mariadb = ["INT24"]; | ||
BaseTypes.INTEGER.types.mariadb = ["LONG"]; | ||
BaseTypes.BIGINT.types.mariadb = ["LONGLONG"]; | ||
BaseTypes.FLOAT.types.mariadb = ["FLOAT"]; | ||
BaseTypes.TIME.types.mariadb = ["TIME"]; | ||
BaseTypes.DATEONLY.types.mariadb = ["DATE"]; | ||
BaseTypes.BOOLEAN.types.mariadb = ["TINY"]; | ||
BaseTypes.BLOB.types.mariadb = ["TINYBLOB", "BLOB", "LONGBLOB"]; | ||
BaseTypes.DECIMAL.types.mariadb = ["NEWDECIMAL"]; | ||
BaseTypes.UUID.types.mariadb = false; | ||
BaseTypes.ENUM.types.mariadb = false; | ||
BaseTypes.REAL.types.mariadb = ['DOUBLE']; | ||
BaseTypes.DOUBLE.types.mariadb = ['DOUBLE']; | ||
BaseTypes.GEOMETRY.types.mariadb = ['GEOMETRY']; | ||
BaseTypes.JSON.types.mariadb = ['JSON']; | ||
BaseTypes.REAL.types.mariadb = ["DOUBLE"]; | ||
BaseTypes.DOUBLE.types.mariadb = ["DOUBLE"]; | ||
BaseTypes.GEOMETRY.types.mariadb = ["GEOMETRY"]; | ||
BaseTypes.JSON.types.mariadb = ["JSON"]; | ||
class DECIMAL extends BaseTypes.DECIMAL { | ||
@@ -43,6 +33,6 @@ toSql() { | ||
if (this._unsigned) { | ||
definition += ' UNSIGNED'; | ||
definition += " UNSIGNED"; | ||
} | ||
if (this._zerofill) { | ||
definition += ' ZEROFILL'; | ||
definition += " ZEROFILL"; | ||
} | ||
@@ -52,10 +42,11 @@ return definition; | ||
} | ||
class DATE extends BaseTypes.DATE { | ||
toSql() { | ||
return this._length ? `DATETIME(${this._length})` : 'DATETIME'; | ||
return this._length ? `DATETIME(${this._length})` : "DATETIME"; | ||
} | ||
_stringify(date, options) { | ||
date = this._applyTimezone(date, options); | ||
return date.format('YYYY-MM-DD HH:mm:ss.SSS'); | ||
if (!moment.isMoment(date)) { | ||
date = this._applyTimezone(date, options); | ||
} | ||
return date.format("YYYY-MM-DD HH:mm:ss.SSS"); | ||
} | ||
@@ -67,6 +58,5 @@ static parse(value, options) { | ||
} | ||
if (moment.tz.zone(options.timezone)) { | ||
value = moment.tz(value, options.timezone).toDate(); | ||
} | ||
else { | ||
if (momentTz.tz.zone(options.timezone)) { | ||
value = momentTz.tz(value, options.timezone).toDate(); | ||
} else { | ||
value = new Date(`${value} ${options.timezone}`); | ||
@@ -77,3 +67,2 @@ } | ||
} | ||
class DATEONLY extends BaseTypes.DATEONLY { | ||
@@ -84,9 +73,7 @@ static parse(value) { | ||
} | ||
class UUID extends BaseTypes.UUID { | ||
toSql() { | ||
return 'CHAR(36) BINARY'; | ||
return "CHAR(36) BINARY"; | ||
} | ||
} | ||
class GEOMETRY extends BaseTypes.GEOMETRY { | ||
@@ -97,4 +84,3 @@ constructor(type, srid) { | ||
this.sqlType = this.key; | ||
} | ||
else { | ||
} else { | ||
this.sqlType = this.type; | ||
@@ -105,8 +91,5 @@ } | ||
value = value.buffer(); | ||
// Empty buffer, MySQL doesn't support POINT EMPTY | ||
// check, https://dev.mysql.com/worklog/task/?id=2381 | ||
if (!value || value.length === 0) { | ||
return null; | ||
} | ||
// For some reason, discard the first 4 bytes | ||
value = value.slice(4); | ||
@@ -119,16 +102,12 @@ return wkx.Geometry.parse(value).toGeoJSON({ shortCrs: true }); | ||
} | ||
class ENUM extends BaseTypes.ENUM { | ||
toSql(options) { | ||
return `ENUM(${this.values.map(value => options.escape(value)).join(', ')})`; | ||
return `ENUM(${this.values.map((value) => options.escape(value)).join(", ")})`; | ||
} | ||
} | ||
class JSONTYPE extends BaseTypes.JSON { | ||
_stringify(value, options) { | ||
return options.operation === 'where' && typeof value === 'string' ? value | ||
: JSON.stringify(value); | ||
return options.operation === "where" && typeof value === "string" ? value : JSON.stringify(value); | ||
} | ||
} | ||
return { | ||
@@ -144,1 +123,2 @@ ENUM, | ||
}; | ||
//# sourceMappingURL=data-types.js.map |
@@ -1,11 +0,9 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const AbstractDialect = require('../abstract'); | ||
const ConnectionManager = require('./connection-manager'); | ||
const Query = require('./query'); | ||
const QueryGenerator = require('./query-generator'); | ||
const { MySQLQueryInterface } = require('../mysql/query-interface'); | ||
const DataTypes = require('../../data-types').mariadb; | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const AbstractDialect = require("../abstract"); | ||
const ConnectionManager = require("./connection-manager"); | ||
const Query = require("./query"); | ||
const QueryGenerator = require("./query-generator"); | ||
const { MySQLQueryInterface } = require("../mysql/query-interface"); | ||
const DataTypes = require("../../data-types").mariadb; | ||
class MariadbDialect extends AbstractDialect { | ||
@@ -23,43 +21,40 @@ constructor(sequelize) { | ||
} | ||
MariadbDialect.prototype.supports = _.merge( | ||
_.cloneDeep(AbstractDialect.prototype.supports), { | ||
'VALUES ()': true, | ||
'LIMIT ON UPDATE': true, | ||
lock: true, | ||
forShare: 'LOCK IN SHARE MODE', | ||
settingIsolationLevelDuringTransaction: false, | ||
schemas: true, | ||
inserts: { | ||
ignoreDuplicates: ' IGNORE', | ||
updateOnDuplicate: ' ON DUPLICATE KEY UPDATE' | ||
}, | ||
index: { | ||
collate: false, | ||
length: true, | ||
parser: true, | ||
type: true, | ||
using: 1 | ||
}, | ||
constraints: { | ||
dropConstraint: false, | ||
check: false | ||
}, | ||
indexViaAlter: true, | ||
indexHints: true, | ||
NUMERIC: true, | ||
GEOMETRY: true, | ||
JSON: true, | ||
REGEXP: true | ||
}); | ||
MariadbDialect.prototype.defaultVersion = '10.1.44'; | ||
MariadbDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), { | ||
"VALUES ()": true, | ||
"LIMIT ON UPDATE": true, | ||
lock: true, | ||
forShare: "LOCK IN SHARE MODE", | ||
settingIsolationLevelDuringTransaction: false, | ||
schemas: true, | ||
inserts: { | ||
ignoreDuplicates: " IGNORE", | ||
updateOnDuplicate: " ON DUPLICATE KEY UPDATE" | ||
}, | ||
index: { | ||
collate: false, | ||
length: true, | ||
parser: true, | ||
type: true, | ||
using: 1 | ||
}, | ||
constraints: { | ||
dropConstraint: false, | ||
check: false | ||
}, | ||
indexViaAlter: true, | ||
indexHints: true, | ||
NUMERIC: true, | ||
GEOMETRY: true, | ||
JSON: true, | ||
REGEXP: true | ||
}); | ||
MariadbDialect.prototype.defaultVersion = "10.1.44"; | ||
MariadbDialect.prototype.Query = Query; | ||
MariadbDialect.prototype.QueryGenerator = QueryGenerator; | ||
MariadbDialect.prototype.DataTypes = DataTypes; | ||
MariadbDialect.prototype.name = 'mariadb'; | ||
MariadbDialect.prototype.TICK_CHAR = '`'; | ||
MariadbDialect.prototype.name = "mariadb"; | ||
MariadbDialect.prototype.TICK_CHAR = "`"; | ||
MariadbDialect.prototype.TICK_CHAR_LEFT = MariadbDialect.prototype.TICK_CHAR; | ||
MariadbDialect.prototype.TICK_CHAR_RIGHT = MariadbDialect.prototype.TICK_CHAR; | ||
module.exports = MariadbDialect; | ||
//# sourceMappingURL=index.js.map |
@@ -1,32 +0,42 @@ | ||
'use strict'; | ||
const MySQLQueryGenerator = require('../mysql/query-generator'); | ||
const Utils = require('./../../utils'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const MySQLQueryGenerator = require("../mysql/query-generator"); | ||
const Utils = require("./../../utils"); | ||
class MariaDBQueryGenerator extends MySQLQueryGenerator { | ||
createSchema(schema, options) { | ||
options = { | ||
options = __spreadValues({ | ||
charset: null, | ||
collate: null, | ||
...options | ||
}; | ||
collate: null | ||
}, options); | ||
return Utils.joinSQLFragments([ | ||
'CREATE SCHEMA IF NOT EXISTS', | ||
"CREATE SCHEMA IF NOT EXISTS", | ||
this.quoteIdentifier(schema), | ||
options.charset && `DEFAULT CHARACTER SET ${this.escape(options.charset)}`, | ||
options.collate && `DEFAULT COLLATE ${this.escape(options.collate)}`, | ||
';' | ||
";" | ||
]); | ||
} | ||
dropSchema(schema) { | ||
return `DROP SCHEMA IF EXISTS ${this.quoteIdentifier(schema)};`; | ||
} | ||
showSchemasQuery(options) { | ||
const schemasToSkip = [ | ||
'\'MYSQL\'', | ||
'\'INFORMATION_SCHEMA\'', | ||
'\'PERFORMANCE_SCHEMA\'' | ||
"'MYSQL'", | ||
"'INFORMATION_SCHEMA'", | ||
"'PERFORMANCE_SCHEMA'" | ||
]; | ||
@@ -39,20 +49,22 @@ if (options.skip && Array.isArray(options.skip) && options.skip.length > 0) { | ||
return Utils.joinSQLFragments([ | ||
'SELECT SCHEMA_NAME as schema_name', | ||
'FROM INFORMATION_SCHEMA.SCHEMATA', | ||
`WHERE SCHEMA_NAME NOT IN (${schemasToSkip.join(', ')})`, | ||
';' | ||
"SELECT SCHEMA_NAME as schema_name", | ||
"FROM INFORMATION_SCHEMA.SCHEMATA", | ||
`WHERE SCHEMA_NAME NOT IN (${schemasToSkip.join(", ")})`, | ||
";" | ||
]); | ||
} | ||
showTablesQuery(database) { | ||
let query = 'SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = \'BASE TABLE\''; | ||
let query = "SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'"; | ||
if (database) { | ||
query += ` AND TABLE_SCHEMA = ${this.escape(database)}`; | ||
} else { | ||
query += ' AND TABLE_SCHEMA NOT IN (\'MYSQL\', \'INFORMATION_SCHEMA\', \'PERFORMANCE_SCHEMA\')'; | ||
query += " AND TABLE_SCHEMA NOT IN ('MYSQL', 'INFORMATION_SCHEMA', 'PERFORMANCE_SCHEMA')"; | ||
} | ||
return `${query};`; | ||
} | ||
quoteIdentifier(identifier, force) { | ||
return Utils.addTicks(Utils.removeTicks(identifier, "`"), "`"); | ||
} | ||
} | ||
module.exports = MariaDBQueryGenerator; | ||
//# sourceMappingURL=query-generator.js.map |
@@ -1,9 +0,23 @@ | ||
'use strict'; | ||
const AbstractQuery = require('../abstract/query'); | ||
const sequelizeErrors = require('../../errors'); | ||
const _ = require('lodash'); | ||
const DataTypes = require('../../data-types'); | ||
const { logger } = require('../../utils/logger'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const AbstractQuery = require("../abstract/query"); | ||
const sequelizeErrors = require("../../errors"); | ||
const _ = require("lodash"); | ||
const DataTypes = require("../../data-types"); | ||
const { logger } = require("../../utils/logger"); | ||
const ER_DUP_ENTRY = 1062; | ||
@@ -13,37 +27,29 @@ const ER_DEADLOCK = 1213; | ||
const ER_NO_REFERENCED_ROW = 1452; | ||
const debug = logger.debugContext('sql:mariadb'); | ||
const debug = logger.debugContext("sql:mariadb"); | ||
class Query extends AbstractQuery { | ||
constructor(connection, sequelize, options) { | ||
super(connection, sequelize, { showWarnings: false, ...options }); | ||
super(connection, sequelize, __spreadValues({ showWarnings: false }, options)); | ||
} | ||
static formatBindParameters(sql, values, dialect) { | ||
const bindParam = []; | ||
const replacementFunc = (match, key, values_) => { | ||
if (values_[key] !== undefined) { | ||
if (values_[key] !== void 0) { | ||
bindParam.push(values_[key]); | ||
return '?'; | ||
return "?"; | ||
} | ||
return undefined; | ||
return void 0; | ||
}; | ||
sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0]; | ||
return [sql, bindParam.length > 0 ? bindParam : undefined]; | ||
return [sql, bindParam.length > 0 ? bindParam : void 0]; | ||
} | ||
async run(sql, parameters) { | ||
this.sql = sql; | ||
const { connection, options } = this; | ||
const showWarnings = this.sequelize.options.showWarnings || options.showWarnings; | ||
const complete = this._logQuery(sql, debug, parameters); | ||
if (parameters) { | ||
debug('parameters(%j)', parameters); | ||
debug("parameters(%j)", parameters); | ||
} | ||
let results; | ||
const errForStack = new Error(); | ||
try { | ||
@@ -53,21 +59,14 @@ results = await connection.query(this.sql, parameters); | ||
if (options.transaction && error.errno === ER_DEADLOCK) { | ||
// MariaDB automatically rolls-back transactions in the event of a deadlock. | ||
// However, we still initiate a manual rollback to ensure the connection gets released - see #13102. | ||
try { | ||
await options.transaction.rollback(); | ||
} catch (error_) { | ||
// Ignore errors - since MariaDB automatically rolled back, we're | ||
// not that worried about this redundant rollback failing. | ||
} | ||
options.transaction.finished = 'rollback'; | ||
options.transaction.finished = "rollback"; | ||
} | ||
error.sql = sql; | ||
error.parameters = parameters; | ||
throw this.formatError(error); | ||
throw this.formatError(error, errForStack.stack); | ||
} finally { | ||
complete(); | ||
} | ||
if (showWarnings && results && results.warningStatus > 0) { | ||
@@ -78,23 +77,4 @@ await this.logWarnings(results); | ||
} | ||
/** | ||
* High level function that handles the results of a query execution. | ||
* | ||
* | ||
* Example: | ||
* query.formatResults([ | ||
* { | ||
* id: 1, // this is from the main table | ||
* attr2: 'snafu', // this is from the main table | ||
* Tasks.id: 1, // this is from the associated table | ||
* Tasks.title: 'task' // this is from the associated table | ||
* } | ||
* ]) | ||
* | ||
* @param {Array} data - The result of the query execution. | ||
* @private | ||
*/ | ||
formatResults(data) { | ||
let result = this.instance; | ||
if (this.isBulkUpdateQuery() || this.isBulkDeleteQuery()) { | ||
@@ -108,13 +88,4 @@ return data.affectedRows; | ||
this.handleInsertQuery(data); | ||
if (!this.instance) { | ||
// handle bulkCreate AI primary key | ||
if ( | ||
this.model | ||
&& this.model.autoIncrementAttribute | ||
&& this.model.autoIncrementAttribute === this.model.primaryKeyAttribute | ||
&& this.model.rawAttributes[this.model.primaryKeyAttribute] | ||
) { | ||
// ONLY TRUE IF @auto_increment_increment is set to 1 !! | ||
// Doesn't work with GALERA => each node will reserve increment (x for first server, x+1 for next node...) | ||
if (this.model && this.model.autoIncrementAttribute && this.model.autoIncrementAttribute === this.model.primaryKeyAttribute && this.model.rawAttributes[this.model.primaryKeyAttribute]) { | ||
const startId = data[this.getInsertIdField()]; | ||
@@ -128,7 +99,5 @@ result = new Array(data.affectedRows); | ||
} | ||
return [data[this.getInsertIdField()], data.affectedRows]; | ||
} | ||
} | ||
if (this.isSelectQuery()) { | ||
@@ -160,12 +129,9 @@ this.handleJsonSelectQuery(data); | ||
result = {}; | ||
for (const _result of data) { | ||
result[_result.Field] = { | ||
type: _result.Type.toLowerCase().startsWith('enum') ? _result.Type.replace(/^enum/i, | ||
'ENUM') : _result.Type.toUpperCase(), | ||
allowNull: _result.Null === 'YES', | ||
type: _result.Type.toLowerCase().startsWith("enum") ? _result.Type.replace(/^enum/i, "ENUM") : _result.Type.toUpperCase(), | ||
allowNull: _result.Null === "YES", | ||
defaultValue: _result.Default, | ||
primaryKey: _result.Key === 'PRI', | ||
autoIncrement: Object.prototype.hasOwnProperty.call(_result, 'Extra') | ||
&& _result.Extra.toLowerCase() === 'auto_increment', | ||
primaryKey: _result.Key === "PRI", | ||
autoIncrement: Object.prototype.hasOwnProperty.call(_result, "Extra") && _result.Extra.toLowerCase() === "auto_increment", | ||
comment: _result.Comment ? _result.Comment : null | ||
@@ -179,6 +145,4 @@ }; | ||
} | ||
return result; | ||
} | ||
handleJsonSelectQuery(rows) { | ||
@@ -191,9 +155,8 @@ if (!this.model || !this.model.fieldRawAttributesMap) { | ||
if (modelField.type instanceof DataTypes.JSON) { | ||
// Value is returned as String, not JSON | ||
rows = rows.map(row => { | ||
row[modelField.fieldName] = row[modelField.fieldName] ? JSON.parse( | ||
row[modelField.fieldName]) : null; | ||
rows = rows.map((row) => { | ||
if (row[modelField.fieldName] && typeof row[modelField.fieldName] === "string") { | ||
row[modelField.fieldName] = JSON.parse(row[modelField.fieldName]); | ||
} | ||
if (DataTypes.JSON.parse) { | ||
return DataTypes.JSON.parse(modelField, this.sequelize.options, | ||
row[modelField.fieldName]); | ||
return DataTypes.JSON.parse(modelField, this.sequelize.options, row[modelField.fieldName]); | ||
} | ||
@@ -205,17 +168,16 @@ return row; | ||
} | ||
async logWarnings(results) { | ||
const warningResults = await this.run('SHOW WARNINGS'); | ||
const warningMessage = `MariaDB Warnings (${this.connection.uuid || 'default'}): `; | ||
const warningResults = await this.run("SHOW WARNINGS"); | ||
const warningMessage = `MariaDB Warnings (${this.connection.uuid || "default"}): `; | ||
const messages = []; | ||
for (const _warningRow of warningResults) { | ||
if (_warningRow === undefined || typeof _warningRow[Symbol.iterator] !== 'function') { | ||
if (_warningRow === void 0 || typeof _warningRow[Symbol.iterator] !== "function") { | ||
continue; | ||
} | ||
for (const _warningResult of _warningRow) { | ||
if (Object.prototype.hasOwnProperty.call(_warningResult, 'Message')) { | ||
if (Object.prototype.hasOwnProperty.call(_warningResult, "Message")) { | ||
messages.push(_warningResult.Message); | ||
} else { | ||
for (const _objectKey of _warningResult.keys()) { | ||
messages.push([_objectKey, _warningResult[_objectKey]].join(': ')); | ||
messages.push([_objectKey, _warningResult[_objectKey]].join(": ")); | ||
} | ||
@@ -225,23 +187,18 @@ } | ||
} | ||
this.sequelize.log(warningMessage + messages.join('; '), this.options); | ||
this.sequelize.log(warningMessage + messages.join("; "), this.options); | ||
return results; | ||
} | ||
formatError(err) { | ||
formatError(err, errStack) { | ||
switch (err.errno) { | ||
case ER_DUP_ENTRY: { | ||
const match = err.message.match( | ||
/Duplicate entry '([\s\S]*)' for key '?((.|\s)*?)'?\s.*$/); | ||
const match = err.message.match(/Duplicate entry '([\s\S]*)' for key '?((.|\s)*?)'?\s.*$/); | ||
let fields = {}; | ||
let message = 'Validation error'; | ||
const values = match ? match[1].split('-') : undefined; | ||
const fieldKey = match ? match[2] : undefined; | ||
const fieldVal = match ? match[1] : undefined; | ||
let message = "Validation error"; | ||
const values = match ? match[1].split("-") : void 0; | ||
const fieldKey = match ? match[2] : void 0; | ||
const fieldVal = match ? match[1] : void 0; | ||
const uniqueKey = this.model && this.model.uniqueKeys[fieldKey]; | ||
if (uniqueKey) { | ||
if (uniqueKey.msg) message = uniqueKey.msg; | ||
if (uniqueKey.msg) | ||
message = uniqueKey.msg; | ||
fields = _.zipObject(uniqueKey.fields, values); | ||
@@ -251,44 +208,29 @@ } else { | ||
} | ||
const errors = []; | ||
_.forOwn(fields, (value, field) => { | ||
errors.push(new sequelizeErrors.ValidationErrorItem( | ||
this.getUniqueConstraintErrorMessage(field), | ||
'unique violation', // sequelizeErrors.ValidationErrorItem.Origins.DB, | ||
field, | ||
value, | ||
this.instance, | ||
'not_unique' | ||
)); | ||
errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, value, this.instance, "not_unique")); | ||
}); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields }); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack }); | ||
} | ||
case ER_ROW_IS_REFERENCED: | ||
case ER_NO_REFERENCED_ROW: { | ||
// e.g. CONSTRAINT `example_constraint_name` FOREIGN KEY (`example_id`) REFERENCES `examples` (`id`) | ||
const match = err.message.match( | ||
/CONSTRAINT ([`"])(.*)\1 FOREIGN KEY \(\1(.*)\1\) REFERENCES \1(.*)\1 \(\1(.*)\1\)/ | ||
); | ||
const quoteChar = match ? match[1] : '`'; | ||
const fields = match ? match[3].split(new RegExp(`${quoteChar}, *${quoteChar}`)) : undefined; | ||
const match = err.message.match(/CONSTRAINT ([`"])(.*)\1 FOREIGN KEY \(\1(.*)\1\) REFERENCES \1(.*)\1 \(\1(.*)\1\)/); | ||
const quoteChar = match ? match[1] : "`"; | ||
const fields = match ? match[3].split(new RegExp(`${quoteChar}, *${quoteChar}`)) : void 0; | ||
return new sequelizeErrors.ForeignKeyConstraintError({ | ||
reltype: err.errno === ER_ROW_IS_REFERENCED ? 'parent' : 'child', | ||
table: match ? match[4] : undefined, | ||
reltype: err.errno === ER_ROW_IS_REFERENCED ? "parent" : "child", | ||
table: match ? match[4] : void 0, | ||
fields, | ||
value: fields && fields.length && this.instance && this.instance[fields[0]] || undefined, | ||
index: match ? match[2] : undefined, | ||
parent: err | ||
value: fields && fields.length && this.instance && this.instance[fields[0]] || void 0, | ||
index: match ? match[2] : void 0, | ||
parent: err, | ||
stack: errStack | ||
}); | ||
} | ||
default: | ||
return new sequelizeErrors.DatabaseError(err); | ||
return new sequelizeErrors.DatabaseError(err, { stack: errStack }); | ||
} | ||
} | ||
handleShowTablesQuery(results) { | ||
return results.map(resultSet => ({ | ||
return results.map((resultSet) => ({ | ||
tableName: resultSet.TABLE_NAME, | ||
@@ -298,12 +240,9 @@ schema: resultSet.TABLE_SCHEMA | ||
} | ||
handleShowIndexesQuery(data) { | ||
let currItem; | ||
const result = []; | ||
data.forEach(item => { | ||
data.forEach((item) => { | ||
if (!currItem || currItem.name !== item.Key_name) { | ||
currItem = { | ||
primary: item.Key_name === 'PRIMARY', | ||
primary: item.Key_name === "PRIMARY", | ||
fields: [], | ||
@@ -317,14 +256,12 @@ name: item.Key_name, | ||
} | ||
currItem.fields[item.Seq_in_index - 1] = { | ||
attribute: item.Column_name, | ||
length: item.Sub_part || undefined, | ||
order: item.Collation === 'A' ? 'ASC' : undefined | ||
length: item.Sub_part || void 0, | ||
order: item.Collation === "A" ? "ASC" : void 0 | ||
}; | ||
}); | ||
return result; | ||
} | ||
} | ||
module.exports = Query; | ||
//# sourceMappingURL=query.js.map |
@@ -1,46 +0,68 @@ | ||
'use strict'; | ||
const BaseError = require('../../errors/base-error'); | ||
const ConnectionError = require('../../errors/connection-error'); | ||
/** | ||
* Thrown when a connection to a database is closed while an operation is in progress | ||
*/ | ||
class AsyncQueueError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
AsyncQueueError: () => AsyncQueueError, | ||
default: () => async_queue_default | ||
}); | ||
var import_base_error = __toModule(require("../../errors/base-error")); | ||
var import_connection_error = __toModule(require("../../errors/connection-error")); | ||
class AsyncQueueError extends import_base_error.default { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'SequelizeAsyncQueueError'; | ||
this.name = "SequelizeAsyncQueueError"; | ||
} | ||
} | ||
exports.AsyncQueueError = AsyncQueueError; | ||
class AsyncQueue { | ||
constructor() { | ||
__publicField(this, "previous"); | ||
__publicField(this, "closed"); | ||
__publicField(this, "rejectCurrent"); | ||
this.previous = Promise.resolve(); | ||
this.closed = false; | ||
this.rejectCurrent = () => {}; | ||
this.rejectCurrent = () => { | ||
}; | ||
} | ||
close() { | ||
this.closed = true; | ||
this.rejectCurrent(new ConnectionError(new AsyncQueueError('the connection was closed before this query could finish executing'))); | ||
this.rejectCurrent(new import_connection_error.default(new AsyncQueueError("the connection was closed before this query could finish executing"))); | ||
} | ||
enqueue(asyncFunction) { | ||
// This outer promise might seems superflous since down below we return asyncFunction().then(resolve, reject). | ||
// However, this ensures that this.previous will never be a rejected promise so the queue will | ||
// always keep going, while still communicating rejection from asyncFunction to the user. | ||
return new Promise((resolve, reject) => { | ||
this.previous = this.previous.then( | ||
() => { | ||
this.rejectCurrent = reject; | ||
if (this.closed) { | ||
return reject(new ConnectionError(new AsyncQueueError('the connection was closed before this query could be executed'))); | ||
} | ||
return asyncFunction().then(resolve, reject); | ||
this.previous = this.previous.then(() => { | ||
this.rejectCurrent = reject; | ||
if (this.closed) { | ||
return reject(new import_connection_error.default(new AsyncQueueError("the connection was closed before this query could be executed"))); | ||
} | ||
); | ||
return asyncFunction().then(resolve, reject); | ||
}); | ||
}); | ||
} | ||
} | ||
exports.default = AsyncQueue; | ||
var async_queue_default = AsyncQueue; | ||
//# sourceMappingURL=async-queue.js.map |
@@ -1,12 +0,10 @@ | ||
'use strict'; | ||
const AbstractConnectionManager = require('../abstract/connection-manager'); | ||
const AsyncQueue = require('./async-queue').default; | ||
const { logger } = require('../../utils/logger'); | ||
const sequelizeErrors = require('../../errors'); | ||
const DataTypes = require('../../data-types').mssql; | ||
const parserStore = require('../parserStore')('mssql'); | ||
const debug = logger.debugContext('connection:mssql'); | ||
const debugTedious = logger.debugContext('connection:mssql:tedious'); | ||
"use strict"; | ||
const AbstractConnectionManager = require("../abstract/connection-manager"); | ||
const AsyncQueue = require("./async-queue").default; | ||
const { logger } = require("../../utils/logger"); | ||
const sequelizeErrors = require("../../errors"); | ||
const DataTypes = require("../../data-types").mssql; | ||
const parserStore = require("../parserStore")("mssql"); | ||
const debug = logger.debugContext("connection:mssql"); | ||
const debugTedious = logger.debugContext("connection:mssql:tedious"); | ||
class ConnectionManager extends AbstractConnectionManager { | ||
@@ -16,14 +14,11 @@ constructor(dialect, sequelize) { | ||
super(dialect, sequelize); | ||
this.lib = this._loadDialectModule('tedious'); | ||
this.lib = this._loadDialectModule("tedious"); | ||
this.refreshTypeParser(DataTypes); | ||
} | ||
_refreshTypeParser(dataType) { | ||
parserStore.refresh(dataType); | ||
} | ||
_clearTypeParser() { | ||
parserStore.clear(); | ||
} | ||
async connect(config) { | ||
@@ -33,6 +28,6 @@ const connectionConfig = { | ||
authentication: { | ||
type: 'default', | ||
type: "default", | ||
options: { | ||
userName: config.username || undefined, | ||
password: config.password || undefined | ||
userName: config.username || void 0, | ||
password: config.password || void 0 | ||
} | ||
@@ -46,19 +41,11 @@ }, | ||
}; | ||
if (config.dialectOptions) { | ||
// only set port if no instance name was provided | ||
if ( | ||
config.dialectOptions.options && | ||
config.dialectOptions.options.instanceName | ||
) { | ||
if (config.dialectOptions.options && config.dialectOptions.options.instanceName) { | ||
delete connectionConfig.options.port; | ||
} | ||
if (config.dialectOptions.authentication) { | ||
Object.assign(connectionConfig.authentication, config.dialectOptions.authentication); | ||
} | ||
Object.assign(connectionConfig.options, config.dialectOptions.options); | ||
} | ||
try { | ||
@@ -72,47 +59,32 @@ return await new Promise((resolve, reject) => { | ||
connection.lib = this.lib; | ||
const connectHandler = error => { | ||
connection.removeListener('end', endHandler); | ||
connection.removeListener('error', errorHandler); | ||
if (error) return reject(error); | ||
debug('connection acquired'); | ||
const connectHandler = (error) => { | ||
connection.removeListener("end", endHandler); | ||
connection.removeListener("error", errorHandler); | ||
if (error) | ||
return reject(error); | ||
debug("connection acquired"); | ||
resolve(connection); | ||
}; | ||
const endHandler = () => { | ||
connection.removeListener('connect', connectHandler); | ||
connection.removeListener('error', errorHandler); | ||
reject(new Error('Connection was closed by remote server')); | ||
connection.removeListener("connect", connectHandler); | ||
connection.removeListener("error", errorHandler); | ||
reject(new Error("Connection was closed by remote server")); | ||
}; | ||
const errorHandler = error => { | ||
connection.removeListener('connect', connectHandler); | ||
connection.removeListener('end', endHandler); | ||
const errorHandler = (error) => { | ||
connection.removeListener("connect", connectHandler); | ||
connection.removeListener("end", endHandler); | ||
reject(error); | ||
}; | ||
connection.once('error', errorHandler); | ||
connection.once('end', endHandler); | ||
connection.once('connect', connectHandler); | ||
/* | ||
* Permanently attach this event before connection is even acquired | ||
* tedious sometime emits error even after connect(with error). | ||
* | ||
* If we dont attach this even that unexpected error event will crash node process | ||
* | ||
* E.g. connectTimeout is set higher than requestTimeout | ||
*/ | ||
connection.on('error', error => { | ||
connection.once("error", errorHandler); | ||
connection.once("end", endHandler); | ||
connection.once("connect", connectHandler); | ||
connection.on("error", (error) => { | ||
switch (error.code) { | ||
case 'ESOCKET': | ||
case 'ECONNRESET': | ||
case "ESOCKET": | ||
case "ECONNRESET": | ||
this.pool.destroy(connection); | ||
} | ||
}); | ||
if (config.dialectOptions && config.dialectOptions.debug) { | ||
connection.on('debug', debugTedious.log.bind(debugTedious)); | ||
connection.on("debug", debugTedious.log.bind(debugTedious)); | ||
} | ||
@@ -124,25 +96,24 @@ }); | ||
} | ||
switch (error.code) { | ||
case 'ESOCKET': | ||
if (error.message.includes('connect EHOSTUNREACH')) { | ||
case "ESOCKET": | ||
if (error.message.includes("connect EHOSTUNREACH")) { | ||
throw new sequelizeErrors.HostNotReachableError(error); | ||
} | ||
if (error.message.includes('connect ENETUNREACH')) { | ||
if (error.message.includes("connect ENETUNREACH")) { | ||
throw new sequelizeErrors.HostNotReachableError(error); | ||
} | ||
if (error.message.includes('connect EADDRNOTAVAIL')) { | ||
if (error.message.includes("connect EADDRNOTAVAIL")) { | ||
throw new sequelizeErrors.HostNotReachableError(error); | ||
} | ||
if (error.message.includes('getaddrinfo ENOTFOUND')) { | ||
if (error.message.includes("getaddrinfo ENOTFOUND")) { | ||
throw new sequelizeErrors.HostNotFoundError(error); | ||
} | ||
if (error.message.includes('connect ECONNREFUSED')) { | ||
if (error.message.includes("connect ECONNREFUSED")) { | ||
throw new sequelizeErrors.ConnectionRefusedError(error); | ||
} | ||
throw new sequelizeErrors.ConnectionError(error); | ||
case 'ER_ACCESS_DENIED_ERROR': | ||
case 'ELOGIN': | ||
case "ER_ACCESS_DENIED_ERROR": | ||
case "ELOGIN": | ||
throw new sequelizeErrors.AccessDeniedError(error); | ||
case 'EINVAL': | ||
case "EINVAL": | ||
throw new sequelizeErrors.InvalidConnectionError(error); | ||
@@ -154,25 +125,20 @@ default: | ||
} | ||
async disconnect(connection) { | ||
// Don't disconnect a connection that is already disconnected | ||
if (connection.closed) { | ||
return; | ||
} | ||
connection.queue.close(); | ||
return new Promise(resolve => { | ||
connection.on('end', resolve); | ||
return new Promise((resolve) => { | ||
connection.on("end", resolve); | ||
connection.close(); | ||
debug('connection closed'); | ||
debug("connection closed"); | ||
}); | ||
} | ||
validate(connection) { | ||
return connection && connection.loggedIn; | ||
return connection && (connection.loggedIn || connection.state.name === "LoggedIn"); | ||
} | ||
} | ||
module.exports = ConnectionManager; | ||
module.exports.ConnectionManager = ConnectionManager; | ||
module.exports.default = ConnectionManager; | ||
//# sourceMappingURL=connection-manager.js.map |
@@ -1,30 +0,14 @@ | ||
'use strict'; | ||
const moment = require('moment'); | ||
module.exports = BaseTypes => { | ||
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://msdn.microsoft.com/en-us/library/ms187752%28v=sql.110%29.aspx'); | ||
/** | ||
* Removes unsupported MSSQL options, i.e., LENGTH, UNSIGNED and ZEROFILL, for the integer data types. | ||
* | ||
* @param {object} dataType The base integer data type. | ||
* @private | ||
*/ | ||
"use strict"; | ||
const moment = require("moment"); | ||
module.exports = (BaseTypes) => { | ||
const warn = BaseTypes.ABSTRACT.warn.bind(void 0, "https://msdn.microsoft.com/en-us/library/ms187752%28v=sql.110%29.aspx"); | ||
function removeUnsupportedIntegerOptions(dataType) { | ||
if (dataType._length || dataType.options.length || dataType._unsigned || dataType._zerofill) { | ||
warn(`MSSQL does not support '${dataType.key}' with options. Plain '${dataType.key}' will be used instead.`); | ||
dataType._length = undefined; | ||
dataType.options.length = undefined; | ||
dataType._unsigned = undefined; | ||
dataType._zerofill = undefined; | ||
dataType._length = void 0; | ||
dataType.options.length = void 0; | ||
dataType._unsigned = void 0; | ||
dataType._zerofill = void 0; | ||
} | ||
} | ||
/** | ||
* types: [hex, ...] | ||
* | ||
* @see hex here https://github.com/tediousjs/tedious/blob/master/src/data-type.ts | ||
*/ | ||
BaseTypes.DATE.types.mssql = [43]; | ||
@@ -34,3 +18,2 @@ BaseTypes.STRING.types.mssql = [231, 173]; | ||
BaseTypes.TEXT.types.mssql = false; | ||
// https://msdn.microsoft.com/en-us/library/ms187745(v=sql.110).aspx | ||
BaseTypes.TINYINT.types.mssql = [30]; | ||
@@ -51,15 +34,13 @@ BaseTypes.SMALLINT.types.mssql = [34]; | ||
BaseTypes.DOUBLE.types.mssql = [109]; | ||
// BaseTypes.GEOMETRY.types.mssql = [240]; // not yet supported | ||
BaseTypes.GEOMETRY.types.mssql = false; | ||
class BLOB extends BaseTypes.BLOB { | ||
toSql() { | ||
if (this._length) { | ||
if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8 | ||
warn('MSSQL does not support BLOB with the `length` = `tiny` option. `VARBINARY(256)` will be used instead.'); | ||
return 'VARBINARY(256)'; | ||
if (this._length.toLowerCase() === "tiny") { | ||
warn("MSSQL does not support BLOB with the `length` = `tiny` option. `VARBINARY(256)` will be used instead."); | ||
return "VARBINARY(256)"; | ||
} | ||
warn('MSSQL does not support BLOB with the `length` option. `VARBINARY(MAX)` will be used instead.'); | ||
warn("MSSQL does not support BLOB with the `length` option. `VARBINARY(MAX)` will be used instead."); | ||
} | ||
return 'VARBINARY(MAX)'; | ||
return "VARBINARY(MAX)"; | ||
} | ||
@@ -70,4 +51,2 @@ _hexify(hex) { | ||
} | ||
class STRING extends BaseTypes.STRING { | ||
@@ -90,50 +69,40 @@ toSql() { | ||
} | ||
STRING.prototype.escape = false; | ||
class TEXT extends BaseTypes.TEXT { | ||
toSql() { | ||
// TEXT is deprecated in mssql and it would normally be saved as a non-unicode string. | ||
// Using unicode is just future proof | ||
if (this._length) { | ||
if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8 | ||
warn('MSSQL does not support TEXT with the `length` = `tiny` option. `NVARCHAR(256)` will be used instead.'); | ||
return 'NVARCHAR(256)'; | ||
if (this._length.toLowerCase() === "tiny") { | ||
warn("MSSQL does not support TEXT with the `length` = `tiny` option. `NVARCHAR(256)` will be used instead."); | ||
return "NVARCHAR(256)"; | ||
} | ||
warn('MSSQL does not support TEXT with the `length` option. `NVARCHAR(MAX)` will be used instead.'); | ||
warn("MSSQL does not support TEXT with the `length` option. `NVARCHAR(MAX)` will be used instead."); | ||
} | ||
return 'NVARCHAR(MAX)'; | ||
return "NVARCHAR(MAX)"; | ||
} | ||
} | ||
class BOOLEAN extends BaseTypes.BOOLEAN { | ||
toSql() { | ||
return 'BIT'; | ||
return "BIT"; | ||
} | ||
} | ||
class UUID extends BaseTypes.UUID { | ||
toSql() { | ||
return 'CHAR(36)'; | ||
return "CHAR(36)"; | ||
} | ||
} | ||
class NOW extends BaseTypes.NOW { | ||
toSql() { | ||
return 'GETDATE()'; | ||
return "GETDATE()"; | ||
} | ||
} | ||
class DATE extends BaseTypes.DATE { | ||
toSql() { | ||
return 'DATETIMEOFFSET'; | ||
return "DATETIMEOFFSET"; | ||
} | ||
} | ||
class DATEONLY extends BaseTypes.DATEONLY { | ||
static parse(value) { | ||
return moment(value).format('YYYY-MM-DD'); | ||
return moment(value).format("YYYY-MM-DD"); | ||
} | ||
} | ||
class INTEGER extends BaseTypes.INTEGER { | ||
@@ -166,9 +135,8 @@ constructor(length) { | ||
super(length, decimals); | ||
// MSSQL does not support any options for real | ||
if (this._length || this.options.length || this._unsigned || this._zerofill) { | ||
warn('MSSQL does not support REAL with options. Plain `REAL` will be used instead.'); | ||
this._length = undefined; | ||
this.options.length = undefined; | ||
this._unsigned = undefined; | ||
this._zerofill = undefined; | ||
warn("MSSQL does not support REAL with options. Plain `REAL` will be used instead."); | ||
this._length = void 0; | ||
this.options.length = void 0; | ||
this._unsigned = void 0; | ||
this._zerofill = void 0; | ||
} | ||
@@ -180,18 +148,14 @@ } | ||
super(length, decimals); | ||
// MSSQL does only support lengths as option. | ||
// Values between 1-24 result in 7 digits precision (4 bytes storage size) | ||
// Values between 25-53 result in 15 digits precision (8 bytes storage size) | ||
// If decimals are provided remove these and print a warning | ||
if (this._decimals) { | ||
warn('MSSQL does not support Float with decimals. Plain `FLOAT` will be used instead.'); | ||
this._length = undefined; | ||
this.options.length = undefined; | ||
warn("MSSQL does not support Float with decimals. Plain `FLOAT` will be used instead."); | ||
this._length = void 0; | ||
this.options.length = void 0; | ||
} | ||
if (this._unsigned) { | ||
warn('MSSQL does not support Float unsigned. `UNSIGNED` was removed.'); | ||
this._unsigned = undefined; | ||
warn("MSSQL does not support Float unsigned. `UNSIGNED` was removed."); | ||
this._unsigned = void 0; | ||
} | ||
if (this._zerofill) { | ||
warn('MSSQL does not support Float zerofill. `ZEROFILL` was removed.'); | ||
this._zerofill = undefined; | ||
warn("MSSQL does not support Float zerofill. `ZEROFILL` was removed."); | ||
this._zerofill = void 0; | ||
} | ||
@@ -202,6 +166,5 @@ } | ||
toSql() { | ||
return 'VARCHAR(255)'; | ||
return "VARCHAR(255)"; | ||
} | ||
} | ||
return { | ||
@@ -225,1 +188,2 @@ BLOB, | ||
}; | ||
//# sourceMappingURL=data-types.js.map |
@@ -1,11 +0,9 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const AbstractDialect = require('../abstract'); | ||
const ConnectionManager = require('./connection-manager'); | ||
const Query = require('./query'); | ||
const QueryGenerator = require('./query-generator'); | ||
const DataTypes = require('../../data-types').mssql; | ||
const { MSSqlQueryInterface } = require('./query-interface'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const AbstractDialect = require("../abstract"); | ||
const ConnectionManager = require("./connection-manager"); | ||
const Query = require("./query"); | ||
const QueryGenerator = require("./query-generator"); | ||
const DataTypes = require("../../data-types").mssql; | ||
const { MSSqlQueryInterface } = require("./query-interface"); | ||
class MssqlDialect extends AbstractDialect { | ||
@@ -23,8 +21,7 @@ constructor(sequelize) { | ||
} | ||
MssqlDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), { | ||
'DEFAULT': true, | ||
'DEFAULT VALUES': true, | ||
'LIMIT ON UPDATE': true, | ||
'ORDER NULLS': false, | ||
DEFAULT: true, | ||
"DEFAULT VALUES": true, | ||
"LIMIT ON UPDATE": true, | ||
"ORDER NULLS": false, | ||
lock: false, | ||
@@ -57,11 +54,10 @@ transactions: true, | ||
}); | ||
MssqlDialect.prototype.defaultVersion = '12.0.2000'; // SQL Server 2014 Express | ||
MssqlDialect.prototype.defaultVersion = "12.0.2000"; | ||
MssqlDialect.prototype.Query = Query; | ||
MssqlDialect.prototype.name = 'mssql'; | ||
MssqlDialect.prototype.name = "mssql"; | ||
MssqlDialect.prototype.TICK_CHAR = '"'; | ||
MssqlDialect.prototype.TICK_CHAR_LEFT = '['; | ||
MssqlDialect.prototype.TICK_CHAR_RIGHT = ']'; | ||
MssqlDialect.prototype.TICK_CHAR_LEFT = "["; | ||
MssqlDialect.prototype.TICK_CHAR_RIGHT = "]"; | ||
MssqlDialect.prototype.DataTypes = DataTypes; | ||
module.exports = MssqlDialect; | ||
//# sourceMappingURL=index.js.map |
@@ -1,89 +0,108 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Utils = require('../../utils'); | ||
const DataTypes = require('../../data-types'); | ||
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 */ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const _ = require("lodash"); | ||
const Utils = require("../../utils"); | ||
const DataTypes = require("../../data-types"); | ||
const TableHints = require("../../table-hints"); | ||
const AbstractQueryGenerator = require("../abstract/query-generator"); | ||
const randomBytes = require("crypto").randomBytes; | ||
const semver = require("semver"); | ||
const Op = require("../../operators"); | ||
const throwMethodUndefined = function(methodName) { | ||
throw new Error(`The method "${methodName}" is not defined! Please add it to your sql dialect.`); | ||
}; | ||
class MSSQLQueryGenerator extends AbstractQueryGenerator { | ||
createDatabaseQuery(databaseName, options) { | ||
options = { collate: null, ...options }; | ||
const collation = options.collate ? `COLLATE ${this.escape(options.collate)}` : ''; | ||
options = __spreadValues({ collate: null }, options); | ||
const collation = options.collate ? `COLLATE ${this.escape(options.collate)}` : ""; | ||
return [ | ||
'IF NOT EXISTS (SELECT * FROM sys.databases WHERE name =', wrapSingleQuote(databaseName), ')', | ||
'BEGIN', | ||
'CREATE DATABASE', this.quoteIdentifier(databaseName), | ||
"IF NOT EXISTS (SELECT * FROM sys.databases WHERE name =", | ||
wrapSingleQuote(databaseName), | ||
")", | ||
"BEGIN", | ||
"CREATE DATABASE", | ||
this.quoteIdentifier(databaseName), | ||
`${collation};`, | ||
'END;' | ||
].join(' '); | ||
"END;" | ||
].join(" "); | ||
} | ||
dropDatabaseQuery(databaseName) { | ||
return [ | ||
'IF EXISTS (SELECT * FROM sys.databases WHERE name =', wrapSingleQuote(databaseName), ')', | ||
'BEGIN', | ||
'DROP DATABASE', this.quoteIdentifier(databaseName), ';', | ||
'END;' | ||
].join(' '); | ||
"IF EXISTS (SELECT * FROM sys.databases WHERE name =", | ||
wrapSingleQuote(databaseName), | ||
")", | ||
"BEGIN", | ||
"DROP DATABASE", | ||
this.quoteIdentifier(databaseName), | ||
";", | ||
"END;" | ||
].join(" "); | ||
} | ||
createSchema(schema) { | ||
return [ | ||
'IF NOT EXISTS (SELECT schema_name', | ||
'FROM information_schema.schemata', | ||
'WHERE schema_name =', wrapSingleQuote(schema), ')', | ||
'BEGIN', | ||
"IF NOT EXISTS (SELECT schema_name", | ||
"FROM information_schema.schemata", | ||
"WHERE schema_name =", | ||
wrapSingleQuote(schema), | ||
")", | ||
"BEGIN", | ||
"EXEC sp_executesql N'CREATE SCHEMA", | ||
this.quoteIdentifier(schema), | ||
";'", | ||
'END;' | ||
].join(' '); | ||
"END;" | ||
].join(" "); | ||
} | ||
dropSchema(schema) { | ||
// Mimics Postgres CASCADE, will drop objects belonging to the schema | ||
const quotedSchema = wrapSingleQuote(schema); | ||
return [ | ||
'IF EXISTS (SELECT schema_name', | ||
'FROM information_schema.schemata', | ||
'WHERE schema_name =', quotedSchema, ')', | ||
'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 )', | ||
"IF EXISTS (SELECT schema_name", | ||
"FROM information_schema.schemata", | ||
"WHERE schema_name =", | ||
quotedSchema, | ||
")", | ||
"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 = ", quotedSchema, | ||
'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", this.quoteIdentifier(schema), ";'", | ||
'END;' | ||
].join(' '); | ||
"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 = ", | ||
quotedSchema, | ||
"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", | ||
this.quoteIdentifier(schema), | ||
";'", | ||
"END;" | ||
].join(" "); | ||
} | ||
showSchemasQuery() { | ||
@@ -94,22 +113,18 @@ return [ | ||
"'INFORMATION_SCHEMA', 'dbo', 'guest', 'sys', 'archive'", | ||
')', 'AND', '"s"."name" NOT LIKE', "'db_%'" | ||
].join(' '); | ||
")", | ||
"AND", | ||
'"s"."name" NOT LIKE', | ||
"'db_%'" | ||
].join(" "); | ||
} | ||
versionQuery() { | ||
// Uses string manipulation to convert the MS Maj.Min.Patch.Build to semver Maj.Min.Patch | ||
return [ | ||
'DECLARE @ms_ver NVARCHAR(20);', | ||
"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'" | ||
].join(' '); | ||
].join(" "); | ||
} | ||
createTableQuery(tableName, attributes, options) { | ||
const primaryKeys = [], | ||
foreignKeys = {}, | ||
attributesClauseParts = []; | ||
let commentStr = ''; | ||
const primaryKeys = [], foreignKeys = {}, attributesClauseParts = []; | ||
let commentStr = ""; | ||
for (const attr in attributes) { | ||
@@ -119,24 +134,18 @@ if (Object.prototype.hasOwnProperty.call(attributes, attr)) { | ||
let match; | ||
if (dataType.includes('COMMENT ')) { | ||
if (dataType.includes("COMMENT ")) { | ||
const commentMatch = dataType.match(/^(.+) (COMMENT.*)$/); | ||
const commentText = commentMatch[2].replace('COMMENT', '').trim(); | ||
const commentText = commentMatch[2].replace("COMMENT", "").trim(); | ||
commentStr += this.commentTemplate(commentText, tableName, attr); | ||
// remove comment related substring from dataType | ||
dataType = commentMatch[1]; | ||
} | ||
if (dataType.includes('PRIMARY KEY')) { | ||
if (dataType.includes("PRIMARY KEY")) { | ||
primaryKeys.push(attr); | ||
if (dataType.includes('REFERENCES')) { | ||
// MSSQL doesn't support inline REFERENCES declarations: move to the end | ||
if (dataType.includes("REFERENCES")) { | ||
match = dataType.match(/^(.+) (REFERENCES.*)$/); | ||
attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${match[1].replace('PRIMARY KEY', '')}`); | ||
attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${match[1].replace("PRIMARY KEY", "")}`); | ||
foreignKeys[attr] = match[2]; | ||
} else { | ||
attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${dataType.replace('PRIMARY KEY', '')}`); | ||
attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${dataType.replace("PRIMARY KEY", "")}`); | ||
} | ||
} else if (dataType.includes('REFERENCES')) { | ||
// MSSQL doesn't support inline REFERENCES declarations: move to the end | ||
} else if (dataType.includes("REFERENCES")) { | ||
match = dataType.match(/^(.+) (REFERENCES.*)$/); | ||
@@ -150,24 +159,16 @@ attributesClauseParts.push(`${this.quoteIdentifier(attr)} ${match[1]}`); | ||
} | ||
const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', '); | ||
const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", "); | ||
if (options.uniqueKeys) { | ||
_.each(options.uniqueKeys, (columns, indexName) => { | ||
if (columns.customIndex) { | ||
if (typeof indexName !== 'string') { | ||
indexName = `uniq_${tableName}_${columns.fields.join('_')}`; | ||
if (typeof indexName !== "string") { | ||
indexName = `uniq_${tableName}_${columns.fields.join("_")}`; | ||
} | ||
attributesClauseParts.push(`CONSTRAINT ${ | ||
this.quoteIdentifier(indexName) | ||
} UNIQUE (${ | ||
columns.fields.map(field => this.quoteIdentifier(field)).join(', ') | ||
})`); | ||
attributesClauseParts.push(`CONSTRAINT ${this.quoteIdentifier(indexName)} UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`); | ||
} | ||
}); | ||
} | ||
if (pkString.length > 0) { | ||
attributesClauseParts.push(`PRIMARY KEY (${pkString})`); | ||
} | ||
for (const fkey in foreignKeys) { | ||
@@ -178,16 +179,13 @@ if (Object.prototype.hasOwnProperty.call(foreignKeys, fkey)) { | ||
} | ||
const quotedTableName = this.quoteTable(tableName); | ||
return Utils.joinSQLFragments([ | ||
`IF OBJECT_ID('${quotedTableName}', 'U') IS NULL`, | ||
`CREATE TABLE ${quotedTableName} (${attributesClauseParts.join(', ')})`, | ||
';', | ||
`CREATE TABLE ${quotedTableName} (${attributesClauseParts.join(", ")})`, | ||
";", | ||
commentStr | ||
]); | ||
} | ||
describeTableQuery(tableName, schema) { | ||
let sql = [ | ||
'SELECT', | ||
"SELECT", | ||
"c.COLUMN_NAME AS 'Name',", | ||
@@ -201,39 +199,40 @@ "c.DATA_TYPE AS 'Type',", | ||
"CAST(prop.value AS NVARCHAR) AS 'Comment'", | ||
'FROM', | ||
'INFORMATION_SCHEMA.TABLES t', | ||
'INNER JOIN', | ||
'INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME AND t.TABLE_SCHEMA = c.TABLE_SCHEMA', | ||
'LEFT JOIN (SELECT tc.table_schema, tc.table_name, ', | ||
'cu.column_name, tc.CONSTRAINT_TYPE ', | ||
'FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc ', | ||
'JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu ', | ||
'ON tc.table_schema=cu.table_schema and tc.table_name=cu.table_name ', | ||
'and tc.constraint_name=cu.constraint_name ', | ||
'and tc.CONSTRAINT_TYPE=\'PRIMARY KEY\') pk ', | ||
'ON pk.table_schema=c.table_schema ', | ||
'AND pk.table_name=c.table_name ', | ||
'AND pk.column_name=c.column_name ', | ||
'INNER JOIN sys.columns AS sc', | ||
"FROM", | ||
"INFORMATION_SCHEMA.TABLES t", | ||
"INNER JOIN", | ||
"INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME AND t.TABLE_SCHEMA = c.TABLE_SCHEMA", | ||
"LEFT JOIN (SELECT tc.table_schema, tc.table_name, ", | ||
"cu.column_name, tc.CONSTRAINT_TYPE ", | ||
"FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc ", | ||
"JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu ", | ||
"ON tc.table_schema=cu.table_schema and tc.table_name=cu.table_name ", | ||
"and tc.constraint_name=cu.constraint_name ", | ||
"and tc.CONSTRAINT_TYPE='PRIMARY KEY') pk ", | ||
"ON pk.table_schema=c.table_schema ", | ||
"AND pk.table_name=c.table_name ", | ||
"AND pk.column_name=c.column_name ", | ||
"INNER JOIN sys.columns AS sc", | ||
"ON sc.object_id = object_id(t.table_schema + '.' + t.table_name) AND sc.name = c.column_name", | ||
'LEFT JOIN sys.extended_properties prop ON prop.major_id = sc.object_id', | ||
'AND prop.minor_id = sc.column_id', | ||
"LEFT JOIN sys.extended_properties prop ON prop.major_id = sc.object_id", | ||
"AND prop.minor_id = sc.column_id", | ||
"AND prop.name = 'MS_Description'", | ||
'WHERE t.TABLE_NAME =', wrapSingleQuote(tableName) | ||
].join(' '); | ||
"WHERE t.TABLE_NAME =", | ||
wrapSingleQuote(tableName) | ||
].join(" "); | ||
if (schema) { | ||
sql += `AND t.TABLE_SCHEMA =${wrapSingleQuote(schema)}`; | ||
} | ||
return sql; | ||
} | ||
renameTableQuery(before, after) { | ||
return `EXEC sp_rename ${this.quoteTable(before)}, ${this.quoteTable(after)};`; | ||
} | ||
showTablesQuery() { | ||
return "SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';"; | ||
} | ||
tableExistsQuery(table) { | ||
const tableName = table.tableName || table; | ||
const schemaName = table.schema || "dbo"; | ||
return `SELECT TABLE_NAME, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = ${this.escape(tableName)} AND TABLE_SCHEMA = ${this.escape(schemaName)}`; | ||
} | ||
dropTableQuery(tableName) { | ||
@@ -243,68 +242,50 @@ const quoteTbl = this.quoteTable(tableName); | ||
`IF OBJECT_ID('${quoteTbl}', 'U') IS NOT NULL`, | ||
'DROP TABLE', | ||
"DROP TABLE", | ||
quoteTbl, | ||
';' | ||
";" | ||
]); | ||
} | ||
addColumnQuery(table, key, dataType) { | ||
// FIXME: attributeToSQL SHOULD be using attributes in addColumnQuery | ||
// but instead we need to pass the key along as the field here | ||
dataType.field = key; | ||
let commentStr = ''; | ||
let commentStr = ""; | ||
if (dataType.comment && _.isString(dataType.comment)) { | ||
commentStr = this.commentTemplate(dataType.comment, table, key); | ||
// attributeToSQL will try to include `COMMENT 'Comment Text'` when it returns if the comment key | ||
// is present. This is needed for createTable statement where that part is extracted with regex. | ||
// Here we can intercept the object and remove comment property since we have the original object. | ||
delete dataType['comment']; | ||
delete dataType["comment"]; | ||
} | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(table), | ||
'ADD', | ||
"ADD", | ||
this.quoteIdentifier(key), | ||
this.attributeToSQL(dataType, { context: 'addColumn' }), | ||
';', | ||
this.attributeToSQL(dataType, { context: "addColumn" }), | ||
";", | ||
commentStr | ||
]); | ||
} | ||
commentTemplate(comment, table, column) { | ||
return ' EXEC sp_addextendedproperty ' + | ||
`@name = N'MS_Description', @value = ${this.escape(comment)}, ` + | ||
'@level0type = N\'Schema\', @level0name = \'dbo\', ' + | ||
`@level1type = N'Table', @level1name = ${this.quoteIdentifier(table)}, ` + | ||
`@level2type = N'Column', @level2name = ${this.quoteIdentifier(column)};`; | ||
return ` EXEC sp_addextendedproperty @name = N'MS_Description', @value = ${this.escape(comment)}, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = ${this.quoteIdentifier(table)}, @level2type = N'Column', @level2name = ${this.quoteIdentifier(column)};`; | ||
} | ||
removeColumnQuery(tableName, attributeName) { | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
'DROP COLUMN', | ||
"DROP COLUMN", | ||
this.quoteIdentifier(attributeName), | ||
';' | ||
";" | ||
]); | ||
} | ||
changeColumnQuery(tableName, attributes) { | ||
const attrString = [], | ||
constraintString = []; | ||
let commentString = ''; | ||
const attrString = [], constraintString = []; | ||
let commentString = ""; | ||
for (const attributeName in attributes) { | ||
const quotedAttrName = this.quoteIdentifier(attributeName); | ||
let definition = attributes[attributeName]; | ||
if (definition.includes('COMMENT ')) { | ||
if (definition.includes("COMMENT ")) { | ||
const commentMatch = definition.match(/^(.+) (COMMENT.*)$/); | ||
const commentText = commentMatch[2].replace('COMMENT', '').trim(); | ||
const commentText = commentMatch[2].replace("COMMENT", "").trim(); | ||
commentString += this.commentTemplate(commentText, tableName, attributeName); | ||
// remove comment related substring from dataType | ||
definition = commentMatch[1]; | ||
} | ||
if (definition.includes('REFERENCES')) { | ||
constraintString.push(`FOREIGN KEY (${quotedAttrName}) ${definition.replace(/.+?(?=REFERENCES)/, '')}`); | ||
if (definition.includes("REFERENCES")) { | ||
constraintString.push(`FOREIGN KEY (${quotedAttrName}) ${definition.replace(/.+?(?=REFERENCES)/, "")}`); | ||
} else { | ||
@@ -314,24 +295,21 @@ attrString.push(`${quotedAttrName} ${definition}`); | ||
} | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
attrString.length && `ALTER COLUMN ${attrString.join(', ')}`, | ||
constraintString.length && `ADD ${constraintString.join(', ')}`, | ||
';', | ||
attrString.length && `ALTER COLUMN ${attrString.join(", ")}`, | ||
constraintString.length && `ADD ${constraintString.join(", ")}`, | ||
";", | ||
commentString | ||
]); | ||
} | ||
renameColumnQuery(tableName, attrBefore, attributes) { | ||
const newName = Object.keys(attributes)[0]; | ||
return Utils.joinSQLFragments([ | ||
'EXEC sp_rename', | ||
"EXEC sp_rename", | ||
`'${this.quoteTable(tableName)}.${attrBefore}',`, | ||
`'${newName}',`, | ||
"'COLUMN'", | ||
';' | ||
";" | ||
]); | ||
} | ||
bulkInsertQuery(tableName, attrValueHashes, options, attributes) { | ||
@@ -341,20 +319,12 @@ const quotedTable = this.quoteTable(tableName); | ||
attributes = attributes || {}; | ||
const tuples = []; | ||
const allAttributes = []; | ||
const allQueries = []; | ||
let needIdentityInsertWrapper = false, | ||
outputFragment = ''; | ||
let needIdentityInsertWrapper = false, outputFragment = ""; | ||
if (options.returning) { | ||
const returnValues = this.generateReturnValues(attributes, options); | ||
outputFragment = returnValues.outputFragment; | ||
} | ||
const emptyQuery = `INSERT INTO ${quotedTable}${outputFragment} DEFAULT VALUES`; | ||
attrValueHashes.forEach(attrValueHash => { | ||
// special case for empty objects with primary keys | ||
attrValueHashes.forEach((attrValueHash) => { | ||
const fields = Object.keys(attrValueHash); | ||
@@ -366,4 +336,2 @@ const firstAttr = attributes[fields[0]]; | ||
} | ||
// normal case | ||
_.forOwn(attrValueHash, (value, key) => { | ||
@@ -373,7 +341,5 @@ if (value !== null && attributes[key] && attributes[key].autoIncrement) { | ||
} | ||
if (!allAttributes.includes(key)) { | ||
if (value === null && attributes[key] && attributes[key].autoIncrement) | ||
return; | ||
allAttributes.push(key); | ||
@@ -383,13 +349,8 @@ } | ||
}); | ||
if (allAttributes.length > 0) { | ||
attrValueHashes.forEach(attrValueHash => { | ||
tuples.push(`(${ | ||
allAttributes.map(key => | ||
this.escape(attrValueHash[key])).join(',') | ||
})`); | ||
attrValueHashes.forEach((attrValueHash) => { | ||
tuples.push(`(${allAttributes.map((key) => this.escape(attrValueHash[key])).join(",")})`); | ||
}); | ||
const quotedAttributes = allAttributes.map(attr => this.quoteIdentifier(attr)).join(','); | ||
allQueries.push(tupleStr => `INSERT INTO ${quotedTable} (${quotedAttributes})${outputFragment} VALUES ${tupleStr};`); | ||
const quotedAttributes = allAttributes.map((attr) => this.quoteIdentifier(attr)).join(","); | ||
allQueries.push((tupleStr) => `INSERT INTO ${quotedTable} (${quotedAttributes})${outputFragment} VALUES ${tupleStr};`); | ||
} | ||
@@ -401,3 +362,3 @@ const commands = []; | ||
const tupleStr = tuples.slice(offset, Math.min(tuples.length, offset + batch)); | ||
let generatedQuery = allQueries.map(v => typeof v === 'string' ? v : v(tupleStr)).join(';'); | ||
let generatedQuery = allQueries.map((v) => typeof v === "string" ? v : v(tupleStr)).join(";"); | ||
if (needIdentityInsertWrapper) { | ||
@@ -409,5 +370,4 @@ generatedQuery = `SET IDENTITY_INSERT ${quotedTable} ON; ${generatedQuery}; SET IDENTITY_INSERT ${quotedTable} OFF;`; | ||
} | ||
return commands.join(';'); | ||
return commands.join(";"); | ||
} | ||
updateQuery(tableName, attrValueHash, where, options, attributes) { | ||
@@ -417,7 +377,6 @@ const sql = super.updateQuery(tableName, attrValueHash, where, options, attributes); | ||
const updateArgs = `UPDATE TOP(${this.escape(options.limit)})`; | ||
sql.query = sql.query.replace('UPDATE', updateArgs); | ||
sql.query = sql.query.replace("UPDATE", updateArgs); | ||
} | ||
return sql; | ||
} | ||
upsertQuery(tableName, insertValues, updateValues, where, model) { | ||
@@ -431,4 +390,2 @@ const targetTableAlias = this.quoteTable(`${tableName}_target`); | ||
let needIdentityInsertWrapper = false; | ||
//Obtain primaryKeys, uniquekeys and identity attrs from rawAttributes as model is not passed | ||
for (const key in model.rawAttributes) { | ||
@@ -445,8 +402,6 @@ if (model.rawAttributes[key].primaryKey) { | ||
} | ||
//Add unique indexes defined by indexes option to uniqueAttrs | ||
for (const index of model._indexes) { | ||
if (index.unique && index.fields) { | ||
for (const field of index.fields) { | ||
const fieldName = typeof field === 'string' ? field : field.name || field.attribute; | ||
const fieldName = typeof field === "string" ? field : field.name || field.attribute; | ||
if (!uniqueAttrs.includes(fieldName) && model.rawAttributes[fieldName]) { | ||
@@ -458,29 +413,17 @@ uniqueAttrs.push(fieldName); | ||
} | ||
const updateKeys = Object.keys(updateValues); | ||
const insertKeys = Object.keys(insertValues); | ||
const insertKeysQuoted = insertKeys.map(key => this.quoteIdentifier(key)).join(', '); | ||
const insertValuesEscaped = insertKeys.map(key => this.escape(insertValues[key])).join(', '); | ||
const sourceTableQuery = `VALUES(${insertValuesEscaped})`; //Virtual Table | ||
const insertKeysQuoted = insertKeys.map((key) => this.quoteIdentifier(key)).join(", "); | ||
const insertValuesEscaped = insertKeys.map((key) => this.escape(insertValues[key])).join(", "); | ||
const sourceTableQuery = `VALUES(${insertValuesEscaped})`; | ||
let joinCondition; | ||
//IDENTITY_INSERT Condition | ||
identityAttrs.forEach(key => { | ||
if (updateValues[key] && updateValues[key] !== null) { | ||
identityAttrs.forEach((key) => { | ||
if (insertValues[key] && insertValues[key] !== null) { | ||
needIdentityInsertWrapper = true; | ||
/* | ||
* IDENTITY_INSERT Column Cannot be updated, only inserted | ||
* http://stackoverflow.com/a/30176254/2254360 | ||
*/ | ||
} | ||
}); | ||
//Filter NULL Clauses | ||
const clauses = where[Op.or].filter(clause => { | ||
const clauses = where[Op.or].filter((clause) => { | ||
let valid = true; | ||
/* | ||
* Exclude NULL Composite PK/UK. Partial Composite clauses should also be excluded as it doesn't guarantee a single row | ||
*/ | ||
for (const key in clause) { | ||
if (typeof clause[key] === 'undefined' || clause[key] == null) { | ||
if (typeof clause[key] === "undefined" || clause[key] == null) { | ||
valid = false; | ||
@@ -492,9 +435,4 @@ break; | ||
}); | ||
/* | ||
* Generate ON condition using PK(s). | ||
* If not, generate using UK(s). Else throw error | ||
*/ | ||
const getJoinSnippet = array => { | ||
return array.map(key => { | ||
const getJoinSnippet = (array) => { | ||
return array.map((key) => { | ||
key = this.quoteIdentifier(key); | ||
@@ -504,11 +442,9 @@ return `${targetTableAlias}.${key} = ${sourceTableAlias}.${key}`; | ||
}; | ||
if (clauses.length === 0) { | ||
throw new Error('Primary Key or Unique key should be passed to upsert query'); | ||
throw new Error("Primary Key or Unique key should be passed to upsert query"); | ||
} else { | ||
// Search for primary key attribute in clauses -- Model can have two separate unique keys | ||
for (const key in clauses) { | ||
const keys = Object.keys(clauses[key]); | ||
if (primaryKeysAttrs.includes(keys[0])) { | ||
joinCondition = getJoinSnippet(primaryKeysAttrs).join(' AND '); | ||
joinCondition = getJoinSnippet(primaryKeysAttrs).join(" AND "); | ||
break; | ||
@@ -518,17 +454,14 @@ } | ||
if (!joinCondition) { | ||
joinCondition = getJoinSnippet(uniqueAttrs).join(' AND '); | ||
joinCondition = getJoinSnippet(uniqueAttrs).join(" AND "); | ||
} | ||
} | ||
// Remove the IDENTITY_INSERT Column from update | ||
const updateSnippet = updateKeys.filter(key => !identityAttrs.includes(key)) | ||
.map(key => { | ||
const value = this.escape(updateValues[key]); | ||
key = this.quoteIdentifier(key); | ||
return `${targetTableAlias}.${key} = ${value}`; | ||
}).join(', '); | ||
const filteredUpdateClauses = updateKeys.filter((key) => !identityAttrs.includes(key)).map((key) => { | ||
const value = this.escape(updateValues[key]); | ||
key = this.quoteIdentifier(key); | ||
return `${targetTableAlias}.${key} = ${value}`; | ||
}); | ||
const updateSnippet = filteredUpdateClauses.length > 0 ? `WHEN MATCHED THEN UPDATE SET ${filteredUpdateClauses.join(", ")}` : ""; | ||
const insertSnippet = `(${insertKeysQuoted}) VALUES(${insertValuesEscaped})`; | ||
let query = `MERGE INTO ${tableNameQuoted} WITH(HOLDLOCK) AS ${targetTableAlias} USING (${sourceTableQuery}) AS ${sourceTableAlias}(${insertKeysQuoted}) ON ${joinCondition}`; | ||
query += ` WHEN MATCHED THEN UPDATE SET ${updateSnippet} WHEN NOT MATCHED THEN INSERT ${insertSnippet} OUTPUT $action, INSERTED.*;`; | ||
query += ` ${updateSnippet} WHEN NOT MATCHED THEN INSERT ${insertSnippet} OUTPUT $action, INSERTED.*;`; | ||
if (needIdentityInsertWrapper) { | ||
@@ -539,42 +472,33 @@ query = `SET IDENTITY_INSERT ${tableNameQuoted} ON; ${query} SET IDENTITY_INSERT ${tableNameQuoted} OFF;`; | ||
} | ||
truncateTableQuery(tableName) { | ||
return `TRUNCATE TABLE ${this.quoteTable(tableName)}`; | ||
} | ||
deleteQuery(tableName, where, options = {}, model) { | ||
const table = this.quoteTable(tableName); | ||
const whereClause = this.getWhereConditions(where, null, model, options); | ||
return Utils.joinSQLFragments([ | ||
'DELETE', | ||
"DELETE", | ||
options.limit && `TOP(${this.escape(options.limit)})`, | ||
'FROM', | ||
"FROM", | ||
table, | ||
whereClause && `WHERE ${whereClause}`, | ||
';', | ||
'SELECT @@ROWCOUNT AS AFFECTEDROWS', | ||
';' | ||
";", | ||
"SELECT @@ROWCOUNT AS AFFECTEDROWS", | ||
";" | ||
]); | ||
} | ||
showIndexesQuery(tableName) { | ||
return `EXEC sys.sp_helpindex @objname = N'${this.quoteTable(tableName)}';`; | ||
} | ||
showConstraintsQuery(tableName) { | ||
return `EXEC sp_helpconstraint @objname = ${this.escape(this.quoteTable(tableName))};`; | ||
} | ||
removeIndexQuery(tableName, indexNameOrAttributes) { | ||
let indexName = indexNameOrAttributes; | ||
if (typeof indexName !== 'string') { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join('_')}`); | ||
if (typeof indexName !== "string") { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); | ||
} | ||
return `DROP INDEX ${this.quoteIdentifiers(indexName)} ON ${this.quoteIdentifiers(tableName)}`; | ||
} | ||
attributeToSQL(attribute) { | ||
attributeToSQL(attribute, options) { | ||
if (!_.isPlainObject(attribute)) { | ||
@@ -585,65 +509,47 @@ attribute = { | ||
} | ||
// handle self referential constraints | ||
if (attribute.references) { | ||
if (attribute.Model && attribute.Model.tableName === attribute.references.model) { | ||
this.sequelize.log('MSSQL does not support self referencial constraints, ' | ||
+ 'we will remove it but we recommend restructuring your query'); | ||
attribute.onDelete = ''; | ||
attribute.onUpdate = ''; | ||
this.sequelize.log("MSSQL does not support self referencial constraints, we will remove it but we recommend restructuring your query"); | ||
attribute.onDelete = ""; | ||
attribute.onUpdate = ""; | ||
} | ||
} | ||
let template; | ||
if (attribute.type instanceof DataTypes.ENUM) { | ||
if (attribute.type.values && !attribute.values) attribute.values = attribute.type.values; | ||
// enums are a special case | ||
if (attribute.type.values && !attribute.values) | ||
attribute.values = attribute.type.values; | ||
template = attribute.type.toSql(); | ||
template += ` CHECK (${this.quoteIdentifier(attribute.field)} IN(${attribute.values.map(value => { | ||
template += ` CHECK (${this.quoteIdentifier(attribute.field)} IN(${attribute.values.map((value) => { | ||
return this.escape(value); | ||
}).join(', ') }))`; | ||
}).join(", ")}))`; | ||
return template; | ||
} | ||
template = attribute.type.toString(); | ||
if (attribute.allowNull === false) { | ||
template += ' NOT NULL'; | ||
template += " NOT NULL"; | ||
} else if (!attribute.primaryKey && !Utils.defaultValueSchemable(attribute.defaultValue)) { | ||
template += ' NULL'; | ||
template += " NULL"; | ||
} | ||
if (attribute.autoIncrement) { | ||
template += ' IDENTITY(1,1)'; | ||
template += " IDENTITY(1,1)"; | ||
} | ||
// Blobs/texts cannot have a defaultValue | ||
if (attribute.type !== 'TEXT' && attribute.type._binary !== true && | ||
Utils.defaultValueSchemable(attribute.defaultValue)) { | ||
if (attribute.type !== "TEXT" && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) { | ||
template += ` DEFAULT ${this.escape(attribute.defaultValue)}`; | ||
} | ||
if (attribute.unique === true) { | ||
template += ' UNIQUE'; | ||
template += " UNIQUE"; | ||
} | ||
if (attribute.primaryKey) { | ||
template += ' PRIMARY KEY'; | ||
template += " PRIMARY KEY"; | ||
} | ||
if (attribute.references) { | ||
if ((!options || !options.withoutForeignKeyConstraints) && attribute.references) { | ||
template += ` REFERENCES ${this.quoteTable(attribute.references.model)}`; | ||
if (attribute.references.key) { | ||
template += ` (${this.quoteIdentifier(attribute.references.key)})`; | ||
} else { | ||
template += ` (${this.quoteIdentifier('id')})`; | ||
template += ` (${this.quoteIdentifier("id")})`; | ||
} | ||
if (attribute.onDelete) { | ||
template += ` ON DELETE ${attribute.onDelete.toUpperCase()}`; | ||
} | ||
if (attribute.onUpdate) { | ||
@@ -653,106 +559,51 @@ template += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`; | ||
} | ||
if (attribute.comment && typeof attribute.comment === 'string') { | ||
if (attribute.comment && typeof attribute.comment === "string") { | ||
template += ` COMMENT ${attribute.comment}`; | ||
} | ||
return template; | ||
} | ||
attributesToSQL(attributes, options) { | ||
const result = {}, | ||
existingConstraints = []; | ||
let key, | ||
attribute; | ||
const result = {}, existingConstraints = []; | ||
let key, attribute; | ||
for (key in attributes) { | ||
attribute = attributes[key]; | ||
if (attribute.references) { | ||
if (existingConstraints.includes(attribute.references.model.toString())) { | ||
// no cascading constraints to a table more than once | ||
attribute.onDelete = ''; | ||
attribute.onUpdate = ''; | ||
attribute.onDelete = ""; | ||
attribute.onUpdate = ""; | ||
} else { | ||
existingConstraints.push(attribute.references.model.toString()); | ||
// NOTE: this really just disables cascading updates for all | ||
// definitions. Can be made more robust to support the | ||
// few cases where MSSQL actually supports them | ||
attribute.onUpdate = ''; | ||
attribute.onUpdate = ""; | ||
} | ||
} | ||
if (key && !attribute.field) attribute.field = key; | ||
if (key && !attribute.field) | ||
attribute.field = key; | ||
result[attribute.field || key] = this.attributeToSQL(attribute, options); | ||
} | ||
return result; | ||
} | ||
createTrigger() { | ||
throwMethodUndefined('createTrigger'); | ||
throwMethodUndefined("createTrigger"); | ||
} | ||
dropTrigger() { | ||
throwMethodUndefined('dropTrigger'); | ||
throwMethodUndefined("dropTrigger"); | ||
} | ||
renameTrigger() { | ||
throwMethodUndefined('renameTrigger'); | ||
throwMethodUndefined("renameTrigger"); | ||
} | ||
createFunction() { | ||
throwMethodUndefined('createFunction'); | ||
throwMethodUndefined("createFunction"); | ||
} | ||
dropFunction() { | ||
throwMethodUndefined('dropFunction'); | ||
throwMethodUndefined("dropFunction"); | ||
} | ||
renameFunction() { | ||
throwMethodUndefined('renameFunction'); | ||
throwMethodUndefined("renameFunction"); | ||
} | ||
/** | ||
* Generate common SQL prefix for ForeignKeysQuery. | ||
* | ||
* @param {string} catalogName | ||
* @returns {string} | ||
*/ | ||
_getForeignKeysQueryPrefix(catalogName) { | ||
return `${'SELECT ' + | ||
'constraint_name = OBJ.NAME, ' + | ||
'constraintName = OBJ.NAME, '}${ | ||
catalogName ? `constraintCatalog = '${catalogName}', ` : '' | ||
}constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), ` + | ||
'tableName = TB.NAME, ' + | ||
`tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), ${ | ||
catalogName ? `tableCatalog = '${catalogName}', ` : '' | ||
}columnName = COL.NAME, ` + | ||
`referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), ${ | ||
catalogName ? `referencedCatalog = '${catalogName}', ` : '' | ||
}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'; | ||
return `${"SELECT constraint_name = OBJ.NAME, constraintName = OBJ.NAME, "}${catalogName ? `constraintCatalog = '${catalogName}', ` : ""}constraintSchema = SCHEMA_NAME(OBJ.SCHEMA_ID), tableName = TB.NAME, tableSchema = SCHEMA_NAME(TB.SCHEMA_ID), ${catalogName ? `tableCatalog = '${catalogName}', ` : ""}columnName = COL.NAME, referencedTableSchema = SCHEMA_NAME(RTB.SCHEMA_ID), ${catalogName ? `referencedCatalog = '${catalogName}', ` : ""}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`; | ||
} | ||
/** | ||
* Generates an SQL query that returns all foreign keys details of a table. | ||
* | ||
* @param {string|object} table | ||
* @param {string} catalogName database name | ||
* @returns {string} | ||
*/ | ||
getForeignKeysQuery(table, catalogName) { | ||
const tableName = table.tableName || table; | ||
let sql = `${this._getForeignKeysQueryPrefix(catalogName) | ||
} WHERE TB.NAME =${wrapSingleQuote(tableName)}`; | ||
let sql = `${this._getForeignKeysQueryPrefix(catalogName)} WHERE TB.NAME =${wrapSingleQuote(tableName)}`; | ||
if (table.schema) { | ||
@@ -763,3 +614,2 @@ sql += ` AND SCHEMA_NAME(TB.SCHEMA_ID) =${wrapSingleQuote(table.schema)}`; | ||
} | ||
getForeignKeyQuery(table, attributeName) { | ||
@@ -769,5 +619,5 @@ const tableName = table.tableName || table; | ||
this._getForeignKeysQueryPrefix(), | ||
'WHERE', | ||
"WHERE", | ||
`TB.NAME =${wrapSingleQuote(tableName)}`, | ||
'AND', | ||
"AND", | ||
`COL.NAME =${wrapSingleQuote(attributeName)}`, | ||
@@ -777,60 +627,52 @@ table.schema && `AND SCHEMA_NAME(TB.SCHEMA_ID) =${wrapSingleQuote(table.schema)}` | ||
} | ||
getPrimaryKeyConstraintQuery(table, attributeName) { | ||
const tableName = wrapSingleQuote(table.tableName || table); | ||
return Utils.joinSQLFragments([ | ||
'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\'', | ||
"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 = ${wrapSingleQuote(attributeName)}`, | ||
`AND K.TABLE_NAME = ${tableName}`, | ||
';' | ||
";" | ||
]); | ||
} | ||
dropForeignKeyQuery(tableName, foreignKey) { | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
'DROP', | ||
"DROP", | ||
this.quoteIdentifier(foreignKey) | ||
]); | ||
} | ||
getDefaultConstraintQuery(tableName, attributeName) { | ||
const quotedTable = this.quoteTable(tableName); | ||
return Utils.joinSQLFragments([ | ||
'SELECT name FROM sys.default_constraints', | ||
"SELECT name FROM sys.default_constraints", | ||
`WHERE PARENT_OBJECT_ID = OBJECT_ID('${quotedTable}', 'U')`, | ||
`AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = ('${attributeName}')`, | ||
`AND object_id = OBJECT_ID('${quotedTable}', 'U'))`, | ||
';' | ||
";" | ||
]); | ||
} | ||
dropConstraintQuery(tableName, constraintName) { | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
'DROP CONSTRAINT', | ||
"DROP CONSTRAINT", | ||
this.quoteIdentifier(constraintName), | ||
';' | ||
";" | ||
]); | ||
} | ||
setIsolationLevelQuery() { | ||
} | ||
generateTransactionId() { | ||
return randomBytes(10).toString('hex'); | ||
return randomBytes(10).toString("hex"); | ||
} | ||
startTransactionQuery(transaction) { | ||
@@ -840,6 +682,4 @@ if (transaction.parent) { | ||
} | ||
return 'BEGIN TRANSACTION;'; | ||
return "BEGIN TRANSACTION;"; | ||
} | ||
commitTransactionQuery(transaction) { | ||
@@ -849,6 +689,4 @@ if (transaction.parent) { | ||
} | ||
return 'COMMIT TRANSACTION;'; | ||
return "COMMIT TRANSACTION;"; | ||
} | ||
rollbackTransactionQuery(transaction) { | ||
@@ -858,23 +696,9 @@ if (transaction.parent) { | ||
} | ||
return 'ROLLBACK TRANSACTION;'; | ||
return "ROLLBACK TRANSACTION;"; | ||
} | ||
selectFromTableFragment(options, model, attributes, tables, mainTableAs, where) { | ||
this._throwOnEmptyAttributes(attributes, { modelName: model && model.name, as: mainTableAs }); | ||
const dbVersion = this.sequelize.options.databaseVersion; | ||
const isSQLServer2008 = semver.valid(dbVersion) && semver.lt(dbVersion, '11.0.0'); | ||
const isSQLServer2008 = semver.valid(dbVersion) && semver.lt(dbVersion, "11.0.0"); | ||
if (isSQLServer2008 && options.offset) { | ||
// For earlier versions of SQL server, we need to nest several queries | ||
// in order to emulate the OFFSET behavior. | ||
// | ||
// 1. The outermost query selects all items from the inner query block. | ||
// This is due to a limitation in SQL server with the use of computed | ||
// columns (e.g. SELECT ROW_NUMBER()...AS x) in WHERE clauses. | ||
// 2. The next query handles the LIMIT and OFFSET behavior by getting | ||
// the TOP N rows of the query where the row number is > OFFSET | ||
// 3. The innermost query is the actual set we want information from | ||
const offset = options.offset || 0; | ||
@@ -886,22 +710,68 @@ const isSubQuery = options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation; | ||
} | ||
if (orders.mainQueryOrder.length === 0) { | ||
orders.mainQueryOrder.push(this.quoteIdentifier(model.primaryKeyField)); | ||
} | ||
const tmpTable = mainTableAs || 'OffsetTable'; | ||
const tmpTable = mainTableAs || "OffsetTable"; | ||
if (options.include) { | ||
const subQuery = options.subQuery === void 0 ? options.limit && options.hasMultiAssociation : options.subQuery; | ||
const mainTable = { | ||
name: mainTableAs, | ||
quotedName: null, | ||
as: null, | ||
model | ||
}; | ||
const topLevelInfo = { | ||
names: mainTable, | ||
options, | ||
subQuery | ||
}; | ||
let mainJoinQueries = []; | ||
for (const include of options.include) { | ||
if (include.separate) { | ||
continue; | ||
} | ||
const joinQueries = this.generateInclude(include, { externalAs: mainTableAs, internalAs: mainTableAs }, topLevelInfo); | ||
mainJoinQueries = mainJoinQueries.concat(joinQueries.mainQuery); | ||
} | ||
return Utils.joinSQLFragments([ | ||
"SELECT TOP 100 PERCENT", | ||
attributes.join(", "), | ||
"FROM (", | ||
[ | ||
"SELECT", | ||
options.limit && `TOP ${options.limit}`, | ||
"* FROM (", | ||
[ | ||
"SELECT ROW_NUMBER() OVER (", | ||
[ | ||
"ORDER BY", | ||
orders.mainQueryOrder.join(", ") | ||
], | ||
`) as row_num, ${tmpTable}.* FROM (`, | ||
[ | ||
"SELECT DISTINCT", | ||
`${tmpTable}.* FROM ${tables} AS ${tmpTable}`, | ||
mainJoinQueries, | ||
where && `WHERE ${where}` | ||
], | ||
`) AS ${tmpTable}` | ||
], | ||
`) AS ${tmpTable} WHERE row_num > ${offset}` | ||
], | ||
`) AS ${tmpTable}` | ||
]); | ||
} | ||
return Utils.joinSQLFragments([ | ||
'SELECT TOP 100 PERCENT', | ||
attributes.join(', '), | ||
'FROM (', | ||
"SELECT TOP 100 PERCENT", | ||
attributes.join(", "), | ||
"FROM (", | ||
[ | ||
'SELECT', | ||
"SELECT", | ||
options.limit && `TOP ${options.limit}`, | ||
'* FROM (', | ||
"* FROM (", | ||
[ | ||
'SELECT ROW_NUMBER() OVER (', | ||
"SELECT ROW_NUMBER() OVER (", | ||
[ | ||
'ORDER BY', | ||
orders.mainQueryOrder.join(', ') | ||
"ORDER BY", | ||
orders.mainQueryOrder.join(", ") | ||
], | ||
@@ -916,7 +786,6 @@ `) as row_num, * FROM ${tables} AS ${tmpTable}`, | ||
} | ||
return Utils.joinSQLFragments([ | ||
'SELECT', | ||
"SELECT", | ||
isSQLServer2008 && options.limit && `TOP ${options.limit}`, | ||
attributes.join(', '), | ||
attributes.join(", "), | ||
`FROM ${tables}`, | ||
@@ -927,23 +796,15 @@ mainTableAs && `AS ${mainTableAs}`, | ||
} | ||
addLimitAndOffset(options, model) { | ||
// Skip handling of limit and offset as postfixes for older SQL Server versions | ||
if (semver.valid(this.sequelize.options.databaseVersion) && semver.lt(this.sequelize.options.databaseVersion, '11.0.0')) { | ||
return ''; | ||
if (semver.valid(this.sequelize.options.databaseVersion) && semver.lt(this.sequelize.options.databaseVersion, "11.0.0")) { | ||
return ""; | ||
} | ||
const offset = options.offset || 0; | ||
const isSubQuery = options.subQuery === undefined | ||
? options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation | ||
: options.subQuery; | ||
let fragment = ''; | ||
const isSubQuery = options.subQuery === void 0 ? options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation : options.subQuery; | ||
let fragment = ""; | ||
let orders = {}; | ||
if (options.order) { | ||
orders = this.getQueryOrders(options, model, isSubQuery); | ||
} | ||
if (options.limit || options.offset) { | ||
if (!options.order || !options.order.length || options.include && !orders.subQueryOrder.length) { | ||
if (!options.order || options.order.length === 0 || options.include && orders.subQueryOrder.length === 0) { | ||
const tablePkFragment = `${this.quoteTable(options.tableAs || model.name)}.${this.quoteIdentifier(model.primaryKeyField)}`; | ||
@@ -953,7 +814,6 @@ if (!options.order || !options.order.length) { | ||
} else { | ||
const orderFieldNames = _.map(options.order, order => order[0]); | ||
const orderFieldNames = _.map(options.order, (order) => order[0]); | ||
const primaryKeyFieldAlreadyPresent = _.includes(orderFieldNames, model.primaryKeyField); | ||
if (!primaryKeyFieldAlreadyPresent) { | ||
fragment += options.order && !isSubQuery ? ', ' : ' ORDER BY '; | ||
fragment += options.order && !isSubQuery ? ", " : " ORDER BY "; | ||
fragment += tablePkFragment; | ||
@@ -963,7 +823,5 @@ } | ||
} | ||
if (options.offset || options.limit) { | ||
fragment += ` OFFSET ${this.escape(offset)} ROWS`; | ||
} | ||
if (options.limit) { | ||
@@ -973,16 +831,15 @@ fragment += ` FETCH NEXT ${this.escape(options.limit)} ROWS ONLY`; | ||
} | ||
return fragment; | ||
} | ||
booleanValue(value) { | ||
return value ? 1 : 0; | ||
} | ||
quoteIdentifier(identifier, force) { | ||
return `[${identifier.replace(/[[\]']+/g, "")}]`; | ||
} | ||
} | ||
// private methods | ||
function wrapSingleQuote(identifier) { | ||
return Utils.addTicks(Utils.removeTicks(identifier, "'"), "'"); | ||
} | ||
module.exports = MSSQLQueryGenerator; | ||
//# sourceMappingURL=query-generator.js.map |
@@ -1,26 +0,29 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Utils = require('../../utils'); | ||
const QueryTypes = require('../../query-types'); | ||
const Op = require('../../operators'); | ||
const { QueryInterface } = require('../abstract/query-interface'); | ||
/** | ||
* The interface that Sequelize uses to talk with MSSQL database | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const _ = require("lodash"); | ||
const Utils = require("../../utils"); | ||
const QueryTypes = require("../../query-types"); | ||
const Op = require("../../operators"); | ||
const { QueryInterface } = require("../abstract/query-interface"); | ||
class MSSqlQueryInterface extends QueryInterface { | ||
/** | ||
* A wrapper that fixes MSSQL's inability to cleanly remove columns from existing tables if they have a default constraint. | ||
* | ||
* @override | ||
*/ | ||
async removeColumn(tableName, attributeName, options) { | ||
options = { raw: true, ...options || {} }; | ||
options = __spreadValues({ raw: true }, options || {}); | ||
const findConstraintSql = this.queryGenerator.getDefaultConstraintQuery(tableName, attributeName); | ||
const [results0] = await this.sequelize.query(findConstraintSql, options); | ||
if (results0.length) { | ||
// No default constraint found -- we can cleanly remove the column | ||
const dropConstraintSql = this.queryGenerator.dropConstraintQuery(tableName, results0[0].name); | ||
@@ -32,7 +35,5 @@ await this.sequelize.query(dropConstraintSql, options); | ||
if (results.length) { | ||
// No foreign key constraints found, so we can remove the column | ||
const dropForeignKeySql = this.queryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name); | ||
await this.sequelize.query(dropForeignKeySql, options); | ||
} | ||
//Check if the current column is a primaryKey | ||
const primaryKeyConstraintSql = this.queryGenerator.getPrimaryKeyConstraintQuery(tableName, attributeName); | ||
@@ -47,20 +48,11 @@ const [result] = await this.sequelize.query(primaryKeyConstraintSql, options); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async upsert(tableName, insertValues, updateValues, where, options) { | ||
const model = options.model; | ||
const wheres = []; | ||
options = { ...options }; | ||
options = __spreadValues({}, options); | ||
if (!Utils.isWhereEmpty(where)) { | ||
wheres.push(where); | ||
} | ||
// Lets combine unique keys and indexes into one | ||
let indexes = Object.values(model.uniqueKeys).map(item => item.fields); | ||
indexes = indexes.concat(Object.values(model._indexes).filter(item => item.unique).map(item => item.fields)); | ||
let indexes = Object.values(model.uniqueKeys).map((item) => item.fields); | ||
indexes = indexes.concat(Object.values(model._indexes).filter((item) => item.unique).map((item) => item.fields)); | ||
const attributes = Object.keys(insertValues); | ||
@@ -76,8 +68,5 @@ for (const index of indexes) { | ||
} | ||
where = { [Op.or]: wheres }; | ||
options.type = QueryTypes.UPSERT; | ||
options.raw = true; | ||
const sql = this.queryGenerator.upsertQuery(tableName, insertValues, updateValues, where, model, options); | ||
@@ -87,3 +76,3 @@ return await this.sequelize.query(sql, options); | ||
} | ||
exports.MSSqlQueryInterface = MSSqlQueryInterface; | ||
//# sourceMappingURL=query-interface.js.map |
@@ -1,27 +0,24 @@ | ||
'use strict'; | ||
const AbstractQuery = require('../abstract/query'); | ||
const sequelizeErrors = require('../../errors'); | ||
const parserStore = require('../parserStore')('mssql'); | ||
const _ = require('lodash'); | ||
const { logger } = require('../../utils/logger'); | ||
const debug = logger.debugContext('sql:mssql'); | ||
"use strict"; | ||
const AbstractQuery = require("../abstract/query"); | ||
const sequelizeErrors = require("../../errors"); | ||
const parserStore = require("../parserStore")("mssql"); | ||
const _ = require("lodash"); | ||
const { logger } = require("../../utils/logger"); | ||
const debug = logger.debugContext("sql:mssql"); | ||
function getScale(aNum) { | ||
if (!Number.isFinite(aNum)) return 0; | ||
if (!Number.isFinite(aNum)) | ||
return 0; | ||
let e = 1; | ||
while (Math.round(aNum * e) / e !== aNum) e *= 10; | ||
while (Math.round(aNum * e) / e !== aNum) | ||
e *= 10; | ||
return Math.log10(e); | ||
} | ||
class Query extends AbstractQuery { | ||
getInsertIdField() { | ||
return 'id'; | ||
return "id"; | ||
} | ||
getSQLTypeFromJsType(value, TYPES) { | ||
const paramType = { type: TYPES.VarChar, typeOptions: {} }; | ||
paramType.type = TYPES.NVarChar; | ||
if (typeof value === 'number') { | ||
if (typeof value === "number") { | ||
if (Number.isInteger(value)) { | ||
@@ -35,6 +32,5 @@ if (value >= -2147483648 && value <= 2147483647) { | ||
paramType.type = TYPES.Numeric; | ||
//Default to a reasonable numeric precision/scale pending more sophisticated logic | ||
paramType.typeOptions = { precision: 30, scale: getScale(value) }; | ||
} | ||
} else if (typeof value === 'boolean') { | ||
} else if (typeof value === "boolean") { | ||
paramType.type = TYPES.Bit; | ||
@@ -47,27 +43,21 @@ } | ||
} | ||
async _run(connection, sql, parameters) { | ||
async _run(connection, sql, parameters, errStack) { | ||
this.sql = sql; | ||
const { options } = this; | ||
const complete = this._logQuery(sql, debug, parameters); | ||
const query = new Promise((resolve, reject) => { | ||
// TRANSACTION SUPPORT | ||
if (sql.startsWith('BEGIN TRANSACTION')) { | ||
return connection.beginTransaction(error => error ? reject(error) : resolve([]), options.transaction.name, connection.lib.ISOLATION_LEVEL[options.isolationLevel]); | ||
if (sql.startsWith("BEGIN TRANSACTION")) { | ||
return connection.beginTransaction((error) => error ? reject(error) : resolve([]), options.transaction.name, connection.lib.ISOLATION_LEVEL[options.isolationLevel]); | ||
} | ||
if (sql.startsWith('COMMIT TRANSACTION')) { | ||
return connection.commitTransaction(error => error ? reject(error) : resolve([])); | ||
if (sql.startsWith("COMMIT TRANSACTION")) { | ||
return connection.commitTransaction((error) => error ? reject(error) : resolve([])); | ||
} | ||
if (sql.startsWith('ROLLBACK TRANSACTION')) { | ||
return connection.rollbackTransaction(error => error ? reject(error) : resolve([]), options.transaction.name); | ||
if (sql.startsWith("ROLLBACK TRANSACTION")) { | ||
return connection.rollbackTransaction((error) => error ? reject(error) : resolve([]), options.transaction.name); | ||
} | ||
if (sql.startsWith('SAVE TRANSACTION')) { | ||
return connection.saveTransaction(error => error ? reject(error) : resolve([]), options.transaction.name); | ||
if (sql.startsWith("SAVE TRANSACTION")) { | ||
return connection.saveTransaction((error) => error ? reject(error) : resolve([]), options.transaction.name); | ||
} | ||
const rows = []; | ||
const request = new connection.lib.Request(sql, (err, rowCount) => err ? reject(err) : resolve([rows, rowCount])); | ||
const rows2 = []; | ||
const request = new connection.lib.Request(sql, (err, rowCount2) => err ? reject(err) : resolve([rows2, rowCount2])); | ||
if (parameters) { | ||
@@ -79,12 +69,8 @@ _.forOwn(parameters, (value, key) => { | ||
} | ||
request.on('row', columns => { | ||
rows.push(columns); | ||
request.on("row", (columns) => { | ||
rows2.push(columns); | ||
}); | ||
connection.execSql(request); | ||
}); | ||
let rows, rowCount; | ||
try { | ||
@@ -95,10 +81,7 @@ [rows, rowCount] = await query; | ||
err.parameters = parameters; | ||
throw this.formatError(err); | ||
throw this.formatError(err, errStack); | ||
} | ||
complete(); | ||
if (Array.isArray(rows)) { | ||
rows = rows.map(columns => { | ||
rows = rows.map((columns) => { | ||
const row = {}; | ||
@@ -109,3 +92,2 @@ for (const column of columns) { | ||
let value = column.value; | ||
if (value !== null & !!parse) { | ||
@@ -119,41 +101,20 @@ value = parse(value); | ||
} | ||
return this.formatResults(rows, rowCount); | ||
} | ||
run(sql, parameters) { | ||
return this.connection.queue.enqueue(() => this._run(this.connection, sql, parameters)); | ||
const errForStack = new Error(); | ||
return this.connection.queue.enqueue(() => this._run(this.connection, sql, parameters, errForStack.stack)); | ||
} | ||
static formatBindParameters(sql, values, dialect) { | ||
const bindParam = {}; | ||
const replacementFunc = (match, key, values) => { | ||
if (values[key] !== undefined) { | ||
bindParam[key] = values[key]; | ||
const replacementFunc = (match, key, values2) => { | ||
if (values2[key] !== void 0) { | ||
bindParam[key] = values2[key]; | ||
return `@${key}`; | ||
} | ||
return undefined; | ||
return void 0; | ||
}; | ||
sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0]; | ||
return [sql, bindParam]; | ||
} | ||
/** | ||
* High level function that handles the results of a query execution. | ||
* | ||
* @param {Array} data - The result of the query execution. | ||
* @param {number} rowCount | ||
* @private | ||
* @example | ||
* Example: | ||
* query.formatResults([ | ||
* { | ||
* id: 1, // this is from the main table | ||
* attr2: 'snafu', // this is from the main table | ||
* Tasks.id: 1, // this is from the associated table | ||
* Tasks.title: 'task' // this is from the associated table | ||
* } | ||
* ]) | ||
*/ | ||
formatResults(data, rowCount) { | ||
@@ -171,20 +132,15 @@ if (this.isInsertQuery(data)) { | ||
if (_result.Default) { | ||
_result.Default = _result.Default.replace("('", '').replace("')", '').replace(/'/g, ''); | ||
_result.Default = _result.Default.replace("('", "").replace("')", "").replace(/'/g, ""); | ||
} | ||
result[_result.Name] = { | ||
type: _result.Type.toUpperCase(), | ||
allowNull: _result.IsNull === 'YES' ? true : false, | ||
allowNull: _result.IsNull === "YES" ? true : false, | ||
defaultValue: _result.Default, | ||
primaryKey: _result.Constraint === 'PRIMARY KEY', | ||
primaryKey: _result.Constraint === "PRIMARY KEY", | ||
autoIncrement: _result.IsIdentity === 1, | ||
comment: _result.Comment | ||
}; | ||
if ( | ||
result[_result.Name].type.includes('CHAR') | ||
&& _result.Length | ||
) { | ||
if (result[_result.Name].type.includes("CHAR") && _result.Length) { | ||
if (_result.Length === -1) { | ||
result[_result.Name].type += '(MAX)'; | ||
result[_result.Name].type += "(MAX)"; | ||
} else { | ||
@@ -210,3 +166,2 @@ result[_result.Name].type += `(${_result.Length})`; | ||
} | ||
return rowCount; | ||
@@ -224,4 +179,7 @@ } | ||
if (this.isUpsertQuery()) { | ||
if (data && data.length === 0) { | ||
return [this.instance || data, false]; | ||
} | ||
this.handleInsertQuery(data); | ||
return [this.instance || data, data[0].$action === 'INSERT']; | ||
return [this.instance || data, data[0].$action === "INSERT"]; | ||
} | ||
@@ -239,5 +197,4 @@ if (this.isUpdateQuery()) { | ||
} | ||
handleShowTablesQuery(results) { | ||
return results.map(resultSet => { | ||
return results.map((resultSet) => { | ||
return { | ||
@@ -249,6 +206,4 @@ tableName: resultSet.TABLE_NAME, | ||
} | ||
handleShowConstraintsQuery(data) { | ||
//Convert snake_case keys to camelCase as it's generated by stored procedure | ||
return data.slice(1).map(result => { | ||
return data.slice(1).map((result) => { | ||
const constraint = {}; | ||
@@ -261,6 +216,4 @@ for (const key in result) { | ||
} | ||
formatError(err) { | ||
formatError(err, errStack) { | ||
let match; | ||
match = err.message.match(/Violation of (?:UNIQUE|PRIMARY) KEY constraint '([^']*)'. Cannot insert duplicate key in object '.*'.(:? The duplicate key value is \((.*)\).)?/); | ||
@@ -271,4 +224,3 @@ match = match || err.message.match(/Cannot insert duplicate key row in object .* with unique index '(.*)'/); | ||
const uniqueKey = this.model && this.model.uniqueKeys[match[1]]; | ||
let message = 'Validation error'; | ||
let message = "Validation error"; | ||
if (uniqueKey && !!uniqueKey.msg) { | ||
@@ -278,3 +230,3 @@ message = uniqueKey.msg; | ||
if (match[3]) { | ||
const values = match[3].split(',').map(part => part.trim()); | ||
const values = match[3].split(",").map((part) => part.trim()); | ||
if (uniqueKey) { | ||
@@ -286,21 +238,9 @@ fields = _.zipObject(uniqueKey.fields, values); | ||
} | ||
const errors = []; | ||
_.forOwn(fields, (value, field) => { | ||
errors.push(new sequelizeErrors.ValidationErrorItem( | ||
this.getUniqueConstraintErrorMessage(field), | ||
'unique violation', // sequelizeErrors.ValidationErrorItem.Origins.DB, | ||
field, | ||
value, | ||
this.instance, | ||
'not_unique' | ||
)); | ||
errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, value, this.instance, "not_unique")); | ||
}); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields }); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack }); | ||
} | ||
match = err.message.match(/Failed on step '(.*)'.Could not create constraint. See previous errors./) || | ||
err.message.match(/The DELETE statement conflicted with the REFERENCE constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) || | ||
err.message.match(/The (?:INSERT|MERGE|UPDATE) statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./); | ||
match = err.message.match(/Failed on step '(.*)'.Could not create constraint. See previous errors./) || err.message.match(/The DELETE statement conflicted with the REFERENCE constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) || err.message.match(/The (?:INSERT|MERGE|UPDATE) statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./); | ||
if (match && match.length > 0) { | ||
@@ -310,13 +250,12 @@ return new sequelizeErrors.ForeignKeyConstraintError({ | ||
index: match[1], | ||
parent: err | ||
parent: err, | ||
stack: errStack | ||
}); | ||
} | ||
match = err.message.match(/Could not drop constraint. See previous errors./); | ||
if (match && match.length > 0) { | ||
let constraint = err.sql.match(/(?:constraint|index) \[(.+?)\]/i); | ||
constraint = constraint ? constraint[1] : undefined; | ||
constraint = constraint ? constraint[1] : void 0; | ||
let table = err.sql.match(/table \[(.+?)\]/i); | ||
table = table ? table[1] : undefined; | ||
table = table ? table[1] : void 0; | ||
return new sequelizeErrors.UnknownConstraintError({ | ||
@@ -326,25 +265,19 @@ message: match[1], | ||
table, | ||
parent: err | ||
parent: err, | ||
stack: errStack | ||
}); | ||
} | ||
return new sequelizeErrors.DatabaseError(err); | ||
return new sequelizeErrors.DatabaseError(err, { stack: errStack }); | ||
} | ||
isShowOrDescribeQuery() { | ||
let result = false; | ||
result = result || this.sql.toLowerCase().startsWith("select c.column_name as 'name', c.data_type as 'type', c.is_nullable as 'isnull'"); | ||
result = result || this.sql.toLowerCase().startsWith('select tablename = t.name, name = ind.name,'); | ||
result = result || this.sql.toLowerCase().startsWith('exec sys.sp_helpindex @objname'); | ||
result = result || this.sql.toLowerCase().startsWith("select tablename = t.name, name = ind.name,"); | ||
result = result || this.sql.toLowerCase().startsWith("exec sys.sp_helpindex @objname"); | ||
return result; | ||
} | ||
isShowIndexesQuery() { | ||
return this.sql.toLowerCase().startsWith('exec sys.sp_helpindex @objname'); | ||
return this.sql.toLowerCase().startsWith("exec sys.sp_helpindex @objname"); | ||
} | ||
handleShowIndexesQuery(data) { | ||
// Group by index name, and collect all fields | ||
data = data.reduce((acc, item) => { | ||
@@ -355,14 +288,12 @@ if (!(item.index_name in acc)) { | ||
} | ||
item.index_keys.split(',').forEach(column => { | ||
item.index_keys.split(",").forEach((column) => { | ||
let columnName = column.trim(); | ||
if (columnName.includes('(-)')) { | ||
columnName = columnName.replace('(-)', ''); | ||
if (columnName.includes("(-)")) { | ||
columnName = columnName.replace("(-)", ""); | ||
} | ||
acc[item.index_name].fields.push({ | ||
attribute: columnName, | ||
length: undefined, | ||
order: column.includes('(-)') ? 'DESC' : 'ASC', | ||
collate: undefined | ||
length: void 0, | ||
order: column.includes("(-)") ? "DESC" : "ASC", | ||
collate: void 0 | ||
}); | ||
@@ -373,24 +304,18 @@ }); | ||
}, {}); | ||
return _.map(data, item => ({ | ||
primary: item.index_name.toLowerCase().startsWith('pk'), | ||
return _.map(data, (item) => ({ | ||
primary: item.index_name.toLowerCase().startsWith("pk"), | ||
fields: item.fields, | ||
name: item.index_name, | ||
tableName: undefined, | ||
unique: item.index_description.toLowerCase().includes('unique'), | ||
type: undefined | ||
tableName: void 0, | ||
unique: item.index_description.toLowerCase().includes("unique"), | ||
type: void 0 | ||
})); | ||
} | ||
handleInsertQuery(results, metaData) { | ||
if (this.instance) { | ||
// add the inserted row id to the instance | ||
const autoIncrementAttribute = this.model.autoIncrementAttribute; | ||
let id = null; | ||
let autoIncrementAttributeAlias = null; | ||
if (Object.prototype.hasOwnProperty.call(this.model.rawAttributes, autoIncrementAttribute) && | ||
this.model.rawAttributes[autoIncrementAttribute].field !== undefined) | ||
if (Object.prototype.hasOwnProperty.call(this.model.rawAttributes, autoIncrementAttribute) && this.model.rawAttributes[autoIncrementAttribute].field !== void 0) | ||
autoIncrementAttributeAlias = this.model.rawAttributes[autoIncrementAttribute].field; | ||
id = id || results && results[0][this.getInsertIdField()]; | ||
@@ -400,5 +325,3 @@ id = id || metaData && metaData[this.getInsertIdField()]; | ||
id = id || autoIncrementAttributeAlias && results && results[0][autoIncrementAttributeAlias]; | ||
this.instance[autoIncrementAttribute] = id; | ||
if (this.instance.dataValues) { | ||
@@ -408,5 +331,3 @@ for (const key in results[0]) { | ||
const record = results[0][key]; | ||
const attr = _.find(this.model.rawAttributes, attribute => attribute.fieldName === key || attribute.field === key); | ||
const attr = _.find(this.model.rawAttributes, (attribute) => attribute.fieldName === key || attribute.field === key); | ||
this.instance.dataValues[attr && attr.fieldName || key] = record; | ||
@@ -416,9 +337,8 @@ } | ||
} | ||
} | ||
} | ||
} | ||
module.exports = Query; | ||
module.exports.Query = Query; | ||
module.exports.default = Query; | ||
//# sourceMappingURL=query.js.map |
@@ -1,21 +0,26 @@ | ||
'use strict'; | ||
const AbstractConnectionManager = require('../abstract/connection-manager'); | ||
const SequelizeErrors = require('../../errors'); | ||
const { logger } = require('../../utils/logger'); | ||
const DataTypes = require('../../data-types').mysql; | ||
const momentTz = require('moment-timezone'); | ||
const debug = logger.debugContext('connection:mysql'); | ||
const parserStore = require('../parserStore')('mysql'); | ||
const { promisify } = require('util'); | ||
/** | ||
* MySQL Connection Manager | ||
* | ||
* Get connections, validate and disconnect them. | ||
* AbstractConnectionManager pooling use it to handle MySQL specific connections | ||
* Use https://github.com/sidorares/node-mysql2 to connect with MySQL server | ||
* | ||
* @private | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const AbstractConnectionManager = require("../abstract/connection-manager"); | ||
const SequelizeErrors = require("../../errors"); | ||
const { logger } = require("../../utils/logger"); | ||
const DataTypes = require("../../data-types").mysql; | ||
const momentTz = require("moment-timezone"); | ||
const debug = logger.debugContext("connection:mysql"); | ||
const parserStore = require("../parserStore")("mysql"); | ||
const { promisify } = require("util"); | ||
class ConnectionManager extends AbstractConnectionManager { | ||
@@ -25,14 +30,11 @@ constructor(dialect, sequelize) { | ||
super(dialect, sequelize); | ||
this.lib = this._loadDialectModule('mysql2'); | ||
this.lib = this._loadDialectModule("mysql2"); | ||
this.refreshTypeParser(DataTypes); | ||
} | ||
_refreshTypeParser(dataType) { | ||
parserStore.refresh(dataType); | ||
} | ||
_clearTypeParser() { | ||
parserStore.clear(); | ||
} | ||
static _typecast(field, next) { | ||
@@ -44,18 +46,8 @@ if (parserStore.get(field.type)) { | ||
} | ||
/** | ||
* Connect with MySQL database based on config, Handle any errors in connection | ||
* Set the pool handlers on connection.error | ||
* Also set proper timezone once connection is connected. | ||
* | ||
* @param {object} config | ||
* @returns {Promise<Connection>} | ||
* @private | ||
*/ | ||
async connect(config) { | ||
const connectionConfig = { | ||
const connectionConfig = __spreadValues({ | ||
host: config.host, | ||
port: config.port, | ||
user: config.username, | ||
flags: '-FOUND_ROWS', | ||
flags: "-FOUND_ROWS", | ||
password: config.password, | ||
@@ -66,62 +58,46 @@ database: config.database, | ||
bigNumberStrings: false, | ||
supportBigNumbers: true, | ||
...config.dialectOptions | ||
}; | ||
supportBigNumbers: true | ||
}, config.dialectOptions); | ||
try { | ||
const connection = await new Promise((resolve, reject) => { | ||
const connection = this.lib.createConnection(connectionConfig); | ||
const errorHandler = e => { | ||
// clean up connect & error event if there is error | ||
connection.removeListener('connect', connectHandler); | ||
connection.removeListener('error', connectHandler); | ||
const connection2 = this.lib.createConnection(connectionConfig); | ||
const errorHandler = (e) => { | ||
connection2.removeListener("connect", connectHandler); | ||
connection2.removeListener("error", connectHandler); | ||
reject(e); | ||
}; | ||
const connectHandler = () => { | ||
// clean up error event if connected | ||
connection.removeListener('error', errorHandler); | ||
resolve(connection); | ||
connection2.removeListener("error", errorHandler); | ||
resolve(connection2); | ||
}; | ||
// don't use connection.once for error event handling here | ||
// mysql2 emit error two times in case handshake was failed | ||
// first error is protocol_lost and second is timeout | ||
// if we will use `once.error` node process will crash on 2nd error emit | ||
connection.on('error', errorHandler); | ||
connection.once('connect', connectHandler); | ||
connection2.on("error", errorHandler); | ||
connection2.once("connect", connectHandler); | ||
}); | ||
debug('connection acquired'); | ||
connection.on('error', error => { | ||
debug("connection acquired"); | ||
connection.on("error", (error) => { | ||
switch (error.code) { | ||
case 'ESOCKET': | ||
case 'ECONNRESET': | ||
case 'EPIPE': | ||
case 'PROTOCOL_CONNECTION_LOST': | ||
case "ESOCKET": | ||
case "ECONNRESET": | ||
case "EPIPE": | ||
case "PROTOCOL_CONNECTION_LOST": | ||
this.pool.destroy(connection); | ||
} | ||
}); | ||
if (!this.sequelize.config.keepDefaultTimezone) { | ||
// set timezone for this connection | ||
// but named timezone are not directly supported in mysql, so get its offset first | ||
let tzOffset = this.sequelize.options.timezone; | ||
tzOffset = /\//.test(tzOffset) ? momentTz.tz(tzOffset).format('Z') : tzOffset; | ||
await promisify(cb => connection.query(`SET time_zone = '${tzOffset}'`, cb))(); | ||
tzOffset = /\//.test(tzOffset) ? momentTz.tz(tzOffset).format("Z") : tzOffset; | ||
await promisify((cb) => connection.query(`SET time_zone = '${tzOffset}'`, cb))(); | ||
} | ||
return connection; | ||
} catch (err) { | ||
switch (err.code) { | ||
case 'ECONNREFUSED': | ||
case "ECONNREFUSED": | ||
throw new SequelizeErrors.ConnectionRefusedError(err); | ||
case 'ER_ACCESS_DENIED_ERROR': | ||
case "ER_ACCESS_DENIED_ERROR": | ||
throw new SequelizeErrors.AccessDeniedError(err); | ||
case 'ENOTFOUND': | ||
case "ENOTFOUND": | ||
throw new SequelizeErrors.HostNotFoundError(err); | ||
case 'EHOSTUNREACH': | ||
case "EHOSTUNREACH": | ||
throw new SequelizeErrors.HostNotReachableError(err); | ||
case 'EINVAL': | ||
case "EINVAL": | ||
throw new SequelizeErrors.InvalidConnectionError(err); | ||
@@ -133,24 +109,16 @@ default: | ||
} | ||
async disconnect(connection) { | ||
// Don't disconnect connections with CLOSED state | ||
if (connection._closing) { | ||
debug('connection tried to disconnect but was already at CLOSED state'); | ||
debug("connection tried to disconnect but was already at CLOSED state"); | ||
return; | ||
} | ||
return await promisify(callback => connection.end(callback))(); | ||
return await promisify((callback) => connection.end(callback))(); | ||
} | ||
validate(connection) { | ||
return connection | ||
&& !connection._fatalError | ||
&& !connection._protocolError | ||
&& !connection._closing | ||
&& !connection.stream.destroyed; | ||
return connection && !connection._fatalError && !connection._protocolError && !connection._closing && !connection.stream.destroyed; | ||
} | ||
} | ||
module.exports = ConnectionManager; | ||
module.exports.ConnectionManager = ConnectionManager; | ||
module.exports.default = ConnectionManager; | ||
//# sourceMappingURL=connection-manager.js.map |
@@ -1,38 +0,29 @@ | ||
'use strict'; | ||
const wkx = require('wkx'); | ||
const _ = require('lodash'); | ||
const moment = require('moment-timezone'); | ||
module.exports = BaseTypes => { | ||
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.mysql.com/doc/refman/5.7/en/data-types.html'; | ||
/** | ||
* types: [buffer_type, ...] | ||
* | ||
* @see buffer_type here https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-statement-type-codes.html | ||
* @see hex here https://github.com/sidorares/node-mysql2/blob/master/lib/constants/types.js | ||
*/ | ||
BaseTypes.DATE.types.mysql = ['DATETIME']; | ||
BaseTypes.STRING.types.mysql = ['VAR_STRING']; | ||
BaseTypes.CHAR.types.mysql = ['STRING']; | ||
BaseTypes.TEXT.types.mysql = ['BLOB']; | ||
BaseTypes.TINYINT.types.mysql = ['TINY']; | ||
BaseTypes.SMALLINT.types.mysql = ['SHORT']; | ||
BaseTypes.MEDIUMINT.types.mysql = ['INT24']; | ||
BaseTypes.INTEGER.types.mysql = ['LONG']; | ||
BaseTypes.BIGINT.types.mysql = ['LONGLONG']; | ||
BaseTypes.FLOAT.types.mysql = ['FLOAT']; | ||
BaseTypes.TIME.types.mysql = ['TIME']; | ||
BaseTypes.DATEONLY.types.mysql = ['DATE']; | ||
BaseTypes.BOOLEAN.types.mysql = ['TINY']; | ||
BaseTypes.BLOB.types.mysql = ['TINYBLOB', 'BLOB', 'LONGBLOB']; | ||
BaseTypes.DECIMAL.types.mysql = ['NEWDECIMAL']; | ||
"use strict"; | ||
const wkx = require("wkx"); | ||
const _ = require("lodash"); | ||
const momentTz = require("moment-timezone"); | ||
const moment = require("moment"); | ||
module.exports = (BaseTypes) => { | ||
BaseTypes.ABSTRACT.prototype.dialectTypes = "https://dev.mysql.com/doc/refman/5.7/en/data-types.html"; | ||
BaseTypes.DATE.types.mysql = ["DATETIME"]; | ||
BaseTypes.STRING.types.mysql = ["VAR_STRING"]; | ||
BaseTypes.CHAR.types.mysql = ["STRING"]; | ||
BaseTypes.TEXT.types.mysql = ["BLOB"]; | ||
BaseTypes.TINYINT.types.mysql = ["TINY"]; | ||
BaseTypes.SMALLINT.types.mysql = ["SHORT"]; | ||
BaseTypes.MEDIUMINT.types.mysql = ["INT24"]; | ||
BaseTypes.INTEGER.types.mysql = ["LONG"]; | ||
BaseTypes.BIGINT.types.mysql = ["LONGLONG"]; | ||
BaseTypes.FLOAT.types.mysql = ["FLOAT"]; | ||
BaseTypes.TIME.types.mysql = ["TIME"]; | ||
BaseTypes.DATEONLY.types.mysql = ["DATE"]; | ||
BaseTypes.BOOLEAN.types.mysql = ["TINY"]; | ||
BaseTypes.BLOB.types.mysql = ["TINYBLOB", "BLOB", "LONGBLOB"]; | ||
BaseTypes.DECIMAL.types.mysql = ["NEWDECIMAL"]; | ||
BaseTypes.UUID.types.mysql = false; | ||
BaseTypes.ENUM.types.mysql = false; | ||
BaseTypes.REAL.types.mysql = ['DOUBLE']; | ||
BaseTypes.DOUBLE.types.mysql = ['DOUBLE']; | ||
BaseTypes.GEOMETRY.types.mysql = ['GEOMETRY']; | ||
BaseTypes.JSON.types.mysql = ['JSON']; | ||
BaseTypes.REAL.types.mysql = ["DOUBLE"]; | ||
BaseTypes.DOUBLE.types.mysql = ["DOUBLE"]; | ||
BaseTypes.GEOMETRY.types.mysql = ["GEOMETRY"]; | ||
BaseTypes.JSON.types.mysql = ["JSON"]; | ||
class DECIMAL extends BaseTypes.DECIMAL { | ||
@@ -42,6 +33,6 @@ toSql() { | ||
if (this._unsigned) { | ||
definition += ' UNSIGNED'; | ||
definition += " UNSIGNED"; | ||
} | ||
if (this._zerofill) { | ||
definition += ' ZEROFILL'; | ||
definition += " ZEROFILL"; | ||
} | ||
@@ -51,14 +42,14 @@ return definition; | ||
} | ||
class DATE extends BaseTypes.DATE { | ||
toSql() { | ||
return this._length ? `DATETIME(${this._length})` : 'DATETIME'; | ||
return this._length ? `DATETIME(${this._length})` : "DATETIME"; | ||
} | ||
_stringify(date, options) { | ||
date = this._applyTimezone(date, options); | ||
// Fractional DATETIMEs only supported on MySQL 5.6.4+ | ||
if (!moment.isMoment(date)) { | ||
date = this._applyTimezone(date, options); | ||
} | ||
if (this._length) { | ||
return date.format('YYYY-MM-DD HH:mm:ss.SSS'); | ||
return date.format("YYYY-MM-DD HH:mm:ss.SSS"); | ||
} | ||
return date.format('YYYY-MM-DD HH:mm:ss'); | ||
return date.format("YYYY-MM-DD HH:mm:ss"); | ||
} | ||
@@ -70,6 +61,5 @@ static parse(value, options) { | ||
} | ||
if (moment.tz.zone(options.timezone)) { | ||
value = moment.tz(value, options.timezone).toDate(); | ||
} | ||
else { | ||
if (momentTz.tz.zone(options.timezone)) { | ||
value = momentTz.tz(value, options.timezone).toDate(); | ||
} else { | ||
value = new Date(`${value} ${options.timezone}`); | ||
@@ -80,3 +70,2 @@ } | ||
} | ||
class DATEONLY extends BaseTypes.DATEONLY { | ||
@@ -89,8 +78,6 @@ static parse(value) { | ||
toSql() { | ||
return 'CHAR(36) BINARY'; | ||
return "CHAR(36) BINARY"; | ||
} | ||
} | ||
const SUPPORTED_GEOMETRY_TYPES = ['POINT', 'LINESTRING', 'POLYGON']; | ||
const SUPPORTED_GEOMETRY_TYPES = ["POINT", "LINESTRING", "POLYGON"]; | ||
class GEOMETRY extends BaseTypes.GEOMETRY { | ||
@@ -107,12 +94,9 @@ constructor(type, srid) { | ||
} | ||
throw new Error(`Supported geometry types are: ${SUPPORTED_GEOMETRY_TYPES.join(', ')}`); | ||
throw new Error(`Supported geometry types are: ${SUPPORTED_GEOMETRY_TYPES.join(", ")}`); | ||
} | ||
static parse(value) { | ||
value = value.buffer(); | ||
// Empty buffer, MySQL doesn't support POINT EMPTY | ||
// check, https://dev.mysql.com/worklog/task/?id=2381 | ||
if (!value || value.length === 0) { | ||
return null; | ||
} | ||
// For some reason, discard the first 4 bytes | ||
value = value.slice(4); | ||
@@ -125,15 +109,12 @@ return wkx.Geometry.parse(value).toGeoJSON({ shortCrs: true }); | ||
} | ||
class ENUM extends BaseTypes.ENUM { | ||
toSql(options) { | ||
return `ENUM(${this.values.map(value => options.escape(value)).join(', ')})`; | ||
return `ENUM(${this.values.map((value) => options.escape(value)).join(", ")})`; | ||
} | ||
} | ||
class JSONTYPE extends BaseTypes.JSON { | ||
_stringify(value, options) { | ||
return options.operation === 'where' && typeof value === 'string' ? value : JSON.stringify(value); | ||
return options.operation === "where" && typeof value === "string" ? value : JSON.stringify(value); | ||
} | ||
} | ||
return { | ||
@@ -149,1 +130,2 @@ ENUM, | ||
}; | ||
//# sourceMappingURL=data-types.js.map |
@@ -1,11 +0,9 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const AbstractDialect = require('../abstract'); | ||
const ConnectionManager = require('./connection-manager'); | ||
const Query = require('./query'); | ||
const QueryGenerator = require('./query-generator'); | ||
const DataTypes = require('../../data-types').mysql; | ||
const { MySQLQueryInterface } = require('./query-interface'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const AbstractDialect = require("../abstract"); | ||
const ConnectionManager = require("./connection-manager"); | ||
const Query = require("./query"); | ||
const QueryGenerator = require("./query-generator"); | ||
const DataTypes = require("../../data-types").mysql; | ||
const { MySQLQueryInterface } = require("./query-interface"); | ||
class MysqlDialect extends AbstractDialect { | ||
@@ -23,12 +21,11 @@ constructor(sequelize) { | ||
} | ||
MysqlDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), { | ||
'VALUES ()': true, | ||
'LIMIT ON UPDATE': true, | ||
"VALUES ()": true, | ||
"LIMIT ON UPDATE": true, | ||
lock: true, | ||
forShare: 'LOCK IN SHARE MODE', | ||
forShare: "LOCK IN SHARE MODE", | ||
settingIsolationLevelDuringTransaction: false, | ||
inserts: { | ||
ignoreDuplicates: ' IGNORE', | ||
updateOnDuplicate: ' ON DUPLICATE KEY UPDATE' | ||
ignoreDuplicates: " IGNORE", | ||
updateOnDuplicate: " ON DUPLICATE KEY UPDATE" | ||
}, | ||
@@ -53,12 +50,11 @@ index: { | ||
}); | ||
MysqlDialect.prototype.defaultVersion = '5.7.0'; | ||
MysqlDialect.prototype.defaultVersion = "5.7.0"; | ||
MysqlDialect.prototype.Query = Query; | ||
MysqlDialect.prototype.QueryGenerator = QueryGenerator; | ||
MysqlDialect.prototype.DataTypes = DataTypes; | ||
MysqlDialect.prototype.name = 'mysql'; | ||
MysqlDialect.prototype.TICK_CHAR = '`'; | ||
MysqlDialect.prototype.name = "mysql"; | ||
MysqlDialect.prototype.TICK_CHAR = "`"; | ||
MysqlDialect.prototype.TICK_CHAR_LEFT = MysqlDialect.prototype.TICK_CHAR; | ||
MysqlDialect.prototype.TICK_CHAR_RIGHT = MysqlDialect.prototype.TICK_CHAR; | ||
module.exports = MysqlDialect; | ||
//# sourceMappingURL=index.js.map |
@@ -1,10 +0,26 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Utils = require('../../utils'); | ||
const AbstractQueryGenerator = require('../abstract/query-generator'); | ||
const util = require('util'); | ||
const Op = require('../../operators'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const _ = require("lodash"); | ||
const Utils = require("../../utils"); | ||
const AbstractQueryGenerator = require("../abstract/query-generator"); | ||
const util = require("util"); | ||
const Op = require("../../operators"); | ||
const JSON_FUNCTION_REGEX = /^\s*((?:[a-z]+_){0,2}jsonb?(?:_[a-z]+){0,2})\([^)]*\)/i; | ||
@@ -14,91 +30,73 @@ const JSON_OPERATOR_REGEX = /^\s*(->>?|@>|<@|\?[|&]?|\|{2}|#-)/i; | ||
const FOREIGN_KEY_FIELDS = [ | ||
'CONSTRAINT_NAME as constraint_name', | ||
'CONSTRAINT_NAME as constraintName', | ||
'CONSTRAINT_SCHEMA as constraintSchema', | ||
'CONSTRAINT_SCHEMA as constraintCatalog', | ||
'TABLE_NAME as tableName', | ||
'TABLE_SCHEMA as tableSchema', | ||
'TABLE_SCHEMA as tableCatalog', | ||
'COLUMN_NAME as columnName', | ||
'REFERENCED_TABLE_SCHEMA as referencedTableSchema', | ||
'REFERENCED_TABLE_SCHEMA as referencedTableCatalog', | ||
'REFERENCED_TABLE_NAME as referencedTableName', | ||
'REFERENCED_COLUMN_NAME as referencedColumnName' | ||
].join(','); | ||
const typeWithoutDefault = new Set(['BLOB', 'TEXT', 'GEOMETRY', 'JSON']); | ||
"CONSTRAINT_NAME as constraint_name", | ||
"CONSTRAINT_NAME as constraintName", | ||
"CONSTRAINT_SCHEMA as constraintSchema", | ||
"CONSTRAINT_SCHEMA as constraintCatalog", | ||
"TABLE_NAME as tableName", | ||
"TABLE_SCHEMA as tableSchema", | ||
"TABLE_SCHEMA as tableCatalog", | ||
"COLUMN_NAME as columnName", | ||
"REFERENCED_TABLE_SCHEMA as referencedTableSchema", | ||
"REFERENCED_TABLE_SCHEMA as referencedTableCatalog", | ||
"REFERENCED_TABLE_NAME as referencedTableName", | ||
"REFERENCED_COLUMN_NAME as referencedColumnName" | ||
].join(","); | ||
const typeWithoutDefault = /* @__PURE__ */ new Set(["BLOB", "TEXT", "GEOMETRY", "JSON"]); | ||
class MySQLQueryGenerator extends AbstractQueryGenerator { | ||
constructor(options) { | ||
super(options); | ||
this.OperatorMap = { | ||
...this.OperatorMap, | ||
[Op.regexp]: 'REGEXP', | ||
[Op.notRegexp]: 'NOT REGEXP' | ||
}; | ||
this.OperatorMap = __spreadProps(__spreadValues({}, this.OperatorMap), { | ||
[Op.regexp]: "REGEXP", | ||
[Op.notRegexp]: "NOT REGEXP" | ||
}); | ||
} | ||
createDatabaseQuery(databaseName, options) { | ||
options = { | ||
options = __spreadValues({ | ||
charset: null, | ||
collate: null, | ||
...options | ||
}; | ||
collate: null | ||
}, options); | ||
return Utils.joinSQLFragments([ | ||
'CREATE DATABASE IF NOT EXISTS', | ||
"CREATE DATABASE IF NOT EXISTS", | ||
this.quoteIdentifier(databaseName), | ||
options.charset && `DEFAULT CHARACTER SET ${this.escape(options.charset)}`, | ||
options.collate && `DEFAULT COLLATE ${this.escape(options.collate)}`, | ||
';' | ||
";" | ||
]); | ||
} | ||
dropDatabaseQuery(databaseName) { | ||
return `DROP DATABASE IF EXISTS ${this.quoteIdentifier(databaseName)};`; | ||
} | ||
createSchema() { | ||
return 'SHOW TABLES'; | ||
return "SHOW TABLES"; | ||
} | ||
showSchemasQuery() { | ||
return 'SHOW TABLES'; | ||
return "SHOW TABLES"; | ||
} | ||
versionQuery() { | ||
return 'SELECT VERSION() as `version`'; | ||
return "SELECT VERSION() as `version`"; | ||
} | ||
createTableQuery(tableName, attributes, options) { | ||
options = { | ||
engine: 'InnoDB', | ||
options = __spreadValues({ | ||
engine: "InnoDB", | ||
charset: null, | ||
rowFormat: null, | ||
...options | ||
}; | ||
rowFormat: null | ||
}, options); | ||
const primaryKeys = []; | ||
const foreignKeys = {}; | ||
const attrStr = []; | ||
for (const attr in attributes) { | ||
if (!Object.prototype.hasOwnProperty.call(attributes, attr)) continue; | ||
if (!Object.prototype.hasOwnProperty.call(attributes, attr)) | ||
continue; | ||
const dataType = attributes[attr]; | ||
let match; | ||
if (dataType.includes('PRIMARY KEY')) { | ||
if (dataType.includes("PRIMARY KEY")) { | ||
primaryKeys.push(attr); | ||
if (dataType.includes('REFERENCES')) { | ||
// MySQL doesn't support inline REFERENCES declarations: move to the end | ||
if (dataType.includes("REFERENCES")) { | ||
match = dataType.match(/^(.+) (REFERENCES.*)$/); | ||
attrStr.push(`${this.quoteIdentifier(attr)} ${match[1].replace('PRIMARY KEY', '')}`); | ||
attrStr.push(`${this.quoteIdentifier(attr)} ${match[1].replace("PRIMARY KEY", "")}`); | ||
foreignKeys[attr] = match[2]; | ||
} else { | ||
attrStr.push(`${this.quoteIdentifier(attr)} ${dataType.replace('PRIMARY KEY', '')}`); | ||
attrStr.push(`${this.quoteIdentifier(attr)} ${dataType.replace("PRIMARY KEY", "")}`); | ||
} | ||
} else if (dataType.includes('REFERENCES')) { | ||
// MySQL doesn't support inline REFERENCES declarations: move to the end | ||
} else if (dataType.includes("REFERENCES")) { | ||
match = dataType.match(/^(.+) (REFERENCES.*)$/); | ||
@@ -111,22 +109,18 @@ attrStr.push(`${this.quoteIdentifier(attr)} ${match[1]}`); | ||
} | ||
const table = this.quoteTable(tableName); | ||
let attributesClause = attrStr.join(', '); | ||
const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', '); | ||
let attributesClause = attrStr.join(", "); | ||
const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", "); | ||
if (options.uniqueKeys) { | ||
_.each(options.uniqueKeys, (columns, indexName) => { | ||
if (columns.customIndex) { | ||
if (typeof indexName !== 'string') { | ||
indexName = `uniq_${tableName}_${columns.fields.join('_')}`; | ||
if (typeof indexName !== "string") { | ||
indexName = `uniq_${tableName}_${columns.fields.join("_")}`; | ||
} | ||
attributesClause += `, UNIQUE ${this.quoteIdentifier(indexName)} (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`; | ||
attributesClause += `, UNIQUE ${this.quoteIdentifier(indexName)} (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`; | ||
} | ||
}); | ||
} | ||
if (pkString.length > 0) { | ||
attributesClause += `, PRIMARY KEY (${pkString})`; | ||
} | ||
for (const fkey in foreignKeys) { | ||
@@ -137,9 +131,8 @@ if (Object.prototype.hasOwnProperty.call(foreignKeys, fkey)) { | ||
} | ||
return Utils.joinSQLFragments([ | ||
'CREATE TABLE IF NOT EXISTS', | ||
"CREATE TABLE IF NOT EXISTS", | ||
table, | ||
`(${attributesClause})`, | ||
`ENGINE=${options.engine}`, | ||
options.comment && typeof options.comment === 'string' && `COMMENT ${this.escape(options.comment)}`, | ||
options.comment && typeof options.comment === "string" && `COMMENT ${this.escape(options.comment)}`, | ||
options.charset && `DEFAULT CHARSET=${options.charset}`, | ||
@@ -149,62 +142,57 @@ options.collate && `COLLATE ${options.collate}`, | ||
options.rowFormat && `ROW_FORMAT=${options.rowFormat}`, | ||
';' | ||
";" | ||
]); | ||
} | ||
describeTableQuery(tableName, schema, schemaDelimiter) { | ||
const table = this.quoteTable( | ||
this.addSchema({ | ||
tableName, | ||
_schema: schema, | ||
_schemaDelimiter: schemaDelimiter | ||
}) | ||
); | ||
const table = this.quoteTable(this.addSchema({ | ||
tableName, | ||
_schema: schema, | ||
_schemaDelimiter: schemaDelimiter | ||
})); | ||
return `SHOW FULL COLUMNS FROM ${table};`; | ||
} | ||
showTablesQuery(database) { | ||
let query = 'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = \'BASE TABLE\''; | ||
let query = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'"; | ||
if (database) { | ||
query += ` AND TABLE_SCHEMA = ${this.escape(database)}`; | ||
} else { | ||
query += ' AND TABLE_SCHEMA NOT IN (\'MYSQL\', \'INFORMATION_SCHEMA\', \'PERFORMANCE_SCHEMA\', \'SYS\')'; | ||
query += " AND TABLE_SCHEMA NOT IN ('MYSQL', 'INFORMATION_SCHEMA', 'PERFORMANCE_SCHEMA', 'SYS', 'mysql', 'information_schema', 'performance_schema', 'sys')"; | ||
} | ||
return `${query};`; | ||
} | ||
tableExistsQuery(table) { | ||
const tableName = this.escape(this.quoteTable(table).slice(1, -1)); | ||
return `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = ${tableName} AND TABLE_SCHEMA = ${this.escape(this.sequelize.config.database)}`; | ||
} | ||
addColumnQuery(table, key, dataType) { | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(table), | ||
'ADD', | ||
"ADD", | ||
this.quoteIdentifier(key), | ||
this.attributeToSQL(dataType, { | ||
context: 'addColumn', | ||
context: "addColumn", | ||
tableName: table, | ||
foreignKey: key | ||
}), | ||
';' | ||
";" | ||
]); | ||
} | ||
removeColumnQuery(tableName, attributeName) { | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
'DROP', | ||
"DROP", | ||
this.quoteIdentifier(attributeName), | ||
';' | ||
";" | ||
]); | ||
} | ||
changeColumnQuery(tableName, attributes) { | ||
const attrString = []; | ||
const constraintString = []; | ||
for (const attributeName in attributes) { | ||
let definition = attributes[attributeName]; | ||
if (definition.includes('REFERENCES')) { | ||
if (definition.includes("REFERENCES")) { | ||
const attrName = this.quoteIdentifier(attributeName); | ||
definition = definition.replace(/.+?(?=REFERENCES)/, ''); | ||
definition = definition.replace(/.+?(?=REFERENCES)/, ""); | ||
constraintString.push(`FOREIGN KEY (${attrName}) ${definition}`); | ||
@@ -215,15 +203,12 @@ } else { | ||
} | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
attrString.length && `CHANGE ${attrString.join(', ')}`, | ||
constraintString.length && `ADD ${constraintString.join(', ')}`, | ||
';' | ||
attrString.length && `CHANGE ${attrString.join(", ")}`, | ||
constraintString.length && `ADD ${constraintString.join(", ")}`, | ||
";" | ||
]); | ||
} | ||
renameColumnQuery(tableName, attrBefore, attributes) { | ||
const attrString = []; | ||
for (const attrName in attributes) { | ||
@@ -233,30 +218,21 @@ const definition = attributes[attrName]; | ||
} | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
'CHANGE', | ||
attrString.join(', '), | ||
';' | ||
"CHANGE", | ||
attrString.join(", "), | ||
";" | ||
]); | ||
} | ||
handleSequelizeMethod(smth, tableName, factory, options, prepend) { | ||
if (smth instanceof Utils.Json) { | ||
// Parse nested object | ||
if (smth.conditions) { | ||
const conditions = this.parseConditionObject(smth.conditions).map(condition => | ||
`${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'` | ||
); | ||
return conditions.join(' AND '); | ||
const conditions = this.parseConditionObject(smth.conditions).map((condition) => `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`); | ||
return conditions.join(" AND "); | ||
} | ||
if (smth.path) { | ||
let str; | ||
// Allow specifying conditions using the sqlite json functions | ||
if (this._checkValidJsonStatement(smth.path)) { | ||
str = smth.path; | ||
} else { | ||
// Also support json property accessors | ||
const paths = _.toPath(smth.path); | ||
@@ -266,7 +242,5 @@ const column = paths.shift(); | ||
} | ||
if (smth.value) { | ||
str += util.format(' = %s', this.escape(smth.value)); | ||
str += util.format(" = %s", this.escape(smth.value)); | ||
} | ||
return str; | ||
@@ -276,49 +250,37 @@ } | ||
if (/timestamp/i.test(smth.type)) { | ||
smth.type = 'datetime'; | ||
smth.type = "datetime"; | ||
} else if (smth.json && /boolean/i.test(smth.type)) { | ||
// true or false cannot be casted as booleans within a JSON structure | ||
smth.type = 'char'; | ||
smth.type = "char"; | ||
} else if (/double precision/i.test(smth.type) || /boolean/i.test(smth.type) || /integer/i.test(smth.type)) { | ||
smth.type = 'decimal'; | ||
smth.type = "decimal"; | ||
} else if (/text/i.test(smth.type)) { | ||
smth.type = 'char'; | ||
smth.type = "char"; | ||
} | ||
} | ||
return super.handleSequelizeMethod(smth, tableName, factory, options, prepend); | ||
} | ||
_toJSONValue(value) { | ||
// true/false are stored as strings in mysql | ||
if (typeof value === 'boolean') { | ||
if (typeof value === "boolean") { | ||
return value.toString(); | ||
} | ||
// null is stored as a string in mysql | ||
if (value === null) { | ||
return 'null'; | ||
return "null"; | ||
} | ||
return value; | ||
} | ||
truncateTableQuery(tableName) { | ||
return `TRUNCATE ${this.quoteTable(tableName)}`; | ||
} | ||
deleteQuery(tableName, where, options = {}, model) { | ||
let limit = ''; | ||
let limit = ""; | ||
let query = `DELETE FROM ${this.quoteTable(tableName)}`; | ||
if (options.limit) { | ||
limit = ` LIMIT ${this.escape(options.limit)}`; | ||
} | ||
where = this.getWhereConditions(where, null, model, options); | ||
if (where) { | ||
query += ` WHERE ${where}`; | ||
} | ||
return query + limit; | ||
} | ||
showIndexesQuery(tableName, options) { | ||
@@ -330,37 +292,31 @@ return Utils.joinSQLFragments([ | ||
} | ||
showConstraintsQuery(table, constraintName) { | ||
const tableName = table.tableName || table; | ||
const schemaName = table.schema; | ||
return Utils.joinSQLFragments([ | ||
'SELECT CONSTRAINT_CATALOG AS constraintCatalog,', | ||
'CONSTRAINT_NAME AS constraintName,', | ||
'CONSTRAINT_SCHEMA AS constraintSchema,', | ||
'CONSTRAINT_TYPE AS constraintType,', | ||
'TABLE_NAME AS tableName,', | ||
'TABLE_SCHEMA AS tableSchema', | ||
'from INFORMATION_SCHEMA.TABLE_CONSTRAINTS', | ||
"SELECT CONSTRAINT_CATALOG AS constraintCatalog,", | ||
"CONSTRAINT_NAME AS constraintName,", | ||
"CONSTRAINT_SCHEMA AS constraintSchema,", | ||
"CONSTRAINT_TYPE AS constraintType,", | ||
"TABLE_NAME AS tableName,", | ||
"TABLE_SCHEMA AS tableSchema", | ||
"from INFORMATION_SCHEMA.TABLE_CONSTRAINTS", | ||
`WHERE table_name='${tableName}'`, | ||
constraintName && `AND constraint_name = '${constraintName}'`, | ||
schemaName && `AND TABLE_SCHEMA = '${schemaName}'`, | ||
';' | ||
";" | ||
]); | ||
} | ||
removeIndexQuery(tableName, indexNameOrAttributes) { | ||
let indexName = indexNameOrAttributes; | ||
if (typeof indexName !== 'string') { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join('_')}`); | ||
if (typeof indexName !== "string") { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); | ||
} | ||
return Utils.joinSQLFragments([ | ||
'DROP INDEX', | ||
"DROP INDEX", | ||
this.quoteIdentifier(indexName), | ||
'ON', | ||
"ON", | ||
this.quoteTable(tableName) | ||
]); | ||
} | ||
attributeToSQL(attribute, options) { | ||
@@ -372,35 +328,24 @@ if (!_.isPlainObject(attribute)) { | ||
} | ||
const attributeString = attribute.type.toString({ escape: this.escape.bind(this) }); | ||
let template = attributeString; | ||
if (attribute.allowNull === false) { | ||
template += ' NOT NULL'; | ||
template += " NOT NULL"; | ||
} | ||
if (attribute.autoIncrement) { | ||
template += ' auto_increment'; | ||
template += " auto_increment"; | ||
} | ||
// BLOB/TEXT/GEOMETRY/JSON cannot have a default value | ||
if (!typeWithoutDefault.has(attributeString) | ||
&& attribute.type._binary !== true | ||
&& Utils.defaultValueSchemable(attribute.defaultValue)) { | ||
if (!typeWithoutDefault.has(attributeString) && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) { | ||
template += ` DEFAULT ${this.escape(attribute.defaultValue)}`; | ||
} | ||
if (attribute.unique === true) { | ||
template += ' UNIQUE'; | ||
template += " UNIQUE"; | ||
} | ||
if (attribute.primaryKey) { | ||
template += ' PRIMARY KEY'; | ||
template += " PRIMARY KEY"; | ||
} | ||
if (attribute.comment) { | ||
template += ` COMMENT ${this.escape(attribute.comment)}`; | ||
} | ||
if (attribute.first) { | ||
template += ' FIRST'; | ||
template += " FIRST"; | ||
} | ||
@@ -410,23 +355,17 @@ if (attribute.after) { | ||
} | ||
if (attribute.references) { | ||
if (options && options.context === 'addColumn' && options.foreignKey) { | ||
if ((!options || !options.withoutForeignKeyConstraints) && attribute.references) { | ||
if (options && options.context === "addColumn" && options.foreignKey) { | ||
const attrName = this.quoteIdentifier(options.foreignKey); | ||
const fkName = this.quoteIdentifier(`${options.tableName}_${attrName}_foreign_idx`); | ||
template += `, ADD CONSTRAINT ${fkName} FOREIGN KEY (${attrName})`; | ||
} | ||
template += ` REFERENCES ${this.quoteTable(attribute.references.model)}`; | ||
if (attribute.references.key) { | ||
template += ` (${this.quoteIdentifier(attribute.references.key)})`; | ||
} else { | ||
template += ` (${this.quoteIdentifier('id')})`; | ||
template += ` (${this.quoteIdentifier("id")})`; | ||
} | ||
if (attribute.onDelete) { | ||
template += ` ON DELETE ${attribute.onDelete.toUpperCase()}`; | ||
} | ||
if (attribute.onUpdate) { | ||
@@ -436,9 +375,6 @@ template += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`; | ||
} | ||
return template; | ||
} | ||
attributesToSQL(attributes, options) { | ||
const result = {}; | ||
for (const key in attributes) { | ||
@@ -448,19 +384,8 @@ const attribute = attributes[key]; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Check whether the statmement is json function or simple path | ||
* | ||
* @param {string} stmt The statement to validate | ||
* @returns {boolean} true if the given statement is json function | ||
* @throws {Error} throw if the statement looks like json function but has invalid token | ||
* @private | ||
*/ | ||
_checkValidJsonStatement(stmt) { | ||
if (typeof stmt !== 'string') { | ||
if (typeof stmt !== "string") { | ||
return false; | ||
} | ||
let currentIndex = 0; | ||
@@ -471,3 +396,2 @@ let openingBrackets = 0; | ||
let hasInvalidToken = false; | ||
while (currentIndex < stmt.length) { | ||
@@ -477,7 +401,6 @@ const string = stmt.substr(currentIndex); | ||
if (functionMatches) { | ||
currentIndex += functionMatches[0].indexOf('('); | ||
currentIndex += functionMatches[0].indexOf("("); | ||
hasJsonFunction = true; | ||
continue; | ||
} | ||
const operatorMatches = JSON_OPERATOR_REGEX.exec(string); | ||
@@ -489,11 +412,10 @@ if (operatorMatches) { | ||
} | ||
const tokenMatches = TOKEN_CAPTURE_REGEX.exec(string); | ||
if (tokenMatches) { | ||
const capturedToken = tokenMatches[1]; | ||
if (capturedToken === '(') { | ||
if (capturedToken === "(") { | ||
openingBrackets++; | ||
} else if (capturedToken === ')') { | ||
} else if (capturedToken === ")") { | ||
closingBrackets++; | ||
} else if (capturedToken === ';') { | ||
} else if (capturedToken === ";") { | ||
hasInvalidToken = true; | ||
@@ -505,53 +427,29 @@ break; | ||
} | ||
break; | ||
} | ||
// Check invalid json statement | ||
if (hasJsonFunction && (hasInvalidToken || openingBrackets !== closingBrackets)) { | ||
throw new Error(`Invalid json statement: ${stmt}`); | ||
} | ||
// return true if the statement has valid json function | ||
return hasJsonFunction; | ||
} | ||
/** | ||
* Generates an SQL query that returns all foreign keys of a table. | ||
* | ||
* @param {object} table The table. | ||
* @param {string} schemaName The name of the schema. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
getForeignKeysQuery(table, schemaName) { | ||
const tableName = table.tableName || table; | ||
return Utils.joinSQLFragments([ | ||
'SELECT', | ||
"SELECT", | ||
FOREIGN_KEY_FIELDS, | ||
`FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '${tableName}'`, | ||
`AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='${schemaName}'`, | ||
'AND REFERENCED_TABLE_NAME IS NOT NULL', | ||
';' | ||
"AND REFERENCED_TABLE_NAME IS NOT NULL", | ||
";" | ||
]); | ||
} | ||
/** | ||
* Generates an SQL query that returns the foreign key constraint of a given column. | ||
* | ||
* @param {object} table The table. | ||
* @param {string} columnName The name of the column. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
getForeignKeyQuery(table, columnName) { | ||
const quotedSchemaName = table.schema ? wrapSingleQuote(table.schema) : ''; | ||
const quotedSchemaName = table.schema ? wrapSingleQuote(table.schema) : ""; | ||
const quotedTableName = wrapSingleQuote(table.tableName || table); | ||
const quotedColumnName = wrapSingleQuote(columnName); | ||
return Utils.joinSQLFragments([ | ||
'SELECT', | ||
"SELECT", | ||
FOREIGN_KEY_FIELDS, | ||
'FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE', | ||
'WHERE (', | ||
"FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE", | ||
"WHERE (", | ||
[ | ||
@@ -562,3 +460,3 @@ `REFERENCED_TABLE_NAME = ${quotedTableName}`, | ||
], | ||
') OR (', | ||
") OR (", | ||
[ | ||
@@ -568,32 +466,24 @@ `TABLE_NAME = ${quotedTableName}`, | ||
`AND COLUMN_NAME = ${quotedColumnName}`, | ||
'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. | ||
* | ||
* @param {string} tableName The name of the table. | ||
* @param {string} foreignKey The name of the foreign key constraint. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
dropForeignKeyQuery(tableName, foreignKey) { | ||
return Utils.joinSQLFragments([ | ||
'ALTER TABLE', | ||
"ALTER TABLE", | ||
this.quoteTable(tableName), | ||
'DROP FOREIGN KEY', | ||
"DROP FOREIGN KEY", | ||
this.quoteIdentifier(foreignKey), | ||
';' | ||
";" | ||
]); | ||
} | ||
quoteIdentifier(identifier, force) { | ||
return Utils.addTicks(Utils.removeTicks(identifier, "`"), "`"); | ||
} | ||
} | ||
// private methods | ||
function wrapSingleQuote(identifier) { | ||
return Utils.addTicks(identifier, '\''); | ||
return Utils.addTicks(identifier, "'"); | ||
} | ||
module.exports = MySQLQueryGenerator; | ||
//# sourceMappingURL=query-generator.js.map |
@@ -1,50 +0,41 @@ | ||
'use strict'; | ||
const sequelizeErrors = require('../../errors'); | ||
const { QueryInterface } = require('../abstract/query-interface'); | ||
const QueryTypes = require('../../query-types'); | ||
/** | ||
* The interface that Sequelize uses to talk with MySQL/MariaDB database | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const sequelizeErrors = require("../../errors"); | ||
const { QueryInterface } = require("../abstract/query-interface"); | ||
const QueryTypes = require("../../query-types"); | ||
class MySQLQueryInterface extends QueryInterface { | ||
/** | ||
* A wrapper that fixes MySQL's inability to cleanly remove columns from existing tables if they have a foreign key constraint. | ||
* | ||
* @override | ||
*/ | ||
async removeColumn(tableName, columnName, options) { | ||
options = options || {}; | ||
const [results] = await this.sequelize.query( | ||
this.queryGenerator.getForeignKeyQuery(tableName.tableName ? tableName : { | ||
tableName, | ||
schema: this.sequelize.config.database | ||
}, columnName), | ||
{ raw: true, ...options } | ||
); | ||
//Exclude primary key constraint | ||
if (results.length && results[0].constraint_name !== 'PRIMARY') { | ||
await Promise.all(results.map(constraint => this.sequelize.query( | ||
this.queryGenerator.dropForeignKeyQuery(tableName, constraint.constraint_name), | ||
{ raw: true, ...options } | ||
))); | ||
const [results] = await this.sequelize.query(this.queryGenerator.getForeignKeyQuery(tableName.tableName ? tableName : { | ||
tableName, | ||
schema: this.sequelize.config.database | ||
}, columnName), __spreadValues({ raw: true }, options)); | ||
if (results.length && results[0].constraint_name !== "PRIMARY") { | ||
await Promise.all(results.map((constraint) => this.sequelize.query(this.queryGenerator.dropForeignKeyQuery(tableName, constraint.constraint_name), __spreadValues({ raw: true }, options)))); | ||
} | ||
return await this.sequelize.query( | ||
this.queryGenerator.removeColumnQuery(tableName, columnName), | ||
{ raw: true, ...options } | ||
); | ||
return await this.sequelize.query(this.queryGenerator.removeColumnQuery(tableName, columnName), __spreadValues({ raw: true }, options)); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async upsert(tableName, insertValues, updateValues, where, options) { | ||
options = { ...options }; | ||
options = __spreadValues({}, options); | ||
options.type = QueryTypes.UPSERT; | ||
options.updateOnDuplicate = Object.keys(updateValues); | ||
options.upsertKeys = Object.values(options.model.primaryKeys).map((item) => item.field); | ||
const model = options.model; | ||
@@ -54,28 +45,20 @@ const sql = this.queryGenerator.insertQuery(tableName, insertValues, model.rawAttributes, options); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async removeConstraint(tableName, constraintName, options) { | ||
const sql = this.queryGenerator.showConstraintsQuery( | ||
tableName.tableName ? tableName : { | ||
tableName, | ||
schema: this.sequelize.config.database | ||
}, constraintName); | ||
const constraints = await this.sequelize.query(sql, { ...options, | ||
type: this.sequelize.QueryTypes.SHOWCONSTRAINTS }); | ||
const sql = this.queryGenerator.showConstraintsQuery(tableName.tableName ? tableName : { | ||
tableName, | ||
schema: this.sequelize.config.database | ||
}, constraintName); | ||
const constraints = await this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { | ||
type: this.sequelize.QueryTypes.SHOWCONSTRAINTS | ||
})); | ||
const constraint = constraints[0]; | ||
let query; | ||
if (!constraint || !constraint.constraintType) { | ||
throw new sequelizeErrors.UnknownConstraintError( | ||
{ | ||
message: `Constraint ${constraintName} on table ${tableName} does not exist`, | ||
constraint: constraintName, | ||
table: tableName | ||
}); | ||
throw new sequelizeErrors.UnknownConstraintError({ | ||
message: `Constraint ${constraintName} on table ${tableName} does not exist`, | ||
constraint: constraintName, | ||
table: tableName | ||
}); | ||
} | ||
if (constraint.constraintType === 'FOREIGN KEY') { | ||
if (constraint.constraintType === "FOREIGN KEY") { | ||
query = this.queryGenerator.dropForeignKeyQuery(tableName, constraintName); | ||
@@ -85,7 +68,6 @@ } else { | ||
} | ||
return await this.sequelize.query(query, options); | ||
} | ||
} | ||
exports.MySQLQueryInterface = MySQLQueryInterface; | ||
//# sourceMappingURL=query-interface.js.map |
@@ -1,8 +0,22 @@ | ||
'use strict'; | ||
const AbstractQuery = require('../abstract/query'); | ||
const sequelizeErrors = require('../../errors'); | ||
const _ = require('lodash'); | ||
const { logger } = require('../../utils/logger'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const AbstractQuery = require("../abstract/query"); | ||
const sequelizeErrors = require("../../errors"); | ||
const _ = require("lodash"); | ||
const { logger } = require("../../utils/logger"); | ||
const ER_DUP_ENTRY = 1062; | ||
@@ -12,49 +26,37 @@ const ER_DEADLOCK = 1213; | ||
const ER_NO_REFERENCED_ROW = 1452; | ||
const debug = logger.debugContext('sql:mysql'); | ||
const debug = logger.debugContext("sql:mysql"); | ||
class Query extends AbstractQuery { | ||
constructor(connection, sequelize, options) { | ||
super(connection, sequelize, { showWarnings: false, ...options }); | ||
super(connection, sequelize, __spreadValues({ showWarnings: false }, options)); | ||
} | ||
static formatBindParameters(sql, values, dialect) { | ||
const bindParam = []; | ||
const replacementFunc = (match, key, values_) => { | ||
if (values_[key] !== undefined) { | ||
if (values_[key] !== void 0) { | ||
bindParam.push(values_[key]); | ||
return '?'; | ||
return "?"; | ||
} | ||
return undefined; | ||
return void 0; | ||
}; | ||
sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0]; | ||
return [sql, bindParam.length > 0 ? bindParam : undefined]; | ||
return [sql, bindParam.length > 0 ? bindParam : void 0]; | ||
} | ||
async run(sql, parameters) { | ||
this.sql = sql; | ||
const { connection, options } = this; | ||
const showWarnings = this.sequelize.options.showWarnings || options.showWarnings; | ||
const complete = this._logQuery(sql, debug, parameters); | ||
if (parameters) { | ||
debug('parameters(%j)', parameters); | ||
debug("parameters(%j)", parameters); | ||
} | ||
let results; | ||
const errForStack = new Error(); | ||
try { | ||
if (parameters && parameters.length) { | ||
results = await new Promise((resolve, reject) => { | ||
connection | ||
.execute(sql, parameters, (error, result) => error ? reject(error) : resolve(result)) | ||
.setMaxListeners(100); | ||
connection.execute(sql, parameters, (error, result) => error ? reject(error) : resolve(result)).setMaxListeners(100); | ||
}); | ||
} else { | ||
results = await new Promise((resolve, reject) => { | ||
connection | ||
.query({ sql }, (error, result) => error ? reject(error) : resolve(result)) | ||
.setMaxListeners(100); | ||
connection.query({ sql }, (error, result) => error ? reject(error) : resolve(result)).setMaxListeners(100); | ||
}); | ||
@@ -64,21 +66,14 @@ } | ||
if (options.transaction && error.errno === ER_DEADLOCK) { | ||
// MySQL automatically rolls-back transactions in the event of a deadlock. | ||
// However, we still initiate a manual rollback to ensure the connection gets released - see #13102. | ||
try { | ||
await options.transaction.rollback(); | ||
} catch (error_) { | ||
// Ignore errors - since MySQL automatically rolled back, we're | ||
// not that worried about this redundant rollback failing. | ||
} | ||
options.transaction.finished = 'rollback'; | ||
options.transaction.finished = "rollback"; | ||
} | ||
error.sql = sql; | ||
error.parameters = parameters; | ||
throw this.formatError(error); | ||
throw this.formatError(error, errForStack.stack); | ||
} finally { | ||
complete(); | ||
} | ||
if (showWarnings && results && results.warningStatus > 0) { | ||
@@ -89,35 +84,8 @@ await this.logWarnings(results); | ||
} | ||
/** | ||
* High level function that handles the results of a query execution. | ||
* | ||
* | ||
* Example: | ||
* query.formatResults([ | ||
* { | ||
* id: 1, // this is from the main table | ||
* attr2: 'snafu', // this is from the main table | ||
* Tasks.id: 1, // this is from the associated table | ||
* Tasks.title: 'task' // this is from the associated table | ||
* } | ||
* ]) | ||
* | ||
* @param {Array} data - The result of the query execution. | ||
* @private | ||
*/ | ||
formatResults(data) { | ||
let result = this.instance; | ||
if (this.isInsertQuery(data)) { | ||
this.handleInsertQuery(data); | ||
if (!this.instance) { | ||
// handle bulkCreate AI primary key | ||
if ( | ||
data.constructor.name === 'ResultSetHeader' | ||
&& this.model | ||
&& this.model.autoIncrementAttribute | ||
&& this.model.autoIncrementAttribute === this.model.primaryKeyAttribute | ||
&& this.model.rawAttributes[this.model.primaryKeyAttribute] | ||
) { | ||
if (data.constructor.name === "ResultSetHeader" && this.model && this.model.autoIncrementAttribute && this.model.autoIncrementAttribute === this.model.primaryKeyAttribute && this.model.rawAttributes[this.model.primaryKeyAttribute]) { | ||
const startId = data[this.getInsertIdField()]; | ||
@@ -133,3 +101,2 @@ result = []; | ||
} | ||
if (this.isSelectQuery()) { | ||
@@ -143,12 +110,10 @@ return this.handleSelectQuery(data); | ||
result = {}; | ||
for (const _result of data) { | ||
const enumRegex = /^enum/i; | ||
result[_result.Field] = { | ||
type: enumRegex.test(_result.Type) ? _result.Type.replace(enumRegex, 'ENUM') : _result.Type.toUpperCase(), | ||
allowNull: _result.Null === 'YES', | ||
type: enumRegex.test(_result.Type) ? _result.Type.replace(enumRegex, "ENUM") : _result.Type.toUpperCase(), | ||
allowNull: _result.Null === "YES", | ||
defaultValue: _result.Default, | ||
primaryKey: _result.Key === 'PRI', | ||
autoIncrement: Object.prototype.hasOwnProperty.call(_result, 'Extra') | ||
&& _result.Extra.toLowerCase() === 'auto_increment', | ||
primaryKey: _result.Key === "PRI", | ||
autoIncrement: Object.prototype.hasOwnProperty.call(_result, "Extra") && _result.Extra.toLowerCase() === "auto_increment", | ||
comment: _result.Comment ? _result.Comment : null | ||
@@ -184,23 +149,20 @@ }; | ||
if (this.isRawQuery()) { | ||
// MySQL returns row data and metadata (affected rows etc) in a single object - let's standarize it, sorta | ||
return [data, data]; | ||
} | ||
return result; | ||
} | ||
async logWarnings(results) { | ||
const warningResults = await this.run('SHOW WARNINGS'); | ||
const warningMessage = `MySQL Warnings (${this.connection.uuid || 'default'}): `; | ||
const warningResults = await this.run("SHOW WARNINGS"); | ||
const warningMessage = `MySQL Warnings (${this.connection.uuid || "default"}): `; | ||
const messages = []; | ||
for (const _warningRow of warningResults) { | ||
if (_warningRow === undefined || typeof _warningRow[Symbol.iterator] !== 'function') { | ||
if (_warningRow === void 0 || typeof _warningRow[Symbol.iterator] !== "function") { | ||
continue; | ||
} | ||
for (const _warningResult of _warningRow) { | ||
if (Object.prototype.hasOwnProperty.call(_warningResult, 'Message')) { | ||
if (Object.prototype.hasOwnProperty.call(_warningResult, "Message")) { | ||
messages.push(_warningResult.Message); | ||
} else { | ||
for (const _objectKey of _warningResult.keys()) { | ||
messages.push([_objectKey, _warningResult[_objectKey]].join(': ')); | ||
messages.push([_objectKey, _warningResult[_objectKey]].join(": ")); | ||
} | ||
@@ -210,11 +172,7 @@ } | ||
} | ||
this.sequelize.log(warningMessage + messages.join('; '), this.options); | ||
this.sequelize.log(warningMessage + messages.join("; "), this.options); | ||
return results; | ||
} | ||
formatError(err) { | ||
formatError(err, errStack) { | ||
const errCode = err.errno || err.code; | ||
switch (errCode) { | ||
@@ -224,10 +182,10 @@ case ER_DUP_ENTRY: { | ||
let fields = {}; | ||
let message = 'Validation error'; | ||
const values = match ? match[1].split('-') : undefined; | ||
const fieldKey = match ? match[2] : undefined; | ||
const fieldVal = match ? match[1] : undefined; | ||
let message = "Validation error"; | ||
const values = match ? match[1].split("-") : void 0; | ||
const fieldKey = match ? match[2].split(".").pop() : void 0; | ||
const fieldVal = match ? match[1] : void 0; | ||
const uniqueKey = this.model && this.model.uniqueKeys[fieldKey]; | ||
if (uniqueKey) { | ||
if (uniqueKey.msg) message = uniqueKey.msg; | ||
if (uniqueKey.msg) | ||
message = uniqueKey.msg; | ||
fields = _.zipObject(uniqueKey.fields, values); | ||
@@ -237,44 +195,28 @@ } else { | ||
} | ||
const errors = []; | ||
_.forOwn(fields, (value, field) => { | ||
errors.push(new sequelizeErrors.ValidationErrorItem( | ||
this.getUniqueConstraintErrorMessage(field), | ||
'unique violation', // sequelizeErrors.ValidationErrorItem.Origins.DB, | ||
field, | ||
value, | ||
this.instance, | ||
'not_unique' | ||
)); | ||
errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, value, this.instance, "not_unique")); | ||
}); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields }); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack }); | ||
} | ||
case ER_ROW_IS_REFERENCED: | ||
case ER_NO_REFERENCED_ROW: { | ||
// e.g. CONSTRAINT `example_constraint_name` FOREIGN KEY (`example_id`) REFERENCES `examples` (`id`) | ||
const match = err.message.match( | ||
/CONSTRAINT ([`"])(.*)\1 FOREIGN KEY \(\1(.*)\1\) REFERENCES \1(.*)\1 \(\1(.*)\1\)/ | ||
); | ||
const quoteChar = match ? match[1] : '`'; | ||
const fields = match ? match[3].split(new RegExp(`${quoteChar}, *${quoteChar}`)) : undefined; | ||
const match = err.message.match(/CONSTRAINT ([`"])(.*)\1 FOREIGN KEY \(\1(.*)\1\) REFERENCES \1(.*)\1 \(\1(.*)\1\)/); | ||
const quoteChar = match ? match[1] : "`"; | ||
const fields = match ? match[3].split(new RegExp(`${quoteChar}, *${quoteChar}`)) : void 0; | ||
return new sequelizeErrors.ForeignKeyConstraintError({ | ||
reltype: String(errCode) === String(ER_ROW_IS_REFERENCED) ? 'parent' : 'child', | ||
table: match ? match[4] : undefined, | ||
reltype: String(errCode) === String(ER_ROW_IS_REFERENCED) ? "parent" : "child", | ||
table: match ? match[4] : void 0, | ||
fields, | ||
value: fields && fields.length && this.instance && this.instance[fields[0]] || undefined, | ||
index: match ? match[2] : undefined, | ||
parent: err | ||
value: fields && fields.length && this.instance && this.instance[fields[0]] || void 0, | ||
index: match ? match[2] : void 0, | ||
parent: err, | ||
stack: errStack | ||
}); | ||
} | ||
default: | ||
return new sequelizeErrors.DatabaseError(err); | ||
return new sequelizeErrors.DatabaseError(err, { stack: errStack }); | ||
} | ||
} | ||
handleShowIndexesQuery(data) { | ||
// Group by index name, and collect all fields | ||
data = data.reduce((acc, item) => { | ||
@@ -285,15 +227,12 @@ if (!(item.Key_name in acc)) { | ||
} | ||
acc[item.Key_name].fields[item.Seq_in_index - 1] = { | ||
attribute: item.Column_name, | ||
length: item.Sub_part || undefined, | ||
order: item.Collation === 'A' ? 'ASC' : undefined | ||
length: item.Sub_part || void 0, | ||
order: item.Collation === "A" ? "ASC" : void 0 | ||
}; | ||
delete item.column_name; | ||
return acc; | ||
}, {}); | ||
return _.map(data, item => ({ | ||
primary: item.Key_name === 'PRIMARY', | ||
return _.map(data, (item) => ({ | ||
primary: item.Key_name === "PRIMARY", | ||
fields: item.fields, | ||
@@ -307,5 +246,5 @@ name: item.Key_name, | ||
} | ||
module.exports = Query; | ||
module.exports.Query = Query; | ||
module.exports.default = Query; | ||
//# sourceMappingURL=query.js.map |
@@ -1,10 +0,7 @@ | ||
'use strict'; | ||
const stores = new Map(); | ||
module.exports = dialect => { | ||
"use strict"; | ||
const stores = /* @__PURE__ */ new Map(); | ||
module.exports = (dialect) => { | ||
if (!stores.has(dialect)) { | ||
stores.set(dialect, new Map()); | ||
stores.set(dialect, /* @__PURE__ */ new Map()); | ||
} | ||
return { | ||
@@ -24,1 +21,2 @@ clear() { | ||
}; | ||
//# sourceMappingURL=parserStore.js.map |
@@ -1,13 +0,11 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const AbstractConnectionManager = require('../abstract/connection-manager'); | ||
const { logger } = require('../../utils/logger'); | ||
const debug = logger.debugContext('connection:pg'); | ||
const sequelizeErrors = require('../../errors'); | ||
const semver = require('semver'); | ||
const dataTypes = require('../../data-types'); | ||
const moment = require('moment-timezone'); | ||
const { promisify } = require('util'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const AbstractConnectionManager = require("../abstract/connection-manager"); | ||
const { logger } = require("../../utils/logger"); | ||
const debug = logger.debugContext("connection:pg"); | ||
const sequelizeErrors = require("../../errors"); | ||
const semver = require("semver"); | ||
const dataTypes = require("../../data-types"); | ||
const momentTz = require("moment-timezone"); | ||
const { promisify } = require("util"); | ||
class ConnectionManager extends AbstractConnectionManager { | ||
@@ -17,6 +15,4 @@ constructor(dialect, sequelize) { | ||
super(dialect, sequelize); | ||
const pgLib = this._loadDialectModule('pg'); | ||
const pgLib = this._loadDialectModule("pg"); | ||
this.lib = this.sequelize.config.native ? pgLib.native : pgLib; | ||
this._clearDynamicOIDs(); | ||
@@ -26,23 +22,19 @@ this._clearTypeParser(); | ||
} | ||
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types | ||
_refreshTypeParser(dataType) { | ||
const arrayParserBuilder = parser => { | ||
return value => this.lib.types.arrayParser.create(value, parser).parse(); | ||
const arrayParserBuilder = (parser2) => { | ||
return (value) => this.lib.types.arrayParser.create(value, parser2).parse(); | ||
}; | ||
const rangeParserBuilder = parser => { | ||
return value => dataType.parse(value, { parser }); | ||
const rangeParserBuilder = (parser2) => { | ||
return (value) => dataType.parse(value, { parser: parser2 }); | ||
}; | ||
// Set range parsers | ||
if (dataType.key.toLowerCase() === 'range') { | ||
if (dataType.key.toLowerCase() === "range") { | ||
for (const name in this.nameOidMap) { | ||
const entry = this.nameOidMap[name]; | ||
if (! entry.rangeOid) continue; | ||
if (!entry.rangeOid) | ||
continue; | ||
const rangeParser = rangeParserBuilder(this.getTypeParser(entry.oid)); | ||
const arrayRangeParser = arrayParserBuilder(rangeParser); | ||
this.oidParserMap.set(entry.rangeOid, rangeParser); | ||
if (! entry.arrayRangeOid) continue; | ||
if (!entry.arrayRangeOid) | ||
continue; | ||
this.oidParserMap.set(entry.arrayRangeOid, arrayRangeParser); | ||
@@ -52,13 +44,9 @@ } | ||
} | ||
// Create parsers for normal or enum data types | ||
const parser = value => dataType.parse(value); | ||
const parser = (value) => dataType.parse(value); | ||
const arrayParser = arrayParserBuilder(parser); | ||
// Set enum parsers | ||
if (dataType.key.toLowerCase() === 'enum') { | ||
this.enumOids.oids.forEach(oid => { | ||
if (dataType.key.toLowerCase() === "enum") { | ||
this.enumOids.oids.forEach((oid) => { | ||
this.oidParserMap.set(oid, parser); | ||
}); | ||
this.enumOids.arrayOids.forEach(arrayOid => { | ||
this.enumOids.arrayOids.forEach((arrayOid) => { | ||
this.oidParserMap.set(arrayOid, arrayParser); | ||
@@ -68,118 +56,88 @@ }); | ||
} | ||
// Set parsers for normal data types | ||
dataType.types.postgres.forEach(name => { | ||
if (! this.nameOidMap[name]) return; | ||
dataType.types.postgres.forEach((name) => { | ||
if (!this.nameOidMap[name]) | ||
return; | ||
this.oidParserMap.set(this.nameOidMap[name].oid, parser); | ||
if (! this.nameOidMap[name].arrayOid) return; | ||
if (!this.nameOidMap[name].arrayOid) | ||
return; | ||
this.oidParserMap.set(this.nameOidMap[name].arrayOid, arrayParser); | ||
}); | ||
} | ||
_clearTypeParser() { | ||
this.oidParserMap = new Map(); | ||
this.oidParserMap = /* @__PURE__ */ new Map(); | ||
} | ||
getTypeParser(oid, ...args) { | ||
if (this.oidParserMap.get(oid)) return this.oidParserMap.get(oid); | ||
if (this.oidParserMap.get(oid)) | ||
return this.oidParserMap.get(oid); | ||
return this.lib.types.getTypeParser(oid, ...args); | ||
} | ||
async connect(config) { | ||
config.user = config.username; | ||
const connectionConfig = _.pick(config, [ | ||
'user', 'password', 'host', 'database', 'port' | ||
"user", | ||
"password", | ||
"host", | ||
"database", | ||
"port" | ||
]); | ||
connectionConfig.types = { | ||
getTypeParser: ConnectionManager.prototype.getTypeParser.bind(this) | ||
}; | ||
if (config.dialectOptions) { | ||
_.merge(connectionConfig, | ||
_.pick(config.dialectOptions, [ | ||
// see [http://www.postgresql.org/docs/9.3/static/runtime-config-logging.html#GUC-APPLICATION-NAME] | ||
'application_name', | ||
// choose the SSL mode with the PGSSLMODE environment variable | ||
// object format: [https://github.com/brianc/node-postgres/blob/ee19e74ffa6309c9c5e8e01746261a8f651661f8/lib/connection.js#L79] | ||
// see also [http://www.postgresql.org/docs/9.3/static/libpq-ssl.html] | ||
'ssl', | ||
// In addition to the values accepted by the corresponding server, | ||
// you can use "auto" to determine the right encoding from the | ||
// current locale in the client (LC_CTYPE environment variable on Unix systems) | ||
'client_encoding', | ||
// !! DO NOT SET THIS TO TRUE !! | ||
// (unless you know what you're doing) | ||
// see [http://www.postgresql.org/message-id/flat/bc9549a50706040852u27633f41ib1e6b09f8339d845@mail.gmail.com#bc9549a50706040852u27633f41ib1e6b09f8339d845@mail.gmail.com] | ||
'binary', | ||
// This should help with backends incorrectly considering idle clients to be dead and prematurely disconnecting them. | ||
// this feature has been added in pg module v6.0.0, check pg/CHANGELOG.md | ||
'keepAlive', | ||
// Times out queries after a set time in milliseconds. Added in pg v7.3 | ||
'statement_timeout', | ||
// Terminate any session with an open transaction that has been idle for longer than the specified duration in milliseconds. Added in pg v7.17.0 only supported in postgres >= 10 | ||
'idle_in_transaction_session_timeout' | ||
])); | ||
_.merge(connectionConfig, _.pick(config.dialectOptions, [ | ||
"application_name", | ||
"ssl", | ||
"client_encoding", | ||
"binary", | ||
"keepAlive", | ||
"statement_timeout", | ||
"query_timeout", | ||
"idle_in_transaction_session_timeout", | ||
"options" | ||
])); | ||
} | ||
const connection = await new Promise((resolve, reject) => { | ||
let responded = false; | ||
const connection = new this.lib.Client(connectionConfig); | ||
const parameterHandler = message => { | ||
const connection2 = new this.lib.Client(connectionConfig); | ||
const parameterHandler = (message) => { | ||
switch (message.parameterName) { | ||
case 'server_version': | ||
case "server_version": | ||
if (this.sequelize.options.databaseVersion === 0) { | ||
const version = semver.coerce(message.parameterValue).version; | ||
this.sequelize.options.databaseVersion = semver.valid(version) | ||
? version | ||
: this.dialect.defaultVersion; | ||
this.sequelize.options.databaseVersion = semver.valid(version) ? version : this.dialect.defaultVersion; | ||
} | ||
break; | ||
case 'standard_conforming_strings': | ||
connection['standard_conforming_strings'] = message.parameterValue; | ||
case "standard_conforming_strings": | ||
connection2["standard_conforming_strings"] = message.parameterValue; | ||
break; | ||
} | ||
}; | ||
const endHandler = () => { | ||
debug('connection timeout'); | ||
debug("connection timeout"); | ||
if (!responded) { | ||
reject(new sequelizeErrors.ConnectionTimedOutError(new Error('Connection timed out'))); | ||
reject(new sequelizeErrors.ConnectionTimedOutError(new Error("Connection timed out"))); | ||
} | ||
}; | ||
// If we didn't ever hear from the client.connect() callback the connection timeout | ||
// node-postgres does not treat this as an error since no active query was ever emitted | ||
connection.once('end', endHandler); | ||
connection2.once("end", endHandler); | ||
if (!this.sequelize.config.native) { | ||
// Receive various server parameters for further configuration | ||
connection.connection.on('parameterStatus', parameterHandler); | ||
connection2.connection.on("parameterStatus", parameterHandler); | ||
} | ||
connection.connect(err => { | ||
connection2.connect((err) => { | ||
responded = true; | ||
if (!this.sequelize.config.native) { | ||
// remove parameter handler | ||
connection.connection.removeListener('parameterStatus', parameterHandler); | ||
connection2.connection.removeListener("parameterStatus", parameterHandler); | ||
} | ||
if (err) { | ||
if (err.code) { | ||
switch (err.code) { | ||
case 'ECONNREFUSED': | ||
case "ECONNREFUSED": | ||
reject(new sequelizeErrors.ConnectionRefusedError(err)); | ||
break; | ||
case 'ENOTFOUND': | ||
case "ENOTFOUND": | ||
reject(new sequelizeErrors.HostNotFoundError(err)); | ||
break; | ||
case 'EHOSTUNREACH': | ||
case "EHOSTUNREACH": | ||
reject(new sequelizeErrors.HostNotReachableError(err)); | ||
break; | ||
case 'EINVAL': | ||
case "EINVAL": | ||
reject(new sequelizeErrors.InvalidConnectionError(err)); | ||
@@ -195,24 +153,22 @@ break; | ||
} else { | ||
debug('connection acquired'); | ||
connection.removeListener('end', endHandler); | ||
resolve(connection); | ||
debug("connection acquired"); | ||
connection2.removeListener("end", endHandler); | ||
resolve(connection2); | ||
} | ||
}); | ||
}); | ||
let query = ''; | ||
if (this.sequelize.options.standardConformingStrings !== false && connection['standard_conforming_strings'] !== 'on') { | ||
// Disable escape characters in strings | ||
// see https://github.com/sequelize/sequelize/issues/3545 (security issue) | ||
// see https://www.postgresql.org/docs/current/static/runtime-config-compatible.html#GUC-STANDARD-CONFORMING-STRINGS | ||
query += 'SET standard_conforming_strings=on;'; | ||
let query = ""; | ||
if (this.sequelize.options.standardConformingStrings !== false && connection["standard_conforming_strings"] !== "on") { | ||
query += "SET standard_conforming_strings=on;"; | ||
} | ||
if (this.sequelize.options.clientMinMessages !== false) { | ||
query += `SET client_min_messages TO ${this.sequelize.options.clientMinMessages};`; | ||
if (this.sequelize.options.clientMinMessages !== void 0) { | ||
console.warn('Usage of "options.clientMinMessages" is deprecated and will be removed in v7.'); | ||
console.warn('Please use the sequelize option "dialectOptions.clientMinMessages" instead.'); | ||
} | ||
if (!(config.dialectOptions && config.dialectOptions.clientMinMessages && config.dialectOptions.clientMinMessages.toLowerCase() === "ignore" || this.sequelize.options.clientMinMessages === false)) { | ||
const clientMinMessages = config.dialectOptions && config.dialectOptions.clientMinMessages || this.sequelize.options.clientMinMessages || "warning"; | ||
query += `SET client_min_messages TO ${clientMinMessages};`; | ||
} | ||
if (!this.sequelize.config.keepDefaultTimezone) { | ||
const isZone = !!moment.tz.zone(this.sequelize.options.timezone); | ||
const isZone = !!momentTz.tz.zone(this.sequelize.options.timezone); | ||
if (isZone) { | ||
@@ -224,13 +180,9 @@ query += `SET TIME ZONE '${this.sequelize.options.timezone}';`; | ||
} | ||
if (query) { | ||
await connection.query(query); | ||
} | ||
if (Object.keys(this.nameOidMap).length === 0 && | ||
this.enumOids.oids.length === 0 && | ||
this.enumOids.arrayOids.length === 0) { | ||
if (Object.keys(this.nameOidMap).length === 0 && this.enumOids.oids.length === 0 && this.enumOids.arrayOids.length === 0) { | ||
await this._refreshDynamicOIDs(connection); | ||
} | ||
// Don't let a Postgres restart (or error) to take down the whole app | ||
connection.on('error', error => { | ||
connection.on("error", (error) => { | ||
connection._invalid = true; | ||
@@ -240,80 +192,49 @@ debug(`connection error ${error.code || error.message}`); | ||
}); | ||
return connection; | ||
} | ||
async disconnect(connection) { | ||
if (connection._ending) { | ||
debug('connection tried to disconnect but was already at ENDING state'); | ||
debug("connection tried to disconnect but was already at ENDING state"); | ||
return; | ||
} | ||
return await promisify(callback => connection.end(callback))(); | ||
return await promisify((callback) => connection.end(callback))(); | ||
} | ||
validate(connection) { | ||
return !connection._invalid && !connection._ending; | ||
} | ||
async _refreshDynamicOIDs(connection) { | ||
const databaseVersion = this.sequelize.options.databaseVersion; | ||
const supportedVersion = '8.3.0'; | ||
// Check for supported version | ||
if ( (databaseVersion && semver.gte(databaseVersion, supportedVersion)) === false) { | ||
const supportedVersion = "8.3.0"; | ||
if ((databaseVersion && semver.gte(databaseVersion, supportedVersion)) === false) { | ||
return; | ||
} | ||
const results = await (connection || this.sequelize).query( | ||
'WITH ranges AS (' + | ||
' SELECT pg_range.rngtypid, pg_type.typname AS rngtypname,' + | ||
' pg_type.typarray AS rngtyparray, pg_range.rngsubtype' + | ||
' FROM pg_range LEFT OUTER JOIN pg_type ON pg_type.oid = pg_range.rngtypid' + | ||
')' + | ||
'SELECT pg_type.typname, pg_type.typtype, pg_type.oid, pg_type.typarray,' + | ||
' ranges.rngtypname, ranges.rngtypid, ranges.rngtyparray' + | ||
' FROM pg_type LEFT OUTER JOIN ranges ON pg_type.oid = ranges.rngsubtype' + | ||
' WHERE (pg_type.typtype IN(\'b\', \'e\'));' | ||
); | ||
const results = await (connection || this.sequelize).query("WITH ranges AS ( SELECT pg_range.rngtypid, pg_type.typname AS rngtypname, pg_type.typarray AS rngtyparray, pg_range.rngsubtype FROM pg_range LEFT OUTER JOIN pg_type ON pg_type.oid = pg_range.rngtypid)SELECT pg_type.typname, pg_type.typtype, pg_type.oid, pg_type.typarray, ranges.rngtypname, ranges.rngtypid, ranges.rngtyparray FROM pg_type LEFT OUTER JOIN ranges ON pg_type.oid = ranges.rngsubtype WHERE (pg_type.typtype IN('b', 'e'));"); | ||
let result = Array.isArray(results) ? results.pop() : results; | ||
// When searchPath is prepended then two statements are executed and the result is | ||
// an array of those two statements. First one is the SET search_path and second is | ||
// the SELECT query result. | ||
if (Array.isArray(result)) { | ||
if (result[0].command === 'SET') { | ||
if (result[0].command === "SET") { | ||
result = result.pop(); | ||
} | ||
} | ||
const newNameOidMap = {}; | ||
const newEnumOids = { oids: [], arrayOids: [] }; | ||
for (const row of result.rows) { | ||
// Mapping enums, handled separatedly | ||
if (row.typtype === 'e') { | ||
if (row.typtype === "e") { | ||
newEnumOids.oids.push(row.oid); | ||
if (row.typarray) newEnumOids.arrayOids.push(row.typarray); | ||
if (row.typarray) | ||
newEnumOids.arrayOids.push(row.typarray); | ||
continue; | ||
} | ||
// Mapping base types and their arrays | ||
newNameOidMap[row.typname] = { oid: row.oid }; | ||
if (row.typarray) newNameOidMap[row.typname].arrayOid = row.typarray; | ||
// Mapping ranges(of base types) and their arrays | ||
if (row.typarray) | ||
newNameOidMap[row.typname].arrayOid = row.typarray; | ||
if (row.rngtypid) { | ||
newNameOidMap[row.typname].rangeOid = row.rngtypid; | ||
if (row.rngtyparray) newNameOidMap[row.typname].arrayRangeOid = row.rngtyparray; | ||
if (row.rngtyparray) | ||
newNameOidMap[row.typname].arrayRangeOid = row.rngtyparray; | ||
} | ||
} | ||
// Replace all OID mappings. Avoids temporary empty OID mappings. | ||
this.nameOidMap = newNameOidMap; | ||
this.enumOids = newEnumOids; | ||
this.refreshTypeParser(dataTypes.postgres); | ||
} | ||
_clearDynamicOIDs() { | ||
@@ -324,5 +245,5 @@ this.nameOidMap = {}; | ||
} | ||
module.exports = ConnectionManager; | ||
module.exports.ConnectionManager = ConnectionManager; | ||
module.exports.default = ConnectionManager; | ||
//# sourceMappingURL=connection-manager.js.map |
@@ -1,51 +0,30 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const wkx = require('wkx'); | ||
module.exports = BaseTypes => { | ||
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'http://www.postgresql.org/docs/9.4/static/datatype.html'); | ||
/** | ||
* Removes unsupported Postgres options, i.e., LENGTH, UNSIGNED and ZEROFILL, for the integer data types. | ||
* | ||
* @param {object} dataType The base integer data type. | ||
* @private | ||
*/ | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const wkx = require("wkx"); | ||
module.exports = (BaseTypes) => { | ||
const warn = BaseTypes.ABSTRACT.warn.bind(void 0, "http://www.postgresql.org/docs/9.4/static/datatype.html"); | ||
function removeUnsupportedIntegerOptions(dataType) { | ||
if (dataType._length || dataType.options.length || dataType._unsigned || dataType._zerofill) { | ||
warn(`PostgresSQL does not support '${dataType.key}' with LENGTH, UNSIGNED or ZEROFILL. Plain '${dataType.key}' will be used instead.`); | ||
dataType._length = undefined; | ||
dataType.options.length = undefined; | ||
dataType._unsigned = undefined; | ||
dataType._zerofill = undefined; | ||
dataType._length = void 0; | ||
dataType.options.length = void 0; | ||
dataType._unsigned = void 0; | ||
dataType._zerofill = void 0; | ||
} | ||
} | ||
/** | ||
* types: | ||
* { | ||
* oids: [oid], | ||
* array_oids: [oid] | ||
* } | ||
* | ||
* @see oid here https://github.com/lib/pq/blob/master/oid/types.go | ||
*/ | ||
BaseTypes.UUID.types.postgres = ['uuid']; | ||
BaseTypes.CIDR.types.postgres = ['cidr']; | ||
BaseTypes.INET.types.postgres = ['inet']; | ||
BaseTypes.MACADDR.types.postgres = ['macaddr']; | ||
BaseTypes.TSVECTOR.types.postgres = ['tsvector']; | ||
BaseTypes.JSON.types.postgres = ['json']; | ||
BaseTypes.JSONB.types.postgres = ['jsonb']; | ||
BaseTypes.TIME.types.postgres = ['time']; | ||
BaseTypes.UUID.types.postgres = ["uuid"]; | ||
BaseTypes.CIDR.types.postgres = ["cidr"]; | ||
BaseTypes.INET.types.postgres = ["inet"]; | ||
BaseTypes.MACADDR.types.postgres = ["macaddr"]; | ||
BaseTypes.TSVECTOR.types.postgres = ["tsvector"]; | ||
BaseTypes.JSON.types.postgres = ["json"]; | ||
BaseTypes.JSONB.types.postgres = ["jsonb"]; | ||
BaseTypes.TIME.types.postgres = ["time"]; | ||
class DATEONLY extends BaseTypes.DATEONLY { | ||
_stringify(value, options) { | ||
if (value === Infinity) { | ||
return 'Infinity'; | ||
return "Infinity"; | ||
} | ||
if (value === -Infinity) { | ||
return '-Infinity'; | ||
return "-Infinity"; | ||
} | ||
@@ -56,8 +35,8 @@ return super._stringify(value, options); | ||
if ((!options || options && !options.raw) && value !== Infinity && value !== -Infinity) { | ||
if (typeof value === 'string') { | ||
if (typeof value === "string") { | ||
const lower = value.toLowerCase(); | ||
if (lower === 'infinity') { | ||
if (lower === "infinity") { | ||
return Infinity; | ||
} | ||
if (lower === '-infinity') { | ||
if (lower === "-infinity") { | ||
return -Infinity; | ||
@@ -71,6 +50,6 @@ } | ||
static parse(value) { | ||
if (value === 'infinity') { | ||
if (value === "infinity") { | ||
return Infinity; | ||
} | ||
if (value === '-infinity') { | ||
if (value === "-infinity") { | ||
return -Infinity; | ||
@@ -81,5 +60,3 @@ } | ||
} | ||
BaseTypes.DATEONLY.types.postgres = ['date']; | ||
BaseTypes.DATEONLY.types.postgres = ["date"]; | ||
class DECIMAL extends BaseTypes.DECIMAL { | ||
@@ -90,10 +67,7 @@ static parse(value) { | ||
} | ||
// numeric | ||
BaseTypes.DECIMAL.types.postgres = ['numeric']; | ||
BaseTypes.DECIMAL.types.postgres = ["numeric"]; | ||
class STRING extends BaseTypes.STRING { | ||
toSql() { | ||
if (this._binary) { | ||
return 'BYTEA'; | ||
return "BYTEA"; | ||
} | ||
@@ -103,17 +77,13 @@ return super.toSql(); | ||
} | ||
BaseTypes.STRING.types.postgres = ['varchar']; | ||
BaseTypes.STRING.types.postgres = ["varchar"]; | ||
class TEXT extends BaseTypes.TEXT { | ||
toSql() { | ||
if (this._length) { | ||
warn('PostgreSQL does not support TEXT with options. Plain `TEXT` will be used instead.'); | ||
this._length = undefined; | ||
warn("PostgreSQL does not support TEXT with options. Plain `TEXT` will be used instead."); | ||
this._length = void 0; | ||
} | ||
return 'TEXT'; | ||
return "TEXT"; | ||
} | ||
} | ||
BaseTypes.TEXT.types.postgres = ['text']; | ||
BaseTypes.TEXT.types.postgres = ["text"]; | ||
class CITEXT extends BaseTypes.CITEXT { | ||
@@ -124,9 +94,7 @@ static parse(value) { | ||
} | ||
BaseTypes.CITEXT.types.postgres = ['citext']; | ||
BaseTypes.CITEXT.types.postgres = ["citext"]; | ||
class CHAR extends BaseTypes.CHAR { | ||
toSql() { | ||
if (this._binary) { | ||
return 'BYTEA'; | ||
return "BYTEA"; | ||
} | ||
@@ -136,21 +104,16 @@ return super.toSql(); | ||
} | ||
BaseTypes.CHAR.types.postgres = ['char', 'bpchar']; | ||
BaseTypes.CHAR.types.postgres = ["char", "bpchar"]; | ||
class BOOLEAN extends BaseTypes.BOOLEAN { | ||
toSql() { | ||
return 'BOOLEAN'; | ||
return "BOOLEAN"; | ||
} | ||
_sanitize(value) { | ||
if (value !== null && value !== undefined) { | ||
if (value !== null && value !== void 0) { | ||
if (Buffer.isBuffer(value) && value.length === 1) { | ||
// Bit fields are returned as buffers | ||
value = value[0]; | ||
} | ||
if (typeof value === 'string') { | ||
// Only take action on valid boolean strings. | ||
return value === 'true' || value === 't' ? true : value === 'false' || value === 'f' ? false : value; | ||
if (typeof value === "string") { | ||
return ["true", "t"].includes(value) ? true : ["false", "f"].includes(value) ? false : value; | ||
} | ||
if (typeof value === 'number') { | ||
// Only take action on valid boolean integers. | ||
if (typeof value === "number") { | ||
return value === 1 ? true : value === 0 ? false : value; | ||
@@ -162,10 +125,7 @@ } | ||
} | ||
BOOLEAN.parse = BOOLEAN.prototype._sanitize; | ||
BaseTypes.BOOLEAN.types.postgres = ['bool']; | ||
BaseTypes.BOOLEAN.types.postgres = ["bool"]; | ||
class DATE extends BaseTypes.DATE { | ||
toSql() { | ||
return 'TIMESTAMP WITH TIME ZONE'; | ||
return "TIMESTAMP WITH TIME ZONE"; | ||
} | ||
@@ -180,6 +140,6 @@ validate(value) { | ||
if (value === Infinity) { | ||
return 'Infinity'; | ||
return "Infinity"; | ||
} | ||
if (value === -Infinity) { | ||
return '-Infinity'; | ||
return "-Infinity"; | ||
} | ||
@@ -190,8 +150,8 @@ return super._stringify(value, options); | ||
if ((!options || options && !options.raw) && !(value instanceof Date) && !!value && value !== Infinity && value !== -Infinity) { | ||
if (typeof value === 'string') { | ||
if (typeof value === "string") { | ||
const lower = value.toLowerCase(); | ||
if (lower === 'infinity') { | ||
if (lower === "infinity") { | ||
return Infinity; | ||
} | ||
if (lower === '-infinity') { | ||
if (lower === "-infinity") { | ||
return -Infinity; | ||
@@ -205,5 +165,3 @@ } | ||
} | ||
BaseTypes.DATE.types.postgres = ['timestamptz']; | ||
BaseTypes.DATE.types.postgres = ["timestamptz"]; | ||
class TINYINT extends BaseTypes.TINYINT { | ||
@@ -215,5 +173,3 @@ constructor(length) { | ||
} | ||
// int2 | ||
BaseTypes.TINYINT.types.postgres = ['int2']; | ||
BaseTypes.TINYINT.types.postgres = ["int2"]; | ||
class SMALLINT extends BaseTypes.SMALLINT { | ||
@@ -225,5 +181,3 @@ constructor(length) { | ||
} | ||
// int2 | ||
BaseTypes.SMALLINT.types.postgres = ['int2']; | ||
BaseTypes.SMALLINT.types.postgres = ["int2"]; | ||
class INTEGER extends BaseTypes.INTEGER { | ||
@@ -238,6 +192,3 @@ constructor(length) { | ||
}; | ||
// int4 | ||
BaseTypes.INTEGER.types.postgres = ['int4']; | ||
BaseTypes.INTEGER.types.postgres = ["int4"]; | ||
class BIGINT extends BaseTypes.BIGINT { | ||
@@ -249,5 +200,3 @@ constructor(length) { | ||
} | ||
// int8 | ||
BaseTypes.BIGINT.types.postgres = ['int8']; | ||
BaseTypes.BIGINT.types.postgres = ["int8"]; | ||
class REAL extends BaseTypes.REAL { | ||
@@ -259,5 +208,3 @@ constructor(length) { | ||
} | ||
// float4 | ||
BaseTypes.REAL.types.postgres = ['float4']; | ||
BaseTypes.REAL.types.postgres = ["float4"]; | ||
class DOUBLE extends BaseTypes.DOUBLE { | ||
@@ -269,46 +216,36 @@ constructor(length) { | ||
} | ||
// float8 | ||
BaseTypes.DOUBLE.types.postgres = ['float8']; | ||
BaseTypes.DOUBLE.types.postgres = ["float8"]; | ||
class FLOAT extends BaseTypes.FLOAT { | ||
constructor(length, decimals) { | ||
super(length, decimals); | ||
// POSTGRES does only support lengths as parameter. | ||
// Values between 1-24 result in REAL | ||
// Values between 25-53 result in DOUBLE PRECISION | ||
// If decimals are provided remove these and print a warning | ||
if (this._decimals) { | ||
warn('PostgreSQL does not support FLOAT with decimals. Plain `FLOAT` will be used instead.'); | ||
this._length = undefined; | ||
this.options.length = undefined; | ||
this._decimals = undefined; | ||
warn("PostgreSQL does not support FLOAT with decimals. Plain `FLOAT` will be used instead."); | ||
this._length = void 0; | ||
this.options.length = void 0; | ||
this._decimals = void 0; | ||
} | ||
if (this._unsigned) { | ||
warn('PostgreSQL does not support FLOAT unsigned. `UNSIGNED` was removed.'); | ||
this._unsigned = undefined; | ||
warn("PostgreSQL does not support FLOAT unsigned. `UNSIGNED` was removed."); | ||
this._unsigned = void 0; | ||
} | ||
if (this._zerofill) { | ||
warn('PostgreSQL does not support FLOAT zerofill. `ZEROFILL` was removed.'); | ||
this._zerofill = undefined; | ||
warn("PostgreSQL does not support FLOAT zerofill. `ZEROFILL` was removed."); | ||
this._zerofill = void 0; | ||
} | ||
} | ||
} | ||
delete FLOAT.parse; // Float has no separate type in PG | ||
delete FLOAT.parse; | ||
class BLOB extends BaseTypes.BLOB { | ||
toSql() { | ||
if (this._length) { | ||
warn('PostgreSQL does not support BLOB (BYTEA) with options. Plain `BYTEA` will be used instead.'); | ||
this._length = undefined; | ||
warn("PostgreSQL does not support BLOB (BYTEA) with options. Plain `BYTEA` will be used instead."); | ||
this._length = void 0; | ||
} | ||
return 'BYTEA'; | ||
return "BYTEA"; | ||
} | ||
_hexify(hex) { | ||
// bytea hex format http://www.postgresql.org/docs/current/static/datatype-binary.html | ||
return `E'\\\\x${hex}'`; | ||
} | ||
} | ||
BaseTypes.BLOB.types.postgres = ['bytea']; | ||
BaseTypes.BLOB.types.postgres = ["bytea"]; | ||
class GEOMETRY extends BaseTypes.GEOMETRY { | ||
@@ -322,3 +259,3 @@ toSql() { | ||
} | ||
result += ')'; | ||
result += ")"; | ||
} | ||
@@ -328,3 +265,3 @@ return result; | ||
static parse(value) { | ||
const b = Buffer.from(value, 'hex'); | ||
const b = Buffer.from(value, "hex"); | ||
return wkx.Geometry.parse(b).toGeoJSON({ shortCrs: true }); | ||
@@ -339,9 +276,6 @@ } | ||
} | ||
BaseTypes.GEOMETRY.types.postgres = ['geometry']; | ||
BaseTypes.GEOMETRY.types.postgres = ["geometry"]; | ||
class GEOGRAPHY extends BaseTypes.GEOGRAPHY { | ||
toSql() { | ||
let result = 'GEOGRAPHY'; | ||
let result = "GEOGRAPHY"; | ||
if (this.type) { | ||
@@ -352,3 +286,3 @@ result += `(${this.type}`; | ||
} | ||
result += ')'; | ||
result += ")"; | ||
} | ||
@@ -358,3 +292,3 @@ return result; | ||
static parse(value) { | ||
const b = Buffer.from(value, 'hex'); | ||
const b = Buffer.from(value, "hex"); | ||
return wkx.Geometry.parse(b).toGeoJSON({ shortCrs: true }); | ||
@@ -369,7 +303,4 @@ } | ||
} | ||
BaseTypes.GEOGRAPHY.types.postgres = ['geography']; | ||
BaseTypes.GEOGRAPHY.types.postgres = ["geography"]; | ||
let hstore; | ||
class HSTORE extends BaseTypes.HSTORE { | ||
@@ -379,4 +310,3 @@ constructor() { | ||
if (!hstore) { | ||
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated | ||
hstore = require('./hstore'); | ||
hstore = require("./hstore"); | ||
} | ||
@@ -386,4 +316,3 @@ } | ||
if (!hstore) { | ||
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated | ||
hstore = require('./hstore'); | ||
hstore = require("./hstore"); | ||
} | ||
@@ -400,4 +329,3 @@ return hstore.stringify(value); | ||
if (!hstore) { | ||
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated | ||
hstore = require('./hstore'); | ||
hstore = require("./hstore"); | ||
} | ||
@@ -407,7 +335,4 @@ return hstore.parse(value); | ||
} | ||
HSTORE.prototype.escape = false; | ||
BaseTypes.HSTORE.types.postgres = ['hstore']; | ||
BaseTypes.HSTORE.types.postgres = ["hstore"]; | ||
class RANGE extends BaseTypes.RANGE { | ||
@@ -420,4 +345,4 @@ _value(values, options) { | ||
const valuesStringified = values.map((value, index) => { | ||
if (_.isObject(value) && Object.prototype.hasOwnProperty.call(value, 'value')) { | ||
if (Object.prototype.hasOwnProperty.call(value, 'inclusive')) { | ||
if (_.isObject(value) && Object.prototype.hasOwnProperty.call(value, "value")) { | ||
if (Object.prototype.hasOwnProperty.call(value, "inclusive")) { | ||
valueInclusivity[index] = value.inclusive; | ||
@@ -428,3 +353,2 @@ } | ||
if (value === null || value === -Infinity || value === Infinity) { | ||
// Pass through "unbounded" bounds unchanged | ||
return value; | ||
@@ -437,3 +361,2 @@ } | ||
}); | ||
// Array.map does not preserve extra array properties | ||
valuesStringified.inclusive = valueInclusivity; | ||
@@ -462,31 +385,27 @@ return range.stringify(valuesStringified); | ||
} | ||
static parse(value, options = { parser: val => val }) { | ||
static parse(value, options = { parser: (val) => val }) { | ||
return range.parse(value, options.parser); | ||
} | ||
} | ||
const range = require('./range'); | ||
const range = require("./range"); | ||
RANGE.prototype.escape = false; | ||
BaseTypes.RANGE.types.postgres = { | ||
subtypes: { | ||
integer: 'int4range', | ||
decimal: 'numrange', | ||
date: 'tstzrange', | ||
dateonly: 'daterange', | ||
bigint: 'int8range' | ||
integer: "int4range", | ||
decimal: "numrange", | ||
date: "tstzrange", | ||
dateonly: "daterange", | ||
bigint: "int8range" | ||
}, | ||
castTypes: { | ||
integer: 'int4', | ||
decimal: 'numeric', | ||
date: 'timestamptz', | ||
dateonly: 'date', | ||
bigint: 'int8' | ||
integer: "int4", | ||
decimal: "numeric", | ||
date: "timestamptz", | ||
dateonly: "date", | ||
bigint: "int8" | ||
} | ||
}; | ||
// TODO: Why are base types being manipulated?? | ||
BaseTypes.ARRAY.prototype.escape = false; | ||
BaseTypes.ARRAY.prototype._value = function _value(values, options) { | ||
return values.map(value => { | ||
return values.map((value) => { | ||
if (options && options.bindParam && this.type && this.type._value) { | ||
@@ -497,3 +416,2 @@ return this.type._value(value, options); | ||
value = this.type.stringify(value, options); | ||
if (this.type.escape === false) { | ||
@@ -507,18 +425,16 @@ return value; | ||
BaseTypes.ARRAY.prototype._stringify = function _stringify(values, options) { | ||
let str = `ARRAY[${this._value(values, options).join(',')}]`; | ||
let str = `ARRAY[${this._value(values, options).join(",")}]`; | ||
if (this.type) { | ||
const Utils = require('../../utils'); | ||
const Utils = require("../../utils"); | ||
let castKey = this.toSql(); | ||
if (this.type instanceof BaseTypes.ENUM) { | ||
castKey = `${Utils.addTicks( | ||
Utils.generateEnumName(options.field.Model.getTableName(), options.field.field), | ||
'"' | ||
) }[]`; | ||
const table = options.field.Model.getTableName(); | ||
const useSchema = table.schema !== void 0; | ||
const schemaWithDelimiter = useSchema ? `${Utils.addTicks(table.schema, '"')}${table.delimiter}` : ""; | ||
castKey = `${Utils.addTicks(Utils.generateEnumName(useSchema ? table.tableName : table, options.field.field), '"')}[]`; | ||
str += `::${schemaWithDelimiter}${castKey}`; | ||
} else { | ||
str += `::${castKey}`; | ||
} | ||
str += `::${castKey}`; | ||
} | ||
return str; | ||
@@ -529,3 +445,2 @@ }; | ||
}; | ||
class ENUM extends BaseTypes.ENUM { | ||
@@ -536,5 +451,3 @@ static parse(value) { | ||
} | ||
BaseTypes.ENUM.types.postgres = [null]; | ||
return { | ||
@@ -555,3 +468,3 @@ DECIMAL, | ||
REAL, | ||
'DOUBLE PRECISION': DOUBLE, | ||
"DOUBLE PRECISION": DOUBLE, | ||
FLOAT, | ||
@@ -565,1 +478,2 @@ GEOMETRY, | ||
}; | ||
//# sourceMappingURL=data-types.js.map |
@@ -1,15 +0,15 @@ | ||
'use strict'; | ||
const hstore = require('pg-hstore')({ sanitize: true }); | ||
"use strict"; | ||
const hstore = require("pg-hstore")({ sanitize: true }); | ||
function stringify(data) { | ||
if (data === null) return null; | ||
if (data === null) | ||
return null; | ||
return hstore.stringify(data); | ||
} | ||
exports.stringify = stringify; | ||
function parse(value) { | ||
if (value === null) return null; | ||
if (value === null) | ||
return null; | ||
return hstore.parse(value); | ||
} | ||
exports.parse = parse; | ||
//# sourceMappingURL=hstore.js.map |
@@ -1,11 +0,9 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const AbstractDialect = require('../abstract'); | ||
const ConnectionManager = require('./connection-manager'); | ||
const Query = require('./query'); | ||
const QueryGenerator = require('./query-generator'); | ||
const DataTypes = require('../../data-types').postgres; | ||
const { PostgresQueryInterface } = require('./query-interface'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const AbstractDialect = require("../abstract"); | ||
const ConnectionManager = require("./connection-manager"); | ||
const Query = require("./query"); | ||
const QueryGenerator = require("./query-generator"); | ||
const DataTypes = require("../../data-types").postgres; | ||
const { PostgresQueryInterface } = require("./query-interface"); | ||
class PostgresDialect extends AbstractDialect { | ||
@@ -23,8 +21,7 @@ constructor(sequelize) { | ||
} | ||
PostgresDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), { | ||
'DEFAULT VALUES': true, | ||
'EXCEPTION': true, | ||
'ON DUPLICATE KEY': false, | ||
'ORDER NULLS': true, | ||
"DEFAULT VALUES": true, | ||
EXCEPTION: true, | ||
"ON DUPLICATE KEY": false, | ||
"ORDER NULLS": true, | ||
returnValues: { | ||
@@ -40,3 +37,3 @@ returning: true | ||
skipLocked: true, | ||
forShare: 'FOR SHARE', | ||
forShare: "FOR SHARE", | ||
index: { | ||
@@ -50,4 +47,5 @@ concurrently: true, | ||
inserts: { | ||
onConflictDoNothing: ' ON CONFLICT DO NOTHING', | ||
updateOnDuplicate: ' ON CONFLICT DO UPDATE SET' | ||
onConflictDoNothing: " ON CONFLICT DO NOTHING", | ||
updateOnDuplicate: " ON CONFLICT DO UPDATE SET", | ||
conflictFields: true | ||
}, | ||
@@ -67,13 +65,12 @@ NUMERIC: true, | ||
}); | ||
PostgresDialect.prototype.defaultVersion = '9.5.0'; | ||
PostgresDialect.prototype.defaultVersion = "9.5.0"; | ||
PostgresDialect.prototype.Query = Query; | ||
PostgresDialect.prototype.DataTypes = DataTypes; | ||
PostgresDialect.prototype.name = 'postgres'; | ||
PostgresDialect.prototype.name = "postgres"; | ||
PostgresDialect.prototype.TICK_CHAR = '"'; | ||
PostgresDialect.prototype.TICK_CHAR_LEFT = PostgresDialect.prototype.TICK_CHAR; | ||
PostgresDialect.prototype.TICK_CHAR_RIGHT = PostgresDialect.prototype.TICK_CHAR; | ||
module.exports = PostgresDialect; | ||
module.exports.default = PostgresDialect; | ||
module.exports.PostgresDialect = PostgresDialect; | ||
//# sourceMappingURL=index.js.map |
@@ -1,10 +0,25 @@ | ||
'use strict'; | ||
const Utils = require('../../utils'); | ||
const util = require('util'); | ||
const DataTypes = require('../../data-types'); | ||
const AbstractQueryGenerator = require('../abstract/query-generator'); | ||
const semver = require('semver'); | ||
const _ = require('lodash'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const Utils = require("../../utils"); | ||
const util = require("util"); | ||
const DataTypes = require("../../data-types"); | ||
const AbstractQueryGenerator = require("../abstract/query-generator"); | ||
const semver = require("semver"); | ||
const _ = require("lodash"); | ||
const POSTGRES_RESERVED_WORDS = "all,analyse,analyze,and,any,array,as,asc,asymmetric,authorization,binary,both,case,cast,check,collate,collation,column,concurrently,constraint,create,cross,current_catalog,current_date,current_role,current_schema,current_time,current_timestamp,current_user,default,deferrable,desc,distinct,do,else,end,except,false,fetch,for,foreign,freeze,from,full,grant,group,having,ilike,in,initially,inner,intersect,into,is,isnull,join,lateral,leading,left,like,limit,localtime,localtimestamp,natural,not,notnull,null,offset,on,only,or,order,outer,overlaps,placing,primary,references,returning,right,select,session_user,similar,some,symmetric,table,tablesample,then,to,trailing,true,union,unique,user,using,variadic,verbose,when,where,window,with".split(","); | ||
class PostgresQueryGenerator extends AbstractQueryGenerator { | ||
@@ -14,67 +29,49 @@ setSearchPath(searchPath) { | ||
} | ||
createDatabaseQuery(databaseName, options) { | ||
options = { | ||
options = __spreadValues({ | ||
encoding: null, | ||
collate: null, | ||
...options | ||
}; | ||
collate: null | ||
}, options); | ||
const values = { | ||
database: this.quoteTable(databaseName), | ||
encoding: options.encoding ? ` ENCODING = ${this.escape(options.encoding)}` : '', | ||
collation: options.collate ? ` LC_COLLATE = ${this.escape(options.collate)}` : '', | ||
ctype: options.ctype ? ` LC_CTYPE = ${this.escape(options.ctype)}` : '', | ||
template: options.template ? ` TEMPLATE = ${this.escape(options.template)}` : '' | ||
encoding: options.encoding ? ` ENCODING = ${this.escape(options.encoding)}` : "", | ||
collation: options.collate ? ` LC_COLLATE = ${this.escape(options.collate)}` : "", | ||
ctype: options.ctype ? ` LC_CTYPE = ${this.escape(options.ctype)}` : "", | ||
template: options.template ? ` TEMPLATE = ${this.escape(options.template)}` : "" | ||
}; | ||
return `CREATE DATABASE ${values.database}${values.encoding}${values.collation}${values.ctype}${values.template};`; | ||
} | ||
dropDatabaseQuery(databaseName) { | ||
return `DROP DATABASE IF EXISTS ${this.quoteTable(databaseName)};`; | ||
} | ||
createSchema(schema) { | ||
const databaseVersion = _.get(this, 'sequelize.options.databaseVersion', 0); | ||
if (databaseVersion && semver.gte(databaseVersion, '9.2.0')) { | ||
const databaseVersion = _.get(this, "sequelize.options.databaseVersion", 0); | ||
if (databaseVersion && semver.gte(databaseVersion, "9.2.0")) { | ||
return `CREATE SCHEMA IF NOT EXISTS ${schema};`; | ||
} | ||
return `CREATE SCHEMA ${schema};`; | ||
} | ||
dropSchema(schema) { | ||
return `DROP SCHEMA IF EXISTS ${schema} CASCADE;`; | ||
} | ||
showSchemasQuery() { | ||
return "SELECT schema_name FROM information_schema.schemata WHERE schema_name <> 'information_schema' AND schema_name != 'public' AND schema_name !~ E'^pg_';"; | ||
} | ||
versionQuery() { | ||
return 'SHOW SERVER_VERSION'; | ||
return "SHOW SERVER_VERSION"; | ||
} | ||
createTableQuery(tableName, attributes, options) { | ||
options = { ...options }; | ||
//Postgres 9.0 does not support CREATE TABLE IF NOT EXISTS, 9.1 and above do | ||
const databaseVersion = _.get(this, 'sequelize.options.databaseVersion', 0); | ||
options = __spreadValues({}, options); | ||
const databaseVersion = _.get(this, "sequelize.options.databaseVersion", 0); | ||
const attrStr = []; | ||
let comments = ''; | ||
let columnComments = ''; | ||
let comments = ""; | ||
let columnComments = ""; | ||
const quotedTable = this.quoteTable(tableName); | ||
if (options.comment && typeof options.comment === 'string') { | ||
if (options.comment && typeof options.comment === "string") { | ||
comments += `; COMMENT ON TABLE ${quotedTable} IS ${this.escape(options.comment)}`; | ||
} | ||
for (const attr in attributes) { | ||
const quotedAttr = this.quoteIdentifier(attr); | ||
const i = attributes[attr].indexOf('COMMENT '); | ||
const i = attributes[attr].indexOf("COMMENT "); | ||
if (i !== -1) { | ||
// Move comment to a separate query | ||
const escapedCommentText = this.escape(attributes[attr].substring(i + 8)); | ||
@@ -84,83 +81,48 @@ columnComments += `; COMMENT ON COLUMN ${quotedTable}.${quotedAttr} IS ${escapedCommentText}`; | ||
} | ||
const dataType = this.dataTypeMapping(tableName, attr, attributes[attr]); | ||
attrStr.push(`${quotedAttr} ${dataType}`); | ||
} | ||
let attributesClause = attrStr.join(', '); | ||
let attributesClause = attrStr.join(", "); | ||
if (options.uniqueKeys) { | ||
_.each(options.uniqueKeys, columns => { | ||
_.each(options.uniqueKeys, (columns) => { | ||
if (columns.customIndex) { | ||
attributesClause += `, UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`; | ||
attributesClause += `, UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`; | ||
} | ||
}); | ||
} | ||
const pks = _.reduce(attributes, (acc, attribute, key) => { | ||
if (attribute.includes('PRIMARY KEY')) { | ||
if (attribute.includes("PRIMARY KEY")) { | ||
acc.push(this.quoteIdentifier(key)); | ||
} | ||
return acc; | ||
}, []).join(','); | ||
}, []).join(","); | ||
if (pks.length > 0) { | ||
attributesClause += `, PRIMARY KEY (${pks})`; | ||
} | ||
return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0') ? 'IF NOT EXISTS ' : ''}${quotedTable} (${attributesClause})${comments}${columnComments};`; | ||
return `CREATE TABLE ${databaseVersion === 0 || semver.gte(databaseVersion, "9.1.0") ? "IF NOT EXISTS " : ""}${quotedTable} (${attributesClause})${comments}${columnComments};`; | ||
} | ||
dropTableQuery(tableName, 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() { | ||
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type LIKE '%TABLE' AND table_name != 'spatial_ref_sys';"; | ||
} | ||
tableExistsQuery(tableName) { | ||
const table = tableName.tableName || tableName; | ||
const schema = tableName.schema || "public"; | ||
return `SELECT table_name FROM information_schema.tables WHERE table_schema = ${this.escape(schema)} AND table_name = ${this.escape(table)}`; | ||
} | ||
describeTableQuery(tableName, schema) { | ||
if (!schema) schema = 'public'; | ||
return 'SELECT ' + | ||
'pk.constraint_type as "Constraint",' + | ||
'c.column_name as "Field", ' + | ||
'c.column_default as "Default",' + | ||
'c.is_nullable as "Null", ' + | ||
'(CASE WHEN c.udt_name = \'hstore\' THEN c.udt_name ELSE c.data_type END) || (CASE WHEN c.character_maximum_length IS NOT NULL THEN \'(\' || c.character_maximum_length || \')\' ELSE \'\' END) as "Type", ' + | ||
'(SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special", ' + | ||
'(SELECT pgd.description FROM pg_catalog.pg_statio_all_tables AS st INNER JOIN pg_catalog.pg_description pgd on (pgd.objoid=st.relid) WHERE c.ordinal_position=pgd.objsubid AND c.table_name=st.relname) AS "Comment" ' + | ||
'FROM information_schema.columns c ' + | ||
'LEFT JOIN (SELECT tc.table_schema, tc.table_name, ' + | ||
'cu.column_name, tc.constraint_type ' + | ||
'FROM information_schema.TABLE_CONSTRAINTS tc ' + | ||
'JOIN information_schema.KEY_COLUMN_USAGE cu ' + | ||
'ON tc.table_schema=cu.table_schema and tc.table_name=cu.table_name ' + | ||
'and tc.constraint_name=cu.constraint_name ' + | ||
'and tc.constraint_type=\'PRIMARY KEY\') pk ' + | ||
'ON pk.table_schema=c.table_schema ' + | ||
'AND pk.table_name=c.table_name ' + | ||
'AND pk.column_name=c.column_name ' + | ||
`WHERE c.table_name = ${this.escape(tableName)} AND c.table_schema = ${this.escape(schema)} `; | ||
if (!schema) | ||
schema = "public"; | ||
return `SELECT pk.constraint_type as "Constraint",c.column_name as "Field", c.column_default as "Default",c.is_nullable as "Null", (CASE WHEN c.udt_name = 'hstore' THEN c.udt_name ELSE c.data_type END) || (CASE WHEN c.character_maximum_length IS NOT NULL THEN '(' || c.character_maximum_length || ')' ELSE '' END) as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special", (SELECT pgd.description FROM pg_catalog.pg_statio_all_tables AS st INNER JOIN pg_catalog.pg_description pgd on (pgd.objoid=st.relid) WHERE c.ordinal_position=pgd.objsubid AND c.table_name=st.relname) AS "Comment" FROM information_schema.columns c LEFT JOIN (SELECT tc.table_schema, tc.table_name, cu.column_name, tc.constraint_type FROM information_schema.TABLE_CONSTRAINTS tc JOIN information_schema.KEY_COLUMN_USAGE cu ON tc.table_schema=cu.table_schema and tc.table_name=cu.table_name and tc.constraint_name=cu.constraint_name and tc.constraint_type='PRIMARY KEY') pk ON pk.table_schema=c.table_schema AND pk.table_name=c.table_name AND pk.column_name=c.column_name WHERE c.table_name = ${this.escape(tableName)} AND c.table_schema = ${this.escape(schema)} `; | ||
} | ||
/** | ||
* Check whether the statmement is json function or simple path | ||
* | ||
* @param {string} stmt The statement to validate | ||
* @returns {boolean} true if the given statement is json function | ||
* @throws {Error} throw if the statement looks like json function but has invalid token | ||
*/ | ||
_checkValidJsonStatement(stmt) { | ||
if (typeof stmt !== 'string') { | ||
if (typeof stmt !== "string") { | ||
return false; | ||
} | ||
// https://www.postgresql.org/docs/current/static/functions-json.html | ||
const jsonFunctionRegex = /^\s*((?:[a-z]+_){0,2}jsonb?(?:_[a-z]+){0,2})\([^)]*\)/i; | ||
const jsonOperatorRegex = /^\s*(->>?|#>>?|@>|<@|\?[|&]?|\|{2}|#-)/i; | ||
const tokenCaptureRegex = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i; | ||
let currentIndex = 0; | ||
@@ -171,3 +133,2 @@ let openingBrackets = 0; | ||
let hasInvalidToken = false; | ||
while (currentIndex < stmt.length) { | ||
@@ -177,7 +138,6 @@ const string = stmt.substr(currentIndex); | ||
if (functionMatches) { | ||
currentIndex += functionMatches[0].indexOf('('); | ||
currentIndex += functionMatches[0].indexOf("("); | ||
hasJsonFunction = true; | ||
continue; | ||
} | ||
const operatorMatches = jsonOperatorRegex.exec(string); | ||
@@ -189,11 +149,10 @@ if (operatorMatches) { | ||
} | ||
const tokenMatches = tokenCaptureRegex.exec(string); | ||
if (tokenMatches) { | ||
const capturedToken = tokenMatches[1]; | ||
if (capturedToken === '(') { | ||
if (capturedToken === "(") { | ||
openingBrackets++; | ||
} else if (capturedToken === ')') { | ||
} else if (capturedToken === ")") { | ||
closingBrackets++; | ||
} else if (capturedToken === ';') { | ||
} else if (capturedToken === ";") { | ||
hasInvalidToken = true; | ||
@@ -205,7 +164,4 @@ break; | ||
} | ||
break; | ||
} | ||
// Check invalid json statement | ||
hasInvalidToken |= openingBrackets !== closingBrackets; | ||
@@ -215,25 +171,15 @@ if (hasJsonFunction && hasInvalidToken) { | ||
} | ||
// return true if the statement has valid json function | ||
return hasJsonFunction; | ||
} | ||
handleSequelizeMethod(smth, tableName, factory, options, prepend) { | ||
if (smth instanceof Utils.Json) { | ||
// Parse nested object | ||
if (smth.conditions) { | ||
const conditions = this.parseConditionObject(smth.conditions).map(condition => | ||
`${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'` | ||
); | ||
return conditions.join(' AND '); | ||
const conditions = this.parseConditionObject(smth.conditions).map((condition) => `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`); | ||
return conditions.join(" AND "); | ||
} | ||
if (smth.path) { | ||
let str; | ||
// Allow specifying conditions using the postgres json syntax | ||
if (this._checkValidJsonStatement(smth.path)) { | ||
str = smth.path; | ||
} else { | ||
// Also support json property accessors | ||
const paths = _.toPath(smth.path); | ||
@@ -243,7 +189,5 @@ const column = paths.shift(); | ||
} | ||
if (smth.value) { | ||
str += util.format(' = %s', this.escape(smth.value)); | ||
str += util.format(" = %s", this.escape(smth.value)); | ||
} | ||
return str; | ||
@@ -254,5 +198,4 @@ } | ||
} | ||
addColumnQuery(table, key, attribute) { | ||
const dbDataType = this.attributeToSQL(attribute, { context: 'addColumn', table, key }); | ||
const dbDataType = this.attributeToSQL(attribute, { context: "addColumn", table, key }); | ||
const dataType = attribute.type || attribute; | ||
@@ -262,5 +205,3 @@ const definition = this.dataTypeMapping(table, key, dbDataType); | ||
const quotedTable = this.quoteTable(this.extractTableDetails(table)); | ||
let query = `ALTER TABLE ${quotedTable} ADD COLUMN ${quotedKey} ${definition};`; | ||
if (dataType instanceof DataTypes.ENUM) { | ||
@@ -271,6 +212,4 @@ query = this.pgEnum(table, key, dataType) + query; | ||
} | ||
return query; | ||
} | ||
removeColumnQuery(tableName, attributeName) { | ||
@@ -281,27 +220,21 @@ const quotedTableName = this.quoteTable(this.extractTableDetails(tableName)); | ||
} | ||
changeColumnQuery(tableName, attributes) { | ||
const query = subQuery => `ALTER TABLE ${this.quoteTable(tableName)} ALTER COLUMN ${subQuery};`; | ||
const query = (subQuery) => `ALTER TABLE ${this.quoteTable(tableName)} ALTER COLUMN ${subQuery};`; | ||
const sql = []; | ||
for (const attributeName in attributes) { | ||
let definition = this.dataTypeMapping(tableName, attributeName, attributes[attributeName]); | ||
let attrSql = ''; | ||
if (definition.includes('NOT NULL')) { | ||
let attrSql = ""; | ||
if (definition.includes("NOT NULL")) { | ||
attrSql += query(`${this.quoteIdentifier(attributeName)} SET NOT NULL`); | ||
definition = definition.replace('NOT NULL', '').trim(); | ||
} else if (!definition.includes('REFERENCES')) { | ||
definition = definition.replace("NOT NULL", "").trim(); | ||
} else if (!definition.includes("REFERENCES")) { | ||
attrSql += query(`${this.quoteIdentifier(attributeName)} DROP NOT NULL`); | ||
} | ||
if (definition.includes('DEFAULT')) { | ||
if (definition.includes("DEFAULT")) { | ||
attrSql += query(`${this.quoteIdentifier(attributeName)} SET DEFAULT ${definition.match(/DEFAULT ([^;]+)/)[1]}`); | ||
definition = definition.replace(/(DEFAULT[^;]+)/, '').trim(); | ||
} else if (!definition.includes('REFERENCES')) { | ||
definition = definition.replace(/(DEFAULT[^;]+)/, "").trim(); | ||
} else if (!definition.includes("REFERENCES")) { | ||
attrSql += query(`${this.quoteIdentifier(attributeName)} DROP DEFAULT`); | ||
} | ||
if (attributes[attributeName].startsWith('ENUM(')) { | ||
if (attributes[attributeName].startsWith("ENUM(")) { | ||
attrSql += this.pgEnum(tableName, attributeName, attributes[attributeName]); | ||
@@ -311,70 +244,53 @@ definition = definition.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attributeName, { schema: false })); | ||
} | ||
if (definition.match(/UNIQUE;*$/)) { | ||
definition = definition.replace(/UNIQUE;*$/, ''); | ||
attrSql += query(`ADD UNIQUE (${this.quoteIdentifier(attributeName)})`).replace('ALTER COLUMN', ''); | ||
definition = definition.replace(/UNIQUE;*$/, ""); | ||
attrSql += query(`ADD UNIQUE (${this.quoteIdentifier(attributeName)})`).replace("ALTER COLUMN", ""); | ||
} | ||
if (definition.includes('REFERENCES')) { | ||
definition = definition.replace(/.+?(?=REFERENCES)/, ''); | ||
attrSql += query(`ADD FOREIGN KEY (${this.quoteIdentifier(attributeName)}) ${definition}`).replace('ALTER COLUMN', ''); | ||
if (definition.includes("REFERENCES")) { | ||
definition = definition.replace(/.+?(?=REFERENCES)/, ""); | ||
attrSql += query(`ADD FOREIGN KEY (${this.quoteIdentifier(attributeName)}) ${definition}`).replace("ALTER COLUMN", ""); | ||
} else { | ||
attrSql += query(`${this.quoteIdentifier(attributeName)} TYPE ${definition}`); | ||
} | ||
sql.push(attrSql); | ||
} | ||
return sql.join(''); | ||
return sql.join(""); | ||
} | ||
renameColumnQuery(tableName, attrBefore, attributes) { | ||
const attrString = []; | ||
for (const attributeName in attributes) { | ||
attrString.push(`${this.quoteIdentifier(attrBefore)} TO ${this.quoteIdentifier(attributeName)}`); | ||
} | ||
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) { | ||
fnName = fnName || 'testfunc'; | ||
language = language || 'plpgsql'; | ||
returns = returns ? `RETURNS ${returns}` : ''; | ||
parameters = parameters || ''; | ||
fnName = fnName || "testfunc"; | ||
language = language || "plpgsql"; | ||
returns = returns ? `RETURNS ${returns}` : ""; | ||
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}();`; | ||
} | ||
truncateTableQuery(tableName, options = {}) { | ||
return [ | ||
`TRUNCATE ${this.quoteTable(tableName)}`, | ||
options.restartIdentity ? ' RESTART IDENTITY' : '', | ||
options.cascade ? ' CASCADE' : '' | ||
].join(''); | ||
options.restartIdentity ? " RESTART IDENTITY" : "", | ||
options.cascade ? " CASCADE" : "" | ||
].join(""); | ||
} | ||
deleteQuery(tableName, where, options = {}, model) { | ||
const table = this.quoteTable(tableName); | ||
let whereClause = this.getWhereConditions(where, null, model, options); | ||
const limit = options.limit ? ` LIMIT ${this.escape(options.limit)}` : ''; | ||
let primaryKeys = ''; | ||
let primaryKeysSelection = ''; | ||
const limit = options.limit ? ` LIMIT ${this.escape(options.limit)}` : ""; | ||
let primaryKeys = ""; | ||
let primaryKeysSelection = ""; | ||
if (whereClause) { | ||
whereClause = ` WHERE ${whereClause}`; | ||
} | ||
if (options.limit) { | ||
if (!model) { | ||
throw new Error('Cannot LIMIT delete without a model.'); | ||
throw new Error("Cannot LIMIT delete without a model."); | ||
} | ||
const pks = Object.values(model.primaryKeys).map(pk => this.quoteIdentifier(pk.field)).join(','); | ||
const pks = Object.values(model.primaryKeys).map((pk) => this.quoteIdentifier(pk.field)).join(","); | ||
primaryKeys = model.primaryKeyAttributes.length > 1 ? `(${pks})` : pks; | ||
primaryKeysSelection = pks; | ||
return `DELETE FROM ${table} WHERE ${primaryKeys} IN (SELECT ${primaryKeysSelection} FROM ${table}${whereClause}${limit})`; | ||
@@ -384,23 +300,13 @@ } | ||
} | ||
showIndexesQuery(tableName) { | ||
let schemaJoin = ''; | ||
let schemaWhere = ''; | ||
if (typeof tableName !== 'string') { | ||
schemaJoin = ', pg_namespace s'; | ||
let schemaJoin = ""; | ||
let schemaWhere = ""; | ||
if (typeof tableName !== "string") { | ||
schemaJoin = ", pg_namespace s"; | ||
schemaWhere = ` AND s.oid = t.relnamespace AND s.nspname = '${tableName.schema}'`; | ||
tableName = tableName.tableName; | ||
} | ||
// This is ARCANE! | ||
return 'SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, ' + | ||
'array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) ' + | ||
`AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a${schemaJoin} ` + | ||
'WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND ' + | ||
`t.relkind = 'r' and t.relname = '${tableName}'${schemaWhere} ` + | ||
'GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;'; | ||
return `SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a${schemaJoin} WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = '${tableName}'${schemaWhere} GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;`; | ||
} | ||
showConstraintsQuery(tableName) { | ||
//Postgres converts camelCased alias to lowercase unless quoted | ||
return [ | ||
@@ -416,31 +322,27 @@ 'SELECT constraint_catalog AS "constraintCatalog",', | ||
'initially_deferred AS "initiallyDeferred"', | ||
'from INFORMATION_SCHEMA.table_constraints', | ||
"from INFORMATION_SCHEMA.table_constraints", | ||
`WHERE table_name='${tableName}';` | ||
].join(' '); | ||
].join(" "); | ||
} | ||
removeIndexQuery(tableName, indexNameOrAttributes) { | ||
removeIndexQuery(tableName, indexNameOrAttributes, options) { | ||
let indexName = indexNameOrAttributes; | ||
if (typeof indexName !== 'string') { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join('_')}`); | ||
if (typeof indexName !== "string") { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); | ||
} | ||
return `DROP INDEX IF EXISTS ${this.quoteIdentifiers(indexName)}`; | ||
return [ | ||
"DROP INDEX", | ||
options && options.concurrently && "CONCURRENTLY", | ||
`IF EXISTS ${this.quoteIdentifiers(indexName)}` | ||
].filter(Boolean).join(" "); | ||
} | ||
addLimitAndOffset(options) { | ||
let fragment = ''; | ||
/* eslint-disable */ | ||
let fragment = ""; | ||
if (options.limit != null) { | ||
fragment += ' LIMIT ' + this.escape(options.limit); | ||
fragment += " LIMIT " + this.escape(options.limit); | ||
} | ||
if (options.offset != null) { | ||
fragment += ' OFFSET ' + this.escape(options.offset); | ||
fragment += " OFFSET " + this.escape(options.offset); | ||
} | ||
/* eslint-enable */ | ||
return fragment; | ||
} | ||
attributeToSQL(attribute, options) { | ||
@@ -452,22 +354,14 @@ if (!_.isPlainObject(attribute)) { | ||
} | ||
let type; | ||
if ( | ||
attribute.type instanceof DataTypes.ENUM || | ||
attribute.type instanceof DataTypes.ARRAY && attribute.type.type instanceof DataTypes.ENUM | ||
) { | ||
if (attribute.type instanceof DataTypes.ENUM || attribute.type instanceof DataTypes.ARRAY && attribute.type.type instanceof DataTypes.ENUM) { | ||
const enumType = attribute.type.type || attribute.type; | ||
let values = attribute.values; | ||
if (enumType.values && !attribute.values) { | ||
values = enumType.values; | ||
} | ||
if (Array.isArray(values) && values.length > 0) { | ||
type = `ENUM(${values.map(value => this.escape(value)).join(', ')})`; | ||
type = `ENUM(${values.map((value) => this.escape(value)).join(", ")})`; | ||
if (attribute.type instanceof DataTypes.ARRAY) { | ||
type += '[]'; | ||
type += "[]"; | ||
} | ||
} else { | ||
@@ -477,47 +371,33 @@ throw new Error("Values for ENUM haven't been defined."); | ||
} | ||
if (!type) { | ||
type = attribute.type; | ||
} | ||
let sql = type.toString(); | ||
if (Object.prototype.hasOwnProperty.call(attribute, 'allowNull') && !attribute.allowNull) { | ||
sql += ' NOT NULL'; | ||
if (Object.prototype.hasOwnProperty.call(attribute, "allowNull") && !attribute.allowNull) { | ||
sql += " NOT NULL"; | ||
} | ||
if (attribute.autoIncrement) { | ||
if (attribute.autoIncrementIdentity) { | ||
sql += ' GENERATED BY DEFAULT AS IDENTITY'; | ||
sql += " GENERATED BY DEFAULT AS IDENTITY"; | ||
} else { | ||
sql += ' SERIAL'; | ||
sql += " SERIAL"; | ||
} | ||
} | ||
if (Utils.defaultValueSchemable(attribute.defaultValue)) { | ||
sql += ` DEFAULT ${this.escape(attribute.defaultValue, attribute)}`; | ||
} | ||
if (attribute.unique === true) { | ||
sql += ' UNIQUE'; | ||
sql += " UNIQUE"; | ||
} | ||
if (attribute.primaryKey) { | ||
sql += ' PRIMARY KEY'; | ||
sql += " PRIMARY KEY"; | ||
} | ||
if (attribute.references) { | ||
let referencesTable = this.quoteTable(attribute.references.model); | ||
let schema; | ||
if (options.schema) { | ||
schema = options.schema; | ||
} else if ( | ||
(!attribute.references.model || typeof attribute.references.model == 'string') | ||
&& options.table | ||
&& options.table.schema | ||
) { | ||
} else if ((!attribute.references.model || typeof attribute.references.model == "string") && options.table && options.table.schema) { | ||
schema = options.table.schema; | ||
} | ||
if (schema) { | ||
@@ -529,28 +409,23 @@ referencesTable = this.quoteTable(this.addSchema({ | ||
} | ||
let referencesKey; | ||
if (attribute.references.key) { | ||
referencesKey = this.quoteIdentifiers(attribute.references.key); | ||
} else { | ||
referencesKey = this.quoteIdentifier('id'); | ||
if (!options.withoutForeignKeyConstraints) { | ||
if (attribute.references.key) { | ||
referencesKey = this.quoteIdentifiers(attribute.references.key); | ||
} else { | ||
referencesKey = this.quoteIdentifier("id"); | ||
} | ||
sql += ` REFERENCES ${referencesTable} (${referencesKey})`; | ||
if (attribute.onDelete) { | ||
sql += ` ON DELETE ${attribute.onDelete.toUpperCase()}`; | ||
} | ||
if (attribute.onUpdate) { | ||
sql += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`; | ||
} | ||
if (attribute.references.deferrable) { | ||
sql += ` ${attribute.references.deferrable.toString(this)}`; | ||
} | ||
} | ||
sql += ` REFERENCES ${referencesTable} (${referencesKey})`; | ||
if (attribute.onDelete) { | ||
sql += ` ON DELETE ${attribute.onDelete.toUpperCase()}`; | ||
} | ||
if (attribute.onUpdate) { | ||
sql += ` ON UPDATE ${attribute.onUpdate.toUpperCase()}`; | ||
} | ||
if (attribute.references.deferrable) { | ||
sql += ` ${attribute.references.deferrable.toString(this)}`; | ||
} | ||
} | ||
if (attribute.comment && typeof attribute.comment === 'string') { | ||
if (options && (options.context === 'addColumn' || options.context === 'changeColumn')) { | ||
if (attribute.comment && typeof attribute.comment === "string") { | ||
if (options && ["addColumn", "changeColumn"].includes(options.context)) { | ||
const quotedAttr = this.quoteIdentifier(options.key); | ||
@@ -560,44 +435,31 @@ const escapedCommentText = this.escape(attribute.comment); | ||
} else { | ||
// for createTable event which does it's own parsing | ||
// TODO: centralize creation of comment statements here | ||
sql += ` COMMENT ${attribute.comment}`; | ||
} | ||
} | ||
return sql; | ||
} | ||
deferConstraintsQuery(options) { | ||
return options.deferrable.toString(this); | ||
} | ||
setConstraintQuery(columns, type) { | ||
let columnFragment = 'ALL'; | ||
let columnFragment = "ALL"; | ||
if (columns) { | ||
columnFragment = columns.map(column => this.quoteIdentifier(column)).join(', '); | ||
columnFragment = columns.map((column) => this.quoteIdentifier(column)).join(", "); | ||
} | ||
return `SET CONSTRAINTS ${columnFragment} ${type}`; | ||
} | ||
setDeferredQuery(columns) { | ||
return this.setConstraintQuery(columns, 'DEFERRED'); | ||
return this.setConstraintQuery(columns, "DEFERRED"); | ||
} | ||
setImmediateQuery(columns) { | ||
return this.setConstraintQuery(columns, 'IMMEDIATE'); | ||
return this.setConstraintQuery(columns, "IMMEDIATE"); | ||
} | ||
attributesToSQL(attributes, options) { | ||
const result = {}; | ||
for (const key in attributes) { | ||
const attribute = attributes[key]; | ||
result[attribute.field || key] = this.attributeToSQL(attribute, { key, ...options }); | ||
result[attribute.field || key] = this.attributeToSQL(attribute, __spreadValues({ key }, options)); | ||
} | ||
return result; | ||
} | ||
createTrigger(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) { | ||
@@ -608,34 +470,25 @@ const decodedEventType = this.decodeTriggerEventType(eventType); | ||
const paramList = this._expandFunctionParamList(functionParams); | ||
return `CREATE ${this.triggerEventTypeIsConstraint(eventType)}TRIGGER ${this.quoteIdentifier(triggerName)} ${decodedEventType} ${ | ||
eventSpec} ON ${this.quoteTable(tableName)}${expandedOptions ? ` ${expandedOptions}` : ''} EXECUTE PROCEDURE ${functionName}(${paramList});`; | ||
return `CREATE ${this.triggerEventTypeIsConstraint(eventType)}TRIGGER ${this.quoteIdentifier(triggerName)} ${decodedEventType} ${eventSpec} ON ${this.quoteTable(tableName)}${expandedOptions ? ` ${expandedOptions}` : ""} EXECUTE PROCEDURE ${functionName}(${paramList});`; | ||
} | ||
dropTrigger(tableName, triggerName) { | ||
return `DROP TRIGGER ${this.quoteIdentifier(triggerName)} ON ${this.quoteTable(tableName)} RESTRICT;`; | ||
} | ||
renameTrigger(tableName, oldTriggerName, newTriggerName) { | ||
return `ALTER TRIGGER ${this.quoteIdentifier(oldTriggerName)} ON ${this.quoteTable(tableName)} RENAME TO ${this.quoteIdentifier(newTriggerName)};`; | ||
} | ||
createFunction(functionName, params, returnType, language, body, optionsArray, 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?"); | ||
const paramList = this._expandFunctionParamList(params); | ||
const variableList = options && options.variables ? this._expandFunctionVariableList(options.variables) : ''; | ||
const variableList = options && options.variables ? this._expandFunctionVariableList(options.variables) : ""; | ||
const expandedOptionsArray = this.expandOptions(optionsArray); | ||
const statement = options && options.force ? 'CREATE OR REPLACE FUNCTION' : 'CREATE FUNCTION'; | ||
const statement = options && options.force ? "CREATE OR REPLACE FUNCTION" : "CREATE FUNCTION"; | ||
return `${statement} ${functionName}(${paramList}) RETURNS ${returnType} AS $func$ ${variableList} BEGIN ${body} END; $func$ language '${language}'${expandedOptionsArray};`; | ||
} | ||
dropFunction(functionName, params) { | ||
if (!functionName) throw new Error('requires functionName'); | ||
// RESTRICT is (currently, as of 9.2) default but we'll be explicit | ||
if (!functionName) | ||
throw new Error("requires functionName"); | ||
const paramList = this._expandFunctionParamList(params); | ||
return `DROP FUNCTION ${functionName}(${paramList}) RESTRICT;`; | ||
} | ||
renameFunction(oldFunctionName, params, newFunctionName) { | ||
@@ -645,39 +498,37 @@ const paramList = this._expandFunctionParamList(params); | ||
} | ||
pgEscapeAndQuote(val) { | ||
return this.quoteIdentifier(Utils.removeTicks(this.escape(val), "'")); | ||
} | ||
_expandFunctionParamList(params) { | ||
if (params === undefined || !Array.isArray(params)) { | ||
throw new Error('_expandFunctionParamList: function parameters array required, including an empty one for no arguments'); | ||
if (params === void 0 || !Array.isArray(params)) { | ||
throw new Error("_expandFunctionParamList: function parameters array required, including an empty one for no arguments"); | ||
} | ||
const paramList = []; | ||
params.forEach(curParam => { | ||
params.forEach((curParam) => { | ||
const paramDef = []; | ||
if (curParam.type) { | ||
if (curParam.direction) { paramDef.push(curParam.direction); } | ||
if (curParam.name) { paramDef.push(curParam.name); } | ||
if (curParam.direction) { | ||
paramDef.push(curParam.direction); | ||
} | ||
if (curParam.name) { | ||
paramDef.push(curParam.name); | ||
} | ||
paramDef.push(curParam.type); | ||
} else { | ||
throw new Error('function or trigger used with a parameter without any type'); | ||
throw new Error("function or trigger used with a parameter without any type"); | ||
} | ||
const joined = paramDef.join(' '); | ||
if (joined) paramList.push(joined); | ||
const joined = paramDef.join(" "); | ||
if (joined) | ||
paramList.push(joined); | ||
}); | ||
return paramList.join(', '); | ||
return paramList.join(", "); | ||
} | ||
_expandFunctionVariableList(variables) { | ||
if (!Array.isArray(variables)) { | ||
throw new Error('_expandFunctionVariableList: function variables must be an array'); | ||
throw new Error("_expandFunctionVariableList: function variables must be an array"); | ||
} | ||
const variableDefinitions = []; | ||
variables.forEach(variable => { | ||
variables.forEach((variable) => { | ||
if (!variable.name || !variable.type) { | ||
throw new Error('function variable must have a name and type'); | ||
throw new Error("function variable must have a name and type"); | ||
} | ||
@@ -688,98 +539,73 @@ let variableDefinition = `DECLARE ${variable.name} ${variable.type}`; | ||
} | ||
variableDefinition += ';'; | ||
variableDefinition += ";"; | ||
variableDefinitions.push(variableDefinition); | ||
}); | ||
return variableDefinitions.join(' '); | ||
return variableDefinitions.join(" "); | ||
} | ||
expandOptions(options) { | ||
return options === undefined || _.isEmpty(options) ? | ||
'' : options.join(' '); | ||
return options === void 0 || _.isEmpty(options) ? "" : options.join(" "); | ||
} | ||
decodeTriggerEventType(eventSpecifier) { | ||
const EVENT_DECODER = { | ||
'after': 'AFTER', | ||
'before': 'BEFORE', | ||
'instead_of': 'INSTEAD OF', | ||
'after_constraint': 'AFTER' | ||
"after": "AFTER", | ||
"before": "BEFORE", | ||
"instead_of": "INSTEAD OF", | ||
"after_constraint": "AFTER" | ||
}; | ||
if (!EVENT_DECODER[eventSpecifier]) { | ||
throw new Error(`Invalid trigger event specified: ${eventSpecifier}`); | ||
} | ||
return EVENT_DECODER[eventSpecifier]; | ||
} | ||
triggerEventTypeIsConstraint(eventSpecifier) { | ||
return eventSpecifier === 'after_constraint' ? 'CONSTRAINT ' : ''; | ||
return eventSpecifier === "after_constraint" ? "CONSTRAINT " : ""; | ||
} | ||
expandTriggerEventSpec(fireOnSpec) { | ||
if (_.isEmpty(fireOnSpec)) { | ||
throw new Error('no table change events specified to trigger on'); | ||
throw new Error("no table change events specified to trigger on"); | ||
} | ||
return _.map(fireOnSpec, (fireValue, fireKey) => { | ||
const EVENT_MAP = { | ||
'insert': 'INSERT', | ||
'update': 'UPDATE', | ||
'delete': 'DELETE', | ||
'truncate': 'TRUNCATE' | ||
"insert": "INSERT", | ||
"update": "UPDATE", | ||
"delete": "DELETE", | ||
"truncate": "TRUNCATE" | ||
}; | ||
if (!EVENT_MAP[fireValue]) { | ||
throw new Error(`parseTriggerEventSpec: undefined trigger event ${fireKey}`); | ||
} | ||
let eventSpec = EVENT_MAP[fireValue]; | ||
if (eventSpec === 'UPDATE') { | ||
if (eventSpec === "UPDATE") { | ||
if (Array.isArray(fireValue) && fireValue.length > 0) { | ||
eventSpec += ` OF ${fireValue.join(', ')}`; | ||
eventSpec += ` OF ${fireValue.join(", ")}`; | ||
} | ||
} | ||
return eventSpec; | ||
}).join(' OR '); | ||
}).join(" OR "); | ||
} | ||
pgEnumName(tableName, attr, options) { | ||
options = options || {}; | ||
const tableDetails = this.extractTableDetails(tableName, options); | ||
let enumName = Utils.addTicks(Utils.generateEnumName(tableDetails.tableName, attr), '"'); | ||
// pgListEnums requires the enum name only, without the schema | ||
if (options.schema !== false && tableDetails.schema) { | ||
enumName = this.quoteIdentifier(tableDetails.schema) + tableDetails.delimiter + enumName; | ||
} | ||
return enumName; | ||
} | ||
pgListEnums(tableName, attrName, options) { | ||
let enumName = ''; | ||
let enumName = ""; | ||
const tableDetails = this.extractTableDetails(tableName, options); | ||
if (tableDetails.tableName && attrName) { | ||
enumName = ` AND t.typname=${this.pgEnumName(tableDetails.tableName, attrName, { schema: false }).replace(/"/g, "'")}`; | ||
} | ||
return 'SELECT t.typname enum_name, array_agg(e.enumlabel ORDER BY enumsortorder) enum_value FROM pg_type t ' + | ||
'JOIN pg_enum e ON t.oid = e.enumtypid ' + | ||
'JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace ' + | ||
`WHERE n.nspname = '${tableDetails.schema}'${enumName} GROUP BY 1`; | ||
return `SELECT t.typname enum_name, array_agg(e.enumlabel ORDER BY enumsortorder) enum_value FROM pg_type t JOIN pg_enum e ON t.oid = e.enumtypid JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname = '${tableDetails.schema}'${enumName} GROUP BY 1`; | ||
} | ||
pgEnum(tableName, attr, dataType, options) { | ||
const enumName = this.pgEnumName(tableName, attr, options); | ||
let values; | ||
if (dataType.values) { | ||
values = `ENUM(${dataType.values.map(value => this.escape(value)).join(', ')})`; | ||
values = `ENUM(${dataType.values.map((value) => this.escape(value)).join(", ")})`; | ||
} else { | ||
values = dataType.toString().match(/^ENUM\(.+\)/)[0]; | ||
} | ||
let sql = `CREATE TYPE ${enumName} AS ${values};`; | ||
@@ -791,12 +617,9 @@ if (!!options && options.force === true) { | ||
} | ||
pgEnumAdd(tableName, attr, value, options) { | ||
const enumName = this.pgEnumName(tableName, attr); | ||
let sql = `ALTER TYPE ${enumName} ADD VALUE `; | ||
if (semver.gte(this.sequelize.options.databaseVersion, '9.3.0')) { | ||
sql += 'IF NOT EXISTS '; | ||
if (semver.gte(this.sequelize.options.databaseVersion, "9.3.0")) { | ||
sql += "IF NOT EXISTS "; | ||
} | ||
sql += this.escape(value); | ||
if (options.before) { | ||
@@ -807,6 +630,4 @@ sql += ` BEFORE ${this.escape(options.before)}`; | ||
} | ||
return sql; | ||
} | ||
pgEnumDrop(tableName, attr, enumName) { | ||
@@ -816,115 +637,60 @@ enumName = enumName || this.pgEnumName(tableName, attr); | ||
} | ||
fromArray(text) { | ||
text = text.replace(/^{/, '').replace(/}$/, ''); | ||
text = text.replace(/^{/, "").replace(/}$/, ""); | ||
let matches = text.match(/("(?:\\.|[^"\\\\])*"|[^,]*)(?:\s*,\s*|\s*$)/ig); | ||
if (matches.length < 1) { | ||
return []; | ||
} | ||
matches = matches.map(m => m.replace(/",$/, '').replace(/,$/, '').replace(/(^"|"$)/g, '')); | ||
matches = matches.map((m) => m.replace(/",$/, "").replace(/,$/, "").replace(/(^"|"$)/g, "")); | ||
return matches.slice(0, -1); | ||
} | ||
dataTypeMapping(tableName, attr, dataType) { | ||
if (dataType.includes('PRIMARY KEY')) { | ||
dataType = dataType.replace('PRIMARY KEY', ''); | ||
if (dataType.includes("PRIMARY KEY")) { | ||
dataType = dataType.replace("PRIMARY KEY", ""); | ||
} | ||
if (dataType.includes('SERIAL')) { | ||
if (dataType.includes('BIGINT')) { | ||
dataType = dataType.replace('SERIAL', 'BIGSERIAL'); | ||
dataType = dataType.replace('BIGINT', ''); | ||
} else if (dataType.includes('SMALLINT')) { | ||
dataType = dataType.replace('SERIAL', 'SMALLSERIAL'); | ||
dataType = dataType.replace('SMALLINT', ''); | ||
if (dataType.includes("SERIAL")) { | ||
if (dataType.includes("BIGINT")) { | ||
dataType = dataType.replace("SERIAL", "BIGSERIAL"); | ||
dataType = dataType.replace("BIGINT", ""); | ||
} else if (dataType.includes("SMALLINT")) { | ||
dataType = dataType.replace("SERIAL", "SMALLSERIAL"); | ||
dataType = dataType.replace("SMALLINT", ""); | ||
} else { | ||
dataType = dataType.replace('INTEGER', ''); | ||
dataType = dataType.replace("INTEGER", ""); | ||
} | ||
dataType = dataType.replace('NOT NULL', ''); | ||
dataType = dataType.replace("NOT NULL", ""); | ||
} | ||
if (dataType.startsWith('ENUM(')) { | ||
if (dataType.startsWith("ENUM(")) { | ||
dataType = dataType.replace(/^ENUM\(.+\)/, this.pgEnumName(tableName, attr)); | ||
} | ||
return dataType; | ||
} | ||
/** | ||
* Generates an SQL query that returns all foreign keys of a table. | ||
* | ||
* @param {string} tableName The name of the table. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
getForeignKeysQuery(tableName) { | ||
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;`; | ||
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;`; | ||
} | ||
/** | ||
* Generate common SQL prefix for getForeignKeyReferencesQuery. | ||
* | ||
* @returns {string} | ||
*/ | ||
_getForeignKeyReferencesQueryPrefix() { | ||
return 'SELECT ' + | ||
'DISTINCT tc.constraint_name as constraint_name, ' + | ||
'tc.constraint_schema as constraint_schema, ' + | ||
'tc.constraint_catalog as constraint_catalog, ' + | ||
'tc.table_name as table_name,' + | ||
'tc.table_schema as table_schema,' + | ||
'tc.table_catalog as table_catalog,' + | ||
'kcu.column_name as column_name,' + | ||
'ccu.table_schema AS referenced_table_schema,' + | ||
'ccu.table_catalog AS referenced_table_catalog,' + | ||
'ccu.table_name AS referenced_table_name,' + | ||
'ccu.column_name AS referenced_column_name ' + | ||
'FROM information_schema.table_constraints AS tc ' + | ||
'JOIN information_schema.key_column_usage AS kcu ' + | ||
'ON tc.constraint_name = kcu.constraint_name ' + | ||
'JOIN information_schema.constraint_column_usage AS ccu ' + | ||
'ON ccu.constraint_name = tc.constraint_name '; | ||
return "SELECT DISTINCT tc.constraint_name as constraint_name, tc.constraint_schema as constraint_schema, tc.constraint_catalog as constraint_catalog, tc.table_name as table_name,tc.table_schema as table_schema,tc.table_catalog as table_catalog,tc.initially_deferred as initially_deferred,tc.is_deferrable as is_deferrable,kcu.column_name as column_name,ccu.table_schema AS referenced_table_schema,ccu.table_catalog AS referenced_table_catalog,ccu.table_name AS referenced_table_name,ccu.column_name AS referenced_column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name "; | ||
} | ||
/** | ||
* Generates an SQL query that returns all foreign keys details of a table. | ||
* | ||
* As for getForeignKeysQuery is not compatible with getForeignKeyReferencesQuery, so add a new function. | ||
* | ||
* @param {string} tableName | ||
* @param {string} catalogName | ||
* @param {string} schemaName | ||
*/ | ||
getForeignKeyReferencesQuery(tableName, catalogName, schemaName) { | ||
return `${this._getForeignKeyReferencesQueryPrefix() | ||
}WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '${tableName}'${ | ||
catalogName ? ` AND tc.table_catalog = '${catalogName}'` : '' | ||
}${schemaName ? ` AND tc.table_schema = '${schemaName}'` : ''}`; | ||
return `${this._getForeignKeyReferencesQueryPrefix()}WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '${tableName}'${catalogName ? ` AND tc.table_catalog = '${catalogName}'` : ""}${schemaName ? ` AND tc.table_schema = '${schemaName}'` : ""}`; | ||
} | ||
getForeignKeyReferenceQuery(table, columnName) { | ||
const tableName = table.tableName || table; | ||
const schema = table.schema; | ||
return `${this._getForeignKeyReferencesQueryPrefix() | ||
}WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name='${tableName}' AND kcu.column_name = '${columnName}'${ | ||
schema ? ` AND tc.table_schema = '${schema}'` : ''}`; | ||
return `${this._getForeignKeyReferencesQueryPrefix()}WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name='${tableName}' AND kcu.column_name = '${columnName}'${schema ? ` AND tc.table_schema = '${schema}'` : ""}`; | ||
} | ||
/** | ||
* Generates an SQL query that removes a foreign key from a table. | ||
* | ||
* @param {string} tableName The name of the table. | ||
* @param {string} foreignKey The name of the foreign key constraint. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
dropForeignKeyQuery(tableName, foreignKey) { | ||
return `ALTER TABLE ${this.quoteTable(tableName)} DROP CONSTRAINT ${this.quoteIdentifier(foreignKey)};`; | ||
} | ||
quoteIdentifier(identifier, force) { | ||
const optForceQuote = force || false; | ||
const optQuoteIdentifiers = this.options.quoteIdentifiers !== false; | ||
const rawIdentifier = Utils.removeTicks(identifier, '"'); | ||
if (optForceQuote === true || optQuoteIdentifiers !== false || identifier.includes(".") || identifier.includes("->") || POSTGRES_RESERVED_WORDS.includes(rawIdentifier.toLowerCase())) { | ||
return Utils.addTicks(rawIdentifier, '"'); | ||
} | ||
return rawIdentifier; | ||
} | ||
} | ||
module.exports = PostgresQueryGenerator; | ||
//# sourceMappingURL=query-generator.js.map |
@@ -1,61 +0,65 @@ | ||
'use strict'; | ||
const DataTypes = require('../../data-types'); | ||
const QueryTypes = require('../../query-types'); | ||
const { QueryInterface } = require('../abstract/query-interface'); | ||
const Utils = require('../../utils'); | ||
/** | ||
* The interface that Sequelize uses to talk with Postgres database | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
var __objRest = (source, exclude) => { | ||
var target = {}; | ||
for (var prop in source) | ||
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) | ||
target[prop] = source[prop]; | ||
if (source != null && __getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(source)) { | ||
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) | ||
target[prop] = source[prop]; | ||
} | ||
return target; | ||
}; | ||
const DataTypes = require("../../data-types"); | ||
const QueryTypes = require("../../query-types"); | ||
const { QueryInterface } = require("../abstract/query-interface"); | ||
const Utils = require("../../utils"); | ||
const Deferrable = require("../../deferrable"); | ||
class PostgresQueryInterface extends QueryInterface { | ||
/** | ||
* Ensure enum and their values. | ||
* | ||
* @param {string} tableName Name of table to create | ||
* @param {object} attributes Object representing a list of normalized table attributes | ||
* @param {object} [options] | ||
* @param {Model} [model] | ||
* | ||
* @protected | ||
*/ | ||
async ensureEnums(tableName, attributes, options, model) { | ||
const keys = Object.keys(attributes); | ||
const keyLen = keys.length; | ||
let sql = ''; | ||
let sql = ""; | ||
let promises = []; | ||
let i = 0; | ||
for (i = 0; i < keyLen; i++) { | ||
const attribute = attributes[keys[i]]; | ||
const type = attribute.type; | ||
if ( | ||
type instanceof DataTypes.ENUM || | ||
type instanceof DataTypes.ARRAY && type.type instanceof DataTypes.ENUM //ARRAY sub type is ENUM | ||
) { | ||
if (type instanceof DataTypes.ENUM || type instanceof DataTypes.ARRAY && type.type instanceof DataTypes.ENUM) { | ||
sql = this.queryGenerator.pgListEnums(tableName, attribute.field || keys[i], options); | ||
promises.push(this.sequelize.query( | ||
sql, | ||
{ ...options, plain: true, raw: true, type: QueryTypes.SELECT } | ||
)); | ||
promises.push(this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { plain: true, raw: true, type: QueryTypes.SELECT }))); | ||
} | ||
} | ||
const results = await Promise.all(promises); | ||
promises = []; | ||
let enumIdx = 0; | ||
// This little function allows us to re-use the same code that prepends or appends new value to enum array | ||
const addEnumValue = (field, value, relativeValue, position = 'before', spliceStart = promises.length) => { | ||
const valueOptions = { ...options }; | ||
const addEnumValue = (field, value, relativeValue, position = "before", spliceStart = promises.length) => { | ||
const valueOptions = __spreadValues({}, options); | ||
valueOptions.before = null; | ||
valueOptions.after = null; | ||
switch (position) { | ||
case 'after': | ||
case "after": | ||
valueOptions.after = relativeValue; | ||
break; | ||
case 'before': | ||
case "before": | ||
default: | ||
@@ -65,10 +69,6 @@ valueOptions.before = relativeValue; | ||
} | ||
promises.splice(spliceStart, 0, () => { | ||
return this.sequelize.query(this.queryGenerator.pgEnumAdd( | ||
tableName, field, value, valueOptions | ||
), valueOptions); | ||
return this.sequelize.query(this.queryGenerator.pgEnumAdd(tableName, field, value, valueOptions), valueOptions); | ||
}); | ||
}; | ||
for (i = 0; i < keyLen; i++) { | ||
@@ -79,11 +79,6 @@ const attribute = attributes[keys[i]]; | ||
const field = attribute.field || keys[i]; | ||
if ( | ||
type instanceof DataTypes.ENUM || | ||
type instanceof DataTypes.ARRAY && enumType instanceof DataTypes.ENUM //ARRAY sub type is ENUM | ||
) { | ||
// If the enum type doesn't exist then create it | ||
if (type instanceof DataTypes.ENUM || type instanceof DataTypes.ARRAY && enumType instanceof DataTypes.ENUM) { | ||
if (!results[enumIdx]) { | ||
promises.push(() => { | ||
return this.sequelize.query(this.queryGenerator.pgEnum(tableName, field, enumType, options), { ...options, raw: true }); | ||
return this.sequelize.query(this.queryGenerator.pgEnum(tableName, field, enumType, options), __spreadProps(__spreadValues({}, options), { raw: true })); | ||
}); | ||
@@ -93,9 +88,2 @@ } else if (!!results[enumIdx] && !!model) { | ||
const vals = enumType.values; | ||
// Going through already existing values allows us to make queries that depend on those values | ||
// We will prepend all new values between the old ones, but keep in mind - we can't change order of already existing values | ||
// Then we append the rest of new values AFTER the latest already existing value | ||
// E.g.: [1,2] -> [0,2,1] ==> [1,0,2] | ||
// E.g.: [1,2,3] -> [2,1,3,4] ==> [1,2,3,4] | ||
// E.g.: [1] -> [0,2,3] ==> [1,0,2,3] | ||
let lastOldEnumValue; | ||
@@ -107,10 +95,7 @@ let rightestPosition = -1; | ||
lastOldEnumValue = enumVal; | ||
if (newIdx === -1) { | ||
continue; | ||
} | ||
const newValuesBefore = vals.slice(0, newIdx); | ||
const promisesLength = promises.length; | ||
// we go in reverse order so we could stop when we meet old value | ||
for (let reverseIdx = newValuesBefore.length - 1; reverseIdx >= 0; reverseIdx--) { | ||
@@ -120,7 +105,4 @@ if (~enumVals.indexOf(newValuesBefore[reverseIdx])) { | ||
} | ||
addEnumValue(field, newValuesBefore[reverseIdx], lastOldEnumValue, 'before', promisesLength); | ||
addEnumValue(field, newValuesBefore[reverseIdx], lastOldEnumValue, "before", promisesLength); | ||
} | ||
// we detect the most 'right' position of old value in new enum array so we can append new values to it | ||
if (newIdx > rightestPosition) { | ||
@@ -130,10 +112,8 @@ rightestPosition = newIdx; | ||
} | ||
if (lastOldEnumValue && rightestPosition < vals.length - 1) { | ||
const remainingEnumValues = vals.slice(rightestPosition + 1); | ||
for (let reverseIdx = remainingEnumValues.length - 1; reverseIdx >= 0; reverseIdx--) { | ||
addEnumValue(field, remainingEnumValues[reverseIdx], lastOldEnumValue, 'after'); | ||
addEnumValue(field, remainingEnumValues[reverseIdx], lastOldEnumValue, "after"); | ||
} | ||
} | ||
enumIdx++; | ||
@@ -143,7 +123,3 @@ } | ||
} | ||
const result = await promises | ||
.reduce(async (promise, asyncFunction) => await asyncFunction(await promise), Promise.resolve()); | ||
// If ENUM processed, then refresh OIDs | ||
const result = await promises.reduce(async (promise, asyncFunction) => await asyncFunction(await promise), Promise.resolve()); | ||
if (promises.length) { | ||
@@ -154,89 +130,39 @@ await this.sequelize.dialect.connectionManager._refreshDynamicOIDs(); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async getForeignKeyReferencesForTable(tableName, options) { | ||
const queryOptions = { | ||
...options, | ||
async getForeignKeyReferencesForTable(table, options) { | ||
const queryOptions = __spreadProps(__spreadValues({}, options), { | ||
type: QueryTypes.FOREIGNKEYS | ||
}; | ||
// postgres needs some special treatment as those field names returned are all lowercase | ||
// in order to keep same result with other dialects. | ||
const query = this.queryGenerator.getForeignKeyReferencesQuery(tableName, this.sequelize.config.database); | ||
}); | ||
const query = this.queryGenerator.getForeignKeyReferencesQuery(table.tableName || table, this.sequelize.config.database); | ||
const result = await this.sequelize.query(query, queryOptions); | ||
return result.map(Utils.camelizeObjectKeys); | ||
return result.map((fkMeta) => { | ||
const _a = Utils.camelizeObjectKeys(fkMeta), { initiallyDeferred, isDeferrable } = _a, remaining = __objRest(_a, ["initiallyDeferred", "isDeferrable"]); | ||
return __spreadProps(__spreadValues({}, remaining), { | ||
deferrable: isDeferrable === "NO" ? Deferrable.NOT : initiallyDeferred === "NO" ? Deferrable.INITIALLY_IMMEDIATE : Deferrable.INITIALLY_DEFERRED | ||
}); | ||
}); | ||
} | ||
/** | ||
* Drop specified enum from database (Postgres only) | ||
* | ||
* @param {string} [enumName] Enum name to drop | ||
* @param {object} options Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropEnum(enumName, options) { | ||
options = options || {}; | ||
return this.sequelize.query( | ||
this.queryGenerator.pgEnumDrop(null, null, this.queryGenerator.pgEscapeAndQuote(enumName)), | ||
{ ...options, raw: true } | ||
); | ||
return this.sequelize.query(this.queryGenerator.pgEnumDrop(null, null, this.queryGenerator.pgEscapeAndQuote(enumName)), __spreadProps(__spreadValues({}, options), { raw: true })); | ||
} | ||
/** | ||
* Drop all enums from database (Postgres only) | ||
* | ||
* @param {object} options Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropAllEnums(options) { | ||
options = options || {}; | ||
const enums = await this.pgListEnums(null, options); | ||
return await Promise.all(enums.map(result => this.sequelize.query( | ||
this.queryGenerator.pgEnumDrop(null, null, this.queryGenerator.pgEscapeAndQuote(result.enum_name)), | ||
{ ...options, raw: true } | ||
))); | ||
return await Promise.all(enums.map((result) => this.sequelize.query(this.queryGenerator.pgEnumDrop(null, null, this.queryGenerator.pgEscapeAndQuote(result.enum_name)), __spreadProps(__spreadValues({}, options), { raw: true })))); | ||
} | ||
/** | ||
* List all enums (Postgres only) | ||
* | ||
* @param {string} [tableName] Table whose enum to list | ||
* @param {object} [options] Query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async pgListEnums(tableName, options) { | ||
options = options || {}; | ||
const sql = this.queryGenerator.pgListEnums(tableName); | ||
return this.sequelize.query(sql, { ...options, plain: false, raw: true, type: QueryTypes.SELECT }); | ||
return this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { plain: false, raw: true, type: QueryTypes.SELECT })); | ||
} | ||
/** | ||
* Since postgres has a special case for enums, we should drop the related | ||
* enum type within the table and attribute | ||
* | ||
* @override | ||
*/ | ||
async dropTable(tableName, options) { | ||
await super.dropTable(tableName, options); | ||
const promises = []; | ||
const instanceTable = this.sequelize.modelManager.getModel(tableName, { attribute: 'tableName' }); | ||
const instanceTable = this.sequelize.modelManager.getModel(tableName, { attribute: "tableName" }); | ||
if (!instanceTable) { | ||
// Do nothing when model is not available | ||
return; | ||
} | ||
const getTableName = (!options || !options.schema || options.schema === 'public' ? '' : `${options.schema}_`) + tableName; | ||
const getTableName = (!options || !options.schema || options.schema === "public" ? "" : `${options.schema}_`) + tableName; | ||
const keys = Object.keys(instanceTable.rawAttributes); | ||
const keyLen = keys.length; | ||
for (let i = 0; i < keyLen; i++) { | ||
@@ -246,10 +172,9 @@ if (instanceTable.rawAttributes[keys[i]].type instanceof DataTypes.ENUM) { | ||
options.supportsSearchPath = false; | ||
promises.push(this.sequelize.query(sql, { ...options, raw: true })); | ||
promises.push(this.sequelize.query(sql, __spreadProps(__spreadValues({}, options), { raw: true }))); | ||
} | ||
} | ||
await Promise.all(promises); | ||
} | ||
} | ||
exports.PostgresQueryInterface = PostgresQueryInterface; | ||
//# sourceMappingURL=query-interface.js.map |
@@ -1,24 +0,11 @@ | ||
'use strict'; | ||
const AbstractQuery = require('../abstract/query'); | ||
const QueryTypes = require('../../query-types'); | ||
const sequelizeErrors = require('../../errors'); | ||
const _ = require('lodash'); | ||
const { logger } = require('../../utils/logger'); | ||
const debug = logger.debugContext('sql:pg'); | ||
"use strict"; | ||
const AbstractQuery = require("../abstract/query"); | ||
const QueryTypes = require("../../query-types"); | ||
const sequelizeErrors = require("../../errors"); | ||
const _ = require("lodash"); | ||
const { logger } = require("../../utils/logger"); | ||
const debug = logger.debugContext("sql:pg"); | ||
class Query extends AbstractQuery { | ||
/** | ||
* Rewrite query with parameters. | ||
* | ||
* @param {string} sql | ||
* @param {Array|object} values | ||
* @param {string} dialect | ||
* @private | ||
*/ | ||
static formatBindParameters(sql, values, dialect) { | ||
const stringReplaceFunc = value => typeof value === 'string' ? value.replace(/\0/g, '\\0') : value; | ||
const stringReplaceFunc = (value) => typeof value === "string" ? value.replace(/\0/g, "\\0") : value; | ||
let bindParam; | ||
@@ -32,13 +19,13 @@ if (Array.isArray(values)) { | ||
const seen = {}; | ||
const replacementFunc = (match, key, values) => { | ||
if (seen[key] !== undefined) { | ||
const replacementFunc = (match, key, values2) => { | ||
if (seen[key] !== void 0) { | ||
return seen[key]; | ||
} | ||
if (values[key] !== undefined) { | ||
if (values2[key] !== void 0) { | ||
i = i + 1; | ||
bindParam.push(stringReplaceFunc(values[key])); | ||
bindParam.push(stringReplaceFunc(values2[key])); | ||
seen[key] = `$${i}`; | ||
return `$${i}`; | ||
} | ||
return undefined; | ||
return void 0; | ||
}; | ||
@@ -49,81 +36,50 @@ sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0]; | ||
} | ||
async run(sql, parameters) { | ||
const { connection } = this; | ||
if (!_.isEmpty(this.options.searchPath)) { | ||
sql = this.sequelize.getQueryInterface().queryGenerator.setSearchPath(this.options.searchPath) + sql; | ||
} | ||
if (this.sequelize.options.minifyAliases && this.options.includeAliases) { | ||
_.toPairs(this.options.includeAliases) | ||
// Sorting to replace the longest aliases first to prevent alias collision | ||
.sort((a, b) => b[1].length - a[1].length) | ||
.forEach(([alias, original]) => { | ||
const reg = new RegExp(_.escapeRegExp(original), 'g'); | ||
sql = sql.replace(reg, alias); | ||
}); | ||
_.toPairs(this.options.includeAliases).sort((a, b) => b[1].length - a[1].length).forEach(([alias, original]) => { | ||
const reg = new RegExp(_.escapeRegExp(original), "g"); | ||
sql = sql.replace(reg, alias); | ||
}); | ||
} | ||
this.sql = sql; | ||
const query = parameters && parameters.length | ||
? new Promise((resolve, reject) => connection.query(sql, parameters, (error, result) => error ? reject(error) : resolve(result))) | ||
: new Promise((resolve, reject) => connection.query(sql, (error, result) => error ? reject(error) : resolve(result))); | ||
const query = parameters && parameters.length ? new Promise((resolve, reject) => connection.query(sql, parameters, (error, result) => error ? reject(error) : resolve(result))) : new Promise((resolve, reject) => connection.query(sql, (error, result) => error ? reject(error) : resolve(result))); | ||
const complete = this._logQuery(sql, debug, parameters); | ||
let queryResult; | ||
const errForStack = new Error(); | ||
try { | ||
queryResult = await query; | ||
} catch (err) { | ||
// set the client so that it will be reaped if the connection resets while executing | ||
if (err.code === 'ECONNRESET') { | ||
} catch (error) { | ||
if (error.code === "ECONNRESET" || /Unable to set non-blocking to true/i.test(error) || /SSL SYSCALL error: EOF detected/i.test(error) || /Local: Authentication failure/i.test(error)) { | ||
connection._invalid = true; | ||
} | ||
err.sql = sql; | ||
err.parameters = parameters; | ||
throw this.formatError(err); | ||
error.sql = sql; | ||
error.parameters = parameters; | ||
throw this.formatError(error, errForStack.stack); | ||
} | ||
complete(); | ||
let rows = Array.isArray(queryResult) | ||
? queryResult.reduce((allRows, r) => allRows.concat(r.rows || []), []) | ||
: queryResult.rows; | ||
const rowCount = Array.isArray(queryResult) | ||
? queryResult.reduce( | ||
(count, r) => Number.isFinite(r.rowCount) ? count + r.rowCount : count, | ||
0 | ||
) | ||
: queryResult.rowCount || 0; | ||
let rows = Array.isArray(queryResult) ? queryResult.reduce((allRows, r) => allRows.concat(r.rows || []), []) : queryResult.rows; | ||
const rowCount = Array.isArray(queryResult) ? queryResult.reduce((count, r) => Number.isFinite(r.rowCount) ? count + r.rowCount : count, 0) : queryResult.rowCount || 0; | ||
if (this.sequelize.options.minifyAliases && this.options.aliasesMapping) { | ||
rows = rows | ||
.map(row => _.toPairs(row) | ||
.reduce((acc, [key, value]) => { | ||
const mapping = this.options.aliasesMapping.get(key); | ||
acc[mapping || key] = value; | ||
return acc; | ||
}, {}) | ||
); | ||
rows = rows.map((row) => _.toPairs(row).reduce((acc, [key, value]) => { | ||
const mapping = this.options.aliasesMapping.get(key); | ||
acc[mapping || key] = value; | ||
return acc; | ||
}, {})); | ||
} | ||
const isTableNameQuery = sql.startsWith('SELECT table_name FROM information_schema.tables'); | ||
const isRelNameQuery = sql.startsWith('SELECT relname FROM pg_class WHERE oid IN'); | ||
const isTableNameQuery = sql.startsWith("SELECT table_name FROM information_schema.tables"); | ||
const isRelNameQuery = sql.startsWith("SELECT relname FROM pg_class WHERE oid IN"); | ||
if (isRelNameQuery) { | ||
return rows.map(row => ({ | ||
return rows.map((row) => ({ | ||
name: row.relname, | ||
tableName: row.relname.split('_')[0] | ||
tableName: row.relname.split("_")[0] | ||
})); | ||
} | ||
if (isTableNameQuery) { | ||
return rows.map(row => Object.values(row)); | ||
return rows.map((row) => Object.values(row)); | ||
} | ||
if (rows[0] && rows[0].sequelize_caught_exception !== undefined) { | ||
if (rows[0] && rows[0].sequelize_caught_exception !== void 0) { | ||
if (rows[0].sequelize_caught_exception !== null) { | ||
@@ -133,3 +89,3 @@ throw this.formatError({ | ||
parameters, | ||
code: '23505', | ||
code: "23505", | ||
detail: rows[0].sequelize_caught_exception | ||
@@ -142,22 +98,12 @@ }); | ||
} | ||
if (this.isShowIndexesQuery()) { | ||
for (const row of rows) { | ||
const attributes = /ON .*? (?:USING .*?\s)?\(([^]*)\)/gi.exec(row.definition)[1].split(','); | ||
// Map column index in table to column name | ||
const columns = _.zipObject( | ||
row.column_indexes, | ||
this.sequelize.getQueryInterface().queryGenerator.fromArray(row.column_names) | ||
); | ||
const attributes = /ON .*? (?:USING .*?\s)?\(([^]*)\)/gi.exec(row.definition)[1].split(","); | ||
const columns = _.zipObject(row.column_indexes, this.sequelize.getQueryInterface().queryGenerator.fromArray(row.column_names)); | ||
delete row.column_indexes; | ||
delete row.column_names; | ||
let field; | ||
let attribute; | ||
// Indkey is the order of attributes in the index, specified by a string of attribute indexes | ||
row.fields = row.indkey.split(' ').map((indKey, index) => { | ||
row.fields = row.indkey.split(" ").map((indKey, index) => { | ||
field = columns[indKey]; | ||
// for functional indices indKey = 0 | ||
if (!field) { | ||
@@ -169,7 +115,7 @@ return null; | ||
attribute: field, | ||
collate: attribute.match(/COLLATE "(.*?)"/) ? /COLLATE "(.*?)"/.exec(attribute)[1] : undefined, | ||
order: attribute.includes('DESC') ? 'DESC' : attribute.includes('ASC') ? 'ASC' : undefined, | ||
length: undefined | ||
collate: attribute.match(/COLLATE "(.*?)"/) ? /COLLATE "(.*?)"/.exec(attribute)[1] : void 0, | ||
order: attribute.includes("DESC") ? "DESC" : attribute.includes("ASC") ? "ASC" : void 0, | ||
length: void 0 | ||
}; | ||
}).filter(n => n !== null); | ||
}).filter((n) => n !== null); | ||
delete row.columns; | ||
@@ -183,3 +129,3 @@ } | ||
let defParts; | ||
if (row.condef !== undefined && (defParts = row.condef.match(/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?/))) { | ||
if (row.condef !== void 0 && (defParts = row.condef.match(/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?/))) { | ||
row.id = row.constraint_name; | ||
@@ -202,4 +148,2 @@ row.table = defParts[2]; | ||
let result = rows; | ||
// Postgres will treat tables as case-insensitive, so fix the case | ||
// of the returned values to match attributes | ||
if (this.options.raw === false && this.sequelize.options.quoteIdentifiers === false) { | ||
@@ -210,6 +154,6 @@ const attrsMap = _.reduce(this.model.rawAttributes, (m, v, k) => { | ||
}, {}); | ||
result = rows.map(row => { | ||
result = rows.map((row) => { | ||
return _.mapKeys(row, (value, key) => { | ||
const targetAttr = attrsMap[key]; | ||
if (typeof targetAttr === 'string' && targetAttr !== key) { | ||
if (typeof targetAttr === "string" && targetAttr !== key) { | ||
return targetAttr; | ||
@@ -225,27 +169,22 @@ } | ||
const result = {}; | ||
for (const row of rows) { | ||
result[row.Field] = { | ||
type: row.Type.toUpperCase(), | ||
allowNull: row.Null === 'YES', | ||
allowNull: row.Null === "YES", | ||
defaultValue: row.Default, | ||
comment: row.Comment, | ||
special: row.special ? this.sequelize.getQueryInterface().queryGenerator.fromArray(row.special) : [], | ||
primaryKey: row.Constraint === 'PRIMARY KEY' | ||
primaryKey: row.Constraint === "PRIMARY KEY" | ||
}; | ||
if (result[row.Field].type === 'BOOLEAN') { | ||
result[row.Field].defaultValue = { 'false': false, 'true': true }[result[row.Field].defaultValue]; | ||
if (result[row.Field].defaultValue === undefined) { | ||
if (result[row.Field].type === "BOOLEAN") { | ||
result[row.Field].defaultValue = { "false": false, "true": true }[result[row.Field].defaultValue]; | ||
if (result[row.Field].defaultValue === void 0) { | ||
result[row.Field].defaultValue = null; | ||
} | ||
} | ||
if (typeof result[row.Field].defaultValue === 'string') { | ||
result[row.Field].defaultValue = result[row.Field].defaultValue.replace(/'/g, ''); | ||
if (result[row.Field].defaultValue.includes('::')) { | ||
const split = result[row.Field].defaultValue.split('::'); | ||
if (split[1].toLowerCase() !== 'regclass)') { | ||
if (typeof result[row.Field].defaultValue === "string") { | ||
result[row.Field].defaultValue = result[row.Field].defaultValue.replace(/'/g, ""); | ||
if (result[row.Field].defaultValue.includes("::")) { | ||
const split = result[row.Field].defaultValue.split("::"); | ||
if (split[1].toLowerCase() !== "regclass)") { | ||
result[row.Field].defaultValue = split[0]; | ||
@@ -256,3 +195,2 @@ } | ||
} | ||
return result; | ||
@@ -277,8 +215,9 @@ } | ||
if (this.instance && this.instance.dataValues) { | ||
if (this.isInsertQuery() && !this.isUpsertQuery() && rowCount === 0) { | ||
throw new sequelizeErrors.EmptyResultError(); | ||
} | ||
for (const key in rows[0]) { | ||
if (Object.prototype.hasOwnProperty.call(rows[0], key)) { | ||
const record = rows[0][key]; | ||
const attr = _.find(this.model.rawAttributes, attribute => attribute.fieldName === key || attribute.field === key); | ||
const attr = _.find(this.model.rawAttributes, (attribute) => attribute.fieldName === key || attribute.field === key); | ||
this.instance.dataValues[attr && attr.fieldName || key] = record; | ||
@@ -288,3 +227,2 @@ } | ||
} | ||
if (this.isUpsertQuery()) { | ||
@@ -296,5 +234,4 @@ return [ | ||
} | ||
return [ | ||
this.instance || rows && (this.options.plain && rows[0] || rows) || undefined, | ||
this.instance || rows && (this.options.plain && rows[0] || rows) || void 0, | ||
rowCount | ||
@@ -308,4 +245,3 @@ ]; | ||
} | ||
formatError(err) { | ||
formatError(err, errStack) { | ||
let match; | ||
@@ -317,36 +253,29 @@ let table; | ||
let message; | ||
const code = err.code || err.sqlState; | ||
const errMessage = err.message || err.messagePrimary; | ||
const errDetail = err.detail || err.messageDetail; | ||
switch (code) { | ||
case '23503': | ||
case "23503": | ||
index = errMessage.match(/violates foreign key constraint "(.+?)"/); | ||
index = index ? index[1] : undefined; | ||
index = index ? index[1] : void 0; | ||
table = errMessage.match(/on table "(.+?)"/); | ||
table = table ? table[1] : undefined; | ||
return new sequelizeErrors.ForeignKeyConstraintError({ message: errMessage, fields: null, index, table, parent: err }); | ||
case '23505': | ||
// there are multiple different formats of error messages for this error code | ||
// this regex should check at least two | ||
if (errDetail && (match = errDetail.replace(/"/g, '').match(/Key \((.*?)\)=\((.*?)\)/))) { | ||
fields = _.zipObject(match[1].split(', '), match[2].split(', ')); | ||
table = table ? table[1] : void 0; | ||
return new sequelizeErrors.ForeignKeyConstraintError({ | ||
message: errMessage, | ||
fields: null, | ||
index, | ||
table, | ||
parent: err, | ||
stack: errStack | ||
}); | ||
case "23505": | ||
if (errDetail && (match = errDetail.replace(/"/g, "").match(/Key \((.*?)\)=\((.*?)\)/))) { | ||
fields = _.zipObject(match[1].split(", "), match[2].split(", ")); | ||
errors = []; | ||
message = 'Validation error'; | ||
message = "Validation error"; | ||
_.forOwn(fields, (value, field) => { | ||
errors.push(new sequelizeErrors.ValidationErrorItem( | ||
this.getUniqueConstraintErrorMessage(field), | ||
'unique violation', // sequelizeErrors.ValidationErrorItem.Origins.DB, | ||
field, | ||
value, | ||
this.instance, | ||
'not_unique' | ||
)); | ||
errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, value, this.instance, "not_unique")); | ||
}); | ||
if (this.model && this.model.uniqueKeys) { | ||
_.forOwn(this.model.uniqueKeys, constraint => { | ||
_.forOwn(this.model.uniqueKeys, (constraint) => { | ||
if (_.isEqual(constraint.fields, Object.keys(fields)) && !!constraint.msg) { | ||
@@ -358,19 +287,15 @@ message = constraint.msg; | ||
} | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields }); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack }); | ||
} | ||
return new sequelizeErrors.UniqueConstraintError({ | ||
message: errMessage, | ||
parent: err | ||
parent: err, | ||
stack: errStack | ||
}); | ||
case '23P01': | ||
case "23P01": | ||
match = errDetail.match(/Key \((.*?)\)=\((.*?)\)/); | ||
if (match) { | ||
fields = _.zipObject(match[1].split(', '), match[2].split(', ')); | ||
fields = _.zipObject(match[1].split(", "), match[2].split(", ")); | ||
} | ||
message = 'Exclusion constraint error'; | ||
message = "Exclusion constraint error"; | ||
return new sequelizeErrors.ExclusionConstraintError({ | ||
@@ -381,13 +306,12 @@ message, | ||
table: err.table, | ||
parent: err | ||
parent: err, | ||
stack: errStack | ||
}); | ||
case '42704': | ||
case "42704": | ||
if (err.sql && /(CONSTRAINT|INDEX)/gi.test(err.sql)) { | ||
message = 'Unknown constraint error'; | ||
message = "Unknown constraint error"; | ||
index = errMessage.match(/(?:constraint|index) "(.+?)"/i); | ||
index = index ? index[1] : undefined; | ||
index = index ? index[1] : void 0; | ||
table = errMessage.match(/relation "(.+?)"/i); | ||
table = table ? table[1] : undefined; | ||
table = table ? table[1] : void 0; | ||
throw new sequelizeErrors.UnknownConstraintError({ | ||
@@ -398,22 +322,20 @@ message, | ||
table, | ||
parent: err | ||
parent: err, | ||
stack: errStack | ||
}); | ||
} | ||
// falls through | ||
default: | ||
return new sequelizeErrors.DatabaseError(err); | ||
return new sequelizeErrors.DatabaseError(err, { stack: errStack }); | ||
} | ||
} | ||
isForeignKeysQuery() { | ||
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 = '.*' LIMIT 1\) AND r\.contype = 'f' ORDER BY 1;/.test(this.sql); | ||
} | ||
getInsertIdField() { | ||
return 'id'; | ||
return "id"; | ||
} | ||
} | ||
module.exports = Query; | ||
module.exports.Query = Query; | ||
module.exports.default = Query; | ||
//# sourceMappingURL=query.js.map |
@@ -1,8 +0,6 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
function stringifyRangeBound(bound) { | ||
if (bound === null) { | ||
return '' ; | ||
return ""; | ||
} | ||
@@ -14,3 +12,2 @@ if (bound === Infinity || bound === -Infinity) { | ||
} | ||
function parseRangeBound(bound, parseType) { | ||
@@ -20,62 +17,60 @@ if (!bound) { | ||
} | ||
if (bound === 'infinity') { | ||
if (bound === "infinity") { | ||
return Infinity; | ||
} | ||
if (bound === '-infinity') { | ||
if (bound === "-infinity") { | ||
return -Infinity; | ||
} | ||
return parseType(bound); | ||
} | ||
function stringify(data) { | ||
if (data === null) return null; | ||
if (!Array.isArray(data)) throw new Error('range must be an array'); | ||
if (!data.length) return 'empty'; | ||
if (data.length !== 2) throw new Error('range array length must be 0 (empty) or 2 (lower and upper bounds)'); | ||
if (Object.prototype.hasOwnProperty.call(data, 'inclusive')) { | ||
if (data.inclusive === false) data.inclusive = [false, false]; | ||
else if (!data.inclusive) data.inclusive = [true, false]; | ||
else if (data.inclusive === true) data.inclusive = [true, true]; | ||
if (data === null) | ||
return null; | ||
if (!Array.isArray(data)) | ||
throw new Error("range must be an array"); | ||
if (!data.length) | ||
return "empty"; | ||
if (data.length !== 2) | ||
throw new Error("range array length must be 0 (empty) or 2 (lower and upper bounds)"); | ||
if (Object.prototype.hasOwnProperty.call(data, "inclusive")) { | ||
if (data.inclusive === false) | ||
data.inclusive = [false, false]; | ||
else if (!data.inclusive) | ||
data.inclusive = [true, false]; | ||
else if (data.inclusive === true) | ||
data.inclusive = [true, true]; | ||
} else { | ||
data.inclusive = [true, false]; | ||
} | ||
_.each(data, (value, index) => { | ||
if (_.isObject(value)) { | ||
if (Object.prototype.hasOwnProperty.call(value, 'inclusive')) data.inclusive[index] = !!value.inclusive; | ||
if (Object.prototype.hasOwnProperty.call(value, 'value')) data[index] = value.value; | ||
if (Object.prototype.hasOwnProperty.call(value, "inclusive")) | ||
data.inclusive[index] = !!value.inclusive; | ||
if (Object.prototype.hasOwnProperty.call(value, "value")) | ||
data[index] = value.value; | ||
} | ||
}); | ||
const lowerBound = stringifyRangeBound(data[0]); | ||
const upperBound = stringifyRangeBound(data[1]); | ||
return `${(data.inclusive[0] ? '[' : '(') + lowerBound},${upperBound}${data.inclusive[1] ? ']' : ')'}`; | ||
return `${(data.inclusive[0] ? "[" : "(") + lowerBound},${upperBound}${data.inclusive[1] ? "]" : ")"}`; | ||
} | ||
exports.stringify = stringify; | ||
function parse(value, parser) { | ||
if (value === null) return null; | ||
if (value === 'empty') { | ||
if (value === null) | ||
return null; | ||
if (value === "empty") { | ||
return []; | ||
} | ||
let result = value | ||
.substring(1, value.length - 1) | ||
.split(',', 2); | ||
if (result.length !== 2) return value; | ||
let result = value.substring(1, value.length - 1).split(",", 2); | ||
if (result.length !== 2) | ||
return value; | ||
result = result.map((item, index) => { | ||
return { | ||
value: parseRangeBound(item, parser), | ||
inclusive: index === 0 ? value[0] === '[' : value[value.length - 1] === ']' | ||
inclusive: index === 0 ? value[0] === "[" : value[value.length - 1] === "]" | ||
}; | ||
}); | ||
return result; | ||
} | ||
exports.parse = parse; | ||
//# sourceMappingURL=range.js.map |
@@ -1,93 +0,68 @@ | ||
'use strict'; | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const AbstractConnectionManager = require('../abstract/connection-manager'); | ||
const { logger } = require('../../utils/logger'); | ||
const debug = logger.debugContext('connection:sqlite'); | ||
const dataTypes = require('../../data-types').sqlite; | ||
const sequelizeErrors = require('../../errors'); | ||
const parserStore = require('../parserStore')('sqlite'); | ||
const { promisify } = require('util'); | ||
"use strict"; | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
const AbstractConnectionManager = require("../abstract/connection-manager"); | ||
const { logger } = require("../../utils/logger"); | ||
const debug = logger.debugContext("connection:sqlite"); | ||
const dataTypes = require("../../data-types").sqlite; | ||
const sequelizeErrors = require("../../errors"); | ||
const parserStore = require("../parserStore")("sqlite"); | ||
const { promisify } = require("util"); | ||
class ConnectionManager extends AbstractConnectionManager { | ||
constructor(dialect, sequelize) { | ||
super(dialect, sequelize); | ||
// We attempt to parse file location from a connection uri | ||
// but we shouldn't match sequelize default host. | ||
if (this.sequelize.options.host === 'localhost') { | ||
if (this.sequelize.options.host === "localhost") { | ||
delete this.sequelize.options.host; | ||
} | ||
this.connections = {}; | ||
this.lib = this._loadDialectModule('sqlite3'); | ||
this.lib = this._loadDialectModule("sqlite3"); | ||
this.refreshTypeParser(dataTypes); | ||
} | ||
async _onProcessExit() { | ||
await Promise.all( | ||
Object.getOwnPropertyNames(this.connections) | ||
.map(connection => promisify(callback => this.connections[connection].close(callback))()) | ||
); | ||
await Promise.all(Object.getOwnPropertyNames(this.connections).map((connection) => promisify((callback) => this.connections[connection].close(callback))())); | ||
return super._onProcessExit.call(this); | ||
} | ||
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types | ||
_refreshTypeParser(dataType) { | ||
parserStore.refresh(dataType); | ||
} | ||
_clearTypeParser() { | ||
parserStore.clear(); | ||
} | ||
async getConnection(options) { | ||
options = options || {}; | ||
options.uuid = options.uuid || 'default'; | ||
options.storage = this.sequelize.options.storage || this.sequelize.options.host || ':memory:'; | ||
options.inMemory = options.storage === ':memory:' ? 1 : 0; | ||
options.uuid = options.uuid || "default"; | ||
if (!!this.sequelize.options.storage !== null && this.sequelize.options.storage !== void 0) { | ||
options.storage = this.sequelize.options.storage; | ||
} else { | ||
options.storage = this.sequelize.options.host || ":memory:"; | ||
} | ||
options.inMemory = options.storage === ":memory:" ? 1 : 0; | ||
const dialectOptions = this.sequelize.options.dialectOptions; | ||
const defaultReadWriteMode = this.lib.OPEN_READWRITE | this.lib.OPEN_CREATE; | ||
options.readWriteMode = dialectOptions && dialectOptions.mode || defaultReadWriteMode; | ||
if (this.connections[options.inMemory || options.uuid]) { | ||
return this.connections[options.inMemory || options.uuid]; | ||
} | ||
if (!options.inMemory && (options.readWriteMode & this.lib.OPEN_CREATE) !== 0) { | ||
// automatic path provision for `options.storage` | ||
fs.mkdirSync(path.dirname(options.storage), { recursive: true }); | ||
} | ||
const connection = await new Promise((resolve, reject) => { | ||
this.connections[options.inMemory || options.uuid] = new this.lib.Database( | ||
options.storage, | ||
options.readWriteMode, | ||
err => { | ||
if (err) return reject(new sequelizeErrors.ConnectionError(err)); | ||
debug(`connection acquired ${options.uuid}`); | ||
resolve(this.connections[options.inMemory || options.uuid]); | ||
} | ||
); | ||
this.connections[options.inMemory || options.uuid] = new this.lib.Database(options.storage, options.readWriteMode, (err) => { | ||
if (err) | ||
return reject(new sequelizeErrors.ConnectionError(err)); | ||
debug(`connection acquired ${options.uuid}`); | ||
resolve(this.connections[options.inMemory || options.uuid]); | ||
}); | ||
}); | ||
if (this.sequelize.config.password) { | ||
// Make it possible to define and use password for sqlite encryption plugin like sqlcipher | ||
connection.run(`PRAGMA KEY=${this.sequelize.escape(this.sequelize.config.password)}`); | ||
} | ||
if (this.sequelize.options.foreignKeys !== false) { | ||
// Make it possible to define and use foreign key constraints unless | ||
// explicitly disallowed. It's still opt-in per relation | ||
connection.run('PRAGMA FOREIGN_KEYS=ON'); | ||
connection.run("PRAGMA FOREIGN_KEYS=ON"); | ||
} | ||
return connection; | ||
} | ||
releaseConnection(connection, force) { | ||
if (connection.filename === ':memory:' && force !== true) return; | ||
if (connection.filename === ":memory:" && force !== true) | ||
return; | ||
if (connection.uuid) { | ||
@@ -100,5 +75,5 @@ connection.close(); | ||
} | ||
module.exports = ConnectionManager; | ||
module.exports.ConnectionManager = ConnectionManager; | ||
module.exports.default = ConnectionManager; | ||
//# sourceMappingURL=connection-manager.js.map |
@@ -1,46 +0,32 @@ | ||
'use strict'; | ||
module.exports = BaseTypes => { | ||
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://www.sqlite.org/datatype3.html'); | ||
/** | ||
* Removes unsupported SQLite options, i.e., UNSIGNED and ZEROFILL, for the integer data types. | ||
* | ||
* @param {object} dataType The base integer data type. | ||
* @private | ||
*/ | ||
"use strict"; | ||
module.exports = (BaseTypes) => { | ||
const warn = BaseTypes.ABSTRACT.warn.bind(void 0, "https://www.sqlite.org/datatype3.html"); | ||
function removeUnsupportedIntegerOptions(dataType) { | ||
if (dataType._zerofill || dataType._unsigned) { | ||
warn(`SQLite does not support '${dataType.key}' with UNSIGNED or ZEROFILL. Plain '${dataType.key}' will be used instead.`); | ||
dataType._unsigned = undefined; | ||
dataType._zerofill = undefined; | ||
dataType._unsigned = void 0; | ||
dataType._zerofill = void 0; | ||
} | ||
} | ||
/** | ||
* @see https://sqlite.org/datatype3.html | ||
*/ | ||
BaseTypes.DATE.types.sqlite = ['DATETIME']; | ||
BaseTypes.STRING.types.sqlite = ['VARCHAR', 'VARCHAR BINARY']; | ||
BaseTypes.CHAR.types.sqlite = ['CHAR', 'CHAR BINARY']; | ||
BaseTypes.TEXT.types.sqlite = ['TEXT']; | ||
BaseTypes.TINYINT.types.sqlite = ['TINYINT']; | ||
BaseTypes.SMALLINT.types.sqlite = ['SMALLINT']; | ||
BaseTypes.MEDIUMINT.types.sqlite = ['MEDIUMINT']; | ||
BaseTypes.INTEGER.types.sqlite = ['INTEGER']; | ||
BaseTypes.BIGINT.types.sqlite = ['BIGINT']; | ||
BaseTypes.FLOAT.types.sqlite = ['FLOAT']; | ||
BaseTypes.TIME.types.sqlite = ['TIME']; | ||
BaseTypes.DATEONLY.types.sqlite = ['DATE']; | ||
BaseTypes.BOOLEAN.types.sqlite = ['TINYINT']; | ||
BaseTypes.BLOB.types.sqlite = ['TINYBLOB', 'BLOB', 'LONGBLOB']; | ||
BaseTypes.DECIMAL.types.sqlite = ['DECIMAL']; | ||
BaseTypes.UUID.types.sqlite = ['UUID']; | ||
BaseTypes.DATE.types.sqlite = ["DATETIME"]; | ||
BaseTypes.STRING.types.sqlite = ["VARCHAR", "VARCHAR BINARY"]; | ||
BaseTypes.CHAR.types.sqlite = ["CHAR", "CHAR BINARY"]; | ||
BaseTypes.TEXT.types.sqlite = ["TEXT"]; | ||
BaseTypes.TINYINT.types.sqlite = ["TINYINT"]; | ||
BaseTypes.SMALLINT.types.sqlite = ["SMALLINT"]; | ||
BaseTypes.MEDIUMINT.types.sqlite = ["MEDIUMINT"]; | ||
BaseTypes.INTEGER.types.sqlite = ["INTEGER"]; | ||
BaseTypes.BIGINT.types.sqlite = ["BIGINT"]; | ||
BaseTypes.FLOAT.types.sqlite = ["FLOAT"]; | ||
BaseTypes.TIME.types.sqlite = ["TIME"]; | ||
BaseTypes.DATEONLY.types.sqlite = ["DATE"]; | ||
BaseTypes.BOOLEAN.types.sqlite = ["TINYINT"]; | ||
BaseTypes.BLOB.types.sqlite = ["TINYBLOB", "BLOB", "LONGBLOB"]; | ||
BaseTypes.DECIMAL.types.sqlite = ["DECIMAL"]; | ||
BaseTypes.UUID.types.sqlite = ["UUID"]; | ||
BaseTypes.ENUM.types.sqlite = false; | ||
BaseTypes.REAL.types.sqlite = ['REAL']; | ||
BaseTypes.DOUBLE.types.sqlite = ['DOUBLE PRECISION']; | ||
BaseTypes.REAL.types.sqlite = ["REAL"]; | ||
BaseTypes.DOUBLE.types.sqlite = ["DOUBLE PRECISION"]; | ||
BaseTypes.GEOMETRY.types.sqlite = false; | ||
BaseTypes.JSON.types.sqlite = ['JSON', 'JSONB']; | ||
BaseTypes.JSON.types.sqlite = ["JSON", "JSONB"]; | ||
class JSONTYPE extends BaseTypes.JSON { | ||
@@ -51,13 +37,10 @@ static parse(data) { | ||
} | ||
class DATE extends BaseTypes.DATE { | ||
static parse(date, options) { | ||
if (!date.includes('+')) { | ||
// For backwards compat. Dates inserted by sequelize < 2.0dev12 will not have a timestamp set | ||
if (!date.includes("+")) { | ||
return new Date(date + options.timezone); | ||
} | ||
return new Date(date); // We already have a timezone stored in the string | ||
return new Date(date); | ||
} | ||
} | ||
class DATEONLY extends BaseTypes.DATEONLY { | ||
@@ -68,3 +51,2 @@ static parse(date) { | ||
} | ||
class STRING extends BaseTypes.STRING { | ||
@@ -78,19 +60,16 @@ toSql() { | ||
} | ||
class TEXT extends BaseTypes.TEXT { | ||
toSql() { | ||
if (this._length) { | ||
warn('SQLite does not support TEXT with options. Plain `TEXT` will be used instead.'); | ||
this._length = undefined; | ||
warn("SQLite does not support TEXT with options. Plain `TEXT` will be used instead."); | ||
this._length = void 0; | ||
} | ||
return 'TEXT'; | ||
return "TEXT"; | ||
} | ||
} | ||
class CITEXT extends BaseTypes.CITEXT { | ||
toSql() { | ||
return 'TEXT COLLATE NOCASE'; | ||
return "TEXT COLLATE NOCASE"; | ||
} | ||
} | ||
class CHAR extends BaseTypes.CHAR { | ||
@@ -104,3 +83,2 @@ toSql() { | ||
} | ||
class NUMBER extends BaseTypes.NUMBER { | ||
@@ -110,13 +88,13 @@ toSql() { | ||
if (this._unsigned) { | ||
result += ' UNSIGNED'; | ||
result += " UNSIGNED"; | ||
} | ||
if (this._zerofill) { | ||
result += ' ZEROFILL'; | ||
result += " ZEROFILL"; | ||
} | ||
if (this._length) { | ||
result += `(${this._length}`; | ||
if (typeof this._decimals === 'number') { | ||
if (typeof this._decimals === "number") { | ||
result += `,${this._decimals}`; | ||
} | ||
result += ')'; | ||
result += ")"; | ||
} | ||
@@ -126,3 +104,2 @@ return result; | ||
} | ||
class TINYINT extends BaseTypes.TINYINT { | ||
@@ -134,3 +111,2 @@ constructor(length) { | ||
} | ||
class SMALLINT extends BaseTypes.SMALLINT { | ||
@@ -142,3 +118,2 @@ constructor(length) { | ||
} | ||
class MEDIUMINT extends BaseTypes.MEDIUMINT { | ||
@@ -150,3 +125,2 @@ constructor(length) { | ||
} | ||
class INTEGER extends BaseTypes.INTEGER { | ||
@@ -158,3 +132,2 @@ constructor(length) { | ||
} | ||
class BIGINT extends BaseTypes.BIGINT { | ||
@@ -166,22 +139,19 @@ constructor(length) { | ||
} | ||
class FLOAT extends BaseTypes.FLOAT { | ||
} | ||
class DOUBLE extends BaseTypes.DOUBLE { | ||
} | ||
class REAL extends BaseTypes.REAL { } | ||
class REAL extends BaseTypes.REAL { | ||
} | ||
function parseFloating(value) { | ||
if (typeof value !== 'string') { | ||
if (typeof value !== "string") { | ||
return value; | ||
} | ||
if (value === 'NaN') { | ||
if (value === "NaN") { | ||
return NaN; | ||
} | ||
if (value === 'Infinity') { | ||
if (value === "Infinity") { | ||
return Infinity; | ||
} | ||
if (value === '-Infinity') { | ||
if (value === "-Infinity") { | ||
return -Infinity; | ||
@@ -193,14 +163,10 @@ } | ||
} | ||
for (const num of [FLOAT, DOUBLE, REAL, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT]) { | ||
num.prototype.toSql = NUMBER.prototype.toSql; | ||
} | ||
class ENUM extends BaseTypes.ENUM { | ||
toSql() { | ||
return 'TEXT'; | ||
return "TEXT"; | ||
} | ||
} | ||
return { | ||
@@ -214,3 +180,3 @@ DATE, | ||
REAL, | ||
'DOUBLE PRECISION': DOUBLE, | ||
"DOUBLE PRECISION": DOUBLE, | ||
TINYINT, | ||
@@ -227,1 +193,2 @@ SMALLINT, | ||
}; | ||
//# sourceMappingURL=data-types.js.map |
@@ -1,11 +0,9 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const AbstractDialect = require('../abstract'); | ||
const ConnectionManager = require('./connection-manager'); | ||
const Query = require('./query'); | ||
const QueryGenerator = require('./query-generator'); | ||
const DataTypes = require('../../data-types').sqlite; | ||
const { SQLiteQueryInterface } = require('./query-interface'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const AbstractDialect = require("../abstract"); | ||
const ConnectionManager = require("./connection-manager"); | ||
const Query = require("./query"); | ||
const QueryGenerator = require("./query-generator"); | ||
const DataTypes = require("../../data-types").sqlite; | ||
const { SQLiteQueryInterface } = require("./query-interface"); | ||
class SqliteDialect extends AbstractDialect { | ||
@@ -20,15 +18,14 @@ constructor(sequelize) { | ||
}); | ||
this.queryInterface = new SQLiteQueryInterface(sequelize, this.queryGenerator); | ||
} | ||
} | ||
SqliteDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), { | ||
'DEFAULT': false, | ||
'DEFAULT VALUES': true, | ||
'UNION ALL': false, | ||
'RIGHT JOIN': false, | ||
DEFAULT: false, | ||
"DEFAULT VALUES": true, | ||
"UNION ALL": false, | ||
"RIGHT JOIN": false, | ||
inserts: { | ||
ignoreDuplicates: ' OR IGNORE', | ||
updateOnDuplicate: ' ON CONFLICT DO UPDATE SET' | ||
ignoreDuplicates: " OR IGNORE", | ||
updateOnDuplicate: " ON CONFLICT DO UPDATE SET", | ||
conflictFields: true | ||
}, | ||
@@ -47,17 +44,15 @@ index: { | ||
}, | ||
joinTableDependent: false, | ||
groupedLimit: false, | ||
JSON: true | ||
}); | ||
SqliteDialect.prototype.defaultVersion = '3.8.0'; | ||
SqliteDialect.prototype.defaultVersion = "3.8.0"; | ||
SqliteDialect.prototype.Query = Query; | ||
SqliteDialect.prototype.DataTypes = DataTypes; | ||
SqliteDialect.prototype.name = 'sqlite'; | ||
SqliteDialect.prototype.TICK_CHAR = '`'; | ||
SqliteDialect.prototype.name = "sqlite"; | ||
SqliteDialect.prototype.TICK_CHAR = "`"; | ||
SqliteDialect.prototype.TICK_CHAR_LEFT = SqliteDialect.prototype.TICK_CHAR; | ||
SqliteDialect.prototype.TICK_CHAR_RIGHT = SqliteDialect.prototype.TICK_CHAR; | ||
module.exports = SqliteDialect; | ||
module.exports.SqliteDialect = SqliteDialect; | ||
module.exports.default = SqliteDialect; | ||
//# sourceMappingURL=index.js.map |
@@ -1,9 +0,26 @@ | ||
'use strict'; | ||
const Utils = require('../../utils'); | ||
const Transaction = require('../../transaction'); | ||
const _ = require('lodash'); | ||
const MySqlQueryGenerator = require('../mysql/query-generator'); | ||
const AbstractQueryGenerator = require('../abstract/query-generator'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const Utils = require("../../utils"); | ||
const Transaction = require("../../transaction"); | ||
const _ = require("lodash"); | ||
const MySqlQueryGenerator = require("../mysql/query-generator"); | ||
const AbstractQueryGenerator = require("../abstract/query-generator"); | ||
class SQLiteQueryGenerator extends MySqlQueryGenerator { | ||
@@ -13,40 +30,31 @@ createSchema() { | ||
} | ||
showSchemasQuery() { | ||
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; | ||
} | ||
versionQuery() { | ||
return 'SELECT sqlite_version() as `version`'; | ||
return "SELECT sqlite_version() as `version`"; | ||
} | ||
createTableQuery(tableName, attributes, options) { | ||
options = options || {}; | ||
const primaryKeys = []; | ||
const needsMultiplePrimaryKeys = Object.values(attributes).filter(definition => definition.includes('PRIMARY KEY')).length > 1; | ||
const needsMultiplePrimaryKeys = Object.values(attributes).filter((definition) => definition.includes("PRIMARY KEY")).length > 1; | ||
const attrArray = []; | ||
for (const attr in attributes) { | ||
if (Object.prototype.hasOwnProperty.call(attributes, attr)) { | ||
const dataType = attributes[attr]; | ||
const containsAutoIncrement = dataType.includes('AUTOINCREMENT'); | ||
const containsAutoIncrement = dataType.includes("AUTOINCREMENT"); | ||
let dataTypeString = dataType; | ||
if (dataType.includes('PRIMARY KEY')) { | ||
if (dataType.includes('INT')) { | ||
// Only INTEGER is allowed for primary key, see https://github.com/sequelize/sequelize/issues/969 (no lenght, unsigned etc) | ||
dataTypeString = containsAutoIncrement ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INTEGER PRIMARY KEY'; | ||
if (dataType.includes(' REFERENCES')) { | ||
dataTypeString += dataType.substr(dataType.indexOf(' REFERENCES')); | ||
if (dataType.includes("PRIMARY KEY")) { | ||
if (dataType.includes("INT")) { | ||
dataTypeString = containsAutoIncrement ? "INTEGER PRIMARY KEY AUTOINCREMENT" : "INTEGER PRIMARY KEY"; | ||
if (dataType.includes(" REFERENCES")) { | ||
dataTypeString += dataType.substr(dataType.indexOf(" REFERENCES")); | ||
} | ||
} | ||
if (needsMultiplePrimaryKeys) { | ||
primaryKeys.push(attr); | ||
if (dataType.includes('NOT NULL')) { | ||
dataTypeString = dataType.replace(' PRIMARY KEY', ''); | ||
if (dataType.includes("NOT NULL")) { | ||
dataTypeString = dataType.replace(" PRIMARY KEY", ""); | ||
} else { | ||
dataTypeString = dataType.replace('PRIMARY KEY', 'NOT NULL'); | ||
dataTypeString = dataType.replace("PRIMARY KEY", "NOT NULL"); | ||
} | ||
@@ -58,43 +66,27 @@ } | ||
} | ||
const table = this.quoteTable(tableName); | ||
let attrStr = attrArray.join(', '); | ||
const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', '); | ||
let attrStr = attrArray.join(", "); | ||
const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", "); | ||
if (options.uniqueKeys) { | ||
_.each(options.uniqueKeys, columns => { | ||
_.each(options.uniqueKeys, (columns) => { | ||
if (columns.customIndex) { | ||
attrStr += `, UNIQUE (${columns.fields.map(field => this.quoteIdentifier(field)).join(', ')})`; | ||
attrStr += `, UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`; | ||
} | ||
}); | ||
} | ||
if (pkString.length > 0) { | ||
attrStr += `, PRIMARY KEY (${pkString})`; | ||
} | ||
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 | ||
* | ||
* @param {string} stmt The statement to validate | ||
* @returns {boolean} true if the given statement is json function | ||
* @throws {Error} throw if the statement looks like json function but has invalid token | ||
*/ | ||
_checkValidJsonStatement(stmt) { | ||
if (typeof stmt !== 'string') { | ||
if (typeof stmt !== "string") { | ||
return false; | ||
} | ||
// https://sqlite.org/json1.html | ||
const jsonFunctionRegex = /^\s*(json(?:_[a-z]+){0,2})\([^)]*\)/i; | ||
const tokenCaptureRegex = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i; | ||
let currentIndex = 0; | ||
@@ -105,3 +97,2 @@ let openingBrackets = 0; | ||
let hasInvalidToken = false; | ||
while (currentIndex < stmt.length) { | ||
@@ -111,15 +102,14 @@ const string = stmt.substr(currentIndex); | ||
if (functionMatches) { | ||
currentIndex += functionMatches[0].indexOf('('); | ||
currentIndex += functionMatches[0].indexOf("("); | ||
hasJsonFunction = true; | ||
continue; | ||
} | ||
const tokenMatches = tokenCaptureRegex.exec(string); | ||
if (tokenMatches) { | ||
const capturedToken = tokenMatches[1]; | ||
if (capturedToken === '(') { | ||
if (capturedToken === "(") { | ||
openingBrackets++; | ||
} else if (capturedToken === ')') { | ||
} else if (capturedToken === ")") { | ||
closingBrackets++; | ||
} else if (capturedToken === ';') { | ||
} else if (capturedToken === ";") { | ||
hasInvalidToken = true; | ||
@@ -131,7 +121,4 @@ break; | ||
} | ||
break; | ||
} | ||
// Check invalid json statement | ||
hasInvalidToken |= openingBrackets !== closingBrackets; | ||
@@ -141,8 +128,4 @@ if (hasJsonFunction && hasInvalidToken) { | ||
} | ||
// return true if the statement has valid json function | ||
return hasJsonFunction; | ||
} | ||
//sqlite can't cast to datetime so we need to convert date values to their ISO strings | ||
_toJSONValue(value) { | ||
@@ -153,8 +136,6 @@ if (value instanceof Date) { | ||
if (Array.isArray(value) && value[0] instanceof Date) { | ||
return value.map(val => val.toISOString()); | ||
return value.map((val) => val.toISOString()); | ||
} | ||
return value; | ||
} | ||
handleSequelizeMethod(smth, tableName, factory, options, prepend) { | ||
@@ -164,33 +145,24 @@ if (smth instanceof Utils.Json) { | ||
} | ||
if (smth instanceof Utils.Cast) { | ||
if (/timestamp/i.test(smth.type)) { | ||
smth.type = 'datetime'; | ||
smth.type = "datetime"; | ||
} | ||
} | ||
return AbstractQueryGenerator.prototype.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend); | ||
} | ||
addColumnQuery(table, key, dataType) { | ||
const attributes = {}; | ||
attributes[key] = dataType; | ||
const fields = this.attributesToSQL(attributes, { context: 'addColumn' }); | ||
const fields = this.attributesToSQL(attributes, { context: "addColumn" }); | ||
const attribute = `${this.quoteIdentifier(key)} ${fields[key]}`; | ||
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\';'; | ||
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; | ||
} | ||
updateQuery(tableName, attrValueHash, where, options, attributes) { | ||
options = options || {}; | ||
_.defaults(options, this.options); | ||
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, options.omitNull, options); | ||
const modelAttributeMap = {}; | ||
@@ -200,3 +172,2 @@ const values = []; | ||
const bindParam = options.bindParam || this.bindParam(bind); | ||
if (attributes) { | ||
@@ -210,48 +181,36 @@ _.each(attributes, (attribute, key) => { | ||
} | ||
for (const key in attrValueHash) { | ||
const value = attrValueHash[key]; | ||
if (value instanceof Utils.SequelizeMethod || options.bindParam === false) { | ||
values.push(`${this.quoteIdentifier(key)}=${this.escape(value, modelAttributeMap && modelAttributeMap[key] || undefined, { context: 'UPDATE' })}`); | ||
values.push(`${this.quoteIdentifier(key)}=${this.escape(value, modelAttributeMap && modelAttributeMap[key] || void 0, { context: "UPDATE" })}`); | ||
} else { | ||
values.push(`${this.quoteIdentifier(key)}=${this.format(value, modelAttributeMap && modelAttributeMap[key] || undefined, { context: 'UPDATE' }, bindParam)}`); | ||
values.push(`${this.quoteIdentifier(key)}=${this.format(value, modelAttributeMap && modelAttributeMap[key] || void 0, { context: "UPDATE" }, bindParam)}`); | ||
} | ||
} | ||
let query; | ||
const whereOptions = { ...options, bindParam }; | ||
const whereOptions = __spreadProps(__spreadValues({}, options), { bindParam }); | ||
if (options.limit) { | ||
query = `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} WHERE rowid IN (SELECT rowid FROM ${this.quoteTable(tableName)} ${this.whereQuery(where, whereOptions)} LIMIT ${this.escape(options.limit)})`; | ||
query = `UPDATE ${this.quoteTable(tableName)} SET ${values.join(",")} WHERE rowid IN (SELECT rowid FROM ${this.quoteTable(tableName)} ${this.whereQuery(where, whereOptions)} LIMIT ${this.escape(options.limit)})`; | ||
} else { | ||
query = `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} ${this.whereQuery(where, whereOptions)}`; | ||
query = `UPDATE ${this.quoteTable(tableName)} SET ${values.join(",")} ${this.whereQuery(where, whereOptions)}`; | ||
} | ||
return { query, bind }; | ||
} | ||
truncateTableQuery(tableName, options = {}) { | ||
return [ | ||
`DELETE FROM ${this.quoteTable(tableName)}`, | ||
options.restartIdentity ? `; DELETE FROM ${this.quoteTable('sqlite_sequence')} WHERE ${this.quoteIdentifier('name')} = ${Utils.addTicks(Utils.removeTicks(this.quoteTable(tableName), '`'), "'")};` : '' | ||
].join(''); | ||
options.restartIdentity ? `; DELETE FROM ${this.quoteTable("sqlite_sequence")} WHERE ${this.quoteIdentifier("name")} = ${Utils.addTicks(Utils.removeTicks(this.quoteTable(tableName), "`"), "'")};` : "" | ||
].join(""); | ||
} | ||
deleteQuery(tableName, where, options = {}, model) { | ||
_.defaults(options, this.options); | ||
let whereClause = this.getWhereConditions(where, null, model, options); | ||
if (whereClause) { | ||
whereClause = `WHERE ${whereClause}`; | ||
} | ||
if (options.limit) { | ||
whereClause = `WHERE rowid IN (SELECT rowid FROM ${this.quoteTable(tableName)} ${whereClause} LIMIT ${this.escape(options.limit)})`; | ||
} | ||
return `DELETE FROM ${this.quoteTable(tableName)} ${whereClause}`; | ||
} | ||
attributesToSQL(attributes) { | ||
@@ -262,32 +221,21 @@ const result = {}; | ||
const fieldName = dataType.field || name; | ||
if (_.isObject(dataType)) { | ||
let sql = dataType.type.toString(); | ||
if (Object.prototype.hasOwnProperty.call(dataType, 'allowNull') && !dataType.allowNull) { | ||
sql += ' NOT NULL'; | ||
if (Object.prototype.hasOwnProperty.call(dataType, "allowNull") && !dataType.allowNull) { | ||
sql += " NOT NULL"; | ||
} | ||
if (Utils.defaultValueSchemable(dataType.defaultValue)) { | ||
// TODO thoroughly check that DataTypes.NOW will properly | ||
// get populated on all databases as DEFAULT value | ||
// i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP | ||
sql += ` DEFAULT ${this.escape(dataType.defaultValue, dataType)}`; | ||
} | ||
if (dataType.unique === true) { | ||
sql += ' UNIQUE'; | ||
sql += " UNIQUE"; | ||
} | ||
if (dataType.primaryKey) { | ||
sql += ' PRIMARY KEY'; | ||
sql += " PRIMARY KEY"; | ||
if (dataType.autoIncrement) { | ||
sql += ' AUTOINCREMENT'; | ||
sql += " AUTOINCREMENT"; | ||
} | ||
} | ||
if (dataType.references) { | ||
const referencesTable = this.quoteTable(dataType.references.model); | ||
let referencesKey; | ||
@@ -297,17 +245,12 @@ if (dataType.references.key) { | ||
} else { | ||
referencesKey = this.quoteIdentifier('id'); | ||
referencesKey = this.quoteIdentifier("id"); | ||
} | ||
sql += ` REFERENCES ${referencesTable} (${referencesKey})`; | ||
if (dataType.onDelete) { | ||
sql += ` ON DELETE ${dataType.onDelete.toUpperCase()}`; | ||
} | ||
if (dataType.onUpdate) { | ||
sql += ` ON UPDATE ${dataType.onUpdate.toUpperCase()}`; | ||
} | ||
} | ||
result[fieldName] = sql; | ||
@@ -318,30 +261,21 @@ } else { | ||
} | ||
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}'`; | ||
if (constraintName) { | ||
sql += ` AND sql LIKE '%${constraintName}%'`; | ||
} | ||
return `${sql};`; | ||
} | ||
removeIndexQuery(tableName, indexNameOrAttributes) { | ||
let indexName = indexNameOrAttributes; | ||
if (typeof indexName !== 'string') { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join('_')}`); | ||
if (typeof indexName !== "string") { | ||
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); | ||
} | ||
return `DROP INDEX IF EXISTS ${this.quoteIdentifier(indexName)}`; | ||
} | ||
describeTableQuery(tableName, schema, schemaDelimiter) { | ||
@@ -355,13 +289,9 @@ const table = { | ||
} | ||
describeCreateTableQuery(tableName) { | ||
return `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}';`; | ||
} | ||
removeColumnQuery(tableName, attributes) { | ||
attributes = this.attributesToSQL(attributes); | ||
let backupTableName; | ||
if (typeof tableName === 'object') { | ||
if (typeof tableName === "object") { | ||
backupTableName = { | ||
@@ -374,22 +304,11 @@ tableName: `${tableName.tableName}_backup`, | ||
} | ||
const quotedTableName = this.quoteTable(tableName); | ||
const quotedBackupTableName = this.quoteTable(backupTableName); | ||
const attributeNames = Object.keys(attributes).map(attr => this.quoteIdentifier(attr)).join(', '); | ||
// Temporary table cannot work for foreign keys. | ||
return `${this.createTableQuery(backupTableName, attributes) | ||
}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};` | ||
+ `DROP TABLE ${quotedTableName};${ | ||
this.createTableQuery(tableName, attributes) | ||
}INSERT INTO ${quotedTableName} SELECT ${attributeNames} FROM ${quotedBackupTableName};` | ||
+ `DROP TABLE ${quotedBackupTableName};`; | ||
const attributeNames = Object.keys(attributes).map((attr) => this.quoteIdentifier(attr)).join(", "); | ||
return `${this.createTableQuery(backupTableName, attributes)}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};DROP TABLE ${quotedTableName};${this.createTableQuery(tableName, attributes)}INSERT INTO ${quotedTableName} SELECT ${attributeNames} FROM ${quotedBackupTableName};DROP TABLE ${quotedBackupTableName};`; | ||
} | ||
_alterConstraintQuery(tableName, attributes, createTableSql) { | ||
let backupTableName; | ||
attributes = this.attributesToSQL(attributes); | ||
if (typeof tableName === 'object') { | ||
if (typeof tableName === "object") { | ||
backupTableName = { | ||
@@ -404,19 +323,9 @@ tableName: `${tableName.tableName}_backup`, | ||
const quotedBackupTableName = this.quoteTable(backupTableName); | ||
const attributeNames = Object.keys(attributes).map(attr => this.quoteIdentifier(attr)).join(', '); | ||
return `${createTableSql | ||
.replace(`CREATE TABLE ${quotedTableName}`, `CREATE TABLE ${quotedBackupTableName}`) | ||
.replace(`CREATE TABLE ${quotedTableName.replace(/`/g, '"')}`, `CREATE TABLE ${quotedBackupTableName}`) | ||
}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};` | ||
+ `DROP TABLE ${quotedTableName};` | ||
+ `ALTER TABLE ${quotedBackupTableName} RENAME TO ${quotedTableName};`; | ||
const attributeNames = Object.keys(attributes).map((attr) => this.quoteIdentifier(attr)).join(", "); | ||
return `${createTableSql.replace(`CREATE TABLE ${quotedTableName}`, `CREATE TABLE ${quotedBackupTableName}`).replace(`CREATE TABLE ${quotedTableName.replace(/`/g, '"')}`, `CREATE TABLE ${quotedBackupTableName}`)}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};DROP TABLE ${quotedTableName};ALTER TABLE ${quotedBackupTableName} RENAME TO ${quotedTableName};`; | ||
} | ||
renameColumnQuery(tableName, attrNameBefore, attrNameAfter, attributes) { | ||
let backupTableName; | ||
attributes = this.attributesToSQL(attributes); | ||
if (typeof tableName === 'object') { | ||
if (typeof tableName === "object") { | ||
backupTableName = { | ||
@@ -429,19 +338,8 @@ tableName: `${tableName.tableName}_backup`, | ||
} | ||
const quotedTableName = this.quoteTable(tableName); | ||
const quotedBackupTableName = this.quoteTable(backupTableName); | ||
const attributeNamesImport = Object.keys(attributes).map(attr => | ||
attrNameAfter === attr ? `${this.quoteIdentifier(attrNameBefore)} AS ${this.quoteIdentifier(attr)}` : this.quoteIdentifier(attr) | ||
).join(', '); | ||
const attributeNamesExport = Object.keys(attributes).map(attr => this.quoteIdentifier(attr)).join(', '); | ||
// Temporary tables don't support foreign keys, so creating a temporary table will not allow foreign keys to be preserved | ||
return `${this.createTableQuery(backupTableName, attributes) | ||
}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNamesImport} FROM ${quotedTableName};` | ||
+ `DROP TABLE ${quotedTableName};${ | ||
this.createTableQuery(tableName, attributes) | ||
}INSERT INTO ${quotedTableName} SELECT ${attributeNamesExport} FROM ${quotedBackupTableName};` | ||
+ `DROP TABLE ${quotedBackupTableName};`; | ||
const attributeNamesImport = Object.keys(attributes).map((attr) => attrNameAfter === attr ? `${this.quoteIdentifier(attrNameBefore)} AS ${this.quoteIdentifier(attr)}` : this.quoteIdentifier(attr)).join(", "); | ||
const attributeNamesExport = Object.keys(attributes).map((attr) => this.quoteIdentifier(attr)).join(", "); | ||
return `${this.createTableQuery(backupTableName, attributes)}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNamesImport} FROM ${quotedTableName};DROP TABLE ${quotedTableName};${this.createTableQuery(tableName, attributes)}INSERT INTO ${quotedTableName} SELECT ${attributeNamesExport} FROM ${quotedBackupTableName};DROP TABLE ${quotedBackupTableName};`; | ||
} | ||
startTransactionQuery(transaction) { | ||
@@ -451,16 +349,14 @@ if (transaction.parent) { | ||
} | ||
return `BEGIN ${transaction.options.type} TRANSACTION;`; | ||
} | ||
setIsolationLevelQuery(value) { | ||
switch (value) { | ||
case Transaction.ISOLATION_LEVELS.REPEATABLE_READ: | ||
return '-- SQLite is not able to choose the isolation level REPEATABLE READ.'; | ||
return "-- SQLite is not able to choose the isolation level REPEATABLE READ."; | ||
case Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED: | ||
return 'PRAGMA read_uncommitted = ON;'; | ||
return "PRAGMA read_uncommitted = ON;"; | ||
case Transaction.ISOLATION_LEVELS.READ_COMMITTED: | ||
return 'PRAGMA read_uncommitted = OFF;'; | ||
return "PRAGMA read_uncommitted = OFF;"; | ||
case Transaction.ISOLATION_LEVELS.SERIALIZABLE: | ||
return '-- SQLite\'s default isolation level is SERIALIZABLE. Nothing to do.'; | ||
return "-- SQLite's default isolation level is SERIALIZABLE. Nothing to do."; | ||
default: | ||
@@ -470,19 +366,16 @@ throw new Error(`Unknown isolation level: ${value}`); | ||
} | ||
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"); | ||
} | ||
/** | ||
* Generates an SQL query that returns all foreign keys of a table. | ||
* | ||
* @param {string} tableName The name of the table. | ||
* @returns {string} The generated sql query. | ||
* @private | ||
*/ | ||
getForeignKeysQuery(tableName) { | ||
return `PRAGMA foreign_key_list(${tableName})`; | ||
return `PRAGMA foreign_key_list(${this.quoteTable(this.addSchema(tableName))})`; | ||
} | ||
tableExistsQuery(tableName) { | ||
return `SELECT name FROM sqlite_master WHERE type='table' AND name=${this.escape(this.addSchema(tableName))};`; | ||
} | ||
quoteIdentifier(identifier, force) { | ||
return Utils.addTicks(Utils.removeTicks(identifier, "`"), "`"); | ||
} | ||
} | ||
module.exports = SQLiteQueryGenerator; | ||
//# sourceMappingURL=query-generator.js.map |
@@ -1,81 +0,59 @@ | ||
'use strict'; | ||
const sequelizeErrors = require('../../errors'); | ||
const QueryTypes = require('../../query-types'); | ||
const { QueryInterface } = require('../abstract/query-interface'); | ||
const { cloneDeep } = require('../../utils'); | ||
const _ = require('lodash'); | ||
/** | ||
* The interface that Sequelize uses to talk with SQLite database | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const sequelizeErrors = require("../../errors"); | ||
const QueryTypes = require("../../query-types"); | ||
const { QueryInterface } = require("../abstract/query-interface"); | ||
const { cloneDeep } = require("../../utils"); | ||
const _ = require("lodash"); | ||
class SQLiteQueryInterface extends QueryInterface { | ||
/** | ||
* A wrapper that fixes SQLite's inability to remove columns from existing tables. | ||
* It will create a backup of the table, drop the table afterwards and create a | ||
* new table with the same name but without the obsolete column. | ||
* | ||
* @override | ||
*/ | ||
async removeColumn(tableName, attributeName, options) { | ||
options = options || {}; | ||
const fields = await this.describeTable(tableName, options); | ||
delete fields[attributeName]; | ||
const sql = this.queryGenerator.removeColumnQuery(tableName, fields); | ||
const subQueries = sql.split(';').filter(q => q !== ''); | ||
for (const subQuery of subQueries) await this.sequelize.query(`${subQuery};`, { raw: true, ...options }); | ||
const subQueries = sql.split(";").filter((q) => q !== ""); | ||
for (const subQuery of subQueries) | ||
await this.sequelize.query(`${subQuery};`, __spreadValues({ raw: true }, options)); | ||
} | ||
/** | ||
* A wrapper that fixes SQLite's inability to change columns from existing tables. | ||
* It will create a backup of the table, drop the table afterwards and create a | ||
* new table with the same name but with a modified version of the respective column. | ||
* | ||
* @override | ||
*/ | ||
async changeColumn(tableName, attributeName, dataTypeOrOptions, options) { | ||
options = options || {}; | ||
const fields = await this.describeTable(tableName, options); | ||
Object.assign(fields[attributeName], this.normalizeAttribute(dataTypeOrOptions)); | ||
const sql = this.queryGenerator.removeColumnQuery(tableName, fields); | ||
const subQueries = sql.split(';').filter(q => q !== ''); | ||
for (const subQuery of subQueries) await this.sequelize.query(`${subQuery};`, { raw: true, ...options }); | ||
const subQueries = sql.split(";").filter((q) => q !== ""); | ||
for (const subQuery of subQueries) | ||
await this.sequelize.query(`${subQuery};`, __spreadValues({ raw: true }, options)); | ||
} | ||
/** | ||
* A wrapper that fixes SQLite's inability to rename columns from existing tables. | ||
* It will create a backup of the table, drop the table afterwards and create a | ||
* new table with the same name but with a renamed version of the respective column. | ||
* | ||
* @override | ||
*/ | ||
async renameColumn(tableName, attrNameBefore, attrNameAfter, options) { | ||
options = options || {}; | ||
const fields = await this.assertTableHasColumn(tableName, attrNameBefore, options); | ||
fields[attrNameAfter] = { ...fields[attrNameBefore] }; | ||
fields[attrNameAfter] = __spreadValues({}, fields[attrNameBefore]); | ||
delete fields[attrNameBefore]; | ||
const sql = this.queryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields); | ||
const subQueries = sql.split(';').filter(q => q !== ''); | ||
for (const subQuery of subQueries) await this.sequelize.query(`${subQuery};`, { raw: true, ...options }); | ||
const subQueries = sql.split(";").filter((q) => q !== ""); | ||
for (const subQuery of subQueries) | ||
await this.sequelize.query(`${subQuery};`, __spreadValues({ raw: true }, options)); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async removeConstraint(tableName, constraintName, options) { | ||
let createTableSql; | ||
const constraints = await this.showConstraint(tableName, constraintName); | ||
// sqlite can't show only one constraint, so we find here the one to remove | ||
const constraint = constraints.find(constaint => constaint.constraintName === constraintName); | ||
const constraint = constraints.find((constaint) => constaint.constraintName === constraintName); | ||
if (!constraint) { | ||
@@ -91,7 +69,6 @@ throw new sequelizeErrors.UnknownConstraintError({ | ||
let constraintSnippet = `, CONSTRAINT ${constraint.constraintName} ${constraint.constraintType} ${constraint.constraintCondition}`; | ||
if (constraint.constraintType === 'FOREIGN KEY') { | ||
if (constraint.constraintType === "FOREIGN KEY") { | ||
const referenceTableName = this.queryGenerator.quoteTable(constraint.referenceTableName); | ||
constraint.referenceTableKeys = constraint.referenceTableKeys.map(columnName => this.queryGenerator.quoteIdentifier(columnName)); | ||
const referenceTableKeys = constraint.referenceTableKeys.join(', '); | ||
constraint.referenceTableKeys = constraint.referenceTableKeys.map((columnName) => this.queryGenerator.quoteIdentifier(columnName)); | ||
const referenceTableKeys = constraint.referenceTableKeys.join(", "); | ||
constraintSnippet += ` REFERENCES ${referenceTableName} (${referenceTableKeys})`; | ||
@@ -101,48 +78,30 @@ constraintSnippet += ` ON UPDATE ${constraint.updateAction}`; | ||
} | ||
createTableSql = createTableSql.replace(constraintSnippet, ''); | ||
createTableSql += ';'; | ||
createTableSql = createTableSql.replace(constraintSnippet, ""); | ||
createTableSql += ";"; | ||
const fields = await this.describeTable(tableName, options); | ||
const sql = this.queryGenerator._alterConstraintQuery(tableName, fields, createTableSql); | ||
const subQueries = sql.split(';').filter(q => q !== ''); | ||
for (const subQuery of subQueries) await this.sequelize.query(`${subQuery};`, { raw: true, ...options }); | ||
const subQueries = sql.split(";").filter((q) => q !== ""); | ||
for (const subQuery of subQueries) | ||
await this.sequelize.query(`${subQuery};`, __spreadValues({ raw: true }, options)); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async addConstraint(tableName, options) { | ||
if (!options.fields) { | ||
throw new Error('Fields must be specified through options.fields'); | ||
throw new Error("Fields must be specified through options.fields"); | ||
} | ||
if (!options.type) { | ||
throw new Error('Constraint type must be specified through options.type'); | ||
throw new Error("Constraint type must be specified through options.type"); | ||
} | ||
options = cloneDeep(options); | ||
const constraintSnippet = this.queryGenerator.getConstraintSnippet(tableName, options); | ||
const describeCreateTableSql = this.queryGenerator.describeCreateTableQuery(tableName); | ||
const constraints = await this.sequelize.query(describeCreateTableSql, { ...options, type: QueryTypes.SELECT, raw: true }); | ||
const constraints = await this.sequelize.query(describeCreateTableSql, __spreadProps(__spreadValues({}, options), { type: QueryTypes.SELECT, raw: true })); | ||
let sql = constraints[0].sql; | ||
const index = sql.length - 1; | ||
//Replace ending ')' with constraint snippet - Simulates String.replaceAt | ||
//http://stackoverflow.com/questions/1431094 | ||
const createTableSql = `${sql.substr(0, index)}, ${constraintSnippet})${sql.substr(index + 1)};`; | ||
const fields = await this.describeTable(tableName, options); | ||
sql = this.queryGenerator._alterConstraintQuery(tableName, fields, createTableSql); | ||
const subQueries = sql.split(';').filter(q => q !== ''); | ||
for (const subQuery of subQueries) await this.sequelize.query(`${subQuery};`, { raw: true, ...options }); | ||
const subQueries = sql.split(";").filter((q) => q !== ""); | ||
for (const subQuery of subQueries) | ||
await this.sequelize.query(`${subQuery};`, __spreadValues({ raw: true }, options)); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async getForeignKeyReferencesForTable(tableName, options) { | ||
@@ -152,3 +111,3 @@ const database = this.sequelize.config.database; | ||
const result = await this.sequelize.query(query, options); | ||
return result.map(row => ({ | ||
return result.map((row) => ({ | ||
tableName, | ||
@@ -162,50 +121,31 @@ columnName: row.from, | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async dropAllTables(options) { | ||
options = options || {}; | ||
const skip = options.skip || []; | ||
const tableNames = await this.showAllTables(options); | ||
await this.sequelize.query('PRAGMA foreign_keys = OFF', options); | ||
await this.sequelize.query("PRAGMA foreign_keys = OFF", options); | ||
await this._dropAllTables(tableNames, skip, options); | ||
await this.sequelize.query('PRAGMA foreign_keys = ON', options); | ||
await this.sequelize.query("PRAGMA foreign_keys = ON", options); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
async describeTable(tableName, options) { | ||
let schema = null; | ||
let schemaDelimiter = null; | ||
if (typeof options === 'string') { | ||
if (typeof options === "string") { | ||
schema = options; | ||
} else if (typeof options === 'object' && options !== null) { | ||
} else if (typeof options === "object" && options !== null) { | ||
schema = options.schema || null; | ||
schemaDelimiter = options.schemaDelimiter || null; | ||
} | ||
if (typeof tableName === 'object' && tableName !== null) { | ||
if (typeof tableName === "object" && tableName !== null) { | ||
schema = tableName.schema; | ||
tableName = tableName.tableName; | ||
} | ||
const sql = this.queryGenerator.describeTableQuery(tableName, schema, schemaDelimiter); | ||
options = { ...options, type: QueryTypes.DESCRIBE }; | ||
options = __spreadProps(__spreadValues({}, options), { type: QueryTypes.DESCRIBE }); | ||
const sqlIndexes = this.queryGenerator.showIndexesQuery(tableName); | ||
try { | ||
const data = await this.sequelize.query(sql, options); | ||
/* | ||
* If no data is returned from the query, then the table name may be wrong. | ||
* Query generators that use information_schema for retrieving table info will just return an empty result set, | ||
* it will not throw an error like built-ins do (e.g. DESCRIBE on MySql). | ||
*/ | ||
if (_.isEmpty(data)) { | ||
throw new Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`); | ||
} | ||
const indexes = await this.sequelize.query(sqlIndexes, options); | ||
@@ -217,3 +157,3 @@ for (const prop in data) { | ||
for (const field of index.fields) { | ||
if (index.unique !== undefined) { | ||
if (index.unique !== void 0) { | ||
data[field.attribute].unique = index.unique; | ||
@@ -223,3 +163,2 @@ } | ||
} | ||
const foreignKeys = await this.getForeignKeyReferencesForTable(tableName, options); | ||
@@ -232,9 +171,7 @@ for (const foreignKey of foreignKeys) { | ||
} | ||
return data; | ||
} catch (e) { | ||
if (e.original && e.original.code === 'ER_NO_SUCH_TABLE') { | ||
if (e.original && e.original.code === "ER_NO_SUCH_TABLE") { | ||
throw new Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`); | ||
} | ||
throw e; | ||
@@ -244,3 +181,3 @@ } | ||
} | ||
exports.SQLiteQueryInterface = SQLiteQueryInterface; | ||
//# sourceMappingURL=query-interface.js.map |
@@ -1,27 +0,14 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Utils = require('../../utils'); | ||
const AbstractQuery = require('../abstract/query'); | ||
const QueryTypes = require('../../query-types'); | ||
const sequelizeErrors = require('../../errors'); | ||
const parserStore = require('../parserStore')('sqlite'); | ||
const { logger } = require('../../utils/logger'); | ||
const debug = logger.debugContext('sql:sqlite'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const Utils = require("../../utils"); | ||
const AbstractQuery = require("../abstract/query"); | ||
const QueryTypes = require("../../query-types"); | ||
const sequelizeErrors = require("../../errors"); | ||
const parserStore = require("../parserStore")("sqlite"); | ||
const { logger } = require("../../utils/logger"); | ||
const debug = logger.debugContext("sql:sqlite"); | ||
class Query extends AbstractQuery { | ||
getInsertIdField() { | ||
return 'lastID'; | ||
return "lastID"; | ||
} | ||
/** | ||
* rewrite query with parameters. | ||
* | ||
* @param {string} sql | ||
* @param {Array|object} values | ||
* @param {string} dialect | ||
* @private | ||
*/ | ||
static formatBindParameters(sql, values, dialect) { | ||
@@ -37,3 +24,3 @@ let bindParam; | ||
bindParam = {}; | ||
if (typeof values === 'object') { | ||
if (typeof values === "object") { | ||
for (const k of Object.keys(values)) { | ||
@@ -47,6 +34,4 @@ bindParam[`$${k}`] = values[k]; | ||
} | ||
_collectModels(include, prefix) { | ||
const ret = {}; | ||
if (include) { | ||
@@ -61,3 +46,2 @@ for (const _include of include) { | ||
ret[key] = _include.model; | ||
if (_include.include) { | ||
@@ -68,25 +52,14 @@ _.merge(ret, this._collectModels(_include.include, key)); | ||
} | ||
return ret; | ||
} | ||
_handleQueryResponse(metaData, columnTypes, err, results) { | ||
_handleQueryResponse(metaData, columnTypes, err, results, errStack) { | ||
if (err) { | ||
err.sql = this.sql; | ||
throw this.formatError(err); | ||
throw this.formatError(err, errStack); | ||
} | ||
let result = this.instance; | ||
// add the inserted row id to the instance | ||
if (this.isInsertQuery(results, metaData) || this.isUpsertQuery()) { | ||
this.handleInsertQuery(results, metaData); | ||
if (!this.instance) { | ||
// handle bulkCreate AI primary key | ||
if ( | ||
metaData.constructor.name === 'Statement' | ||
&& this.model | ||
&& this.model.autoIncrementAttribute | ||
&& this.model.autoIncrementAttribute === this.model.primaryKeyAttribute | ||
&& this.model.rawAttributes[this.model.primaryKeyAttribute] | ||
) { | ||
if (metaData.constructor.name === "Statement" && this.model && this.model.autoIncrementAttribute && this.model.autoIncrementAttribute === this.model.primaryKeyAttribute && this.model.rawAttributes[this.model.primaryKeyAttribute]) { | ||
const startId = metaData[this.getInsertIdField()] - metaData.changes + 1; | ||
@@ -102,5 +75,4 @@ result = []; | ||
} | ||
if (this.isShowTablesQuery()) { | ||
return results.map(row => row.name); | ||
return results.map((row) => row.name); | ||
} | ||
@@ -118,13 +90,9 @@ if (this.isShowConstraintsQuery()) { | ||
} | ||
// This is a map of prefix strings to models, e.g. user.projects -> Project model | ||
const prefixes = this._collectModels(this.options.include); | ||
results = results.map(result => { | ||
return _.mapValues(result, (value, name) => { | ||
results = results.map((result2) => { | ||
return _.mapValues(result2, (value, name) => { | ||
let model; | ||
if (name.includes('.')) { | ||
const lastind = name.lastIndexOf('.'); | ||
if (name.includes(".")) { | ||
const lastind = name.lastIndexOf("."); | ||
model = prefixes[name.substr(0, lastind)]; | ||
name = name.substr(lastind + 1); | ||
@@ -134,8 +102,5 @@ } else { | ||
} | ||
const tableName = model.getTableName().toString().replace(/`/g, ''); | ||
const tableName = model.getTableName().toString().replace(/`/g, ""); | ||
const tableTypes = columnTypes[tableName] || {}; | ||
if (tableTypes && !(name in tableTypes)) { | ||
// The column is aliased | ||
_.forOwn(model.rawAttributes, (attribute, key) => { | ||
@@ -148,9 +113,5 @@ if (name === key && attribute.field) { | ||
} | ||
return Object.prototype.hasOwnProperty.call(tableTypes, name) | ||
? this.applyParsers(tableTypes[name], value) | ||
: value; | ||
return Object.prototype.hasOwnProperty.call(tableTypes, name) ? this.applyParsers(tableTypes[name], value) : value; | ||
}); | ||
}); | ||
return this.handleSelectQuery(results); | ||
@@ -161,19 +122,15 @@ } | ||
} | ||
if (this.sql.includes('PRAGMA INDEX_LIST')) { | ||
if (this.sql.includes("PRAGMA INDEX_LIST")) { | ||
return this.handleShowIndexesQuery(results); | ||
} | ||
if (this.sql.includes('PRAGMA INDEX_INFO')) { | ||
if (this.sql.includes("PRAGMA INDEX_INFO")) { | ||
return results; | ||
} | ||
if (this.sql.includes('PRAGMA TABLE_INFO')) { | ||
// this is the sqlite way of getting the metadata of a table | ||
if (this.sql.includes("PRAGMA TABLE_INFO")) { | ||
result = {}; | ||
let defaultValue; | ||
for (const _result of results) { | ||
if (_result.dflt_value === null) { | ||
// Column schema omits any "DEFAULT ..." | ||
defaultValue = undefined; | ||
} else if (_result.dflt_value === 'NULL') { | ||
// Column schema is a "DEFAULT NULL" | ||
defaultValue = void 0; | ||
} else if (_result.dflt_value === "NULL") { | ||
defaultValue = null; | ||
@@ -183,3 +140,2 @@ } else { | ||
} | ||
result[_result.name] = { | ||
@@ -191,9 +147,7 @@ type: _result.type, | ||
}; | ||
if (result[_result.name].type === 'TINYINT(1)') { | ||
result[_result.name].defaultValue = { '0': false, '1': true }[result[_result.name].defaultValue]; | ||
if (result[_result.name].type === "TINYINT(1)") { | ||
result[_result.name].defaultValue = { "0": false, "1": true }[result[_result.name].defaultValue]; | ||
} | ||
if (typeof result[_result.name].defaultValue === 'string') { | ||
result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, ''); | ||
if (typeof result[_result.name].defaultValue === "string") { | ||
result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, ""); | ||
} | ||
@@ -203,9 +157,9 @@ } | ||
} | ||
if (this.sql.includes('PRAGMA foreign_keys;')) { | ||
if (this.sql.includes("PRAGMA foreign_keys;")) { | ||
return results[0]; | ||
} | ||
if (this.sql.includes('PRAGMA foreign_keys')) { | ||
if (this.sql.includes("PRAGMA foreign_keys")) { | ||
return results; | ||
} | ||
if (this.sql.includes('PRAGMA foreign_key_list')) { | ||
if (this.sql.includes("PRAGMA foreign_key_list")) { | ||
return results; | ||
@@ -230,3 +184,2 @@ } | ||
} | ||
async run(sql, parameters) { | ||
@@ -237,17 +190,14 @@ const conn = this.connection; | ||
const complete = this._logQuery(sql, debug, parameters); | ||
return new Promise((resolve, reject) => conn.serialize(async () => { | ||
const columnTypes = {}; | ||
const errForStack = new Error(); | ||
const executeSql = () => { | ||
if (sql.startsWith('-- ')) { | ||
if (sql.startsWith("-- ")) { | ||
return resolve(); | ||
} | ||
const query = this; | ||
// cannot use arrow function here because the function is bound to the statement | ||
function afterExecute(executionError, results) { | ||
try { | ||
complete(); | ||
// `this` is passed from sqlite, we have no control over this. | ||
// eslint-disable-next-line no-invalid-this | ||
resolve(query._handleQueryResponse(this, columnTypes, executionError, results)); | ||
resolve(query._handleQueryResponse(this, columnTypes, executionError, results, errForStack.stack)); | ||
return; | ||
@@ -258,10 +208,8 @@ } catch (error) { | ||
} | ||
if (!parameters) parameters = []; | ||
if (!parameters) | ||
parameters = []; | ||
conn[method](sql, parameters, afterExecute); | ||
return null; | ||
}; | ||
if (this.getDatabaseMethod() === 'all') { | ||
if (this.getDatabaseMethod() === "all") { | ||
let tableNames = []; | ||
@@ -273,23 +221,18 @@ if (this.options && this.options.tableNames) { | ||
} | ||
// If we already have the metadata for the table, there's no need to ask for it again | ||
tableNames = tableNames.filter(tableName => !(tableName in columnTypes) && tableName !== 'sqlite_master'); | ||
tableNames = tableNames.filter((tableName) => !(tableName in columnTypes) && tableName !== "sqlite_master"); | ||
if (!tableNames.length) { | ||
return executeSql(); | ||
} | ||
await Promise.all(tableNames.map(tableName => | ||
new Promise(resolve => { | ||
tableName = tableName.replace(/`/g, ''); | ||
columnTypes[tableName] = {}; | ||
conn.all(`PRAGMA table_info(\`${tableName}\`)`, (err, results) => { | ||
if (!err) { | ||
for (const result of results) { | ||
columnTypes[tableName][result.name] = result.type; | ||
} | ||
await Promise.all(tableNames.map((tableName) => new Promise((resolve2) => { | ||
tableName = tableName.replace(/`/g, ""); | ||
columnTypes[tableName] = {}; | ||
conn.all(`PRAGMA table_info(\`${tableName}\`)`, (err, results) => { | ||
if (!err) { | ||
for (const result of results) { | ||
columnTypes[tableName][result.name] = result.type; | ||
} | ||
resolve(); | ||
}); | ||
}))); | ||
} | ||
resolve2(); | ||
}); | ||
}))); | ||
} | ||
@@ -299,38 +242,29 @@ return executeSql(); | ||
} | ||
parseConstraintsFromSql(sql) { | ||
let constraints = sql.split('CONSTRAINT '); | ||
let constraints = sql.split("CONSTRAINT "); | ||
let referenceTableName, referenceTableKeys, updateAction, deleteAction; | ||
constraints.splice(0, 1); | ||
constraints = constraints.map(constraintSql => { | ||
//Parse foreign key snippets | ||
if (constraintSql.includes('REFERENCES')) { | ||
//Parse out the constraint condition form sql string | ||
constraints = constraints.map((constraintSql) => { | ||
if (constraintSql.includes("REFERENCES")) { | ||
updateAction = constraintSql.match(/ON UPDATE (CASCADE|SET NULL|RESTRICT|NO ACTION|SET DEFAULT){1}/); | ||
deleteAction = constraintSql.match(/ON DELETE (CASCADE|SET NULL|RESTRICT|NO ACTION|SET DEFAULT){1}/); | ||
if (updateAction) { | ||
updateAction = updateAction[1]; | ||
} | ||
if (deleteAction) { | ||
deleteAction = deleteAction[1]; | ||
} | ||
const referencesRegex = /REFERENCES.+\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/; | ||
const referenceConditions = constraintSql.match(referencesRegex)[0].split(' '); | ||
const referenceConditions = constraintSql.match(referencesRegex)[0].split(" "); | ||
referenceTableName = Utils.removeTicks(referenceConditions[1]); | ||
let columnNames = referenceConditions[2]; | ||
columnNames = columnNames.replace(/\(|\)/g, '').split(', '); | ||
referenceTableKeys = columnNames.map(column => Utils.removeTicks(column)); | ||
columnNames = columnNames.replace(/\(|\)/g, "").split(", "); | ||
referenceTableKeys = columnNames.map((column) => Utils.removeTicks(column)); | ||
} | ||
const constraintCondition = constraintSql.match(/\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/)[0]; | ||
constraintSql = constraintSql.replace(/\(.+\)/, ''); | ||
const constraint = constraintSql.split(' '); | ||
if (constraint[1] === 'PRIMARY' || constraint[1] === 'FOREIGN') { | ||
constraint[1] += ' KEY'; | ||
constraintSql = constraintSql.replace(/\(.+\)/, ""); | ||
const constraint = constraintSql.split(" "); | ||
if (["PRIMARY", "FOREIGN"].includes(constraint[1])) { | ||
constraint[1] += " KEY"; | ||
} | ||
return { | ||
@@ -341,3 +275,3 @@ constraintName: Utils.removeTicks(constraint[0]), | ||
deleteAction, | ||
sql: sql.replace(/"/g, '`'), //Sqlite returns double quotes for table name | ||
sql: sql.replace(/"/g, "`"), | ||
constraintCondition, | ||
@@ -348,15 +282,11 @@ referenceTableName, | ||
}); | ||
return constraints; | ||
} | ||
applyParsers(type, value) { | ||
if (type.includes('(')) { | ||
// Remove the length part | ||
type = type.substr(0, type.indexOf('(')); | ||
if (type.includes("(")) { | ||
type = type.substr(0, type.indexOf("(")); | ||
} | ||
type = type.replace('UNSIGNED', '').replace('ZEROFILL', ''); | ||
type = type.replace("UNSIGNED", "").replace("ZEROFILL", ""); | ||
type = type.trim().toUpperCase(); | ||
const parse = parserStore.get(type); | ||
if (value !== null && parse) { | ||
@@ -367,44 +297,32 @@ return parse(value, { timezone: this.sequelize.options.timezone }); | ||
} | ||
formatError(err) { | ||
formatError(err, errStack) { | ||
switch (err.code) { | ||
case 'SQLITE_CONSTRAINT': { | ||
if (err.message.includes('FOREIGN KEY constraint failed')) { | ||
case "SQLITE_CONSTRAINT_UNIQUE": | ||
case "SQLITE_CONSTRAINT_PRIMARYKEY": | ||
case "SQLITE_CONSTRAINT_TRIGGER": | ||
case "SQLITE_CONSTRAINT_FOREIGNKEY": | ||
case "SQLITE_CONSTRAINT": { | ||
if (err.message.includes("FOREIGN KEY constraint failed")) { | ||
return new sequelizeErrors.ForeignKeyConstraintError({ | ||
parent: err | ||
parent: err, | ||
stack: errStack | ||
}); | ||
} | ||
let fields = []; | ||
// Sqlite pre 2.2 behavior - Error: SQLITE_CONSTRAINT: columns x, y are not unique | ||
let match = err.message.match(/columns (.*?) are/); | ||
if (match !== null && match.length >= 2) { | ||
fields = match[1].split(', '); | ||
fields = match[1].split(", "); | ||
} else { | ||
// Sqlite post 2.2 behavior - Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: table.x, table.y | ||
match = err.message.match(/UNIQUE constraint failed: (.*)/); | ||
if (match !== null && match.length >= 2) { | ||
fields = match[1].split(', ').map(columnWithTable => columnWithTable.split('.')[1]); | ||
fields = match[1].split(", ").map((columnWithTable) => columnWithTable.split(".")[1]); | ||
} | ||
} | ||
const errors = []; | ||
let message = 'Validation error'; | ||
let message = "Validation error"; | ||
for (const field of fields) { | ||
errors.push(new sequelizeErrors.ValidationErrorItem( | ||
this.getUniqueConstraintErrorMessage(field), | ||
'unique violation', // sequelizeErrors.ValidationErrorItem.Origins.DB, | ||
field, | ||
this.instance && this.instance[field], | ||
this.instance, | ||
'not_unique' | ||
)); | ||
errors.push(new sequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, this.instance && this.instance[field], this.instance, "not_unique")); | ||
} | ||
if (this.model) { | ||
_.forOwn(this.model.uniqueKeys, constraint => { | ||
_.forOwn(this.model.uniqueKeys, (constraint) => { | ||
if (_.isEqual(constraint.fields, fields) && !!constraint.msg) { | ||
@@ -416,16 +334,12 @@ message = constraint.msg; | ||
} | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields }); | ||
return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields, stack: errStack }); | ||
} | ||
case 'SQLITE_BUSY': | ||
return new sequelizeErrors.TimeoutError(err); | ||
case "SQLITE_BUSY": | ||
return new sequelizeErrors.TimeoutError(err, { stack: errStack }); | ||
default: | ||
return new sequelizeErrors.DatabaseError(err); | ||
return new sequelizeErrors.DatabaseError(err, { stack: errStack }); | ||
} | ||
} | ||
async handleShowIndexesQuery(data) { | ||
// Sqlite returns indexes so the one that was defined last is returned first. Lets reverse that! | ||
return Promise.all(data.reverse().map(async item => { | ||
return Promise.all(data.reverse().map(async (item) => { | ||
item.fields = []; | ||
@@ -439,21 +353,19 @@ item.primary = false; | ||
attribute: column.name, | ||
length: undefined, | ||
order: undefined | ||
length: void 0, | ||
order: void 0 | ||
}; | ||
} | ||
return item; | ||
})); | ||
} | ||
getDatabaseMethod() { | ||
if (this.isInsertQuery() || this.isUpdateQuery() || this.isUpsertQuery() || this.isBulkUpdateQuery() || this.sql.toLowerCase().includes('CREATE TEMPORARY TABLE'.toLowerCase()) || this.options.type === QueryTypes.BULKDELETE) { | ||
return 'run'; | ||
if (this.isInsertQuery() || this.isUpdateQuery() || this.isUpsertQuery() || this.isBulkUpdateQuery() || this.sql.toLowerCase().includes("CREATE TEMPORARY TABLE".toLowerCase()) || this.options.type === QueryTypes.BULKDELETE) { | ||
return "run"; | ||
} | ||
return 'all'; | ||
return "all"; | ||
} | ||
} | ||
module.exports = Query; | ||
module.exports.Query = Query; | ||
module.exports.default = Query; | ||
//# sourceMappingURL=query.js.map |
@@ -1,34 +0,48 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* A wrapper for multiple Errors | ||
* | ||
* @param {Error[]} [errors] Array of errors | ||
* | ||
* @property errors {Error[]} | ||
*/ | ||
class AggregateError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => aggregate_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class AggregateError extends import_base_error.default { | ||
constructor(errors) { | ||
super(); | ||
__publicField(this, "errors"); | ||
this.errors = errors; | ||
this.name = 'AggregateError'; | ||
this.name = "AggregateError"; | ||
} | ||
toString() { | ||
const message = `AggregateError of:\n${ | ||
this.errors.map(error => | ||
error === this | ||
? '[Circular AggregateError]' | ||
: error instanceof AggregateError | ||
? String(error).replace(/\n$/, '').replace(/^/mg, ' ') | ||
: String(error).replace(/^/mg, ' ').substring(2) | ||
).join('\n') | ||
}\n`; | ||
const message = `AggregateError of: | ||
${this.errors.map((error) => error === this ? "[Circular AggregateError]" : error instanceof AggregateError ? String(error).replace(/\n$/, "").replace(/^/gm, " ") : String(error).replace(/^/gm, " ").substring(2)).join("\n")} | ||
`; | ||
return message; | ||
} | ||
} | ||
module.exports = AggregateError; | ||
var aggregate_error_default = AggregateError; | ||
//# sourceMappingURL=aggregate-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Thrown when an association is improperly constructed (see message for details) | ||
*/ | ||
class AssociationError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => association_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class AssociationError extends import_base_error.default { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'SequelizeAssociationError'; | ||
this.name = "SequelizeAssociationError"; | ||
} | ||
} | ||
module.exports = AssociationError; | ||
var association_error_default = AssociationError; | ||
//# sourceMappingURL=association-error.js.map |
@@ -1,17 +0,18 @@ | ||
'use strict'; | ||
/** | ||
* Sequelize provides a host of custom error classes, to allow you to do easier debugging. All of these errors are exposed on the sequelize object and the sequelize constructor. | ||
* All sequelize errors inherit from the base JS error object. | ||
* | ||
* This means that errors can be accessed using `Sequelize.ValidationError` | ||
* The Base Error all Sequelize Errors inherit from. | ||
*/ | ||
var __defProp = Object.defineProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
__export(exports, { | ||
default: () => base_error_default | ||
}); | ||
class BaseError extends Error { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'SequelizeBaseError'; | ||
this.name = "SequelizeBaseError"; | ||
} | ||
} | ||
module.exports = BaseError; | ||
var base_error_default = BaseError; | ||
//# sourceMappingURL=base-error.js.map |
@@ -1,16 +0,39 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Thrown when bulk operation fails, it represent per record level error. | ||
* Used with AggregateError | ||
* | ||
* @param {Error} error Error for a given record/instance | ||
* @param {object} record DAO instance that error belongs to | ||
*/ | ||
class BulkRecordError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => bulk_record_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class BulkRecordError extends import_base_error.default { | ||
constructor(error, record) { | ||
super(error.message); | ||
this.name = 'SequelizeBulkRecordError'; | ||
__publicField(this, "errors"); | ||
__publicField(this, "record"); | ||
this.name = "SequelizeBulkRecordError"; | ||
this.errors = error; | ||
@@ -20,3 +43,3 @@ this.record = record; | ||
} | ||
module.exports = BulkRecordError; | ||
var bulk_record_error_default = BulkRecordError; | ||
//# sourceMappingURL=bulk-record-error.js.map |
@@ -1,17 +0,39 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* A base class for all connection related errors. | ||
*/ | ||
class ConnectionError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => connection_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class ConnectionError extends import_base_error.default { | ||
constructor(parent) { | ||
super(parent ? parent.message : ''); | ||
this.name = 'SequelizeConnectionError'; | ||
/** | ||
* The connection specific error which triggered this one | ||
* | ||
* @type {Error} | ||
*/ | ||
super(parent ? parent.message : ""); | ||
__publicField(this, "parent"); | ||
__publicField(this, "original"); | ||
this.name = "SequelizeConnectionError"; | ||
this.parent = parent; | ||
@@ -21,3 +43,3 @@ this.original = parent; | ||
} | ||
module.exports = ConnectionError; | ||
var connection_error_default = ConnectionError; | ||
//# sourceMappingURL=connection-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const ConnectionError = require('./../connection-error'); | ||
/** | ||
* Thrown when a connection to a database is refused due to insufficient privileges | ||
*/ | ||
class AccessDeniedError extends ConnectionError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => access_denied_error_default | ||
}); | ||
var import_connection_error = __toModule(require("../connection-error")); | ||
class AccessDeniedError extends import_connection_error.default { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeAccessDeniedError'; | ||
this.name = "SequelizeAccessDeniedError"; | ||
} | ||
} | ||
module.exports = AccessDeniedError; | ||
var access_denied_error_default = AccessDeniedError; | ||
//# sourceMappingURL=access-denied-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const ConnectionError = require('./../connection-error'); | ||
/** | ||
* Thrown when connection is not acquired due to timeout | ||
*/ | ||
class ConnectionAcquireTimeoutError extends ConnectionError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => connection_acquire_timeout_error_default | ||
}); | ||
var import_connection_error = __toModule(require("../connection-error")); | ||
class ConnectionAcquireTimeoutError extends import_connection_error.default { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeConnectionAcquireTimeoutError'; | ||
this.name = "SequelizeConnectionAcquireTimeoutError"; | ||
} | ||
} | ||
module.exports = ConnectionAcquireTimeoutError; | ||
var connection_acquire_timeout_error_default = ConnectionAcquireTimeoutError; | ||
//# sourceMappingURL=connection-acquire-timeout-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const ConnectionError = require('./../connection-error'); | ||
/** | ||
* Thrown when a connection to a database is refused | ||
*/ | ||
class ConnectionRefusedError extends ConnectionError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => connection_refused_error_default | ||
}); | ||
var import_connection_error = __toModule(require("../connection-error")); | ||
class ConnectionRefusedError extends import_connection_error.default { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeConnectionRefusedError'; | ||
this.name = "SequelizeConnectionRefusedError"; | ||
} | ||
} | ||
module.exports = ConnectionRefusedError; | ||
var connection_refused_error_default = ConnectionRefusedError; | ||
//# sourceMappingURL=connection-refused-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const ConnectionError = require('./../connection-error'); | ||
/** | ||
* Thrown when a connection to a database times out | ||
*/ | ||
class ConnectionTimedOutError extends ConnectionError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => connection_timed_out_error_default | ||
}); | ||
var import_connection_error = __toModule(require("../connection-error")); | ||
class ConnectionTimedOutError extends import_connection_error.default { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeConnectionTimedOutError'; | ||
this.name = "SequelizeConnectionTimedOutError"; | ||
} | ||
} | ||
module.exports = ConnectionTimedOutError; | ||
var connection_timed_out_error_default = ConnectionTimedOutError; | ||
//# sourceMappingURL=connection-timed-out-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const ConnectionError = require('./../connection-error'); | ||
/** | ||
* Thrown when a connection to a database has a hostname that was not found | ||
*/ | ||
class HostNotFoundError extends ConnectionError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => host_not_found_error_default | ||
}); | ||
var import_connection_error = __toModule(require("../connection-error")); | ||
class HostNotFoundError extends import_connection_error.default { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeHostNotFoundError'; | ||
this.name = "SequelizeHostNotFoundError"; | ||
} | ||
} | ||
module.exports = HostNotFoundError; | ||
var host_not_found_error_default = HostNotFoundError; | ||
//# sourceMappingURL=host-not-found-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const ConnectionError = require('./../connection-error'); | ||
/** | ||
* Thrown when a connection to a database has a hostname that was not reachable | ||
*/ | ||
class HostNotReachableError extends ConnectionError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => host_not_reachable_error_default | ||
}); | ||
var import_connection_error = __toModule(require("../connection-error")); | ||
class HostNotReachableError extends import_connection_error.default { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeHostNotReachableError'; | ||
this.name = "SequelizeHostNotReachableError"; | ||
} | ||
} | ||
module.exports = HostNotReachableError; | ||
var host_not_reachable_error_default = HostNotReachableError; | ||
//# sourceMappingURL=host-not-reachable-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const ConnectionError = require('./../connection-error'); | ||
/** | ||
* Thrown when a connection to a database has invalid values for any of the connection parameters | ||
*/ | ||
class InvalidConnectionError extends ConnectionError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => invalid_connection_error_default | ||
}); | ||
var import_connection_error = __toModule(require("../connection-error")); | ||
class InvalidConnectionError extends import_connection_error.default { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeInvalidConnectionError'; | ||
this.name = "SequelizeInvalidConnectionError"; | ||
} | ||
} | ||
module.exports = InvalidConnectionError; | ||
var invalid_connection_error_default = InvalidConnectionError; | ||
//# sourceMappingURL=invalid-connection-error.js.map |
@@ -1,35 +0,52 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* A base class for all database related errors. | ||
*/ | ||
class DatabaseError extends BaseError { | ||
constructor(parent) { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => database_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class DatabaseError extends import_base_error.default { | ||
constructor(parent, options = {}) { | ||
super(parent.message); | ||
this.name = 'SequelizeDatabaseError'; | ||
/** | ||
* @type {Error} | ||
*/ | ||
__publicField(this, "parent"); | ||
__publicField(this, "original"); | ||
__publicField(this, "sql"); | ||
__publicField(this, "parameters"); | ||
var _a; | ||
this.name = "SequelizeDatabaseError"; | ||
this.parent = parent; | ||
/** | ||
* @type {Error} | ||
*/ | ||
this.original = parent; | ||
/** | ||
* The SQL that triggered the error | ||
* | ||
* @type {string} | ||
*/ | ||
this.sql = parent.sql; | ||
/** | ||
* The parameters for the sql that triggered the error | ||
* | ||
* @type {Array<any>} | ||
*/ | ||
this.parameters = parent.parameters; | ||
this.parameters = (_a = parent.parameters) != null ? _a : {}; | ||
if (options.stack) { | ||
this.stack = options.stack; | ||
} | ||
} | ||
} | ||
module.exports = DatabaseError; | ||
var database_error_default = DatabaseError; | ||
//# sourceMappingURL=database-error.js.map |
@@ -1,17 +0,43 @@ | ||
'use strict'; | ||
const DatabaseError = require('./../database-error'); | ||
/** | ||
* Thrown when an exclusion constraint is violated in the database | ||
*/ | ||
class ExclusionConstraintError extends DatabaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => exclusion_constraint_error_default | ||
}); | ||
var import_database_error = __toModule(require("../database-error")); | ||
class ExclusionConstraintError extends import_database_error.default { | ||
constructor(options) { | ||
options = options || {}; | ||
options.parent = options.parent || { sql: '' }; | ||
super(options.parent); | ||
this.name = 'SequelizeExclusionConstraintError'; | ||
this.message = options.message || options.parent.message || ''; | ||
options.parent = options.parent || { sql: "", name: "", message: "" }; | ||
super(options.parent, { stack: options.stack }); | ||
__publicField(this, "constraint"); | ||
__publicField(this, "fields"); | ||
__publicField(this, "table"); | ||
this.name = "SequelizeExclusionConstraintError"; | ||
this.message = options.message || options.parent.message || ""; | ||
this.constraint = options.constraint; | ||
@@ -22,3 +48,3 @@ this.fields = options.fields; | ||
} | ||
module.exports = ExclusionConstraintError; | ||
var exclusion_constraint_error_default = ExclusionConstraintError; | ||
//# sourceMappingURL=exclusion-constraint-error.js.map |
@@ -1,17 +0,51 @@ | ||
'use strict'; | ||
const DatabaseError = require('./../database-error'); | ||
/** | ||
* Thrown when a foreign key constraint is violated in the database | ||
*/ | ||
class ForeignKeyConstraintError extends DatabaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
RelationshipType: () => RelationshipType, | ||
default: () => foreign_key_constraint_error_default | ||
}); | ||
var import_database_error = __toModule(require("../database-error")); | ||
var RelationshipType = /* @__PURE__ */ ((RelationshipType2) => { | ||
RelationshipType2["parent"] = "parent"; | ||
RelationshipType2["child"] = "child"; | ||
return RelationshipType2; | ||
})(RelationshipType || {}); | ||
class ForeignKeyConstraintError extends import_database_error.default { | ||
constructor(options) { | ||
options = options || {}; | ||
options.parent = options.parent || { sql: '' }; | ||
super(options.parent); | ||
this.name = 'SequelizeForeignKeyConstraintError'; | ||
this.message = options.message || options.parent.message || 'Database Error'; | ||
options.parent = options.parent || { sql: "", name: "", message: "" }; | ||
super(options.parent, { stack: options.stack }); | ||
__publicField(this, "table"); | ||
__publicField(this, "fields"); | ||
__publicField(this, "value"); | ||
__publicField(this, "index"); | ||
__publicField(this, "reltype"); | ||
this.name = "SequelizeForeignKeyConstraintError"; | ||
this.message = options.message || options.parent.message || "Database Error"; | ||
this.fields = options.fields; | ||
@@ -24,3 +58,3 @@ this.table = options.table; | ||
} | ||
module.exports = ForeignKeyConstraintError; | ||
var foreign_key_constraint_error_default = ForeignKeyConstraintError; | ||
//# sourceMappingURL=foreign-key-constraint-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const DatabaseError = require('./../database-error'); | ||
/** | ||
* Thrown when a database query times out because of a deadlock | ||
*/ | ||
class TimeoutError extends DatabaseError { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeTimeoutError'; | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => timeout_error_default | ||
}); | ||
var import_database_error = __toModule(require("../database-error")); | ||
class TimeoutError extends import_database_error.default { | ||
constructor(parent, options = {}) { | ||
super(parent, options); | ||
this.name = "SequelizeTimeoutError"; | ||
} | ||
} | ||
module.exports = TimeoutError; | ||
var timeout_error_default = TimeoutError; | ||
//# sourceMappingURL=timeout-error.js.map |
@@ -1,17 +0,43 @@ | ||
'use strict'; | ||
const DatabaseError = require('./../database-error'); | ||
/** | ||
* Thrown when constraint name is not found in the database | ||
*/ | ||
class UnknownConstraintError extends DatabaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => unknown_constraint_error_default | ||
}); | ||
var import_database_error = __toModule(require("../database-error")); | ||
class UnknownConstraintError extends import_database_error.default { | ||
constructor(options) { | ||
options = options || {}; | ||
options.parent = options.parent || { sql: '' }; | ||
super(options.parent); | ||
this.name = 'SequelizeUnknownConstraintError'; | ||
this.message = options.message || 'The specified constraint does not exist'; | ||
options.parent = options.parent || { sql: "", name: "", message: "" }; | ||
super(options.parent, { stack: options.stack }); | ||
__publicField(this, "constraint"); | ||
__publicField(this, "fields"); | ||
__publicField(this, "table"); | ||
this.name = "SequelizeUnknownConstraintError"; | ||
this.message = options.message || "The specified constraint does not exist"; | ||
this.constraint = options.constraint; | ||
@@ -22,3 +48,3 @@ this.fields = options.fields; | ||
} | ||
module.exports = UnknownConstraintError; | ||
var unknown_constraint_error_default = UnknownConstraintError; | ||
//# sourceMappingURL=unknown-constraint-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Thrown when an include statement is improperly constructed (see message for details) | ||
*/ | ||
class EagerLoadingError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => eager_loading_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class EagerLoadingError extends import_base_error.default { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'SequelizeEagerLoadingError'; | ||
this.name = "SequelizeEagerLoadingError"; | ||
} | ||
} | ||
module.exports = EagerLoadingError; | ||
var eager_loading_error_default = EagerLoadingError; | ||
//# sourceMappingURL=eager-loading-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Thrown when a record was not found, Usually used with rejectOnEmpty mode (see message for details) | ||
*/ | ||
class EmptyResultError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => empty_result_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class EmptyResultError extends import_base_error.default { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'SequelizeEmptyResultError'; | ||
this.name = "SequelizeEmptyResultError"; | ||
} | ||
} | ||
module.exports = EmptyResultError; | ||
var empty_result_error_default = EmptyResultError; | ||
//# sourceMappingURL=empty-result-error.js.map |
@@ -1,33 +0,81 @@ | ||
'use strict'; | ||
exports.BaseError = require('./base-error'); | ||
exports.AggregateError = require('./aggregate-error'); | ||
exports.AsyncQueueError = require('../dialects/mssql/async-queue').AsyncQueueError; | ||
exports.AssociationError = require('./association-error'); | ||
exports.BulkRecordError = require('./bulk-record-error'); | ||
exports.ConnectionError = require('./connection-error'); | ||
exports.DatabaseError = require('./database-error'); | ||
exports.EagerLoadingError = require('./eager-loading-error'); | ||
exports.EmptyResultError = require('./empty-result-error'); | ||
exports.InstanceError = require('./instance-error'); | ||
exports.OptimisticLockError = require('./optimistic-lock-error'); | ||
exports.QueryError = require('./query-error'); | ||
exports.SequelizeScopeError = require('./sequelize-scope-error'); | ||
exports.ValidationError = require('./validation-error'); | ||
exports.ValidationErrorItem = exports.ValidationError.ValidationErrorItem; | ||
exports.AccessDeniedError = require('./connection/access-denied-error'); | ||
exports.ConnectionAcquireTimeoutError = require('./connection/connection-acquire-timeout-error'); | ||
exports.ConnectionRefusedError = require('./connection/connection-refused-error'); | ||
exports.ConnectionTimedOutError = require('./connection/connection-timed-out-error'); | ||
exports.HostNotFoundError = require('./connection/host-not-found-error'); | ||
exports.HostNotReachableError = require('./connection/host-not-reachable-error'); | ||
exports.InvalidConnectionError = require('./connection/invalid-connection-error'); | ||
exports.ExclusionConstraintError = require('./database/exclusion-constraint-error'); | ||
exports.ForeignKeyConstraintError = require('./database/foreign-key-constraint-error'); | ||
exports.TimeoutError = require('./database/timeout-error'); | ||
exports.UnknownConstraintError = require('./database/unknown-constraint-error'); | ||
exports.UniqueConstraintError = require('./validation/unique-constraint-error'); | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
AccessDeniedError: () => import_access_denied_error.default, | ||
AggregateError: () => import_aggregate_error.default, | ||
AssociationError: () => import_association_error.default, | ||
AsyncQueueError: () => import_async_queue.AsyncQueueError, | ||
BaseError: () => import_base_error.default, | ||
BulkRecordError: () => import_bulk_record_error.default, | ||
ConnectionAcquireTimeoutError: () => import_connection_acquire_timeout_error.default, | ||
ConnectionError: () => import_connection_error.default, | ||
ConnectionRefusedError: () => import_connection_refused_error.default, | ||
ConnectionTimedOutError: () => import_connection_timed_out_error.default, | ||
DatabaseError: () => import_database_error.default, | ||
EagerLoadingError: () => import_eager_loading_error.default, | ||
EmptyResultError: () => import_empty_result_error.default, | ||
ExclusionConstraintError: () => import_exclusion_constraint_error.default, | ||
ForeignKeyConstraintError: () => import_foreign_key_constraint_error.default, | ||
HostNotFoundError: () => import_host_not_found_error.default, | ||
HostNotReachableError: () => import_host_not_reachable_error.default, | ||
InstanceError: () => import_instance_error.default, | ||
InvalidConnectionError: () => import_invalid_connection_error.default, | ||
OptimisticLockError: () => import_optimistic_lock_error.default, | ||
QueryError: () => import_query_error.default, | ||
SequelizeScopeError: () => import_sequelize_scope_error.default, | ||
TimeoutError: () => import_timeout_error.default, | ||
UniqueConstraintError: () => import_unique_constraint_error.default, | ||
UnknownConstraintError: () => import_unknown_constraint_error.default, | ||
ValidationError: () => import_validation_error.default, | ||
ValidationErrorItem: () => import_validation_error.ValidationErrorItem, | ||
ValidationErrorItemOrigin: () => import_validation_error.ValidationErrorItemOrigin, | ||
ValidationErrorItemType: () => import_validation_error.ValidationErrorItemType | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
var import_database_error = __toModule(require("./database-error")); | ||
var import_aggregate_error = __toModule(require("./aggregate-error")); | ||
var import_association_error = __toModule(require("./association-error")); | ||
var import_bulk_record_error = __toModule(require("./bulk-record-error")); | ||
var import_connection_error = __toModule(require("./connection-error")); | ||
var import_eager_loading_error = __toModule(require("./eager-loading-error")); | ||
var import_empty_result_error = __toModule(require("./empty-result-error")); | ||
var import_instance_error = __toModule(require("./instance-error")); | ||
var import_optimistic_lock_error = __toModule(require("./optimistic-lock-error")); | ||
var import_query_error = __toModule(require("./query-error")); | ||
var import_sequelize_scope_error = __toModule(require("./sequelize-scope-error")); | ||
var import_validation_error = __toModule(require("./validation-error")); | ||
var import_access_denied_error = __toModule(require("./connection/access-denied-error")); | ||
var import_connection_acquire_timeout_error = __toModule(require("./connection/connection-acquire-timeout-error")); | ||
var import_connection_refused_error = __toModule(require("./connection/connection-refused-error")); | ||
var import_connection_timed_out_error = __toModule(require("./connection/connection-timed-out-error")); | ||
var import_host_not_found_error = __toModule(require("./connection/host-not-found-error")); | ||
var import_host_not_reachable_error = __toModule(require("./connection/host-not-reachable-error")); | ||
var import_invalid_connection_error = __toModule(require("./connection/invalid-connection-error")); | ||
var import_exclusion_constraint_error = __toModule(require("./database/exclusion-constraint-error")); | ||
var import_foreign_key_constraint_error = __toModule(require("./database/foreign-key-constraint-error")); | ||
var import_timeout_error = __toModule(require("./database/timeout-error")); | ||
var import_unknown_constraint_error = __toModule(require("./database/unknown-constraint-error")); | ||
var import_unique_constraint_error = __toModule(require("./validation/unique-constraint-error")); | ||
var import_async_queue = __toModule(require("../dialects/mssql/async-queue")); | ||
//# sourceMappingURL=index.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Thrown when a some problem occurred with Instance methods (see message for details) | ||
*/ | ||
class InstanceError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => instance_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class InstanceError extends import_base_error.default { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'SequelizeInstanceError'; | ||
this.name = "SequelizeInstanceError"; | ||
} | ||
} | ||
module.exports = InstanceError; | ||
var instance_error_default = InstanceError; | ||
//# sourceMappingURL=instance-error.js.map |
@@ -1,9 +0,34 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Thrown when attempting to update a stale model instance | ||
*/ | ||
class OptimisticLockError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => optimistic_lock_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class OptimisticLockError extends import_base_error.default { | ||
constructor(options) { | ||
@@ -13,23 +38,12 @@ options = options || {}; | ||
super(options.message); | ||
this.name = 'SequelizeOptimisticLockError'; | ||
/** | ||
* The name of the model on which the update was attempted | ||
* | ||
* @type {string} | ||
*/ | ||
__publicField(this, "modelName"); | ||
__publicField(this, "values"); | ||
__publicField(this, "where"); | ||
this.name = "SequelizeOptimisticLockError"; | ||
this.modelName = options.modelName; | ||
/** | ||
* The values of the attempted update | ||
* | ||
* @type {object} | ||
*/ | ||
this.values = options.values; | ||
/** | ||
* | ||
* @type {object} | ||
*/ | ||
this.where = options.where; | ||
} | ||
} | ||
module.exports = OptimisticLockError; | ||
var optimistic_lock_error_default = OptimisticLockError; | ||
//# sourceMappingURL=optimistic-lock-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Thrown when a query is passed invalid options (see message for details) | ||
*/ | ||
class QueryError extends BaseError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => query_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class QueryError extends import_base_error.default { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'SequelizeQueryError'; | ||
this.name = "SequelizeQueryError"; | ||
} | ||
} | ||
module.exports = QueryError; | ||
var query_error_default = QueryError; | ||
//# sourceMappingURL=query-error.js.map |
@@ -1,15 +0,35 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Scope Error. Thrown when the sequelize cannot query the specified scope. | ||
*/ | ||
class SequelizeScopeError extends BaseError { | ||
constructor(parent) { | ||
super(parent); | ||
this.name = 'SequelizeScopeError'; | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
default: () => sequelize_scope_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
class SequelizeScopeError extends import_base_error.default { | ||
constructor(message) { | ||
super(message); | ||
this.name = "SequelizeScopeError"; | ||
} | ||
} | ||
module.exports = SequelizeScopeError; | ||
var sequelize_scope_error_default = SequelizeScopeError; | ||
//# sourceMappingURL=sequelize-scope-error.js.map |
@@ -1,136 +0,76 @@ | ||
'use strict'; | ||
const BaseError = require('./base-error'); | ||
/** | ||
* Validation Error. Thrown when the sequelize validation has failed. The error contains an `errors` property, | ||
* which is an array with 1 or more ValidationErrorItems, one for each validation that failed. | ||
* | ||
* @param {string} message Error message | ||
* @param {Array} [errors] Array of ValidationErrorItem objects describing the validation errors | ||
* | ||
* @property errors {ValidationErrorItems[]} | ||
*/ | ||
class ValidationError extends BaseError { | ||
constructor(message, errors) { | ||
super(message); | ||
this.name = 'SequelizeValidationError'; | ||
this.message = 'Validation Error'; | ||
/** | ||
* | ||
* @type {ValidationErrorItem[]} | ||
*/ | ||
this.errors = errors || []; | ||
// Use provided error message if available... | ||
if (message) { | ||
this.message = message; | ||
// ... otherwise create a concatenated message out of existing errors. | ||
} else if (this.errors.length > 0 && this.errors[0].message) { | ||
this.message = this.errors.map(err => `${err.type || err.origin}: ${err.message}`).join(',\n'); | ||
} | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
/** | ||
* Gets all validation error items for the path / field specified. | ||
* | ||
* @param {string} path The path to be checked for error items | ||
* | ||
* @returns {Array<ValidationErrorItem>} Validation error items for the specified path | ||
*/ | ||
get(path) { | ||
return this.errors.reduce((reduced, error) => { | ||
if (error.path === path) { | ||
reduced.push(error); | ||
} | ||
return reduced; | ||
}, []); | ||
} | ||
} | ||
/** | ||
* Validation Error Item | ||
* Instances of this class are included in the `ValidationError.errors` property. | ||
*/ | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
ValidationErrorItem: () => ValidationErrorItem, | ||
ValidationErrorItemOrigin: () => ValidationErrorItemOrigin, | ||
ValidationErrorItemType: () => ValidationErrorItemType, | ||
default: () => validation_error_default | ||
}); | ||
var import_base_error = __toModule(require("./base-error")); | ||
var ValidationErrorItemType = /* @__PURE__ */ ((ValidationErrorItemType2) => { | ||
ValidationErrorItemType2["notnull violation"] = "CORE"; | ||
ValidationErrorItemType2["string violation"] = "CORE"; | ||
ValidationErrorItemType2["unique violation"] = "DB"; | ||
ValidationErrorItemType2["validation error"] = "FUNCTION"; | ||
return ValidationErrorItemType2; | ||
})(ValidationErrorItemType || {}); | ||
var ValidationErrorItemOrigin = /* @__PURE__ */ ((ValidationErrorItemOrigin2) => { | ||
ValidationErrorItemOrigin2["CORE"] = "CORE"; | ||
ValidationErrorItemOrigin2["DB"] = "DB"; | ||
ValidationErrorItemOrigin2["FUNCTION"] = "FUNCTION"; | ||
return ValidationErrorItemOrigin2; | ||
})(ValidationErrorItemOrigin || {}); | ||
class ValidationErrorItem { | ||
/** | ||
* Creates a new ValidationError item. Instances of this class are included in the `ValidationError.errors` property. | ||
* | ||
* @param {string} [message] An error message | ||
* @param {string} [type] The type/origin of the validation error | ||
* @param {string} [path] The field that triggered the validation error | ||
* @param {string} [value] The value that generated the error | ||
* @param {Model} [instance] the DAO instance that caused the validation error | ||
* @param {string} [validatorKey] a validation "key", used for identification | ||
* @param {string} [fnName] property name of the BUILT-IN validator function that caused the validation error (e.g. "in" or "len"), if applicable | ||
* @param {Array} [fnArgs] parameters used with the BUILT-IN validator function, if applicable | ||
*/ | ||
constructor(message, type, path, value, instance, validatorKey, fnName, fnArgs) { | ||
/** | ||
* An error message | ||
* | ||
* @type {string} message | ||
*/ | ||
this.message = message || ''; | ||
/** | ||
* The type/origin of the validation error | ||
* | ||
* @type {string | null} | ||
*/ | ||
__publicField(this, "message"); | ||
__publicField(this, "type"); | ||
__publicField(this, "path"); | ||
__publicField(this, "value"); | ||
__publicField(this, "origin"); | ||
__publicField(this, "instance"); | ||
__publicField(this, "validatorKey"); | ||
__publicField(this, "validatorName"); | ||
__publicField(this, "validatorArgs"); | ||
this.message = message || ""; | ||
this.type = null; | ||
/** | ||
* The field that triggered the validation error | ||
* | ||
* @type {string | null} | ||
*/ | ||
this.path = path || null; | ||
/** | ||
* The value that generated the error | ||
* | ||
* @type {string | null} | ||
*/ | ||
this.value = value !== undefined ? value : null; | ||
this.value = value !== void 0 ? value : null; | ||
this.origin = null; | ||
/** | ||
* The DAO instance that caused the validation error | ||
* | ||
* @type {Model | null} | ||
*/ | ||
this.instance = instance || null; | ||
/** | ||
* A validation "key", used for identification | ||
* | ||
* @type {string | null} | ||
*/ | ||
this.validatorKey = validatorKey || null; | ||
/** | ||
* Property name of the BUILT-IN validator function that caused the validation error (e.g. "in" or "len"), if applicable | ||
* | ||
* @type {string | null} | ||
*/ | ||
this.validatorName = fnName || null; | ||
/** | ||
* Parameters used with the BUILT-IN validator function, if applicable | ||
* | ||
* @type {Array} | ||
*/ | ||
this.validatorArgs = fnArgs || []; | ||
if (type) { | ||
if (ValidationErrorItem.Origins[ type ]) { | ||
if (this.isValidationErrorItemOrigin(type)) { | ||
this.origin = type; | ||
} else { | ||
const lowercaseType = `${type}`.toLowerCase().trim(); | ||
const realType = ValidationErrorItem.TypeStringMap[ lowercaseType ]; | ||
if (realType && ValidationErrorItem.Origins[ realType ]) { | ||
const lowercaseType = this.normalizeString(type); | ||
const realType = ValidationErrorItemType[lowercaseType]; | ||
if (realType && ValidationErrorItemOrigin[realType]) { | ||
this.origin = realType; | ||
@@ -141,69 +81,52 @@ this.type = type; | ||
} | ||
// This doesn't need captureStackTrace because it's not a subclass of Error | ||
} | ||
/** | ||
* return a lowercase, trimmed string "key" that identifies the validator. | ||
* | ||
* Note: the string will be empty if the instance has neither a valid `validatorKey` property nor a valid `validatorName` property | ||
* | ||
* @param {boolean} [useTypeAsNS=true] controls whether the returned value is "namespace", | ||
* this parameter is ignored if the validator's `type` is not one of ValidationErrorItem.Origins | ||
* @param {string} [NSSeparator='.'] a separator string for concatenating the namespace, must be not be empty, | ||
* defaults to "." (fullstop). only used and validated if useTypeAsNS is TRUE. | ||
* @throws {Error} thrown if NSSeparator is found to be invalid. | ||
* @returns {string} | ||
* | ||
* @private | ||
*/ | ||
isValidationErrorItemOrigin(origin) { | ||
return ValidationErrorItemOrigin[origin] !== void 0; | ||
} | ||
normalizeString(str) { | ||
return str.toLowerCase().trim(); | ||
} | ||
getValidatorKey(useTypeAsNS, NSSeparator) { | ||
const useTANS = useTypeAsNS === undefined || !!useTypeAsNS; | ||
const NSSep = NSSeparator === undefined ? '.' : NSSeparator; | ||
const useTANS = useTypeAsNS === void 0 || !!useTypeAsNS; | ||
const NSSep = NSSeparator === void 0 ? "." : NSSeparator; | ||
const type = this.origin; | ||
const key = this.validatorKey || this.validatorName; | ||
const useNS = useTANS && type && ValidationErrorItem.Origins[ type ]; | ||
if (useNS && (typeof NSSep !== 'string' || !NSSep.length)) { | ||
throw new Error('Invalid namespace separator given, must be a non-empty string'); | ||
const useNS = useTANS && type && ValidationErrorItemOrigin[type]; | ||
if (useNS && (typeof NSSep !== "string" || !NSSep.length)) { | ||
throw new Error("Invalid namespace separator given, must be a non-empty string"); | ||
} | ||
if (!(typeof key === 'string' && key.length)) { | ||
return ''; | ||
if (!(typeof key === "string" && key.length)) { | ||
return ""; | ||
} | ||
return (useNS ? [type, key].join(NSSep) : key).toLowerCase().trim(); | ||
return (useNS ? [this.origin, key].join(NSSep) : key).toLowerCase().trim(); | ||
} | ||
} | ||
/** | ||
* An enum that defines valid ValidationErrorItem `origin` values | ||
* | ||
* @type {object} | ||
* @property CORE {string} specifies errors that originate from the sequelize "core" | ||
* @property DB {string} specifies validation errors that originate from the storage engine | ||
* @property FUNCTION {string} specifies validation errors that originate from validator functions (both built-in and custom) defined for a given attribute | ||
*/ | ||
ValidationErrorItem.Origins = { | ||
CORE: 'CORE', | ||
DB: 'DB', | ||
FUNCTION: 'FUNCTION' | ||
}; | ||
/** | ||
* An object that is used internally by the `ValidationErrorItem` class | ||
* that maps current `type` strings (as given to ValidationErrorItem.constructor()) to | ||
* our new `origin` values. | ||
* | ||
* @type {object} | ||
*/ | ||
ValidationErrorItem.TypeStringMap = { | ||
'notnull violation': 'CORE', | ||
'string violation': 'CORE', | ||
'unique violation': 'DB', | ||
'validation error': 'FUNCTION' | ||
}; | ||
module.exports = ValidationError; | ||
module.exports.ValidationErrorItem = ValidationErrorItem; | ||
__publicField(ValidationErrorItem, "TypeStringMap", ValidationErrorItemType); | ||
__publicField(ValidationErrorItem, "Origins", ValidationErrorItemOrigin); | ||
class ValidationError extends import_base_error.default { | ||
constructor(message, errors, options = {}) { | ||
super(message); | ||
__publicField(this, "errors"); | ||
this.name = "SequelizeValidationError"; | ||
this.message = "Validation Error"; | ||
this.errors = errors || []; | ||
if (message) { | ||
this.message = message; | ||
} else if (this.errors.length > 0 && this.errors[0].message) { | ||
this.message = this.errors.map((err) => `${err.type || err.origin}: ${err.message}`).join(",\n"); | ||
} | ||
if (options.stack) { | ||
this.stack = options.stack; | ||
} | ||
} | ||
get(path) { | ||
return this.errors.reduce((reduced, error) => { | ||
if (error.path === path) { | ||
reduced.push(error); | ||
} | ||
return reduced; | ||
}, []); | ||
} | ||
} | ||
var validation_error_default = ValidationError; | ||
//# sourceMappingURL=validation-error.js.map |
@@ -1,19 +0,47 @@ | ||
'use strict'; | ||
const ValidationError = require('./../validation-error'); | ||
/** | ||
* Thrown when a unique constraint is violated in the database | ||
*/ | ||
class UniqueConstraintError extends ValidationError { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
default: () => unique_constraint_error_default | ||
}); | ||
var import_validation_error = __toModule(require("../validation-error")); | ||
class UniqueConstraintError extends import_validation_error.default { | ||
constructor(options) { | ||
options = options || {}; | ||
options.parent = options.parent || { sql: '' }; | ||
options.message = options.message || options.parent.message || 'Validation Error'; | ||
options.errors = options.errors || {}; | ||
super(options.message, options.errors); | ||
this.name = 'SequelizeUniqueConstraintError'; | ||
this.errors = options.errors; | ||
this.fields = options.fields; | ||
var _a, _b, _c; | ||
options = options != null ? options : {}; | ||
options.parent = (_a = options.parent) != null ? _a : { sql: "", name: "", message: "" }; | ||
options.message = options.message || options.parent.message || "Validation Error"; | ||
options.errors = (_b = options.errors) != null ? _b : []; | ||
super(options.message, options.errors, { stack: options.stack }); | ||
__publicField(this, "parent"); | ||
__publicField(this, "original"); | ||
__publicField(this, "fields"); | ||
__publicField(this, "sql"); | ||
this.name = "SequelizeUniqueConstraintError"; | ||
this.fields = (_c = options.fields) != null ? _c : {}; | ||
this.parent = options.parent; | ||
@@ -24,3 +52,3 @@ this.original = options.parent; | ||
} | ||
module.exports = UniqueConstraintError; | ||
var unique_constraint_error_default = UniqueConstraintError; | ||
//# sourceMappingURL=unique-constraint-error.js.map |
494
lib/hooks.js
@@ -1,7 +0,5 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const { logger } = require('./utils/logger'); | ||
const debug = logger.debugContext('hooks'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const { logger } = require("./utils/logger"); | ||
const debug = logger.debugContext("hooks"); | ||
const hookTypes = { | ||
@@ -19,4 +17,4 @@ beforeValidate: { params: 2 }, | ||
afterUpdate: { params: 2 }, | ||
beforeSave: { params: 2, proxies: ['beforeUpdate', 'beforeCreate'] }, | ||
afterSave: { params: 2, proxies: ['afterUpdate', 'afterCreate'] }, | ||
beforeSave: { params: 2, proxies: ["beforeUpdate", "beforeCreate"] }, | ||
afterSave: { params: 2, proxies: ["afterUpdate", "afterCreate"] }, | ||
beforeUpsert: { params: 2 }, | ||
@@ -55,48 +53,22 @@ afterUpsert: { params: 2 }, | ||
exports.hooks = hookTypes; | ||
/** | ||
* get array of current hook and its proxies combined | ||
* | ||
* @param {string} hookType any hook type @see {@link hookTypes} | ||
* | ||
* @private | ||
*/ | ||
const getProxiedHooks = hookType => | ||
hookTypes[hookType].proxies | ||
? hookTypes[hookType].proxies.concat(hookType) | ||
: [hookType] | ||
; | ||
const getProxiedHooks = (hookType) => hookTypes[hookType].proxies ? hookTypes[hookType].proxies.concat(hookType) : [hookType]; | ||
function getHooks(hooked, hookType) { | ||
return (hooked.options.hooks || {})[hookType] || []; | ||
} | ||
const Hooks = { | ||
/** | ||
* Process user supplied hooks definition | ||
* | ||
* @param {object} hooks hooks definition | ||
* | ||
* @private | ||
* @memberof Sequelize | ||
* @memberof Sequelize.Model | ||
*/ | ||
_setupHooks(hooks) { | ||
this.options.hooks = {}; | ||
_.map(hooks || {}, (hooksArray, hookName) => { | ||
if (!Array.isArray(hooksArray)) hooksArray = [hooksArray]; | ||
hooksArray.forEach(hookFn => this.addHook(hookName, hookFn)); | ||
if (!Array.isArray(hooksArray)) | ||
hooksArray = [hooksArray]; | ||
hooksArray.forEach((hookFn) => this.addHook(hookName, hookFn)); | ||
}); | ||
}, | ||
async runHooks(hooks, ...hookArgs) { | ||
if (!hooks) throw new Error('runHooks requires at least 1 argument'); | ||
if (!hooks) | ||
throw new Error("runHooks requires at least 1 argument"); | ||
let hookType; | ||
if (typeof hooks === 'string') { | ||
if (typeof hooks === "string") { | ||
hookType = hooks; | ||
hooks = getHooks(this, hookType); | ||
if (this.sequelize) { | ||
@@ -106,14 +78,10 @@ hooks = hooks.concat(getHooks(this.sequelize, hookType)); | ||
} | ||
if (!Array.isArray(hooks)) { | ||
hooks = [hooks]; | ||
} | ||
// synchronous hooks | ||
if (hookTypes[hookType] && hookTypes[hookType].sync) { | ||
for (let hook of hooks) { | ||
if (typeof hook === 'object') { | ||
if (typeof hook === "object") { | ||
hook = hook.fn; | ||
} | ||
debug(`running hook(sync) ${hookType}`); | ||
@@ -124,9 +92,6 @@ hook.apply(this, hookArgs); | ||
} | ||
// asynchronous hooks (default) | ||
for (let hook of hooks) { | ||
if (typeof hook === 'object') { | ||
if (typeof hook === "object") { | ||
hook = hook.fn; | ||
} | ||
debug(`running hook ${hookType}`); | ||
@@ -136,24 +101,10 @@ await hook.apply(this, hookArgs); | ||
}, | ||
/** | ||
* Add a hook to the model | ||
* | ||
* @param {string} hookType hook name @see {@link hookTypes} | ||
* @param {string|Function} [name] Provide a name for the hook function. It can be used to remove the hook later or to order hooks based on some sort of priority system in the future. | ||
* @param {Function} fn The hook function | ||
* | ||
* @memberof Sequelize | ||
* @memberof Sequelize.Model | ||
*/ | ||
addHook(hookType, name, fn) { | ||
if (typeof name === 'function') { | ||
if (typeof name === "function") { | ||
fn = name; | ||
name = null; | ||
} | ||
debug(`adding hook ${hookType}`); | ||
// check for proxies, add them too | ||
hookType = getProxiedHooks(hookType); | ||
hookType.forEach(type => { | ||
hookType.forEach((type) => { | ||
const hooks = getHooks(this, type); | ||
@@ -163,33 +114,17 @@ hooks.push(name ? { name, fn } : fn); | ||
}); | ||
return this; | ||
}, | ||
/** | ||
* Remove hook from the model | ||
* | ||
* @param {string} hookType @see {@link hookTypes} | ||
* @param {string|Function} name name of hook or function reference which was attached | ||
* | ||
* @memberof Sequelize | ||
* @memberof Sequelize.Model | ||
*/ | ||
removeHook(hookType, name) { | ||
const isReference = typeof name === 'function' ? true : false; | ||
const isReference = typeof name === "function" ? true : false; | ||
if (!this.hasHook(hookType)) { | ||
return this; | ||
} | ||
debug(`removing hook ${hookType}`); | ||
// check for proxies, add them too | ||
hookType = getProxiedHooks(hookType); | ||
for (const type of hookType) { | ||
this.options.hooks[type] = this.options.hooks[type].filter(hook => { | ||
if (isReference && typeof hook === 'function') { | ||
return hook !== name; // check if same method | ||
this.options.hooks[type] = this.options.hooks[type].filter((hook) => { | ||
if (isReference && typeof hook === "function") { | ||
return hook !== name; | ||
} | ||
if (!isReference && typeof hook === 'object') { | ||
if (!isReference && typeof hook === "object") { | ||
return hook.name !== name; | ||
@@ -200,16 +135,4 @@ } | ||
} | ||
return this; | ||
}, | ||
/** | ||
* Check whether the mode has any hooks of this type | ||
* | ||
* @param {string} hookType @see {@link hookTypes} | ||
* | ||
* @alias hasHooks | ||
* | ||
* @memberof Sequelize | ||
* @memberof Sequelize.Model | ||
*/ | ||
hasHook(hookType) { | ||
@@ -220,7 +143,4 @@ return this.options.hooks[hookType] && !!this.options.hooks[hookType].length; | ||
Hooks.hasHooks = Hooks.hasHook; | ||
function applyTo(target, isModel = false) { | ||
_.mixin(target, Hooks); | ||
for (const hook of Object.keys(hookTypes)) { | ||
@@ -236,370 +156,2 @@ if (isModel && hookTypes[hook].noModel) { | ||
exports.applyTo = applyTo; | ||
/** | ||
* A hook that is run before validation | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* @name beforeValidate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after validation | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* @name afterValidate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run when validation fails | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options, error. Error is the | ||
* SequelizeValidationError. If the callback throws an error, it will replace the original validation error. | ||
* @name validationFailed | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before creating a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with attributes, options | ||
* @name beforeCreate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after creating a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with attributes, options | ||
* @name afterCreate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before creating or updating a single instance, It proxies `beforeCreate` and `beforeUpdate` | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with attributes, options | ||
* @name beforeSave | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before upserting | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with attributes, options | ||
* @name beforeUpsert | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after upserting | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with the result of upsert(), options | ||
* @name afterUpsert | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after creating or updating a single instance, It proxies `afterCreate` and `afterUpdate` | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with attributes, options | ||
* @name afterSave | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before destroying a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* | ||
* @name beforeDestroy | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after destroying a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* | ||
* @name afterDestroy | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before restoring a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* | ||
* @name beforeRestore | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after restoring a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* | ||
* @name afterRestore | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before updating a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* @name beforeUpdate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after updating a single instance | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance, options | ||
* @name afterUpdate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before creating instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instances, options | ||
* @name beforeBulkCreate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after creating instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instances, options | ||
* @name afterBulkCreate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before destroying instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* | ||
* @name beforeBulkDestroy | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after destroying instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* | ||
* @name afterBulkDestroy | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before restoring instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* | ||
* @name beforeBulkRestore | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after restoring instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* | ||
* @name afterBulkRestore | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before updating instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* @name beforeBulkUpdate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after updating instances in bulk | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* @name afterBulkUpdate | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before a find (select) query | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* @name beforeFind | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* @name beforeFindAfterExpandIncludeAll | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before a find (select) query, after all option parsing is complete | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* @name beforeFindAfterOptions | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run after a find (select) query | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with instance(s), options | ||
* @name afterFind | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before a count query | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options | ||
* @name beforeCount | ||
* @memberof Sequelize.Model | ||
*/ | ||
/** | ||
* A hook that is run before a define call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with attributes, options | ||
* @name beforeDefine | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run after a define call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with factory | ||
* @name afterDefine | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run before Sequelize() call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with config, options | ||
* @name beforeInit | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run after Sequelize() call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with sequelize | ||
* @name afterInit | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run before a connection is created | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with config passed to connection | ||
* @name beforeConnect | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run after a connection is created | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with the connection object and the config passed to connection | ||
* @name afterConnect | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run before a connection is disconnected | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with the connection object | ||
* @name beforeDisconnect | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run after a connection is disconnected | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with the connection object | ||
* @name afterDisconnect | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run before Model.sync call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options passed to Model.sync | ||
* @name beforeSync | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run after Model.sync call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options passed to Model.sync | ||
* @name afterSync | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run before sequelize.sync call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options passed to sequelize.sync | ||
* @name beforeBulkSync | ||
* @memberof Sequelize | ||
*/ | ||
/** | ||
* A hook that is run after sequelize.sync call | ||
* | ||
* @param {string} name | ||
* @param {Function} fn A callback function that is called with options passed to sequelize.sync | ||
* @name afterBulkSync | ||
* @memberof Sequelize | ||
*/ | ||
//# sourceMappingURL=hooks.js.map |
@@ -1,14 +0,7 @@ | ||
'use strict'; | ||
/** | ||
* An enum of index hints to be used in mysql for querying with index hints | ||
* | ||
* @property USE | ||
* @property FORCE | ||
* @property IGNORE | ||
*/ | ||
const IndexHints = module.exports = { // eslint-disable-line | ||
USE: 'USE', | ||
FORCE: 'FORCE', | ||
IGNORE: 'IGNORE' | ||
"use strict"; | ||
const IndexHints = module.exports = { | ||
USE: "USE", | ||
FORCE: "FORCE", | ||
IGNORE: "IGNORE" | ||
}; | ||
//# sourceMappingURL=index-hints.js.map |
@@ -1,27 +0,30 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Utils = require('./utils'); | ||
const sequelizeError = require('./errors'); | ||
const DataTypes = require('./data-types'); | ||
const BelongsTo = require('./associations/belongs-to'); | ||
const validator = require('./utils/validator-extras').validator; | ||
const { promisify } = require('util'); | ||
/** | ||
* Instance Validator. | ||
* | ||
* @param {Instance} modelInstance The model instance. | ||
* @param {object} options A dictionary with options. | ||
* | ||
* @private | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const _ = require("lodash"); | ||
const Utils = require("./utils"); | ||
const sequelizeError = require("./errors"); | ||
const DataTypes = require("./data-types"); | ||
const BelongsTo = require("./associations/belongs-to"); | ||
const validator = require("./utils/validator-extras").validator; | ||
const { promisify } = require("util"); | ||
class InstanceValidator { | ||
constructor(modelInstance, options) { | ||
options = { | ||
// assign defined and default options | ||
hooks: true, | ||
...options | ||
}; | ||
options = __spreadValues({ | ||
hooks: true | ||
}, options); | ||
if (options.fields && !options.skip) { | ||
@@ -32,42 +35,12 @@ options.skip = _.difference(Object.keys(modelInstance.constructor.rawAttributes), options.fields); | ||
} | ||
this.options = options; | ||
this.modelInstance = modelInstance; | ||
/** | ||
* Exposes a reference to validator.js. This allows you to add custom validations using `validator.extend` | ||
* | ||
* @name validator | ||
* @private | ||
*/ | ||
this.validator = validator; | ||
/** | ||
* All errors will be stored here from the validations. | ||
* | ||
* @type {Array} Will contain keys that correspond to attributes which will | ||
* be Arrays of Errors. | ||
* @private | ||
*/ | ||
this.errors = []; | ||
/** | ||
* @type {boolean} Indicates if validations are in progress | ||
* @private | ||
*/ | ||
this.inProgress = false; | ||
} | ||
/** | ||
* The main entry point for the Validation module, invoke to start the dance. | ||
* | ||
* @returns {Promise} | ||
* @private | ||
*/ | ||
async _validate() { | ||
if (this.inProgress) throw new Error('Validations already in progress.'); | ||
if (this.inProgress) | ||
throw new Error("Validations already in progress."); | ||
this.inProgress = true; | ||
await Promise.all([ | ||
@@ -77,3 +50,2 @@ this._perAttributeValidators(), | ||
]); | ||
if (this.errors.length) { | ||
@@ -83,52 +55,19 @@ throw new sequelizeError.ValidationError(null, this.errors); | ||
} | ||
/** | ||
* Invoke the Validation sequence and run validation hooks if defined | ||
* - Before Validation Model Hooks | ||
* - Validation | ||
* - On validation success: After Validation Model Hooks | ||
* - On validation failure: Validation Failed Model Hooks | ||
* | ||
* @returns {Promise} | ||
* @private | ||
*/ | ||
async validate() { | ||
return await (this.options.hooks ? this._validateAndRunHooks() : this._validate()); | ||
} | ||
/** | ||
* Invoke the Validation sequence and run hooks | ||
* - Before Validation Model Hooks | ||
* - Validation | ||
* - On validation success: After Validation Model Hooks | ||
* - On validation failure: Validation Failed Model Hooks | ||
* | ||
* @returns {Promise} | ||
* @private | ||
*/ | ||
async _validateAndRunHooks() { | ||
const runHooks = this.modelInstance.constructor.runHooks.bind(this.modelInstance.constructor); | ||
await runHooks('beforeValidate', this.modelInstance, this.options); | ||
await runHooks("beforeValidate", this.modelInstance, this.options); | ||
try { | ||
await this._validate(); | ||
} catch (error) { | ||
const newError = await runHooks('validationFailed', this.modelInstance, this.options, error); | ||
const newError = await runHooks("validationFailed", this.modelInstance, this.options, error); | ||
throw newError || error; | ||
} | ||
await runHooks('afterValidate', this.modelInstance, this.options); | ||
await runHooks("afterValidate", this.modelInstance, this.options); | ||
return this.modelInstance; | ||
} | ||
/** | ||
* Will run all the validators defined per attribute (built-in validators and custom validators) | ||
* | ||
* @returns {Promise<Array>} | ||
* @private | ||
*/ | ||
async _perAttributeValidators() { | ||
// promisify all attribute invocations | ||
const validators = []; | ||
_.forIn(this.modelInstance.rawAttributes, (rawAttribute, field) => { | ||
@@ -138,14 +77,9 @@ if (this.options.skip.includes(field)) { | ||
} | ||
const value = this.modelInstance.dataValues[field]; | ||
if (value instanceof Utils.SequelizeMethod) { | ||
return; | ||
} | ||
if (!rawAttribute._autoGenerated && !rawAttribute.autoIncrement) { | ||
// perform validations based on schema | ||
this._validateSchema(rawAttribute, field, value); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(this.modelInstance.validators, field)) { | ||
@@ -155,54 +89,24 @@ validators.push(this._singleAttrValidate(value, field, rawAttribute.allowNull)); | ||
}); | ||
return await Promise.all(validators); | ||
} | ||
/** | ||
* Will run all the custom validators defined in the model's options. | ||
* | ||
* @returns {Promise<Array>} | ||
* @private | ||
*/ | ||
async _customValidators() { | ||
const validators = []; | ||
_.each(this.modelInstance.constructor.options.validate, (validator, validatorType) => { | ||
_.each(this.modelInstance.constructor.options.validate, (validator2, validatorType) => { | ||
if (this.options.skip.includes(validatorType)) { | ||
return; | ||
} | ||
const valprom = this._invokeCustomValidator(validator, validatorType) | ||
// errors are handled in settling, stub this | ||
.catch(() => {}); | ||
const valprom = this._invokeCustomValidator(validator2, validatorType).catch(() => { | ||
}); | ||
validators.push(valprom); | ||
}); | ||
return await Promise.all(validators); | ||
} | ||
/** | ||
* Validate a single attribute with all the defined built-in validators and custom validators. | ||
* | ||
* @private | ||
* | ||
* @param {*} value Anything. | ||
* @param {string} field The field name. | ||
* @param {boolean} allowNull Whether or not the schema allows null values | ||
* | ||
* @returns {Promise} A promise, will always resolve, auto populates error on this.error local object. | ||
*/ | ||
async _singleAttrValidate(value, field, allowNull) { | ||
// If value is null and allowNull is false, no validators should run (see #9143) | ||
if ((value === null || value === undefined) && !allowNull) { | ||
// The schema validator (_validateSchema) has already generated the validation error. Nothing to do here. | ||
if ((value === null || value === void 0) && !allowNull) { | ||
return; | ||
} | ||
// Promisify each validator | ||
const validators = []; | ||
_.forIn(this.modelInstance.validators[field], (test, validatorType) => { | ||
if (validatorType === 'isUrl' || validatorType === 'isURL' || validatorType === 'isEmail') { | ||
// Preserve backwards compat. Validator.js now expects the second param to isURL and isEmail to be an object | ||
if (typeof test === 'object' && test !== null && test.msg) { | ||
if (["isUrl", "isURL", "isEmail"].includes(validatorType)) { | ||
if (typeof test === "object" && test !== null && test.msg) { | ||
test = { | ||
@@ -215,45 +119,22 @@ msg: test.msg | ||
} | ||
// Custom validators should always run, except if value is null and allowNull is false (see #9143) | ||
if (typeof test === 'function') { | ||
if (typeof test === "function") { | ||
validators.push(this._invokeCustomValidator(test, validatorType, true, value, field)); | ||
return; | ||
} | ||
// If value is null, built-in validators should not run (only custom validators have to run) (see #9134). | ||
if (value === null || value === undefined) { | ||
if (value === null || value === void 0) { | ||
return; | ||
} | ||
const validatorPromise = this._invokeBuiltinValidator(value, test, validatorType, field); | ||
// errors are handled in settling, stub this | ||
validatorPromise.catch(() => {}); | ||
validatorPromise.catch(() => { | ||
}); | ||
validators.push(validatorPromise); | ||
}); | ||
return Promise | ||
.all(validators.map(validator => validator.catch(rejection => { | ||
const isBuiltIn = !!rejection.validatorName; | ||
this._pushError(isBuiltIn, field, rejection, value, rejection.validatorName, rejection.validatorArgs); | ||
}))); | ||
return Promise.all(validators.map((validator2) => validator2.catch((rejection) => { | ||
const isBuiltIn = !!rejection.validatorName; | ||
this._pushError(isBuiltIn, field, rejection, value, rejection.validatorName, rejection.validatorArgs); | ||
}))); | ||
} | ||
/** | ||
* Prepare and invoke a custom validator. | ||
* | ||
* @private | ||
* | ||
* @param {Function} validator The custom validator. | ||
* @param {string} validatorType the custom validator type (name). | ||
* @param {boolean} optAttrDefined Set to true if custom validator was defined from the attribute | ||
* @param {*} optValue value for attribute | ||
* @param {string} optField field for attribute | ||
* | ||
* @returns {Promise} A promise. | ||
*/ | ||
async _invokeCustomValidator(validator, validatorType, optAttrDefined, optValue, optField) { | ||
async _invokeCustomValidator(validator2, validatorType, optAttrDefined, optValue, optField) { | ||
let isAsync = false; | ||
const validatorArity = validator.length; | ||
// check if validator is async and requires a callback | ||
const validatorArity = validator2.length; | ||
let asyncArity = 1; | ||
@@ -270,9 +151,8 @@ let errorKey = validatorType; | ||
} | ||
if (isAsync) { | ||
try { | ||
if (optAttrDefined) { | ||
return await promisify(validator.bind(this.modelInstance, invokeArgs))(); | ||
return await promisify(validator2.bind(this.modelInstance, invokeArgs))(); | ||
} | ||
return await promisify(validator.bind(this.modelInstance))(); | ||
return await promisify(validator2.bind(this.modelInstance))(); | ||
} catch (e) { | ||
@@ -282,5 +162,4 @@ return this._pushError(false, errorKey, e, optValue, validatorType); | ||
} | ||
try { | ||
return await validator.call(this.modelInstance, invokeArgs); | ||
return await validator2.call(this.modelInstance, invokeArgs); | ||
} catch (e) { | ||
@@ -290,25 +169,8 @@ return this._pushError(false, errorKey, e, optValue, validatorType); | ||
} | ||
/** | ||
* Prepare and invoke a build-in validator. | ||
* | ||
* @private | ||
* | ||
* @param {*} value Anything. | ||
* @param {*} test The test case. | ||
* @param {string} validatorType One of known to Sequelize validators. | ||
* @param {string} field The field that is being validated | ||
* | ||
* @returns {object} An object with specific keys to invoke the validator. | ||
*/ | ||
async _invokeBuiltinValidator(value, test, validatorType, field) { | ||
// Cast value as string to pass new Validator.js string requirement | ||
const valueString = String(value); | ||
// check if Validator knows that kind of validation test | ||
if (typeof validator[validatorType] !== 'function') { | ||
if (typeof validator[validatorType] !== "function") { | ||
throw new Error(`Invalid validator function: ${validatorType}`); | ||
} | ||
const validatorArgs = this._extractValidatorArgs(test, validatorType, field); | ||
if (!validator[validatorType](valueString, ...validatorArgs)) { | ||
@@ -318,20 +180,9 @@ throw Object.assign(new Error(test.msg || `Validation ${validatorType} on ${field} failed`), { validatorName: validatorType, validatorArgs }); | ||
} | ||
/** | ||
* Will extract arguments for the validator. | ||
* | ||
* @param {*} test The test case. | ||
* @param {string} validatorType One of known to Sequelize validators. | ||
* @param {string} field The field that is being validated. | ||
* | ||
* @private | ||
*/ | ||
_extractValidatorArgs(test, validatorType, field) { | ||
let validatorArgs = test.args || test; | ||
const isLocalizedValidator = typeof validatorArgs !== 'string' && (validatorType === 'isAlpha' || validatorType === 'isAlphanumeric' || validatorType === 'isMobilePhone'); | ||
const isLocalizedValidator = typeof validatorArgs !== "string" && ["isAlpha", "isAlphanumeric", "isMobilePhone"].includes(validatorType); | ||
if (!Array.isArray(validatorArgs)) { | ||
if (validatorType === 'isImmutable') { | ||
if (validatorType === "isImmutable") { | ||
validatorArgs = [validatorArgs, field, this.modelInstance]; | ||
} else if (isLocalizedValidator || validatorType === 'isIP') { | ||
} else if (isLocalizedValidator || validatorType === "isIP") { | ||
validatorArgs = []; | ||
@@ -346,84 +197,28 @@ } else { | ||
} | ||
/** | ||
* Will validate a single field against its schema definition (isnull). | ||
* | ||
* @param {object} rawAttribute As defined in the Schema. | ||
* @param {string} field The field name. | ||
* @param {*} value anything. | ||
* | ||
* @private | ||
*/ | ||
_validateSchema(rawAttribute, field, value) { | ||
if (rawAttribute.allowNull === false && (value === null || value === undefined)) { | ||
const association = Object.values(this.modelInstance.constructor.associations).find(association => association instanceof BelongsTo && association.foreignKey === rawAttribute.fieldName); | ||
if (rawAttribute.allowNull === false && (value === null || value === void 0)) { | ||
const association = Object.values(this.modelInstance.constructor.associations).find((association2) => association2 instanceof BelongsTo && association2.foreignKey === rawAttribute.fieldName); | ||
if (!association || !this.modelInstance.get(association.associationAccessor)) { | ||
const validators = this.modelInstance.validators[field]; | ||
const errMsg = _.get(validators, 'notNull.msg', `${this.modelInstance.constructor.name}.${field} cannot be null`); | ||
this.errors.push(new sequelizeError.ValidationErrorItem( | ||
errMsg, | ||
'notNull Violation', // sequelizeError.ValidationErrorItem.Origins.CORE, | ||
field, | ||
value, | ||
this.modelInstance, | ||
'is_null' | ||
)); | ||
const errMsg = _.get(validators, "notNull.msg", `${this.modelInstance.constructor.name}.${field} cannot be null`); | ||
this.errors.push(new sequelizeError.ValidationErrorItem(errMsg, "notNull Violation", field, value, this.modelInstance, "is_null")); | ||
} | ||
} | ||
if (rawAttribute.type instanceof DataTypes.STRING || rawAttribute.type instanceof DataTypes.TEXT || rawAttribute.type instanceof DataTypes.CITEXT) { | ||
if (Array.isArray(value) || _.isObject(value) && !(value instanceof Utils.SequelizeMethod) && !Buffer.isBuffer(value)) { | ||
this.errors.push(new sequelizeError.ValidationErrorItem( | ||
`${field} cannot be an array or an object`, | ||
'string violation', // sequelizeError.ValidationErrorItem.Origins.CORE, | ||
field, | ||
value, | ||
this.modelInstance, | ||
'not_a_string' | ||
)); | ||
this.errors.push(new sequelizeError.ValidationErrorItem(`${field} cannot be an array or an object`, "string violation", field, value, this.modelInstance, "not_a_string")); | ||
} | ||
} | ||
} | ||
/** | ||
* Signs all errors retaining the original. | ||
* | ||
* @param {boolean} isBuiltin - Determines if error is from builtin validator. | ||
* @param {string} errorKey - name of invalid attribute. | ||
* @param {Error|string} rawError - The original error. | ||
* @param {string|number} value - The data that triggered the error. | ||
* @param {string} fnName - Name of the validator, if any | ||
* @param {Array} fnArgs - Arguments for the validator [function], if any | ||
* | ||
* @private | ||
*/ | ||
_pushError(isBuiltin, errorKey, rawError, value, fnName, fnArgs) { | ||
const message = rawError.message || rawError || 'Validation error'; | ||
const error = new sequelizeError.ValidationErrorItem( | ||
message, | ||
'Validation error', // sequelizeError.ValidationErrorItem.Origins.FUNCTION, | ||
errorKey, | ||
value, | ||
this.modelInstance, | ||
fnName, | ||
isBuiltin ? fnName : undefined, | ||
isBuiltin ? fnArgs : undefined | ||
); | ||
const message = rawError.message || rawError || "Validation error"; | ||
const error = new sequelizeError.ValidationErrorItem(message, "Validation error", errorKey, value, this.modelInstance, fnName, isBuiltin ? fnName : void 0, isBuiltin ? fnArgs : void 0); | ||
error[InstanceValidator.RAW_KEY_NAME] = rawError; | ||
this.errors.push(error); | ||
} | ||
} | ||
/** | ||
* The error key for arguments as passed by custom validators | ||
* | ||
* @type {string} | ||
* @private | ||
*/ | ||
InstanceValidator.RAW_KEY_NAME = 'original'; | ||
InstanceValidator.RAW_KEY_NAME = "original"; | ||
module.exports = InstanceValidator; | ||
module.exports.InstanceValidator = InstanceValidator; | ||
module.exports.default = InstanceValidator; | ||
//# sourceMappingURL=instance-validator.js.map |
@@ -1,6 +0,4 @@ | ||
'use strict'; | ||
const Toposort = require('toposort-class'); | ||
const _ = require('lodash'); | ||
"use strict"; | ||
const Toposort = require("toposort-class"); | ||
const _ = require("lodash"); | ||
class ModelManager { | ||
@@ -11,67 +9,38 @@ constructor(sequelize) { | ||
} | ||
addModel(model) { | ||
this.models.push(model); | ||
this.sequelize.models[model.name] = model; | ||
return model; | ||
} | ||
removeModel(modelToRemove) { | ||
this.models = this.models.filter(model => model.name !== modelToRemove.name); | ||
this.models = this.models.filter((model) => model.name !== modelToRemove.name); | ||
delete this.sequelize.models[modelToRemove.name]; | ||
} | ||
getModel(against, options) { | ||
options = _.defaults(options || {}, { | ||
attribute: 'name' | ||
attribute: "name" | ||
}); | ||
return this.models.find(model => model[options.attribute] === against); | ||
return this.models.find((model) => model[options.attribute] === against); | ||
} | ||
get all() { | ||
return this.models; | ||
} | ||
/** | ||
* Iterate over Models in an order suitable for e.g. creating tables. | ||
* Will take foreign key constraints into account so that dependencies are visited before dependents. | ||
* | ||
* @param {Function} iterator method to execute on each model | ||
* @param {object} [options] iterator options | ||
* @private | ||
*/ | ||
forEachModel(iterator, options) { | ||
const models = {}; | ||
getModelsTopoSortedByForeignKey() { | ||
const models = /* @__PURE__ */ new Map(); | ||
const sorter = new Toposort(); | ||
let sorted; | ||
let dep; | ||
options = _.defaults(options || {}, { | ||
reverse: true | ||
}); | ||
for (const model of this.models) { | ||
let deps = []; | ||
let tableName = model.getTableName(); | ||
if (_.isObject(tableName)) { | ||
tableName = `${tableName.schema}.${tableName.tableName}`; | ||
} | ||
models[tableName] = model; | ||
models.set(tableName, model); | ||
for (const attrName in model.rawAttributes) { | ||
if (Object.prototype.hasOwnProperty.call(model.rawAttributes, attrName)) { | ||
const attribute = model.rawAttributes[attrName]; | ||
if (attribute.references) { | ||
dep = attribute.references.model; | ||
let dep = attribute.references.model; | ||
if (_.isObject(dep)) { | ||
dep = `${dep.schema}.${dep.tableName}`; | ||
} | ||
deps.push(dep); | ||
@@ -81,20 +50,37 @@ } | ||
} | ||
deps = deps.filter(dep => tableName !== dep); | ||
deps = deps.filter((dep) => tableName !== dep); | ||
sorter.add(tableName, deps); | ||
} | ||
sorted = sorter.sort(); | ||
let sorted; | ||
try { | ||
sorted = sorter.sort(); | ||
} catch (e) { | ||
if (!e.message.startsWith("Cyclic dependency found.")) { | ||
throw e; | ||
} | ||
return null; | ||
} | ||
return sorted.map((modelName) => { | ||
return models.get(modelName); | ||
}).filter(Boolean); | ||
} | ||
forEachModel(iterator, options) { | ||
const sortedModels = this.getModelsTopoSortedByForeignKey(); | ||
if (sortedModels == null) { | ||
throw new Error("Cyclic dependency found."); | ||
} | ||
options = _.defaults(options || {}, { | ||
reverse: true | ||
}); | ||
if (options.reverse) { | ||
sorted = sorted.reverse(); | ||
sortedModels.reverse(); | ||
} | ||
for (const name of sorted) { | ||
iterator(models[name], name); | ||
for (const model of sortedModels) { | ||
iterator(model); | ||
} | ||
} | ||
} | ||
module.exports = ModelManager; | ||
module.exports.ModelManager = ModelManager; | ||
module.exports.default = ModelManager; | ||
//# sourceMappingURL=model-manager.js.map |
@@ -1,91 +0,56 @@ | ||
'use strict'; | ||
/** | ||
* Operator symbols to be used when querying data | ||
* | ||
* @see {@link Model#where} | ||
* | ||
* @property eq | ||
* @property ne | ||
* @property gte | ||
* @property gt | ||
* @property lte | ||
* @property lt | ||
* @property not | ||
* @property is | ||
* @property in | ||
* @property notIn | ||
* @property like | ||
* @property notLike | ||
* @property iLike | ||
* @property notILike | ||
* @property startsWith | ||
* @property endsWith | ||
* @property substring | ||
* @property regexp | ||
* @property notRegexp | ||
* @property iRegexp | ||
* @property notIRegexp | ||
* @property between | ||
* @property notBetween | ||
* @property overlap | ||
* @property contains | ||
* @property contained | ||
* @property adjacent | ||
* @property strictLeft | ||
* @property strictRight | ||
* @property noExtendRight | ||
* @property noExtendLeft | ||
* @property and | ||
* @property or | ||
* @property any | ||
* @property all | ||
* @property values | ||
* @property col | ||
* @property placeholder | ||
* @property join | ||
*/ | ||
var __defProp = Object.defineProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
__export(exports, { | ||
Op: () => Op, | ||
default: () => operators_default | ||
}); | ||
const Op = { | ||
eq: Symbol.for('eq'), | ||
ne: Symbol.for('ne'), | ||
gte: Symbol.for('gte'), | ||
gt: Symbol.for('gt'), | ||
lte: Symbol.for('lte'), | ||
lt: Symbol.for('lt'), | ||
not: Symbol.for('not'), | ||
is: Symbol.for('is'), | ||
in: Symbol.for('in'), | ||
notIn: Symbol.for('notIn'), | ||
like: Symbol.for('like'), | ||
notLike: Symbol.for('notLike'), | ||
iLike: Symbol.for('iLike'), | ||
notILike: Symbol.for('notILike'), | ||
startsWith: Symbol.for('startsWith'), | ||
endsWith: Symbol.for('endsWith'), | ||
substring: Symbol.for('substring'), | ||
regexp: Symbol.for('regexp'), | ||
notRegexp: Symbol.for('notRegexp'), | ||
iRegexp: Symbol.for('iRegexp'), | ||
notIRegexp: Symbol.for('notIRegexp'), | ||
between: Symbol.for('between'), | ||
notBetween: Symbol.for('notBetween'), | ||
overlap: Symbol.for('overlap'), | ||
contains: Symbol.for('contains'), | ||
contained: Symbol.for('contained'), | ||
adjacent: Symbol.for('adjacent'), | ||
strictLeft: Symbol.for('strictLeft'), | ||
strictRight: Symbol.for('strictRight'), | ||
noExtendRight: Symbol.for('noExtendRight'), | ||
noExtendLeft: Symbol.for('noExtendLeft'), | ||
and: Symbol.for('and'), | ||
or: Symbol.for('or'), | ||
any: Symbol.for('any'), | ||
all: Symbol.for('all'), | ||
values: Symbol.for('values'), | ||
col: Symbol.for('col'), | ||
placeholder: Symbol.for('placeholder'), | ||
join: Symbol.for('join'), | ||
match: Symbol.for('match') | ||
eq: Symbol.for("eq"), | ||
ne: Symbol.for("ne"), | ||
gte: Symbol.for("gte"), | ||
gt: Symbol.for("gt"), | ||
lte: Symbol.for("lte"), | ||
lt: Symbol.for("lt"), | ||
not: Symbol.for("not"), | ||
is: Symbol.for("is"), | ||
in: Symbol.for("in"), | ||
notIn: Symbol.for("notIn"), | ||
like: Symbol.for("like"), | ||
notLike: Symbol.for("notLike"), | ||
iLike: Symbol.for("iLike"), | ||
notILike: Symbol.for("notILike"), | ||
startsWith: Symbol.for("startsWith"), | ||
endsWith: Symbol.for("endsWith"), | ||
substring: Symbol.for("substring"), | ||
regexp: Symbol.for("regexp"), | ||
notRegexp: Symbol.for("notRegexp"), | ||
iRegexp: Symbol.for("iRegexp"), | ||
notIRegexp: Symbol.for("notIRegexp"), | ||
between: Symbol.for("between"), | ||
notBetween: Symbol.for("notBetween"), | ||
overlap: Symbol.for("overlap"), | ||
contains: Symbol.for("contains"), | ||
contained: Symbol.for("contained"), | ||
adjacent: Symbol.for("adjacent"), | ||
strictLeft: Symbol.for("strictLeft"), | ||
strictRight: Symbol.for("strictRight"), | ||
noExtendRight: Symbol.for("noExtendRight"), | ||
noExtendLeft: Symbol.for("noExtendLeft"), | ||
and: Symbol.for("and"), | ||
or: Symbol.for("or"), | ||
any: Symbol.for("any"), | ||
all: Symbol.for("all"), | ||
values: Symbol.for("values"), | ||
col: Symbol.for("col"), | ||
placeholder: Symbol.for("placeholder"), | ||
join: Symbol.for("join"), | ||
match: Symbol.for("match") | ||
}; | ||
var operators_default = Op; | ||
module.exports = Op; | ||
//# sourceMappingURL=operators.js.map |
@@ -1,38 +0,18 @@ | ||
'use strict'; | ||
/** | ||
* An enum of query types used by `sequelize.query` | ||
* | ||
* @see {@link Sequelize#query} | ||
* | ||
* @property SELECT | ||
* @property INSERT | ||
* @property UPDATE | ||
* @property BULKUPDATE | ||
* @property BULKDELETE | ||
* @property DELETE | ||
* @property UPSERT | ||
* @property VERSION | ||
* @property SHOWTABLES | ||
* @property SHOWINDEXES | ||
* @property DESCRIBE | ||
* @property RAW | ||
* @property FOREIGNKEYS | ||
* @property SHOWCONSTRAINTS | ||
*/ | ||
const QueryTypes = module.exports = { // eslint-disable-line | ||
SELECT: 'SELECT', | ||
INSERT: 'INSERT', | ||
UPDATE: 'UPDATE', | ||
BULKUPDATE: 'BULKUPDATE', | ||
BULKDELETE: 'BULKDELETE', | ||
DELETE: 'DELETE', | ||
UPSERT: 'UPSERT', | ||
VERSION: 'VERSION', | ||
SHOWTABLES: 'SHOWTABLES', | ||
SHOWINDEXES: 'SHOWINDEXES', | ||
DESCRIBE: 'DESCRIBE', | ||
RAW: 'RAW', | ||
FOREIGNKEYS: 'FOREIGNKEYS', | ||
SHOWCONSTRAINTS: 'SHOWCONSTRAINTS' | ||
"use strict"; | ||
const QueryTypes = module.exports = { | ||
SELECT: "SELECT", | ||
INSERT: "INSERT", | ||
UPDATE: "UPDATE", | ||
BULKUPDATE: "BULKUPDATE", | ||
BULKDELETE: "BULKDELETE", | ||
DELETE: "DELETE", | ||
UPSERT: "UPSERT", | ||
VERSION: "VERSION", | ||
SHOWTABLES: "SHOWTABLES", | ||
SHOWINDEXES: "SHOWINDEXES", | ||
DESCRIBE: "DESCRIBE", | ||
RAW: "RAW", | ||
FOREIGNKEYS: "FOREIGNKEYS", | ||
SHOWCONSTRAINTS: "SHOWCONSTRAINTS" | ||
}; | ||
//# sourceMappingURL=query-types.js.map |
1212
lib/sequelize.js
@@ -1,220 +0,80 @@ | ||
'use strict'; | ||
const url = require('url'); | ||
const path = require('path'); | ||
const retry = require('retry-as-promised'); | ||
const _ = require('lodash'); | ||
const Utils = require('./utils'); | ||
const Model = require('./model'); | ||
const DataTypes = require('./data-types'); | ||
const Deferrable = require('./deferrable'); | ||
const ModelManager = require('./model-manager'); | ||
const Transaction = require('./transaction'); | ||
const QueryTypes = require('./query-types'); | ||
const TableHints = require('./table-hints'); | ||
const IndexHints = require('./index-hints'); | ||
const sequelizeErrors = require('./errors'); | ||
const Hooks = require('./hooks'); | ||
const Association = require('./associations/index'); | ||
const Validator = require('./utils/validator-extras').validator; | ||
const Op = require('./operators'); | ||
const deprecations = require('./utils/deprecations'); | ||
/** | ||
* This is the main class, the entry point to sequelize. | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __defProps = Object.defineProperties; | ||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | ||
const url = require("url"); | ||
const path = require("path"); | ||
const pgConnectionString = require("pg-connection-string"); | ||
const retry = require("retry-as-promised"); | ||
const _ = require("lodash"); | ||
const Utils = require("./utils"); | ||
const Model = require("./model"); | ||
const DataTypes = require("./data-types"); | ||
const Deferrable = require("./deferrable"); | ||
const ModelManager = require("./model-manager"); | ||
const Transaction = require("./transaction"); | ||
const QueryTypes = require("./query-types"); | ||
const TableHints = require("./table-hints"); | ||
const IndexHints = require("./index-hints"); | ||
const sequelizeErrors = require("./errors"); | ||
const Hooks = require("./hooks"); | ||
const Association = require("./associations/index"); | ||
const Validator = require("./utils/validator-extras").validator; | ||
const Op = require("./operators"); | ||
const deprecations = require("./utils/deprecations"); | ||
const { QueryInterface } = require("./dialects/abstract/query-interface"); | ||
const { BelongsTo } = require("./associations/belongs-to"); | ||
const HasOne = require("./associations/has-one"); | ||
const { BelongsToMany } = require("./associations/belongs-to-many"); | ||
const { HasMany } = require("./associations/has-many"); | ||
const { withSqliteForeignKeysOff } = require("./dialects/sqlite/sqlite-utils"); | ||
const { injectReplacements } = require("./utils/sql"); | ||
class Sequelize { | ||
/** | ||
* Instantiate sequelize with name of database, username and password. | ||
* | ||
* @example | ||
* // without password / with blank password | ||
* const sequelize = new Sequelize('database', 'username', null, { | ||
* dialect: 'mysql' | ||
* }) | ||
* | ||
* // with password and options | ||
* const sequelize = new Sequelize('my_database', 'john', 'doe', { | ||
* dialect: 'postgres' | ||
* }) | ||
* | ||
* // with database, username, and password in the options object | ||
* const sequelize = new Sequelize({ database, username, password, dialect: 'mssql' }); | ||
* | ||
* // with uri | ||
* const sequelize = new Sequelize('mysql://localhost:3306/database', {}) | ||
* | ||
* // option examples | ||
* const sequelize = new Sequelize('database', 'username', 'password', { | ||
* // the sql dialect of the database | ||
* // currently supported: 'mysql', 'sqlite', 'postgres', 'mssql' | ||
* dialect: 'mysql', | ||
* | ||
* // custom host; default: localhost | ||
* host: 'my.server.tld', | ||
* // for postgres, you can also specify an absolute path to a directory | ||
* // containing a UNIX socket to connect over | ||
* // host: '/sockets/psql_sockets'. | ||
* | ||
* // custom port; default: dialect default | ||
* port: 12345, | ||
* | ||
* // custom protocol; default: 'tcp' | ||
* // postgres only, useful for Heroku | ||
* protocol: null, | ||
* | ||
* // disable logging or provide a custom logging function; default: console.log | ||
* logging: false, | ||
* | ||
* // you can also pass any dialect options to the underlying dialect library | ||
* // - default is empty | ||
* // - currently supported: 'mysql', 'postgres', 'mssql' | ||
* dialectOptions: { | ||
* socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock', | ||
* supportBigNumbers: true, | ||
* bigNumberStrings: true | ||
* }, | ||
* | ||
* // the storage engine for sqlite | ||
* // - default ':memory:' | ||
* storage: 'path/to/database.sqlite', | ||
* | ||
* // disable inserting undefined values as NULL | ||
* // - default: false | ||
* omitNull: true, | ||
* | ||
* // a flag for using a native library or not. | ||
* // in the case of 'pg' -- set this to true will allow SSL support | ||
* // - default: false | ||
* native: true, | ||
* | ||
* // Specify options, which are used when sequelize.define is called. | ||
* // The following example: | ||
* // define: { timestamps: false } | ||
* // is basically the same as: | ||
* // Model.init(attributes, { timestamps: false }); | ||
* // sequelize.define(name, attributes, { timestamps: false }); | ||
* // so defining the timestamps for each model will be not necessary | ||
* define: { | ||
* underscored: false, | ||
* freezeTableName: false, | ||
* charset: 'utf8', | ||
* dialectOptions: { | ||
* collate: 'utf8_general_ci' | ||
* }, | ||
* timestamps: true | ||
* }, | ||
* | ||
* // similar for sync: you can define this to always force sync for models | ||
* sync: { force: true }, | ||
* | ||
* // pool configuration used to pool database connections | ||
* pool: { | ||
* max: 5, | ||
* idle: 30000, | ||
* acquire: 60000, | ||
* }, | ||
* | ||
* // isolation level of each transaction | ||
* // defaults to dialect default | ||
* isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ | ||
* }) | ||
* | ||
* @param {string} [database] The name of the database | ||
* @param {string} [username=null] The username which is used to authenticate against the database. | ||
* @param {string} [password=null] The password which is used to authenticate against the database. Supports SQLCipher encryption for SQLite. | ||
* @param {object} [options={}] An object with options. | ||
* @param {string} [options.host='localhost'] The host of the relational database. | ||
* @param {number} [options.port=] The port of the relational database. | ||
* @param {string} [options.username=null] The username which is used to authenticate against the database. | ||
* @param {string} [options.password=null] The password which is used to authenticate against the database. | ||
* @param {string} [options.database=null] The name of the database | ||
* @param {string} [options.dialect] The dialect of the database you are connecting to. One of mysql, postgres, sqlite and mssql. | ||
* @param {string} [options.dialectModule=null] If specified, use this dialect library. For example, if you want to use pg.js instead of pg when connecting to a pg database, you should specify 'require("pg.js")' here | ||
* @param {string} [options.dialectModulePath=null] If specified, load the dialect library from this path. For example, if you want to use pg.js instead of pg when connecting to a pg database, you should specify '/path/to/pg.js' here | ||
* @param {object} [options.dialectOptions] An object of additional options, which are passed directly to the connection library | ||
* @param {string} [options.storage] Only used by sqlite. Defaults to ':memory:' | ||
* @param {string} [options.protocol='tcp'] The protocol of the relational database. | ||
* @param {object} [options.define={}] Default options for model definitions. See {@link Model.init}. | ||
* @param {object} [options.query={}] Default options for sequelize.query | ||
* @param {string} [options.schema=null] A schema to use | ||
* @param {object} [options.set={}] Default options for sequelize.set | ||
* @param {object} [options.sync={}] Default options for sequelize.sync | ||
* @param {string} [options.timezone='+00:00'] The timezone used when converting a date from the database into a JavaScript date. The timezone is also used to SET TIMEZONE when connecting to the server, to ensure that the result of NOW, CURRENT_TIMESTAMP and other time related functions have in the right timezone. For best cross platform performance use the format +/-HH:MM. Will also accept string versions of timezones used by moment.js (e.g. 'America/Los_Angeles'); this is useful to capture daylight savings time changes. | ||
* @param {string|boolean} [options.clientMinMessages='warning'] The PostgreSQL `client_min_messages` session parameter. Set to `false` to not override the database's default. | ||
* @param {boolean} [options.standardConformingStrings=true] The PostgreSQL `standard_conforming_strings` session parameter. Set to `false` to not set the option. WARNING: Setting this to false may expose vulnerabilities and is not recommended! | ||
* @param {Function} [options.logging=console.log] A function that gets executed every time Sequelize would log something. Function may receive multiple parameters but only first one is printed by `console.log`. To print all values use `(...msg) => console.log(msg)` | ||
* @param {boolean} [options.benchmark=false] Pass query execution time in milliseconds as second argument to logging function (options.logging). | ||
* @param {boolean} [options.omitNull=false] A flag that defines if null values should be passed as values to CREATE/UPDATE SQL queries or not. | ||
* @param {boolean} [options.native=false] A flag that defines if native library shall be used or not. Currently only has an effect for postgres | ||
* @param {boolean} [options.replication=false] Use read / write replication. To enable replication, pass an object, with two properties, read and write. Write should be an object (a single server for handling writes), and read an array of object (several servers to handle reads). Each read/write server can have the following properties: `host`, `port`, `username`, `password`, `database` | ||
* @param {object} [options.pool] sequelize connection pool configuration | ||
* @param {number} [options.pool.max=5] Maximum number of connection in pool | ||
* @param {number} [options.pool.min=0] Minimum number of connection in pool | ||
* @param {number} [options.pool.idle=10000] The maximum time, in milliseconds, that a connection can be idle before being released. | ||
* @param {number} [options.pool.acquire=60000] The maximum time, in milliseconds, that pool will try to get connection before throwing error | ||
* @param {number} [options.pool.evict=1000] The time interval, in milliseconds, after which sequelize-pool will remove idle connections. | ||
* @param {Function} [options.pool.validate] A function that validates a connection. Called with client. The default function checks that client is an object, and that its state is not disconnected | ||
* @param {number} [options.pool.maxUses=Infinity] The number of times a connection can be used before discarding it for a replacement, [`used for eventual cluster rebalancing`](https://github.com/sequelize/sequelize-pool). | ||
* @param {boolean} [options.quoteIdentifiers=true] Set to `false` to make table names and attributes case-insensitive on Postgres and skip double quoting of them. WARNING: Setting this to false may expose vulnerabilities and is not recommended! | ||
* @param {string} [options.transactionType='DEFERRED'] Set the default transaction type. See `Sequelize.Transaction.TYPES` for possible options. Sqlite only. | ||
* @param {string} [options.isolationLevel] Set the default transaction isolation level. See `Sequelize.Transaction.ISOLATION_LEVELS` for possible options. | ||
* @param {object} [options.retry] Set of flags that control when a query is automatically retried. Accepts all options for [`retry-as-promised`](https://github.com/mickhansen/retry-as-promised). | ||
* @param {Array} [options.retry.match] Only retry a query if the error matches one of these strings. | ||
* @param {number} [options.retry.max] How many times a failing query is automatically retried. Set to 0 to disable retrying on SQL_BUSY error. | ||
* @param {boolean} [options.typeValidation=false] Run built-in type validators on insert and update, and select with where clause, e.g. validate that arguments passed to integer fields are integer-like. | ||
* @param {object} [options.operatorsAliases] String based operator alias. Pass object to limit set of aliased operators. | ||
* @param {object} [options.hooks] An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See `Sequelize.Model.init()` for a list). Additionally, `beforeConnect()`, `afterConnect()`, `beforeDisconnect()`, and `afterDisconnect()` hooks may be defined here. | ||
* @param {boolean} [options.minifyAliases=false] A flag that defines if aliases should be minified (mostly useful to avoid Postgres alias character limit of 64) | ||
* @param {boolean} [options.logQueryParameters=false] A flag that defines if show bind parameters in log. | ||
*/ | ||
constructor(database, username, password, options) { | ||
let config; | ||
if (arguments.length === 1 && typeof database === 'object') { | ||
// new Sequelize({ ... options }) | ||
if (arguments.length === 1 && typeof database === "object") { | ||
options = database; | ||
config = _.pick(options, 'host', 'port', 'database', 'username', 'password'); | ||
} else if (arguments.length === 1 && typeof database === 'string' || arguments.length === 2 && typeof username === 'object') { | ||
// new Sequelize(URI, { ... options }) | ||
config = _.pick(options, "host", "port", "database", "username", "password"); | ||
} else if (arguments.length === 1 && typeof database === "string" || arguments.length === 2 && typeof username === "object") { | ||
config = {}; | ||
options = username || {}; | ||
const urlParts = url.parse(arguments[0], true); | ||
options.dialect = urlParts.protocol.replace(/:$/, ''); | ||
options.dialect = urlParts.protocol.replace(/:$/, ""); | ||
options.host = urlParts.hostname; | ||
if (options.dialect === 'sqlite' && urlParts.pathname && !urlParts.pathname.startsWith('/:memory')) { | ||
if (options.dialect === "sqlite" && urlParts.pathname && !urlParts.pathname.startsWith("/:memory")) { | ||
const storagePath = path.join(options.host, urlParts.pathname); | ||
options.storage = path.resolve(options.storage || storagePath); | ||
} | ||
if (urlParts.pathname) { | ||
config.database = urlParts.pathname.replace(/^\//, ''); | ||
config.database = urlParts.pathname.replace(/^\//, ""); | ||
} | ||
if (urlParts.port) { | ||
options.port = urlParts.port; | ||
} | ||
if (urlParts.auth) { | ||
const authParts = urlParts.auth.split(':'); | ||
const authParts = urlParts.auth.split(":"); | ||
config.username = authParts[0]; | ||
if (authParts.length > 1) | ||
config.password = authParts.slice(1).join(':'); | ||
config.password = authParts.slice(1).join(":"); | ||
} | ||
if (urlParts.query) { | ||
// Allow host query argument to override the url host. | ||
// Enables specifying domain socket hosts which cannot be specified via the typical | ||
// host part of a url. | ||
if (urlParts.query.host) { | ||
options.host = urlParts.query.host; | ||
} | ||
if (options.dialectOptions) { | ||
@@ -229,4 +89,2 @@ Object.assign(options.dialectOptions, urlParts.query); | ||
} catch (e) { | ||
// Nothing to do, string is not a valid JSON | ||
// an thus does not need any further processing | ||
} | ||
@@ -236,23 +94,21 @@ } | ||
} | ||
if (["postgres", "postgresql"].includes(options.dialect)) { | ||
Object.assign(options.dialectOptions, pgConnectionString.parse(arguments[0])); | ||
} | ||
} else { | ||
// new Sequelize(database, username, password, { ... options }) | ||
options = options || {}; | ||
config = { database, username, password }; | ||
} | ||
Sequelize.runHooks('beforeInit', config, options); | ||
this.options = { | ||
Sequelize.runHooks("beforeInit", config, options); | ||
this.options = __spreadValues({ | ||
dialect: null, | ||
dialectModule: null, | ||
dialectModulePath: null, | ||
host: 'localhost', | ||
protocol: 'tcp', | ||
host: "localhost", | ||
protocol: "tcp", | ||
define: {}, | ||
query: {}, | ||
sync: {}, | ||
timezone: '+00:00', | ||
clientMinMessages: 'warning', | ||
timezone: "+00:00", | ||
standardConformingStrings: true, | ||
// eslint-disable-next-line no-console | ||
logging: console.log, | ||
@@ -262,3 +118,3 @@ omitNull: false, | ||
replication: false, | ||
ssl: undefined, | ||
ssl: void 0, | ||
pool: {}, | ||
@@ -270,3 +126,3 @@ quoteIdentifiers: true, | ||
match: [ | ||
'SQLITE_BUSY: database is locked' | ||
"SQLITE_BUSY: database is locked" | ||
] | ||
@@ -280,26 +136,18 @@ }, | ||
minifyAliases: false, | ||
logQueryParameters: false, | ||
...options | ||
}; | ||
logQueryParameters: false | ||
}, options); | ||
if (!this.options.dialect) { | ||
throw new Error('Dialect needs to be explicitly supplied as of v4.0.0'); | ||
throw new Error("Dialect needs to be explicitly supplied as of v4.0.0"); | ||
} | ||
if (this.options.dialect === 'postgresql') { | ||
this.options.dialect = 'postgres'; | ||
if (this.options.dialect === "postgresql") { | ||
this.options.dialect = "postgres"; | ||
} | ||
if (this.options.dialect === 'sqlite' && this.options.timezone !== '+00:00') { | ||
throw new Error('Setting a custom timezone is not supported by SQLite, dates are always returned as UTC. Please remove the custom timezone parameter.'); | ||
if (this.options.dialect === "sqlite" && this.options.timezone !== "+00:00") { | ||
throw new Error("Setting a custom timezone is not supported by SQLite, dates are always returned as UTC. Please remove the custom timezone parameter."); | ||
} | ||
if (this.options.logging === true) { | ||
deprecations.noTrueLogging(); | ||
// eslint-disable-next-line no-console | ||
this.options.logging = console.log; | ||
} | ||
this._setupHooks(options.hooks); | ||
this.config = { | ||
@@ -321,139 +169,62 @@ database: config.database || this.options.database, | ||
}; | ||
let Dialect; | ||
// Requiring the dialect in a switch-case to keep the | ||
// require calls static. (Browserify fix) | ||
switch (this.getDialect()) { | ||
case 'mariadb': | ||
Dialect = require('./dialects/mariadb'); | ||
case "mariadb": | ||
Dialect = require("./dialects/mariadb"); | ||
break; | ||
case 'mssql': | ||
Dialect = require('./dialects/mssql'); | ||
case "mssql": | ||
Dialect = require("./dialects/mssql"); | ||
break; | ||
case 'mysql': | ||
Dialect = require('./dialects/mysql'); | ||
case "mysql": | ||
Dialect = require("./dialects/mysql"); | ||
break; | ||
case 'postgres': | ||
Dialect = require('./dialects/postgres'); | ||
case "postgres": | ||
Dialect = require("./dialects/postgres"); | ||
break; | ||
case 'sqlite': | ||
Dialect = require('./dialects/sqlite'); | ||
case "sqlite": | ||
Dialect = require("./dialects/sqlite"); | ||
break; | ||
case "db2": | ||
Dialect = require("./dialects/db2"); | ||
break; | ||
case "snowflake": | ||
Dialect = require("./dialects/snowflake"); | ||
break; | ||
default: | ||
throw new Error(`The dialect ${this.getDialect()} is not supported. Supported dialects: mssql, mariadb, mysql, postgres, and sqlite.`); | ||
throw new Error(`The dialect ${this.getDialect()} is not supported. Supported dialects: mssql, mariadb, mysql, postgres, db2 and sqlite.`); | ||
} | ||
this.dialect = new Dialect(this); | ||
this.dialect.queryGenerator.typeValidation = options.typeValidation; | ||
if (_.isPlainObject(this.options.operatorsAliases)) { | ||
deprecations.noStringOperators(); | ||
this.dialect.queryGenerator.setOperatorsAliases(this.options.operatorsAliases); | ||
} else if (typeof this.options.operatorsAliases === 'boolean') { | ||
} else if (typeof this.options.operatorsAliases === "boolean") { | ||
deprecations.noBoolOperatorAliases(); | ||
} | ||
this.queryInterface = this.dialect.queryInterface; | ||
/** | ||
* Models are stored here under the name given to `sequelize.define` | ||
*/ | ||
this.models = {}; | ||
this.modelManager = new ModelManager(this); | ||
this.connectionManager = this.dialect.connectionManager; | ||
Sequelize.runHooks('afterInit', this); | ||
Sequelize.runHooks("afterInit", this); | ||
} | ||
/** | ||
* Refresh data types and parsers. | ||
* | ||
* @private | ||
*/ | ||
refreshTypes() { | ||
this.connectionManager.refreshTypeParser(DataTypes); | ||
} | ||
/** | ||
* Returns the specified dialect. | ||
* | ||
* @returns {string} The specified dialect. | ||
*/ | ||
getDialect() { | ||
return this.options.dialect; | ||
} | ||
/** | ||
* Returns the database name. | ||
* | ||
* @returns {string} The database name. | ||
*/ | ||
getDatabaseName() { | ||
return this.config.database; | ||
} | ||
/** | ||
* Returns an instance of QueryInterface. | ||
* | ||
* @returns {QueryInterface} An instance (singleton) of QueryInterface. | ||
*/ | ||
getQueryInterface() { | ||
return this.queryInterface; | ||
} | ||
/** | ||
* Define a new model, representing a table in the database. | ||
* | ||
* The table columns are defined by the object that is given as the second argument. Each key of the object represents a column | ||
* | ||
* @param {string} modelName The name of the model. The model will be stored in `sequelize.models` under this name | ||
* @param {object} attributes An object, where each attribute is a column of the table. See {@link Model.init} | ||
* @param {object} [options] These options are merged with the default define options provided to the Sequelize constructor and passed to Model.init() | ||
* | ||
* @see | ||
* {@link Model.init} for a more comprehensive specification of the `options` and `attributes` objects. | ||
* @see | ||
* <a href="/master/manual/model-basics.html">Model Basics</a> guide | ||
* | ||
* @returns {Model} Newly defined model | ||
* | ||
* @example | ||
* sequelize.define('modelName', { | ||
* columnA: { | ||
* type: Sequelize.BOOLEAN, | ||
* validate: { | ||
* is: ["[a-z]",'i'], // will only allow letters | ||
* max: 23, // only allow values <= 23 | ||
* isIn: { | ||
* args: [['en', 'zh']], | ||
* msg: "Must be English or Chinese" | ||
* } | ||
* }, | ||
* field: 'column_a' | ||
* }, | ||
* columnB: Sequelize.STRING, | ||
* columnC: 'MY VERY OWN COLUMN TYPE' | ||
* }); | ||
* | ||
* sequelize.models.modelName // The model will now be available in models under the name given to define | ||
*/ | ||
define(modelName, attributes, options = {}) { | ||
options.modelName = modelName; | ||
options.sequelize = this; | ||
const model = class extends Model {}; | ||
const model = class extends Model { | ||
}; | ||
model.init(attributes, options); | ||
return model; | ||
} | ||
/** | ||
* Fetch a Model which is already defined | ||
* | ||
* @param {string} modelName The name of a model defined with Sequelize.define | ||
* | ||
* @throws Will throw an error if the model is not defined (that is, if sequelize#isDefined returns false) | ||
* @returns {Model} Specified model | ||
*/ | ||
model(modelName) { | ||
@@ -463,78 +234,22 @@ if (!this.isDefined(modelName)) { | ||
} | ||
return this.modelManager.getModel(modelName); | ||
} | ||
/** | ||
* Checks whether a model with the given name is defined | ||
* | ||
* @param {string} modelName The name of a model defined with Sequelize.define | ||
* | ||
* @returns {boolean} Returns true if model is already defined, otherwise false | ||
*/ | ||
isDefined(modelName) { | ||
return !!this.modelManager.models.find(model => model.name === modelName); | ||
return !!this.modelManager.models.find((model) => model.name === modelName); | ||
} | ||
/** | ||
* Execute a query on the DB, optionally bypassing all the Sequelize goodness. | ||
* | ||
* By default, the function will return two arguments: an array of results, and a metadata object, containing number of affected rows etc. | ||
* | ||
* If you are running a type of query where you don't need the metadata, for example a `SELECT` query, you can pass in a query type to make sequelize format the results: | ||
* | ||
* ```js | ||
* const [results, metadata] = await sequelize.query('SELECT...'); // Raw query - use array destructuring | ||
* | ||
* const results = await sequelize.query('SELECT...', { type: sequelize.QueryTypes.SELECT }); // SELECT query - no destructuring | ||
* ``` | ||
* | ||
* @param {string} sql | ||
* @param {object} [options={}] Query options. | ||
* @param {boolean} [options.raw] If true, sequelize will not try to format the results of the query, or build an instance of a model from the result | ||
* @param {Transaction} [options.transaction=null] The transaction that the query should be executed under | ||
* @param {QueryTypes} [options.type='RAW'] The type of query you are executing. The query type affects how results are formatted before they are passed back. The type is a string, but `Sequelize.QueryTypes` is provided as convenience shortcuts. | ||
* @param {boolean} [options.nest=false] If true, transforms objects with `.` separated property names into nested objects using [dottie.js](https://github.com/mickhansen/dottie.js). For example { 'user.username': 'john' } becomes { user: { username: 'john' }}. When `nest` is true, the query type is assumed to be `'SELECT'`, unless otherwise specified | ||
* @param {boolean} [options.plain=false] Sets the query type to `SELECT` and return a single row | ||
* @param {object|Array} [options.replacements] Either an object of named parameter replacements in the format `:param` or an array of unnamed replacements to replace `?` in your SQL. | ||
* @param {object|Array} [options.bind] Either an object of named bind parameter in the format `_param` or an array of unnamed bind parameter to replace `$1, $2, ...` in your SQL. | ||
* @param {boolean} [options.useMaster=false] Force the query to use the write pool, regardless of the query type. | ||
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. | ||
* @param {Model} [options.instance] A sequelize model instance whose Model is to be used to build the query result | ||
* @param {typeof Model} [options.model] A sequelize model used to build the returned model instances | ||
* @param {object} [options.retry] Set of flags that control when a query is automatically retried. Accepts all options for [`retry-as-promised`](https://github.com/mickhansen/retry-as-promised). | ||
* @param {Array} [options.retry.match] Only retry a query if the error matches one of these strings. | ||
* @param {Integer} [options.retry.max] How many times a failing query is automatically retried. | ||
* @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only) | ||
* @param {boolean} [options.supportsSearchPath] If false do not prepend the query with the search_path (Postgres only) | ||
* @param {boolean} [options.mapToModel=false] Map returned fields to model's fields if `options.model` or `options.instance` is present. Mapping will occur before building the model instance. | ||
* @param {object} [options.fieldMap] Map returned fields to arbitrary names for `SELECT` query type. | ||
* | ||
* @returns {Promise} | ||
* | ||
* @see {@link Model.build} for more information about instance option. | ||
*/ | ||
async query(sql, options) { | ||
options = { ...this.options.query, ...options }; | ||
options = __spreadValues(__spreadValues({}, this.options.query), options); | ||
if (options.instance && !options.model) { | ||
options.model = options.instance.constructor; | ||
} | ||
if (!options.instance && !options.model) { | ||
options.raw = true; | ||
} | ||
// map raw fields to model attributes | ||
if (options.mapToModel) { | ||
options.fieldMap = _.get(options, 'model.fieldAttributeMap', {}); | ||
options.fieldMap = _.get(options, "model.fieldAttributeMap", {}); | ||
} | ||
options = _.defaults(options, { | ||
// eslint-disable-next-line no-console | ||
logging: Object.prototype.hasOwnProperty.call(this.options, 'logging') ? this.options.logging : console.log, | ||
searchPath: Object.prototype.hasOwnProperty.call(this.options, 'searchPath') ? this.options.searchPath : 'DEFAULT' | ||
logging: Object.prototype.hasOwnProperty.call(this.options, "logging") ? this.options.logging : console.log, | ||
searchPath: Object.prototype.hasOwnProperty.call(this.options, "searchPath") ? this.options.searchPath : "DEFAULT" | ||
}); | ||
if (!options.type) { | ||
@@ -547,58 +262,35 @@ if (options.model || options.nest || options.plain) { | ||
} | ||
//if dialect doesn't support search_path or dialect option | ||
//to prepend searchPath is not true delete the searchPath option | ||
if ( | ||
!this.dialect.supports.searchPath || | ||
!this.options.dialectOptions || | ||
!this.options.dialectOptions.prependSearchPath || | ||
options.supportsSearchPath === false | ||
) { | ||
if (!this.dialect.supports.searchPath || !this.options.dialectOptions || !this.options.dialectOptions.prependSearchPath || options.supportsSearchPath === false) { | ||
delete options.searchPath; | ||
} else if (!options.searchPath) { | ||
//if user wants to always prepend searchPath (dialectOptions.preprendSearchPath = true) | ||
//then set to DEFAULT if none is provided | ||
options.searchPath = 'DEFAULT'; | ||
options.searchPath = "DEFAULT"; | ||
} | ||
if (typeof sql === 'object') { | ||
if (sql.values !== undefined) { | ||
if (options.replacements !== undefined) { | ||
throw new Error('Both `sql.values` and `options.replacements` cannot be set at the same time'); | ||
if (typeof sql === "object") { | ||
if (sql.values !== void 0) { | ||
if (options.replacements !== void 0) { | ||
throw new Error("Both `sql.values` and `options.replacements` cannot be set at the same time"); | ||
} | ||
options.replacements = sql.values; | ||
} | ||
if (sql.bind !== undefined) { | ||
if (options.bind !== undefined) { | ||
throw new Error('Both `sql.bind` and `options.bind` cannot be set at the same time'); | ||
if (sql.bind !== void 0) { | ||
if (options.bind !== void 0) { | ||
throw new Error("Both `sql.bind` and `options.bind` cannot be set at the same time"); | ||
} | ||
options.bind = sql.bind; | ||
} | ||
if (sql.query !== undefined) { | ||
if (sql.query !== void 0) { | ||
sql = sql.query; | ||
} | ||
} | ||
sql = sql.trim(); | ||
if (options.replacements && options.bind) { | ||
throw new Error('Both `replacements` and `bind` cannot be set at the same time'); | ||
throw new Error("Both `replacements` and `bind` cannot be set at the same time"); | ||
} | ||
if (options.replacements) { | ||
if (Array.isArray(options.replacements)) { | ||
sql = Utils.format([sql].concat(options.replacements), this.options.dialect); | ||
} else { | ||
sql = Utils.formatNamedParameters(sql, options.replacements, this.options.dialect); | ||
} | ||
sql = injectReplacements(sql, this.dialect, options.replacements); | ||
} | ||
let bindParameters; | ||
if (options.bind) { | ||
[sql, bindParameters] = this.dialect.Query.formatBindParameters(sql, options.bind, this.options.dialect); | ||
} | ||
const checkTransaction = () => { | ||
@@ -611,23 +303,23 @@ if (options.transaction && options.transaction.finished && !options.completesTransaction) { | ||
}; | ||
const retryOptions = { ...this.options.retry, ...options.retry }; | ||
const retryOptions = __spreadValues(__spreadValues({}, this.options.retry), options.retry); | ||
return retry(async () => { | ||
if (options.transaction === undefined && Sequelize._cls) { | ||
options.transaction = Sequelize._cls.get('transaction'); | ||
if (options.transaction === void 0 && Sequelize._cls) { | ||
options.transaction = Sequelize._cls.get("transaction"); | ||
} | ||
checkTransaction(); | ||
const connection = await (options.transaction ? options.transaction.connection : this.connectionManager.getConnection(options)); | ||
if (this.options.dialect === "db2" && options.alter) { | ||
if (options.alter.drop === false) { | ||
connection.dropTable = false; | ||
} | ||
} | ||
const query = new this.dialect.Query(connection, this, options); | ||
try { | ||
await this.runHooks('beforeQuery', options, query); | ||
await this.runHooks("beforeQuery", options, query); | ||
checkTransaction(); | ||
return await query.run(sql, bindParameters); | ||
} finally { | ||
await this.runHooks('afterQuery', options, query); | ||
await this.runHooks("afterQuery", options, query); | ||
if (!options.transaction) { | ||
await this.connectionManager.releaseConnection(connection); | ||
this.connectionManager.releaseConnection(connection); | ||
} | ||
@@ -637,139 +329,35 @@ } | ||
} | ||
/** | ||
* Execute a query which would set an environment or user variable. The variables are set per connection, so this function needs a transaction. | ||
* Only works for MySQL. | ||
* | ||
* @param {object} variables Object with multiple variables. | ||
* @param {object} [options] query options. | ||
* @param {Transaction} [options.transaction] The transaction that the query should be executed under | ||
* | ||
* @memberof Sequelize | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async set(variables, options) { | ||
// Prepare options | ||
options = { ...this.options.set, ...typeof options === 'object' && options }; | ||
if (this.options.dialect !== 'mysql') { | ||
throw new Error('sequelize.set is only supported for mysql'); | ||
options = __spreadValues(__spreadValues({}, this.options.set), typeof options === "object" && options); | ||
if (!["mysql", "mariadb"].includes(this.options.dialect)) { | ||
throw new Error("sequelize.set is only supported for mysql or mariadb"); | ||
} | ||
if (!options.transaction || !(options.transaction instanceof Transaction) ) { | ||
throw new TypeError('options.transaction is required'); | ||
if (!options.transaction || !(options.transaction instanceof Transaction)) { | ||
throw new TypeError("options.transaction is required"); | ||
} | ||
// Override some options, since this isn't a SELECT | ||
options.raw = true; | ||
options.plain = true; | ||
options.type = 'SET'; | ||
// Generate SQL Query | ||
const query = | ||
`SET ${ | ||
_.map(variables, (v, k) => `@${k} := ${typeof v === 'string' ? `"${v}"` : v}`).join(', ')}`; | ||
options.type = "SET"; | ||
const query = `SET ${_.map(variables, (v, k) => `@${k} := ${typeof v === "string" ? `"${v}"` : v}`).join(", ")}`; | ||
return await this.query(query, options); | ||
} | ||
/** | ||
* Escape value. | ||
* | ||
* @param {string} value string value to escape | ||
* | ||
* @returns {string} | ||
*/ | ||
escape(value) { | ||
return this.dialect.queryGenerator.escape(value); | ||
} | ||
/** | ||
* Create a new database schema. | ||
* | ||
* **Note:** this is a schema in the [postgres sense of the word](http://www.postgresql.org/docs/9.1/static/ddl-schemas.html), | ||
* not a database table. In mysql and sqlite, this command will do nothing. | ||
* | ||
* @see | ||
* {@link Model.schema} | ||
* | ||
* @param {string} schema Name of the schema | ||
* @param {object} [options={}] query options | ||
* @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async createSchema(schema, options) { | ||
return await this.getQueryInterface().createSchema(schema, options); | ||
} | ||
/** | ||
* Show all defined schemas | ||
* | ||
* **Note:** this is a schema in the [postgres sense of the word](http://www.postgresql.org/docs/9.1/static/ddl-schemas.html), | ||
* not a database table. In mysql and sqlite, this will show all tables. | ||
* | ||
* @param {object} [options={}] query options | ||
* @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async showAllSchemas(options) { | ||
return await this.getQueryInterface().showAllSchemas(options); | ||
} | ||
/** | ||
* Drop a single schema | ||
* | ||
* **Note:** this is a schema in the [postgres sense of the word](http://www.postgresql.org/docs/9.1/static/ddl-schemas.html), | ||
* not a database table. In mysql and sqlite, this drop a table matching the schema name | ||
* | ||
* @param {string} schema Name of the schema | ||
* @param {object} [options={}] query options | ||
* @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropSchema(schema, options) { | ||
return await this.getQueryInterface().dropSchema(schema, options); | ||
} | ||
/** | ||
* Drop all schemas. | ||
* | ||
* **Note:** this is a schema in the [postgres sense of the word](http://www.postgresql.org/docs/9.1/static/ddl-schemas.html), | ||
* not a database table. In mysql and sqlite, this is the equivalent of drop all tables. | ||
* | ||
* @param {object} [options={}] query options | ||
* @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async dropAllSchemas(options) { | ||
return await this.getQueryInterface().dropAllSchemas(options); | ||
} | ||
/** | ||
* Sync all defined models to the DB. | ||
* | ||
* @param {object} [options={}] sync options | ||
* @param {boolean} [options.force=false] If force is true, each Model will run `DROP TABLE IF EXISTS`, before it tries to create its own table | ||
* @param {RegExp} [options.match] Match a regex against the database name before syncing, a safety check for cases where force: true is used in tests but not live code | ||
* @param {boolean|Function} [options.logging=console.log] A function that logs sql queries, or false for no logging | ||
* @param {string} [options.schema='public'] The schema that the tables should be created in. This can be overridden for each table in sequelize.define | ||
* @param {string} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only) | ||
* @param {boolean} [options.hooks=true] If hooks is true then beforeSync, afterSync, beforeBulkSync, afterBulkSync hooks will be called | ||
* @param {boolean|object} [options.alter=false] Alters tables to fit models. Provide an object for additional configuration. Not recommended for production use. If not further configured deletes data in columns that were removed or had their type changed in the model. | ||
* @param {boolean} [options.alter.drop=true] Prevents any drop statements while altering a table when set to `false` | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async sync(options) { | ||
options = { | ||
...this.options, | ||
...this.options.sync, | ||
...options, | ||
options = __spreadProps(__spreadValues(__spreadValues(__spreadValues({}, this.options), this.options.sync), options), { | ||
hooks: options ? options.hooks !== false : true | ||
}; | ||
}); | ||
if (options.match) { | ||
@@ -780,5 +368,4 @@ if (!options.match.test(this.config.database)) { | ||
} | ||
if (options.hooks) { | ||
await this.runHooks('beforeBulkSync', options); | ||
await this.runHooks("beforeBulkSync", options); | ||
} | ||
@@ -788,306 +375,133 @@ if (options.force) { | ||
} | ||
const models = []; | ||
// Topologically sort by foreign key constraints to give us an appropriate | ||
// creation order | ||
this.modelManager.forEachModel(model => { | ||
if (model) { | ||
models.push(model); | ||
} else { | ||
// DB should throw an SQL error if referencing non-existent table | ||
} | ||
}); | ||
// no models defined, just authenticate | ||
if (!models.length) { | ||
if (this.modelManager.models.length === 0) { | ||
await this.authenticate(options); | ||
} else { | ||
for (const model of models) await model.sync(options); | ||
const models = this.modelManager.getModelsTopoSortedByForeignKey(); | ||
if (models == null) { | ||
return this._syncModelsWithCyclicReferences(options); | ||
} | ||
models.reverse(); | ||
for (const model of models) { | ||
await model.sync(options); | ||
} | ||
} | ||
if (options.hooks) { | ||
await this.runHooks('afterBulkSync', options); | ||
await this.runHooks("afterBulkSync", options); | ||
} | ||
return this; | ||
} | ||
/** | ||
* Truncate all tables defined through the sequelize models. | ||
* This is done by calling `Model.truncate()` on each model. | ||
* | ||
* @param {object} [options] The options passed to Model.destroy in addition to truncate | ||
* @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging | ||
* @returns {Promise} | ||
* | ||
* @see | ||
* {@link Model.truncate} for more information | ||
*/ | ||
async _syncModelsWithCyclicReferences(options) { | ||
if (this.dialect.name === "sqlite") { | ||
await withSqliteForeignKeysOff(this, options, async () => { | ||
for (const model of this.modelManager.models) { | ||
await model.sync(options); | ||
} | ||
}); | ||
return; | ||
} | ||
for (const model of this.modelManager.models) { | ||
await model.sync(__spreadProps(__spreadValues({}, options), { withoutForeignKeyConstraints: true })); | ||
} | ||
for (const model of this.modelManager.models) { | ||
await model.sync(__spreadProps(__spreadValues({}, options), { force: false, alter: true })); | ||
} | ||
} | ||
async truncate(options) { | ||
const models = []; | ||
this.modelManager.forEachModel(model => { | ||
if (model) { | ||
models.push(model); | ||
} | ||
}, { reverse: false }); | ||
const sortedModels = this.modelManager.getModelsTopoSortedByForeignKey(); | ||
const models = sortedModels || this.modelManager.models; | ||
const hasCyclicDependencies = sortedModels == null; | ||
if (hasCyclicDependencies && (!options || !options.cascade)) { | ||
throw new Error('Sequelize#truncate: Some of your models have cyclic references (foreign keys). You need to use the "cascade" option to be able to delete rows from models that have cyclic references.'); | ||
} | ||
if (hasCyclicDependencies && this.dialect.name === "sqlite") { | ||
return withSqliteForeignKeysOff(this, options, async () => { | ||
await Promise.all(models.map((model) => model.truncate(options))); | ||
}); | ||
} | ||
if (options && options.cascade) { | ||
for (const model of models) await model.truncate(options); | ||
for (const model of models) | ||
await model.truncate(options); | ||
} else { | ||
await Promise.all(models.map(model => model.truncate(options))); | ||
await Promise.all(models.map((model) => model.truncate(options))); | ||
} | ||
} | ||
/** | ||
* Drop all tables defined through this sequelize instance. | ||
* This is done by calling Model.drop on each model. | ||
* | ||
* @see | ||
* {@link Model.drop} for options | ||
* | ||
* @param {object} [options] The options passed to each call to Model.drop | ||
* @param {boolean|Function} [options.logging] A function that logs sql queries, or false for no logging | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async drop(options) { | ||
const models = []; | ||
this.modelManager.forEachModel(model => { | ||
if (model) { | ||
models.push(model); | ||
if (options && options.cascade) { | ||
for (const model of this.modelManager.models) { | ||
await model.drop(options); | ||
} | ||
}, { reverse: false }); | ||
for (const model of models) await model.drop(options); | ||
} | ||
const sortedModels = this.modelManager.getModelsTopoSortedByForeignKey(); | ||
if (sortedModels) { | ||
for (const model of sortedModels) { | ||
await model.drop(options); | ||
} | ||
} | ||
if (this.dialect.name === "sqlite") { | ||
await withSqliteForeignKeysOff(this, options, async () => { | ||
for (const model of this.modelManager.models) { | ||
await model.drop(options); | ||
} | ||
}); | ||
return; | ||
} | ||
for (const model of this.modelManager.models) { | ||
const tableName = model.getTableName(); | ||
const foreignKeys = await this.queryInterface.getForeignKeyReferencesForTable(tableName, options); | ||
await Promise.all(foreignKeys.map((foreignKey) => { | ||
return this.queryInterface.removeConstraint(tableName, foreignKey.constraintName, options); | ||
})); | ||
} | ||
for (const model of this.modelManager.models) { | ||
await model.drop(options); | ||
} | ||
} | ||
/** | ||
* Test the connection by trying to authenticate. It runs `SELECT 1+1 AS result` query. | ||
* | ||
* @param {object} [options={}] query options | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async authenticate(options) { | ||
options = { | ||
options = __spreadValues({ | ||
raw: true, | ||
plain: true, | ||
type: QueryTypes.SELECT, | ||
...options | ||
}; | ||
await this.query('SELECT 1+1 AS result', options); | ||
type: QueryTypes.SELECT | ||
}, options); | ||
await this.query("SELECT 1+1 AS result", options); | ||
return; | ||
} | ||
async databaseVersion(options) { | ||
return await this.getQueryInterface().databaseVersion(options); | ||
} | ||
/** | ||
* Get the fn for random based on the dialect | ||
* | ||
* @returns {Sequelize.fn} | ||
*/ | ||
random() { | ||
const dia = this.getDialect(); | ||
if (dia === 'postgres' || dia === 'sqlite') { | ||
return this.fn('RANDOM'); | ||
if (["postgres", "sqlite", "snowflake"].includes(this.getDialect())) { | ||
return this.fn("RANDOM"); | ||
} | ||
return this.fn('RAND'); | ||
return this.fn("RAND"); | ||
} | ||
/** | ||
* Creates an object representing a database function. This can be used in search queries, both in where and order parts, and as default values in column definitions. | ||
* If you want to refer to columns in your function, you should use `sequelize.col`, so that the columns are properly interpreted as columns and not a strings. | ||
* | ||
* @see | ||
* {@link Model.findAll} | ||
* @see | ||
* {@link Sequelize.define} | ||
* @see | ||
* {@link Sequelize.col} | ||
* | ||
* @param {string} fn The function you want to call | ||
* @param {any} args All further arguments will be passed as arguments to the function | ||
* | ||
* @since v2.0.0-dev3 | ||
* @memberof Sequelize | ||
* @returns {Sequelize.fn} | ||
* | ||
* @example <caption>Convert a user's username to upper case</caption> | ||
* instance.update({ | ||
* username: sequelize.fn('upper', sequelize.col('username')) | ||
* }); | ||
*/ | ||
static fn(fn, ...args) { | ||
return new Utils.Fn(fn, args); | ||
} | ||
/** | ||
* Creates an object which represents a column in the DB, this allows referencing another column in your query. This is often useful in conjunction with `sequelize.fn`, since raw string arguments to fn will be escaped. | ||
* | ||
* @see | ||
* {@link Sequelize#fn} | ||
* | ||
* @param {string} col The name of the column | ||
* @since v2.0.0-dev3 | ||
* @memberof Sequelize | ||
* | ||
* @returns {Sequelize.col} | ||
*/ | ||
static col(col) { | ||
return new Utils.Col(col); | ||
} | ||
/** | ||
* Creates an object representing a call to the cast function. | ||
* | ||
* @param {any} val The value to cast | ||
* @param {string} type The type to cast it to | ||
* @since v2.0.0-dev3 | ||
* @memberof Sequelize | ||
* | ||
* @returns {Sequelize.cast} | ||
*/ | ||
static cast(val, type) { | ||
return new Utils.Cast(val, type); | ||
} | ||
/** | ||
* Creates an object representing a literal, i.e. something that will not be escaped. | ||
* | ||
* @param {any} val literal value | ||
* @since v2.0.0-dev3 | ||
* @memberof Sequelize | ||
* | ||
* @returns {Sequelize.literal} | ||
*/ | ||
static literal(val) { | ||
return new Utils.Literal(val); | ||
} | ||
/** | ||
* An AND query | ||
* | ||
* @see | ||
* {@link Model.findAll} | ||
* | ||
* @param {...string|object} args Each argument will be joined by AND | ||
* @since v2.0.0-dev3 | ||
* @memberof Sequelize | ||
* | ||
* @returns {Sequelize.and} | ||
*/ | ||
static and(...args) { | ||
return { [Op.and]: args }; | ||
} | ||
/** | ||
* An OR query | ||
* | ||
* @see | ||
* {@link Model.findAll} | ||
* | ||
* @param {...string|object} args Each argument will be joined by OR | ||
* @since v2.0.0-dev3 | ||
* @memberof Sequelize | ||
* | ||
* @returns {Sequelize.or} | ||
*/ | ||
static or(...args) { | ||
return { [Op.or]: args }; | ||
} | ||
/** | ||
* Creates an object representing nested where conditions for postgres/sqlite/mysql json data-type. | ||
* | ||
* @see | ||
* {@link Model.findAll} | ||
* | ||
* @param {string|object} conditionsOrPath A hash containing strings/numbers or other nested hash, a string using dot notation or a string using postgres/sqlite/mysql json syntax. | ||
* @param {string|number|boolean} [value] An optional value to compare against. Produces a string of the form "<json path> = '<value>'". | ||
* @memberof Sequelize | ||
* | ||
* @returns {Sequelize.json} | ||
*/ | ||
static json(conditionsOrPath, value) { | ||
return new Utils.Json(conditionsOrPath, value); | ||
} | ||
/** | ||
* A way of specifying attr = condition. | ||
* | ||
* The attr can either be an object taken from `Model.rawAttributes` (for example `Model.rawAttributes.id` or `Model.rawAttributes.name`). The | ||
* attribute should be defined in your model definition. The attribute can also be an object from one of the sequelize utility functions (`sequelize.fn`, `sequelize.col` etc.) | ||
* | ||
* For string attributes, use the regular `{ where: { attr: something }}` syntax. If you don't want your string to be escaped, use `sequelize.literal`. | ||
* | ||
* @see | ||
* {@link Model.findAll} | ||
* | ||
* @param {object} attr The attribute, which can be either an attribute object from `Model.rawAttributes` or a sequelize object, for example an instance of `sequelize.fn`. For simple string attributes, use the POJO syntax | ||
* @param {symbol} [comparator='Op.eq'] operator | ||
* @param {string|object} logic The condition. Can be both a simply type, or a further condition (`or`, `and`, `.literal` etc.) | ||
* @since v2.0.0-dev3 | ||
*/ | ||
static where(attr, comparator, logic) { | ||
return new Utils.Where(attr, comparator, logic); | ||
} | ||
/** | ||
* Start a transaction. When using transactions, you should pass the transaction in the options argument in order for the query to happen under that transaction @see {@link Transaction} | ||
* | ||
* If you have [CLS](https://github.com/Jeff-Lewis/cls-hooked) enabled, the transaction will automatically be passed to any query that runs within the callback | ||
* | ||
* @example | ||
* | ||
* try { | ||
* const transaction = await sequelize.transaction(); | ||
* const user = await User.findOne(..., { transaction }); | ||
* await user.update(..., { transaction }); | ||
* await transaction.commit(); | ||
* } catch { | ||
* await transaction.rollback() | ||
* } | ||
* | ||
* @example <caption>A syntax for automatically committing or rolling back based on the promise chain resolution is also supported</caption> | ||
* | ||
* try { | ||
* await sequelize.transaction(transaction => { // Note that we pass a callback rather than awaiting the call with no arguments | ||
* const user = await User.findOne(..., {transaction}); | ||
* await user.update(..., {transaction}); | ||
* }); | ||
* // Committed | ||
* } catch(err) { | ||
* // Rolled back | ||
* console.error(err); | ||
* } | ||
* @example <caption>To enable CLS, add it do your project, create a namespace and set it on the sequelize constructor:</caption> | ||
* | ||
* const cls = require('cls-hooked'); | ||
* const namespace = cls.createNamespace('....'); | ||
* const Sequelize = require('sequelize'); | ||
* Sequelize.useCLS(namespace); | ||
* | ||
* // Note, that CLS is enabled for all sequelize instances, and all instances will share the same namespace | ||
* | ||
* @param {object} [options] Transaction options | ||
* @param {string} [options.type='DEFERRED'] See `Sequelize.Transaction.TYPES` for possible options. Sqlite only. | ||
* @param {string} [options.isolationLevel] See `Sequelize.Transaction.ISOLATION_LEVELS` for possible options | ||
* @param {string} [options.deferrable] Sets the constraints to be deferred or immediately checked. See `Sequelize.Deferrable`. PostgreSQL Only | ||
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. | ||
* @param {Function} [autoCallback] The callback is called with the transaction object, and should return a promise. If the promise is resolved, the transaction commits; if the promise rejects, the transaction rolls back | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async transaction(options, autoCallback) { | ||
if (typeof options === 'function') { | ||
if (typeof options === "function") { | ||
autoCallback = options; | ||
options = undefined; | ||
options = void 0; | ||
} | ||
const transaction = new Transaction(this, options); | ||
if (!autoCallback) { | ||
@@ -1097,74 +511,37 @@ await transaction.prepareEnvironment(false); | ||
} | ||
// autoCallback provided | ||
return Sequelize._clsRun(async () => { | ||
await transaction.prepareEnvironment(true); | ||
let result; | ||
try { | ||
await transaction.prepareEnvironment(); | ||
const result = await autoCallback(transaction); | ||
await transaction.commit(); | ||
return await result; | ||
result = await autoCallback(transaction); | ||
} catch (err) { | ||
try { | ||
if (!transaction.finished) { | ||
await transaction.rollback(); | ||
} else { | ||
// release the connection, even if we don't need to rollback | ||
await transaction.cleanup(); | ||
} | ||
} catch (err0) { | ||
// ignore | ||
await transaction.rollback(); | ||
} catch (ignore) { | ||
} | ||
throw err; | ||
} | ||
await transaction.commit(); | ||
return result; | ||
}); | ||
} | ||
/** | ||
* Use CLS (Continuation Local Storage) with Sequelize. With Continuation | ||
* Local Storage, all queries within the transaction callback will | ||
* automatically receive the transaction object. | ||
* | ||
* CLS namespace provided is stored as `Sequelize._cls` | ||
* | ||
* @param {object} ns CLS namespace | ||
* @returns {object} Sequelize constructor | ||
*/ | ||
static useCLS(ns) { | ||
// check `ns` is valid CLS namespace | ||
if (!ns || typeof ns !== 'object' || typeof ns.bind !== 'function' || typeof ns.run !== 'function') throw new Error('Must provide CLS namespace'); | ||
// save namespace as `Sequelize._cls` | ||
this._cls = ns; | ||
// return Sequelize for chaining | ||
if (!ns || typeof ns !== "object" || typeof ns.bind !== "function" || typeof ns.run !== "function") | ||
throw new Error("Must provide CLS namespace"); | ||
Sequelize._cls = ns; | ||
return this; | ||
} | ||
/** | ||
* Run function in CLS context. | ||
* If no CLS context in use, just runs the function normally | ||
* | ||
* @private | ||
* @param {Function} fn Function to run | ||
* @returns {*} Return value of function | ||
*/ | ||
static _clsRun(fn) { | ||
const ns = Sequelize._cls; | ||
if (!ns) return fn(); | ||
if (!ns) | ||
return fn(); | ||
let res; | ||
ns.run(context => res = fn(context)); | ||
ns.run((context) => res = fn(context)); | ||
return res; | ||
} | ||
log(...args) { | ||
let options; | ||
const last = _.last(args); | ||
if (last && _.isPlainObject(last) && Object.prototype.hasOwnProperty.call(last, 'logging')) { | ||
if (last && _.isPlainObject(last) && Object.prototype.hasOwnProperty.call(last, "logging")) { | ||
options = last; | ||
// remove options from set of logged arguments if options.logging is equal to console.log | ||
// eslint-disable-next-line no-console | ||
if (options.logging === console.log) { | ||
@@ -1176,43 +553,25 @@ args.splice(args.length - 1, 1); | ||
} | ||
if (options.logging) { | ||
if (options.logging === true) { | ||
deprecations.noTrueLogging(); | ||
// eslint-disable-next-line no-console | ||
options.logging = console.log; | ||
} | ||
// second argument is sql-timings, when benchmarking option enabled | ||
// eslint-disable-next-line no-console | ||
if ((this.options.benchmark || options.benchmark) && options.logging === console.log) { | ||
args = [`${args[0]} Elapsed time: ${args[1]}ms`]; | ||
} | ||
options.logging(...args); | ||
} | ||
} | ||
/** | ||
* Close all connections used by this sequelize instance, and free all references so the instance can be garbage collected. | ||
* | ||
* Normally this is done on process exit, so you only need to call this method if you are creating multiple instances, and want | ||
* to garbage collect some of them. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
close() { | ||
return this.connectionManager.close(); | ||
} | ||
normalizeDataType(Type) { | ||
let type = typeof Type === 'function' ? new Type() : Type; | ||
let type = typeof Type === "function" ? new Type() : Type; | ||
const dialectTypes = this.dialect.DataTypes || {}; | ||
if (dialectTypes[type.key]) { | ||
type = dialectTypes[type.key].extend(type); | ||
} | ||
if (type instanceof DataTypes.ARRAY) { | ||
if (!type.type) { | ||
throw new Error('ARRAY is missing type definition for its values.'); | ||
throw new Error("ARRAY is missing type definition for its values."); | ||
} | ||
@@ -1223,6 +582,4 @@ if (dialectTypes[type.type.key]) { | ||
} | ||
return type; | ||
} | ||
normalizeAttribute(attribute) { | ||
@@ -1232,19 +589,11 @@ if (!_.isPlainObject(attribute)) { | ||
} | ||
if (!attribute.type) return attribute; | ||
if (!attribute.type) | ||
return attribute; | ||
attribute.type = this.normalizeDataType(attribute.type); | ||
if (Object.prototype.hasOwnProperty.call(attribute, 'defaultValue')) { | ||
if (typeof attribute.defaultValue === 'function' && ( | ||
attribute.defaultValue === DataTypes.NOW || | ||
attribute.defaultValue === DataTypes.UUIDV1 || | ||
attribute.defaultValue === DataTypes.UUIDV4 | ||
)) { | ||
if (Object.prototype.hasOwnProperty.call(attribute, "defaultValue")) { | ||
if (typeof attribute.defaultValue === "function" && [DataTypes.NOW, DataTypes.UUIDV1, DataTypes.UUIDV4].includes(attribute.defaultValue)) { | ||
attribute.defaultValue = new attribute.defaultValue(); | ||
} | ||
} | ||
if (attribute.type instanceof DataTypes.ENUM) { | ||
// The ENUM is a special case where the type is an object containing the values | ||
if (attribute.values) { | ||
@@ -1255,13 +604,9 @@ attribute.type.values = attribute.type.options.values = attribute.values; | ||
} | ||
if (!attribute.values.length) { | ||
throw new Error('Values for ENUM have not been defined.'); | ||
throw new Error("Values for ENUM have not been defined."); | ||
} | ||
} | ||
return attribute; | ||
} | ||
} | ||
// Aliases | ||
Sequelize.prototype.fn = Sequelize.fn; | ||
@@ -1276,67 +621,23 @@ Sequelize.prototype.col = Sequelize.col; | ||
Sequelize.prototype.validate = Sequelize.prototype.authenticate; | ||
/** | ||
* Sequelize version number. | ||
*/ | ||
Sequelize.version = require('../package.json').version; | ||
Object.defineProperty(Sequelize, "version", { | ||
enumerable: true, | ||
get() { | ||
return require("../package.json").version; | ||
} | ||
}); | ||
Sequelize.options = { hooks: {} }; | ||
/** | ||
* @private | ||
*/ | ||
Sequelize.Utils = Utils; | ||
/** | ||
* Operators symbols to be used for querying data | ||
* | ||
* @see {@link Operators} | ||
*/ | ||
Sequelize.Op = Op; | ||
/** | ||
* Available table hints to be used for querying data in mssql for table hints | ||
* | ||
* @see {@link TableHints} | ||
*/ | ||
Sequelize.TableHints = TableHints; | ||
/** | ||
* Available index hints to be used for querying data in mysql for index hints | ||
* | ||
* @see {@link IndexHints} | ||
*/ | ||
Sequelize.IndexHints = IndexHints; | ||
/** | ||
* A reference to the sequelize transaction class. Use this to access isolationLevels and types when creating a transaction | ||
* | ||
* @see {@link Transaction} | ||
* @see {@link Sequelize.transaction} | ||
*/ | ||
Sequelize.Transaction = Transaction; | ||
/** | ||
* A reference to Sequelize constructor from sequelize. Useful for accessing DataTypes, Errors etc. | ||
* | ||
* @see {@link Sequelize} | ||
*/ | ||
Sequelize.prototype.Sequelize = Sequelize; | ||
/** | ||
* Available query types for use with `sequelize.query` | ||
* | ||
* @see {@link QueryTypes} | ||
*/ | ||
Sequelize.prototype.QueryTypes = Sequelize.QueryTypes = QueryTypes; | ||
/** | ||
* Exposes the validator.js object, so you can extend it with custom validation functions. The validator is exposed both on the instance, and on the constructor. | ||
* | ||
* @see https://github.com/chriso/validator.js | ||
*/ | ||
Sequelize.prototype.Validator = Sequelize.Validator = Validator; | ||
Sequelize.Model = Model; | ||
Sequelize.QueryInterface = QueryInterface; | ||
Sequelize.BelongsTo = BelongsTo; | ||
Sequelize.HasOne = HasOne; | ||
Sequelize.HasMany = HasMany; | ||
Sequelize.BelongsToMany = BelongsToMany; | ||
Sequelize.DataTypes = DataTypes; | ||
@@ -1346,45 +647,14 @@ for (const dataType in DataTypes) { | ||
} | ||
/** | ||
* A reference to the deferrable collection. Use this to access the different deferrable options. | ||
* | ||
* @see {@link Transaction.Deferrable} | ||
* @see {@link Sequelize#transaction} | ||
*/ | ||
Sequelize.Deferrable = Deferrable; | ||
/** | ||
* A reference to the sequelize association class. | ||
* | ||
* @see {@link Association} | ||
*/ | ||
Sequelize.prototype.Association = Sequelize.Association = Association; | ||
/** | ||
* Provide alternative version of `inflection` module to be used by `Utils.pluralize` etc. | ||
* | ||
* @param {object} _inflection - `inflection` module | ||
*/ | ||
Sequelize.useInflection = Utils.useInflection; | ||
/** | ||
* Allow hooks to be defined on Sequelize + on sequelize instance as universal hooks to run on all models | ||
* and on Sequelize/sequelize methods e.g. Sequelize(), Sequelize#define() | ||
*/ | ||
Hooks.applyTo(Sequelize); | ||
Hooks.applyTo(Sequelize.prototype); | ||
/** | ||
* Expose various errors available | ||
*/ | ||
// expose alias to BaseError | ||
Sequelize.Error = sequelizeErrors.BaseError; | ||
for (const error of Object.keys(sequelizeErrors)) { | ||
Sequelize[error] = sequelizeErrors[error]; | ||
} | ||
module.exports = Sequelize; | ||
module.exports.Sequelize = Sequelize; | ||
module.exports.default = Sequelize; | ||
//# sourceMappingURL=sequelize.js.map |
@@ -1,48 +0,38 @@ | ||
'use strict'; | ||
const dataTypes = require('./data-types'); | ||
const { logger } = require('./utils/logger'); | ||
function arrayToList(array, timeZone, dialect, format) { | ||
"use strict"; | ||
const dataTypes = require("./data-types"); | ||
const { logger } = require("./utils/logger"); | ||
function arrayToList(array, timeZone, dialect, format2) { | ||
return array.reduce((sql, val, i) => { | ||
if (i !== 0) { | ||
sql += ', '; | ||
sql += ", "; | ||
} | ||
if (Array.isArray(val)) { | ||
sql += `(${arrayToList(val, timeZone, dialect, format)})`; | ||
sql += `(${arrayToList(val, timeZone, dialect, format2)})`; | ||
} else { | ||
sql += escape(val, timeZone, dialect, format); | ||
sql += escape(val, timeZone, dialect, format2); | ||
} | ||
return sql; | ||
}, ''); | ||
}, ""); | ||
} | ||
exports.arrayToList = arrayToList; | ||
function escape(val, timeZone, dialect, format) { | ||
function escape(val, timeZone, dialect, format2) { | ||
let prependN = false; | ||
if (val === undefined || val === null) { | ||
return 'NULL'; | ||
if (val === void 0 || val === null) { | ||
return "NULL"; | ||
} | ||
switch (typeof val) { | ||
case 'boolean': | ||
// SQLite doesn't have true/false support. MySQL aliases true/false to 1/0 | ||
// for us. Postgres actually has a boolean type with true/false literals, | ||
// but sequelize doesn't use it yet. | ||
if (dialect === 'sqlite' || dialect === 'mssql') { | ||
case "boolean": | ||
if (["sqlite", "mssql"].includes(dialect)) { | ||
return +!!val; | ||
} | ||
return (!!val).toString(); | ||
case 'number': | ||
case "number": | ||
return val.toString(); | ||
case 'string': | ||
// In mssql, prepend N to all quoted vals which are originally a string (for | ||
// unicode compatibility) | ||
prependN = dialect === 'mssql'; | ||
case "string": | ||
prependN = dialect === "mssql"; | ||
break; | ||
} | ||
if (val instanceof Date) { | ||
val = dataTypes[dialect].DATE.prototype.stringify(val, { timezone: timeZone }); | ||
} | ||
if (Buffer.isBuffer(val)) { | ||
@@ -52,38 +42,36 @@ if (dataTypes[dialect].BLOB) { | ||
} | ||
return dataTypes.BLOB.prototype.stringify(val); | ||
} | ||
if (Array.isArray(val)) { | ||
const partialEscape = escVal => escape(escVal, timeZone, dialect, format); | ||
if (dialect === 'postgres' && !format) { | ||
const partialEscape = (escVal) => escape(escVal, timeZone, dialect, format2); | ||
if (dialect === "postgres" && !format2) { | ||
return dataTypes.ARRAY.prototype.stringify(val, { escape: partialEscape }); | ||
} | ||
return arrayToList(val, timeZone, dialect, format); | ||
return arrayToList(val, timeZone, dialect, format2); | ||
} | ||
if (!val.replace) { | ||
throw new Error(`Invalid value ${logger.inspect(val)}`); | ||
} | ||
if (dialect === 'postgres' || dialect === 'sqlite' || dialect === 'mssql') { | ||
// http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS | ||
// http://stackoverflow.com/q/603572/130598 | ||
if (["postgres", "sqlite", "mssql", "snowflake", "db2"].includes(dialect)) { | ||
val = val.replace(/'/g, "''"); | ||
if (dialect === 'postgres') { | ||
// null character is not allowed in Postgres | ||
val = val.replace(/\0/g, '\\0'); | ||
if (dialect === "postgres") { | ||
val = val.replace(/\0/g, "\\0"); | ||
} | ||
} else { | ||
// eslint-disable-next-line no-control-regex | ||
val = val.replace(/[\0\n\r\b\t\\'"\x1a]/g, s => { | ||
val = val.replace(/[\0\n\r\b\t\\'"\x1a]/g, (s) => { | ||
switch (s) { | ||
case '\0': return '\\0'; | ||
case '\n': return '\\n'; | ||
case '\r': return '\\r'; | ||
case '\b': return '\\b'; | ||
case '\t': return '\\t'; | ||
case '\x1a': return '\\Z'; | ||
default: return `\\${s}`; | ||
case "\0": | ||
return "\\0"; | ||
case "\n": | ||
return "\\n"; | ||
case "\r": | ||
return "\\r"; | ||
case "\b": | ||
return "\\b"; | ||
case " ": | ||
return "\\t"; | ||
case "": | ||
return "\\Z"; | ||
default: | ||
return `\\${s}`; | ||
} | ||
@@ -95,15 +83,11 @@ }); | ||
exports.escape = escape; | ||
function format(sql, values, timeZone, dialect) { | ||
values = [].concat(values); | ||
if (typeof sql !== 'string') { | ||
if (typeof sql !== "string") { | ||
throw new Error(`Invalid SQL string provided: ${sql}`); | ||
} | ||
return sql.replace(/\?/g, match => { | ||
return sql.replace(/\?/g, (match) => { | ||
if (!values.length) { | ||
return match; | ||
} | ||
return escape(values.shift(), timeZone, dialect, true); | ||
@@ -113,10 +97,8 @@ }); | ||
exports.format = format; | ||
function formatNamedParameters(sql, values, timeZone, dialect) { | ||
return sql.replace(/:+(?!\d)(\w+)/g, (value, key) => { | ||
if ('postgres' === dialect && '::' === value.slice(0, 2)) { | ||
if (dialect === "postgres" && value.slice(0, 2) === "::") { | ||
return value; | ||
} | ||
if (values[key] !== undefined) { | ||
if (values[key] !== void 0) { | ||
return escape(values[key], timeZone, dialect, true); | ||
@@ -128,1 +110,2 @@ } | ||
exports.formatNamedParameters = formatNamedParameters; | ||
//# sourceMappingURL=sql-string.js.map |
@@ -1,38 +0,19 @@ | ||
'use strict'; | ||
/** | ||
* An enum of table hints to be used in mssql for querying with table hints | ||
* | ||
* @property NOLOCK | ||
* @property READUNCOMMITTED | ||
* @property UPDLOCK | ||
* @property REPEATABLEREAD | ||
* @property SERIALIZABLE | ||
* @property READCOMMITTED | ||
* @property TABLOCK | ||
* @property TABLOCKX | ||
* @property PAGLOCK | ||
* @property ROWLOCK | ||
* @property NOWAIT | ||
* @property READPAST | ||
* @property XLOCK | ||
* @property SNAPSHOT | ||
* @property NOEXPAND | ||
*/ | ||
const TableHints = module.exports = { // eslint-disable-line | ||
NOLOCK: 'NOLOCK', | ||
READUNCOMMITTED: 'READUNCOMMITTED', | ||
UPDLOCK: 'UPDLOCK', | ||
REPEATABLEREAD: 'REPEATABLEREAD', | ||
SERIALIZABLE: 'SERIALIZABLE', | ||
READCOMMITTED: 'READCOMMITTED', | ||
TABLOCK: 'TABLOCK', | ||
TABLOCKX: 'TABLOCKX', | ||
PAGLOCK: 'PAGLOCK', | ||
ROWLOCK: 'ROWLOCK', | ||
NOWAIT: 'NOWAIT', | ||
READPAST: 'READPAST', | ||
XLOCK: 'XLOCK', | ||
SNAPSHOT: 'SNAPSHOT', | ||
NOEXPAND: 'NOEXPAND' | ||
"use strict"; | ||
const TableHints = module.exports = { | ||
NOLOCK: "NOLOCK", | ||
READUNCOMMITTED: "READUNCOMMITTED", | ||
UPDLOCK: "UPDLOCK", | ||
REPEATABLEREAD: "REPEATABLEREAD", | ||
SERIALIZABLE: "SERIALIZABLE", | ||
READCOMMITTED: "READCOMMITTED", | ||
TABLOCK: "TABLOCK", | ||
TABLOCKX: "TABLOCKX", | ||
PAGLOCK: "PAGLOCK", | ||
ROWLOCK: "ROWLOCK", | ||
NOWAIT: "NOWAIT", | ||
READPAST: "READPAST", | ||
XLOCK: "XLOCK", | ||
SNAPSHOT: "SNAPSHOT", | ||
NOEXPAND: "NOEXPAND" | ||
}; | ||
//# sourceMappingURL=table-hints.js.map |
@@ -1,21 +0,19 @@ | ||
'use strict'; | ||
/** | ||
* The transaction object is used to identify a running transaction. | ||
* It is created by calling `Sequelize.transaction()`. | ||
* To run a query under a transaction, you should pass the transaction in the options object. | ||
* | ||
* @class Transaction | ||
* @see {@link Sequelize.transaction} | ||
*/ | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
class Transaction { | ||
/** | ||
* Creates a new transaction instance | ||
* | ||
* @param {Sequelize} sequelize A configured sequelize Instance | ||
* @param {object} options An object with options | ||
* @param {string} [options.type] Sets the type of the transaction. Sqlite only | ||
* @param {string} [options.isolationLevel] Sets the isolation level of the transaction. | ||
* @param {string} [options.deferrable] Sets the constraints to be deferred or immediately checked. PostgreSQL only | ||
*/ | ||
constructor(sequelize, options) { | ||
@@ -25,15 +23,9 @@ this.sequelize = sequelize; | ||
this._afterCommitHooks = []; | ||
// get dialect specific transaction options | ||
const generateTransactionId = this.sequelize.dialect.queryGenerator.generateTransactionId; | ||
this.options = { | ||
this.options = __spreadValues({ | ||
type: sequelize.options.transactionType, | ||
isolationLevel: sequelize.options.isolationLevel, | ||
readOnly: false, | ||
...options | ||
}; | ||
readOnly: false | ||
}, options); | ||
this.parent = this.options.transaction; | ||
if (this.parent) { | ||
@@ -46,11 +38,4 @@ this.id = this.parent.id; | ||
} | ||
delete this.options.transaction; | ||
} | ||
/** | ||
* Commit the transaction | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async commit() { | ||
@@ -60,8 +45,11 @@ if (this.finished) { | ||
} | ||
try { | ||
return await this.sequelize.getQueryInterface().commitTransaction(this, this.options); | ||
await this.sequelize.getQueryInterface().commitTransaction(this, this.options); | ||
this.cleanup(); | ||
} catch (e) { | ||
console.warn(`Committing transaction ${this.id} failed with error ${JSON.stringify(e.message)}. We are killing its connection as it is now in an undetermined state.`); | ||
await this.forceCleanup(); | ||
throw e; | ||
} finally { | ||
this.finished = 'commit'; | ||
this.cleanup(); | ||
this.finished = "commit"; | ||
for (const hook of this._afterCommitHooks) { | ||
@@ -72,8 +60,2 @@ await hook.apply(this, [this]); | ||
} | ||
/** | ||
* Rollback (abort) the transaction | ||
* | ||
* @returns {Promise} | ||
*/ | ||
async rollback() { | ||
@@ -83,31 +65,16 @@ if (this.finished) { | ||
} | ||
if (!this.connection) { | ||
throw new Error('Transaction cannot be rolled back because it never started'); | ||
throw new Error("Transaction cannot be rolled back because it never started"); | ||
} | ||
try { | ||
return await this | ||
.sequelize | ||
.getQueryInterface() | ||
.rollbackTransaction(this, this.options); | ||
} finally { | ||
await this.sequelize.getQueryInterface().rollbackTransaction(this, this.options); | ||
this.cleanup(); | ||
} catch (e) { | ||
console.warn(`Rolling back transaction ${this.id} failed with error ${JSON.stringify(e.message)}. We are killing its connection as it is now in an undetermined state.`); | ||
await this.forceCleanup(); | ||
throw e; | ||
} | ||
} | ||
/** | ||
* Called to acquire a connection to use and set the correct options on the connection. | ||
* We should ensure all of the environment that's set up is cleaned up in `cleanup()` below. | ||
* | ||
* @param {boolean} useCLS Defaults to true: Use CLS (Continuation Local Storage) with Sequelize. With CLS, all queries within the transaction callback will automatically receive the transaction object. | ||
* @returns {Promise} | ||
*/ | ||
async prepareEnvironment(useCLS) { | ||
async prepareEnvironment(useCLS = true) { | ||
let connectionPromise; | ||
if (useCLS === undefined) { | ||
useCLS = true; | ||
} | ||
if (this.parent) { | ||
@@ -118,7 +85,6 @@ connectionPromise = Promise.resolve(this.parent.connection); | ||
if (this.options.readOnly) { | ||
acquireOptions.type = 'SELECT'; | ||
acquireOptions.type = "SELECT"; | ||
} | ||
connectionPromise = this.sequelize.connectionManager.getConnection(acquireOptions); | ||
} | ||
let result; | ||
@@ -128,3 +94,2 @@ const connection = await connectionPromise; | ||
this.connection.uuid = this.id; | ||
try { | ||
@@ -137,65 +102,50 @@ await this.begin(); | ||
} finally { | ||
throw setupErr; // eslint-disable-line no-unsafe-finally | ||
throw setupErr; | ||
} | ||
} | ||
if (useCLS && this.sequelize.constructor._cls) { | ||
this.sequelize.constructor._cls.set('transaction', this); | ||
this.sequelize.constructor._cls.set("transaction", this); | ||
} | ||
return result; | ||
} | ||
async setDeferrable() { | ||
if (this.options.deferrable) { | ||
return await this | ||
.sequelize | ||
.getQueryInterface() | ||
.deferConstraints(this, this.options); | ||
return await this.sequelize.getQueryInterface().deferConstraints(this, this.options); | ||
} | ||
} | ||
async begin() { | ||
const queryInterface = this.sequelize.getQueryInterface(); | ||
if ( this.sequelize.dialect.supports.settingIsolationLevelDuringTransaction ) { | ||
if (this.sequelize.dialect.supports.settingIsolationLevelDuringTransaction) { | ||
await queryInterface.startTransaction(this, this.options); | ||
return queryInterface.setIsolationLevel(this, this.options.isolationLevel, this.options); | ||
} | ||
await queryInterface.setIsolationLevel(this, this.options.isolationLevel, this.options); | ||
return queryInterface.startTransaction(this, this.options); | ||
} | ||
cleanup() { | ||
// Don't release the connection if there's a parent transaction or | ||
// if we've already cleaned up | ||
if (this.parent || this.connection.uuid === undefined) return; | ||
if (this.parent || this.connection.uuid === void 0) { | ||
return; | ||
} | ||
this._clearCls(); | ||
const res = this.sequelize.connectionManager.releaseConnection(this.connection); | ||
this.connection.uuid = undefined; | ||
return res; | ||
this.sequelize.connectionManager.releaseConnection(this.connection); | ||
this.connection.uuid = void 0; | ||
} | ||
async forceCleanup() { | ||
if (this.parent || this.connection.uuid === void 0) { | ||
return; | ||
} | ||
this._clearCls(); | ||
await this.sequelize.connectionManager.destroyConnection(this.connection); | ||
this.connection.uuid = void 0; | ||
} | ||
_clearCls() { | ||
const cls = this.sequelize.constructor._cls; | ||
if (cls) { | ||
if (cls.get('transaction') === this) { | ||
cls.set('transaction', null); | ||
if (cls.get("transaction") === this) { | ||
cls.set("transaction", null); | ||
} | ||
} | ||
} | ||
/** | ||
* A hook that is run after a transaction is committed | ||
* | ||
* @param {Function} fn A callback function that is called with the committed transaction | ||
* @name afterCommit | ||
* @memberof Sequelize.Transaction | ||
*/ | ||
afterCommit(fn) { | ||
if (!fn || typeof fn !== 'function') { | ||
if (!fn || typeof fn !== "function") { | ||
throw new Error('"fn" must be a function'); | ||
@@ -205,115 +155,25 @@ } | ||
} | ||
/** | ||
* Types can be set per-transaction by passing `options.type` to `sequelize.transaction`. | ||
* Default to `DEFERRED` but you can override the default type by passing `options.transactionType` in `new Sequelize`. | ||
* Sqlite only. | ||
* | ||
* Pass in the desired level as the first argument: | ||
* | ||
* @example | ||
* try { | ||
* await sequelize.transaction({ type: Sequelize.Transaction.TYPES.EXCLUSIVE }, transaction => { | ||
* // your transactions | ||
* }); | ||
* // transaction has been committed. Do something after the commit if required. | ||
* } catch(err) { | ||
* // do something with the err. | ||
* } | ||
* | ||
* @property DEFERRED | ||
* @property IMMEDIATE | ||
* @property EXCLUSIVE | ||
*/ | ||
static get TYPES() { | ||
return { | ||
DEFERRED: 'DEFERRED', | ||
IMMEDIATE: 'IMMEDIATE', | ||
EXCLUSIVE: 'EXCLUSIVE' | ||
DEFERRED: "DEFERRED", | ||
IMMEDIATE: "IMMEDIATE", | ||
EXCLUSIVE: "EXCLUSIVE" | ||
}; | ||
} | ||
/** | ||
* Isolation levels can be set per-transaction by passing `options.isolationLevel` to `sequelize.transaction`. | ||
* Sequelize uses the default isolation level of the database, you can override this by passing `options.isolationLevel` in Sequelize constructor options. | ||
* | ||
* Pass in the desired level as the first argument: | ||
* | ||
* @example | ||
* try { | ||
* const result = await sequelize.transaction({isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE}, transaction => { | ||
* // your transactions | ||
* }); | ||
* // transaction has been committed. Do something after the commit if required. | ||
* } catch(err) { | ||
* // do something with the err. | ||
* } | ||
* | ||
* @property READ_UNCOMMITTED | ||
* @property READ_COMMITTED | ||
* @property REPEATABLE_READ | ||
* @property SERIALIZABLE | ||
*/ | ||
static get ISOLATION_LEVELS() { | ||
return { | ||
READ_UNCOMMITTED: 'READ UNCOMMITTED', | ||
READ_COMMITTED: 'READ COMMITTED', | ||
REPEATABLE_READ: 'REPEATABLE READ', | ||
SERIALIZABLE: 'SERIALIZABLE' | ||
READ_UNCOMMITTED: "READ UNCOMMITTED", | ||
READ_COMMITTED: "READ COMMITTED", | ||
REPEATABLE_READ: "REPEATABLE READ", | ||
SERIALIZABLE: "SERIALIZABLE" | ||
}; | ||
} | ||
/** | ||
* Possible options for row locking. Used in conjunction with `find` calls: | ||
* | ||
* @example | ||
* // t1 is a transaction | ||
* Model.findAll({ | ||
* where: ..., | ||
* transaction: t1, | ||
* lock: t1.LOCK... | ||
* }); | ||
* | ||
* @example <caption>Postgres also supports specific locks while eager loading by using OF:</caption> | ||
* UserModel.findAll({ | ||
* where: ..., | ||
* include: [TaskModel, ...], | ||
* transaction: t1, | ||
* lock: { | ||
* level: t1.LOCK..., | ||
* of: UserModel | ||
* } | ||
* }); | ||
* | ||
* # UserModel will be locked but TaskModel won't! | ||
* | ||
* @example <caption>You can also skip locked rows:</caption> | ||
* // t1 is a transaction | ||
* Model.findAll({ | ||
* where: ..., | ||
* transaction: t1, | ||
* lock: true, | ||
* skipLocked: true | ||
* }); | ||
* # The query will now return any rows that aren't locked by another transaction | ||
* | ||
* @returns {object} | ||
* @property UPDATE | ||
* @property SHARE | ||
* @property KEY_SHARE Postgres 9.3+ only | ||
* @property NO_KEY_UPDATE Postgres 9.3+ only | ||
*/ | ||
static get LOCK() { | ||
return { | ||
UPDATE: 'UPDATE', | ||
SHARE: 'SHARE', | ||
KEY_SHARE: 'KEY SHARE', | ||
NO_KEY_UPDATE: 'NO KEY UPDATE' | ||
UPDATE: "UPDATE", | ||
SHARE: "SHARE", | ||
KEY_SHARE: "KEY SHARE", | ||
NO_KEY_UPDATE: "NO KEY UPDATE" | ||
}; | ||
} | ||
/** | ||
* Please see {@link Transaction.LOCK} | ||
*/ | ||
get LOCK() { | ||
@@ -323,5 +183,5 @@ return Transaction.LOCK; | ||
} | ||
module.exports = Transaction; | ||
module.exports.Transaction = Transaction; | ||
module.exports.default = Transaction; | ||
//# sourceMappingURL=transaction.js.map |
379
lib/utils.js
@@ -1,17 +0,29 @@ | ||
'use strict'; | ||
const DataTypes = require('./data-types'); | ||
const SqlString = require('./sql-string'); | ||
const _ = require('lodash'); | ||
const baseIsNative = require('lodash/_baseIsNative'); | ||
const uuidv1 = require('uuid').v1; | ||
const uuidv4 = require('uuid').v4; | ||
const operators = require('./operators'); | ||
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
const DataTypes = require("./data-types"); | ||
const SqlString = require("./sql-string"); | ||
const _ = require("lodash"); | ||
const baseIsNative = require("lodash/_baseIsNative"); | ||
const uuidv1 = require("uuid").v1; | ||
const uuidv4 = require("uuid").v4; | ||
const operators = require("./operators"); | ||
const operatorsSet = new Set(Object.values(operators)); | ||
let inflection = require('inflection'); | ||
exports.classToInvokable = require('./utils/class-to-invokable').classToInvokable; | ||
exports.joinSQLFragments = require('./utils/join-sql-fragments').joinSQLFragments; | ||
let inflection = require("inflection"); | ||
exports.classToInvokable = require("./utils/class-to-invokable").classToInvokable; | ||
exports.joinSQLFragments = require("./utils/join-sql-fragments").joinSQLFragments; | ||
function useInflection(_inflection) { | ||
@@ -21,38 +33,26 @@ inflection = _inflection; | ||
exports.useInflection = useInflection; | ||
function camelizeIf(str, condition) { | ||
let result = str; | ||
if (condition) { | ||
result = camelize(str); | ||
} | ||
return result; | ||
} | ||
exports.camelizeIf = camelizeIf; | ||
function underscoredIf(str, condition) { | ||
let result = str; | ||
if (condition) { | ||
result = underscore(str); | ||
} | ||
return result; | ||
} | ||
exports.underscoredIf = underscoredIf; | ||
function isPrimitive(val) { | ||
const type = typeof val; | ||
return type === 'string' || type === 'number' || type === 'boolean'; | ||
return ["string", "number", "boolean"].includes(type); | ||
} | ||
exports.isPrimitive = isPrimitive; | ||
// Same concept as _.merge, but don't overwrite properties that have already been assigned | ||
function mergeDefaults(a, b) { | ||
return _.mergeWith(a, b, (objectValue, sourceValue) => { | ||
// If it's an object, let _ handle it this time, we will be called again for each property | ||
if (!_.isPlainObject(objectValue) && objectValue !== undefined) { | ||
// _.isNative includes a check for core-js and throws an error if present. | ||
// Depending on _baseIsNative bypasses the core-js check. | ||
if (!_.isPlainObject(objectValue) && objectValue !== void 0) { | ||
if (_.isFunction(objectValue) && baseIsNative(objectValue)) { | ||
@@ -66,12 +66,7 @@ return sourceValue || objectValue; | ||
exports.mergeDefaults = mergeDefaults; | ||
// An alternative to _.merge, which doesn't clone its arguments | ||
// Cloning is a bad idea because options arguments may contain references to sequelize | ||
// models - which again reference database libs which don't like to be cloned (in particular pg-native) | ||
function merge() { | ||
const result = {}; | ||
for (const obj of arguments) { | ||
_.forOwn(obj, (value, key) => { | ||
if (value !== undefined) { | ||
if (value !== void 0) { | ||
if (!result[key]) { | ||
@@ -89,7 +84,5 @@ result[key] = value; | ||
} | ||
return result; | ||
} | ||
exports.merge = merge; | ||
function spliceStr(str, index, count, add) { | ||
@@ -99,3 +92,2 @@ return str.slice(0, index) + add + str.slice(index + count); | ||
exports.spliceStr = spliceStr; | ||
function camelize(str) { | ||
@@ -105,3 +97,2 @@ return str.trim().replace(/[-_\s]+(.)?/g, (match, c) => c.toUpperCase()); | ||
exports.camelize = camelize; | ||
function underscore(str) { | ||
@@ -111,3 +102,2 @@ return inflection.underscore(str); | ||
exports.underscore = underscore; | ||
function singularize(str) { | ||
@@ -117,3 +107,2 @@ return inflection.singularize(str); | ||
exports.singularize = singularize; | ||
function pluralize(str) { | ||
@@ -123,10 +112,7 @@ return inflection.pluralize(str); | ||
exports.pluralize = pluralize; | ||
function format(arr, dialect) { | ||
const timeZone = null; | ||
// Make a clone of the array beacuse format modifies the passed args | ||
return SqlString.format(arr[0], arr.slice(1), timeZone, dialect); | ||
} | ||
exports.format = format; | ||
function formatNamedParameters(sql, parameters, dialect) { | ||
@@ -137,19 +123,12 @@ const timeZone = null; | ||
exports.formatNamedParameters = formatNamedParameters; | ||
function cloneDeep(obj, onlyPlain) { | ||
obj = obj || {}; | ||
return _.cloneDeepWith(obj, elem => { | ||
// Do not try to customize cloning of arrays or POJOs | ||
return _.cloneDeepWith(obj, (elem) => { | ||
if (Array.isArray(elem) || _.isPlainObject(elem)) { | ||
return undefined; | ||
return void 0; | ||
} | ||
// If we specified to clone only plain objects & arrays, we ignore everyhing else | ||
// In any case, don't clone stuff that's an object, but not a plain one - fx example sequelize models and instances | ||
if (onlyPlain || typeof elem === 'object') { | ||
if (onlyPlain || typeof elem === "object") { | ||
return elem; | ||
} | ||
// Preserve special data-types like `fn` across clones. _.get() is used for checking up the prototype chain | ||
if (elem && typeof elem.clone === 'function') { | ||
if (elem && typeof elem.clone === "function") { | ||
return elem.clone(); | ||
@@ -160,23 +139,16 @@ } | ||
exports.cloneDeep = cloneDeep; | ||
/* Expand and normalize finder options */ | ||
function mapFinderOptions(options, Model) { | ||
if (options.attributes && Array.isArray(options.attributes)) { | ||
options.attributes = Model._injectDependentVirtualAttributes(options.attributes); | ||
options.attributes = options.attributes.filter(v => !Model._virtualAttributes.has(v)); | ||
options.attributes = options.attributes.filter((v) => !Model._virtualAttributes.has(v)); | ||
} | ||
mapOptionFieldNames(options, Model); | ||
return options; | ||
} | ||
exports.mapFinderOptions = mapFinderOptions; | ||
/* Used to map field names in attributes and where conditions */ | ||
function mapOptionFieldNames(options, Model) { | ||
if (Array.isArray(options.attributes)) { | ||
options.attributes = options.attributes.map(attr => { | ||
// Object lookups will force any variable to strings, we don't want that for special objects etc | ||
if (typeof attr !== 'string') return attr; | ||
// Map attributes to aliased syntax attributes | ||
options.attributes = options.attributes.map((attr) => { | ||
if (typeof attr !== "string") | ||
return attr; | ||
if (Model.rawAttributes[attr] && attr !== Model.rawAttributes[attr].field) { | ||
@@ -188,17 +160,13 @@ return [Model.rawAttributes[attr].field, attr]; | ||
} | ||
if (options.where && _.isPlainObject(options.where)) { | ||
options.where = mapWhereFieldNames(options.where, Model); | ||
} | ||
return options; | ||
} | ||
exports.mapOptionFieldNames = mapOptionFieldNames; | ||
function mapWhereFieldNames(attributes, Model) { | ||
if (attributes) { | ||
attributes = cloneDeep(attributes); | ||
getComplexKeys(attributes).forEach(attribute => { | ||
getComplexKeys(attributes).forEach((attribute) => { | ||
const rawAttribute = Model.rawAttributes[attribute]; | ||
if (rawAttribute && rawAttribute.field !== rawAttribute.fieldName) { | ||
@@ -208,7 +176,3 @@ attributes[rawAttribute.field] = attributes[attribute]; | ||
} | ||
if (_.isPlainObject(attributes[attribute]) | ||
&& !(rawAttribute && ( | ||
rawAttribute.type instanceof DataTypes.HSTORE | ||
|| rawAttribute.type instanceof DataTypes.JSON))) { // Prevent renaming of HSTORE & JSON fields | ||
if (_.isPlainObject(attributes[attribute]) && !(rawAttribute && (rawAttribute.type instanceof DataTypes.HSTORE || rawAttribute.type instanceof DataTypes.JSON))) { | ||
attributes[attribute] = mapOptionFieldNames({ | ||
@@ -218,3 +182,2 @@ where: attributes[attribute] | ||
} | ||
if (Array.isArray(attributes[attribute])) { | ||
@@ -227,17 +190,11 @@ attributes[attribute].forEach((where, index) => { | ||
} | ||
}); | ||
} | ||
return attributes; | ||
} | ||
exports.mapWhereFieldNames = mapWhereFieldNames; | ||
/* Used to map field names in values */ | ||
function mapValueFieldNames(dataValues, fields, Model) { | ||
const values = {}; | ||
for (const attr of fields) { | ||
if (dataValues[attr] !== undefined && !Model._virtualAttributes.has(attr)) { | ||
// Field name mapping | ||
if (dataValues[attr] !== void 0 && !Model._virtualAttributes.has(attr)) { | ||
if (Model.rawAttributes[attr] && Model.rawAttributes[attr].field && Model.rawAttributes[attr].field !== attr) { | ||
@@ -250,17 +207,13 @@ values[Model.rawAttributes[attr].field] = dataValues[attr]; | ||
} | ||
return values; | ||
} | ||
exports.mapValueFieldNames = mapValueFieldNames; | ||
function isColString(value) { | ||
return typeof value === 'string' && value[0] === '$' && value[value.length - 1] === '$'; | ||
return typeof value === "string" && value[0] === "$" && value[value.length - 1] === "$"; | ||
} | ||
exports.isColString = isColString; | ||
function canTreatArrayAsAnd(arr) { | ||
return arr.some(arg => _.isPlainObject(arg) || arg instanceof Where); | ||
return arr.some((arg) => _.isPlainObject(arg) || arg instanceof Where); | ||
} | ||
exports.canTreatArrayAsAnd = canTreatArrayAsAnd; | ||
function combineTableNames(tableName1, tableName2) { | ||
@@ -270,5 +223,4 @@ return tableName1.toLowerCase() < tableName2.toLowerCase() ? tableName1 + tableName2 : tableName2 + tableName1; | ||
exports.combineTableNames = combineTableNames; | ||
function toDefaultValue(value, dialect) { | ||
if (typeof value === 'function') { | ||
if (typeof value === "function") { | ||
const tmp = value(); | ||
@@ -293,3 +245,3 @@ if (tmp instanceof DataTypes.ABSTRACT) { | ||
if (_.isPlainObject(value)) { | ||
return { ...value }; | ||
return __spreadValues({}, value); | ||
} | ||
@@ -299,48 +251,32 @@ return value; | ||
exports.toDefaultValue = toDefaultValue; | ||
/** | ||
* Determine if the default value provided exists and can be described | ||
* in a db schema using the DEFAULT directive. | ||
* | ||
* @param {*} value Any default value. | ||
* @returns {boolean} yes / no. | ||
* @private | ||
*/ | ||
function defaultValueSchemable(value) { | ||
if (value === undefined) { return false; } | ||
// TODO this will be schemable when all supported db | ||
// have been normalized for this case | ||
if (value instanceof DataTypes.NOW) { return false; } | ||
if (value instanceof DataTypes.UUIDV1 || value instanceof DataTypes.UUIDV4) { return false; } | ||
return typeof value !== 'function'; | ||
if (value === void 0) { | ||
return false; | ||
} | ||
if (value instanceof DataTypes.NOW) { | ||
return false; | ||
} | ||
if (value instanceof DataTypes.UUIDV1 || value instanceof DataTypes.UUIDV4) { | ||
return false; | ||
} | ||
return typeof value !== "function"; | ||
} | ||
exports.defaultValueSchemable = defaultValueSchemable; | ||
function removeNullValuesFromHash(hash, omitNull, options) { | ||
let result = hash; | ||
options = options || {}; | ||
options.allowNull = options.allowNull || []; | ||
if (omitNull) { | ||
const _hash = {}; | ||
_.forIn(hash, (val, key) => { | ||
if (options.allowNull.includes(key) || key.endsWith('Id') || val !== null && val !== undefined) { | ||
if (options.allowNull.includes(key) || key.endsWith("Id") || val !== null && val !== void 0) { | ||
_hash[key] = val; | ||
} | ||
}); | ||
result = _hash; | ||
} | ||
return result; | ||
} | ||
exports.removeNullValuesFromHash = removeNullValuesFromHash; | ||
const dialects = new Set(['mariadb', 'mysql', 'postgres', 'sqlite', 'mssql']); | ||
const dialects = /* @__PURE__ */ new Set(["mariadb", "mysql", "postgres", "sqlite", "mssql", "db2"]); | ||
function now(dialect) { | ||
@@ -354,9 +290,4 @@ const d = new Date(); | ||
exports.now = now; | ||
// Note: Use the `quoteIdentifier()` and `escape()` methods on the | ||
// `QueryInterface` instead for more portable code. | ||
const TICK_CHAR = '`'; | ||
const TICK_CHAR = "`"; | ||
exports.TICK_CHAR = TICK_CHAR; | ||
function addTicks(s, tickChar) { | ||
@@ -367,46 +298,15 @@ tickChar = tickChar || TICK_CHAR; | ||
exports.addTicks = addTicks; | ||
function removeTicks(s, tickChar) { | ||
tickChar = tickChar || TICK_CHAR; | ||
return s.replace(new RegExp(tickChar, 'g'), ''); | ||
return s.replace(new RegExp(tickChar, "g"), ""); | ||
} | ||
exports.removeTicks = removeTicks; | ||
/** | ||
* Receives a tree-like object and returns a plain object which depth is 1. | ||
* | ||
* - Input: | ||
* | ||
* { | ||
* name: 'John', | ||
* address: { | ||
* street: 'Fake St. 123', | ||
* coordinates: { | ||
* longitude: 55.6779627, | ||
* latitude: 12.5964313 | ||
* } | ||
* } | ||
* } | ||
* | ||
* - Output: | ||
* | ||
* { | ||
* name: 'John', | ||
* address.street: 'Fake St. 123', | ||
* address.coordinates.latitude: 55.6779627, | ||
* address.coordinates.longitude: 12.5964313 | ||
* } | ||
* | ||
* @param {object} value an Object | ||
* @returns {object} a flattened object | ||
* @private | ||
*/ | ||
function flattenObjectDeep(value) { | ||
if (!_.isPlainObject(value)) return value; | ||
if (!_.isPlainObject(value)) | ||
return value; | ||
const flattenedObj = {}; | ||
function flattenObject(obj, subPath) { | ||
Object.keys(obj).forEach(key => { | ||
Object.keys(obj).forEach((key) => { | ||
const pathToProperty = subPath ? `${subPath}.${key}` : key; | ||
if (typeof obj[key] === 'object' && obj[key] !== null) { | ||
if (typeof obj[key] === "object" && obj[key] !== null) { | ||
flattenObject(obj[key], pathToProperty); | ||
@@ -419,16 +319,8 @@ } else { | ||
} | ||
return flattenObject(value, undefined); | ||
return flattenObject(value, void 0); | ||
} | ||
exports.flattenObjectDeep = flattenObjectDeep; | ||
/** | ||
* Utility functions for representing SQL functions, and columns that should be escaped. | ||
* Please do not use these functions directly, use Sequelize.fn and Sequelize.col instead. | ||
* | ||
* @private | ||
*/ | ||
class SequelizeMethod {} | ||
class SequelizeMethod { | ||
} | ||
exports.SequelizeMethod = SequelizeMethod; | ||
class Fn extends SequelizeMethod { | ||
@@ -445,3 +337,2 @@ constructor(fn, args) { | ||
exports.Fn = Fn; | ||
class Col extends SequelizeMethod { | ||
@@ -457,3 +348,2 @@ constructor(col, ...args) { | ||
exports.Col = Col; | ||
class Cast extends SequelizeMethod { | ||
@@ -463,3 +353,3 @@ constructor(val, type, json) { | ||
this.val = val; | ||
this.type = (type || '').trim(); | ||
this.type = (type || "").trim(); | ||
this.json = json || false; | ||
@@ -469,3 +359,2 @@ } | ||
exports.Cast = Cast; | ||
class Literal extends SequelizeMethod { | ||
@@ -478,3 +367,2 @@ constructor(val) { | ||
exports.Literal = Literal; | ||
class Json extends SequelizeMethod { | ||
@@ -494,11 +382,9 @@ constructor(conditionsOrPath, value) { | ||
exports.Json = Json; | ||
class Where extends SequelizeMethod { | ||
constructor(attribute, comparator, logic) { | ||
super(); | ||
if (logic === undefined) { | ||
if (logic === void 0) { | ||
logic = comparator; | ||
comparator = '='; | ||
comparator = "="; | ||
} | ||
this.attribute = attribute; | ||
@@ -510,24 +396,6 @@ this.comparator = comparator; | ||
exports.Where = Where; | ||
//Collection of helper methods to make it easier to work with symbol operators | ||
/** | ||
* getOperators | ||
* | ||
* @param {object} obj | ||
* @returns {Array<symbol>} All operators properties of obj | ||
* @private | ||
*/ | ||
function getOperators(obj) { | ||
return Object.getOwnPropertySymbols(obj).filter(s => operatorsSet.has(s)); | ||
return Object.getOwnPropertySymbols(obj).filter((s) => operatorsSet.has(s)); | ||
} | ||
exports.getOperators = getOperators; | ||
/** | ||
* getComplexKeys | ||
* | ||
* @param {object} obj | ||
* @returns {Array<string|symbol>} All keys including operators | ||
* @private | ||
*/ | ||
function getComplexKeys(obj) { | ||
@@ -537,10 +405,2 @@ return getOperators(obj).concat(Object.keys(obj)); | ||
exports.getComplexKeys = getComplexKeys; | ||
/** | ||
* getComplexSize | ||
* | ||
* @param {object|Array} obj | ||
* @returns {number} Length of object properties including operators if obj is array returns its length | ||
* @private | ||
*/ | ||
function getComplexSize(obj) { | ||
@@ -550,10 +410,2 @@ return Array.isArray(obj) ? obj.length : getComplexKeys(obj).length; | ||
exports.getComplexSize = getComplexSize; | ||
/** | ||
* Returns true if a where clause is empty, even with Symbols | ||
* | ||
* @param {object} obj | ||
* @returns {boolean} | ||
* @private | ||
*/ | ||
function isWhereEmpty(obj) { | ||
@@ -563,11 +415,2 @@ return !!obj && _.isEmpty(obj) && getOperators(obj).length === 0; | ||
exports.isWhereEmpty = isWhereEmpty; | ||
/** | ||
* Returns ENUM name by joining table and column name | ||
* | ||
* @param {string} tableName | ||
* @param {string} columnName | ||
* @returns {string} | ||
* @private | ||
*/ | ||
function generateEnumName(tableName, columnName) { | ||
@@ -577,13 +420,5 @@ return `enum_${tableName}_${columnName}`; | ||
exports.generateEnumName = generateEnumName; | ||
/** | ||
* Returns an new Object which keys are camelized | ||
* | ||
* @param {object} obj | ||
* @returns {string} | ||
* @private | ||
*/ | ||
function camelizeObjectKeys(obj) { | ||
const newObj = new Object(); | ||
Object.keys(obj).forEach(key => { | ||
Object.keys(obj).forEach((key) => { | ||
newObj[camelize(key)] = obj[key]; | ||
@@ -594,31 +429,10 @@ }); | ||
exports.camelizeObjectKeys = camelizeObjectKeys; | ||
/** | ||
* Assigns own and inherited enumerable string and symbol keyed properties of source | ||
* objects to the destination object. | ||
* | ||
* https://lodash.com/docs/4.17.4#defaults | ||
* | ||
* **Note:** This method mutates `object`. | ||
* | ||
* @param {object} object The destination object. | ||
* @param {...object} [sources] The source objects. | ||
* @returns {object} Returns `object`. | ||
* @private | ||
*/ | ||
function defaults(object, ...sources) { | ||
object = Object(object); | ||
sources.forEach(source => { | ||
sources.forEach((source) => { | ||
if (source) { | ||
source = Object(source); | ||
getComplexKeys(source).forEach(key => { | ||
getComplexKeys(source).forEach((key) => { | ||
const value = object[key]; | ||
if ( | ||
value === undefined || | ||
_.eq(value, Object.prototype[key]) && | ||
!Object.prototype.hasOwnProperty.call(object, key) | ||
) { | ||
if (value === void 0 || _.eq(value, Object.prototype[key]) && !Object.prototype.hasOwnProperty.call(object, key)) { | ||
object[key] = source[key]; | ||
@@ -629,41 +443,28 @@ } | ||
}); | ||
return object; | ||
} | ||
exports.defaults = defaults; | ||
/** | ||
* | ||
* @param {object} index | ||
* @param {Array} index.fields | ||
* @param {string} [index.name] | ||
* @param {string|object} tableName | ||
* | ||
* @returns {object} | ||
* @private | ||
*/ | ||
function nameIndex(index, tableName) { | ||
if (tableName.tableName) tableName = tableName.tableName; | ||
if (!Object.prototype.hasOwnProperty.call(index, 'name')) { | ||
const fields = index.fields.map( | ||
field => typeof field === 'string' ? field : field.name || field.attribute | ||
); | ||
index.name = underscore(`${tableName}_${fields.join('_')}`); | ||
if (tableName.tableName) | ||
tableName = tableName.tableName; | ||
if (!Object.prototype.hasOwnProperty.call(index, "name")) { | ||
const fields = index.fields.map((field) => typeof field === "string" ? field : field.name || field.attribute); | ||
index.name = underscore(`${tableName}_${fields.join("_")}`); | ||
} | ||
return index; | ||
} | ||
exports.nameIndex = nameIndex; | ||
/** | ||
* Checks if 2 arrays intersect. | ||
* | ||
* @param {Array} arr1 | ||
* @param {Array} arr2 | ||
* @private | ||
*/ | ||
function intersects(arr1, arr2) { | ||
return arr1.some(v => arr2.includes(v)); | ||
return arr1.some((v) => arr2.includes(v)); | ||
} | ||
exports.intersects = intersects; | ||
function safeStringifyJson(value) { | ||
return JSON.stringify(value, (key, value2) => { | ||
if (typeof value2 === "bigint") { | ||
return String(value2); | ||
} | ||
return value2; | ||
}); | ||
} | ||
exports.safeStringifyJson = safeStringifyJson; | ||
//# sourceMappingURL=utils.js.map |
@@ -1,24 +0,21 @@ | ||
'use strict'; | ||
/** | ||
* Wraps a constructor to not need the `new` keyword using a proxy. | ||
* Only used for data types. | ||
* | ||
* @param {Function} Class The class instance to wrap as invocable. | ||
* @returns {Proxy} Wrapped class instance. | ||
* @private | ||
*/ | ||
var __defProp = Object.defineProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
__export(exports, { | ||
classToInvokable: () => classToInvokable | ||
}); | ||
function classToInvokable(Class) { | ||
return new Proxy(Class, { | ||
apply(Target, thisArg, args) { | ||
return new Target(...args); | ||
apply(_target, _thisArg, args) { | ||
return new Class(...args); | ||
}, | ||
construct(Target, args) { | ||
return new Target(...args); | ||
}, | ||
get(target, p) { | ||
return target[p]; | ||
construct(_target, args) { | ||
return new Class(...args); | ||
} | ||
}); | ||
} | ||
exports.classToInvokable = classToInvokable; | ||
//# sourceMappingURL=class-to-invokable.js.map |
@@ -1,12 +0,41 @@ | ||
'use strict'; | ||
const { deprecate } = require('util'); | ||
const noop = () => {}; | ||
exports.noRawAttributes = deprecate(noop, 'Use sequelize.fn / sequelize.literal to construct attributes', 'SEQUELIZE0001'); | ||
exports.noTrueLogging = deprecate(noop, 'The logging-option should be either a function or false. Default: console.log', 'SEQUELIZE0002'); | ||
exports.noStringOperators = deprecate(noop, 'String based operators are deprecated. Please use Symbol based operators for better security, read more at https://sequelize.org/master/manual/querying.html#operators', 'SEQUELIZE0003'); | ||
exports.noBoolOperatorAliases = deprecate(noop, 'A boolean value was passed to options.operatorsAliases. This is a no-op with v5 and should be removed.', 'SEQUELIZE0004'); | ||
exports.noDoubleNestedGroup = deprecate(noop, 'Passing a double nested nested array to `group` is unsupported and will be removed in v6.', 'SEQUELIZE0005'); | ||
exports.unsupportedEngine = deprecate(noop, 'This database engine version is not supported, please update your database server. More information https://github.com/sequelize/sequelize/blob/main/ENGINE.md', 'SEQUELIZE0006'); | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
__export(exports, { | ||
noBoolOperatorAliases: () => noBoolOperatorAliases, | ||
noDoubleNestedGroup: () => noDoubleNestedGroup, | ||
noRawAttributes: () => noRawAttributes, | ||
noStringOperators: () => noStringOperators, | ||
noTrueLogging: () => noTrueLogging, | ||
unsupportedEngine: () => unsupportedEngine | ||
}); | ||
var import_util = __toModule(require("util")); | ||
const noop = () => { | ||
}; | ||
const noRawAttributes = (0, import_util.deprecate)(noop, "Use sequelize.fn / sequelize.literal to construct attributes", "SEQUELIZE0001"); | ||
const noTrueLogging = (0, import_util.deprecate)(noop, "The logging-option should be either a function or false. Default: console.log", "SEQUELIZE0002"); | ||
const noStringOperators = (0, import_util.deprecate)(noop, "String based operators are deprecated. Please use Symbol based operators for better security, read more at https://sequelize.org/master/manual/querying.html#operators", "SEQUELIZE0003"); | ||
const noBoolOperatorAliases = (0, import_util.deprecate)(noop, "A boolean value was passed to options.operatorsAliases. This is a no-op with v5 and should be removed.", "SEQUELIZE0004"); | ||
const noDoubleNestedGroup = (0, import_util.deprecate)(noop, "Passing a double nested nested array to `group` is unsupported and will be removed in v6.", "SEQUELIZE0005"); | ||
const unsupportedEngine = (0, import_util.deprecate)(noop, "This database engine version is not supported, please update your database server. More information https://github.com/sequelize/sequelize/blob/main/ENGINE.md", "SEQUELIZE0006"); | ||
//# sourceMappingURL=deprecations.js.map |
@@ -1,3 +0,17 @@ | ||
'use strict'; | ||
var __defProp = Object.defineProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
JoinSQLFragmentsError: () => JoinSQLFragmentsError, | ||
joinSQLFragments: () => joinSQLFragments | ||
}); | ||
function doesNotWantLeadingSpace(str) { | ||
@@ -9,14 +23,2 @@ return /^[;,)]/.test(str); | ||
} | ||
/** | ||
* Joins an array of strings with a single space between them, | ||
* except for: | ||
* | ||
* - Strings starting with ';', ',' and ')', which do not get a leading space. | ||
* - Strings ending with '(', which do not get a trailing space. | ||
* | ||
* @param {string[]} parts | ||
* @returns {string} | ||
* @private | ||
*/ | ||
function singleSpaceJoinHelper(parts) { | ||
@@ -35,27 +37,10 @@ return parts.reduce(({ skipNextLeadingSpace, result }, part) => { | ||
skipNextLeadingSpace: true, | ||
result: '' | ||
result: "" | ||
}).result; | ||
} | ||
/** | ||
* Joins an array with a single space, auto trimming when needed. | ||
* | ||
* Certain elements do not get leading/trailing spaces. | ||
* | ||
* @param {any[]} array The array to be joined. Falsy values are skipped. If an | ||
* element is another array, this function will be called recursively on that array. | ||
* Otherwise, if a non-string, non-falsy value is present, a TypeError will be thrown. | ||
* | ||
* @returns {string} The joined string. | ||
* | ||
* @private | ||
*/ | ||
function joinSQLFragments(array) { | ||
if (array.length === 0) return ''; | ||
// Skip falsy fragments | ||
array = array.filter(x => x); | ||
// Resolve recursive calls | ||
array = array.map(fragment => { | ||
if (array.length === 0) | ||
return ""; | ||
const truthyArray = array.filter((x) => !!x); | ||
const flattenedArray = truthyArray.map((fragment) => { | ||
if (Array.isArray(fragment)) { | ||
@@ -66,21 +51,21 @@ return joinSQLFragments(fragment); | ||
}); | ||
// Ensure strings | ||
for (const fragment of array) { | ||
if (fragment && typeof fragment !== 'string') { | ||
const error = new TypeError(`Tried to construct a SQL string with a non-string, non-falsy fragment (${fragment}).`); | ||
error.args = array; | ||
error.fragment = fragment; | ||
throw error; | ||
for (const fragment of flattenedArray) { | ||
if (fragment && typeof fragment !== "string") { | ||
throw new JoinSQLFragmentsError(flattenedArray, fragment, `Tried to construct a SQL string with a non-string, non-falsy fragment (${fragment}).`); | ||
} | ||
} | ||
// Trim fragments | ||
array = array.map(x => x.trim()); | ||
// Skip full-whitespace fragments (empty after the above trim) | ||
array = array.filter(x => x !== ''); | ||
return singleSpaceJoinHelper(array); | ||
const trimmedArray = flattenedArray.map((x) => x.trim()); | ||
const nonEmptyStringArray = trimmedArray.filter((x) => x !== ""); | ||
return singleSpaceJoinHelper(nonEmptyStringArray); | ||
} | ||
exports.joinSQLFragments = joinSQLFragments; | ||
class JoinSQLFragmentsError extends TypeError { | ||
constructor(args, fragment, message) { | ||
super(message); | ||
__publicField(this, "args"); | ||
__publicField(this, "fragment"); | ||
this.args = args; | ||
this.fragment = fragment; | ||
this.name = "JoinSQLFragmentsError"; | ||
} | ||
} | ||
//# sourceMappingURL=join-sql-fragments.js.map |
@@ -1,40 +0,82 @@ | ||
'use strict'; | ||
/** | ||
* Sequelize module for debug and deprecation messages. | ||
* It require a `context` for which messages will be printed. | ||
* | ||
* @module logging | ||
* @private | ||
*/ | ||
const debug = require('debug'); | ||
const util = require('util'); | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | ||
var __objRest = (source, exclude) => { | ||
var target = {}; | ||
for (var prop in source) | ||
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) | ||
target[prop] = source[prop]; | ||
if (source != null && __getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(source)) { | ||
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) | ||
target[prop] = source[prop]; | ||
} | ||
return target; | ||
}; | ||
var __export = (target, all) => { | ||
__markAsModule(target); | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __reExport = (target, module2, desc) => { | ||
if (module2 && typeof module2 === "object" || typeof module2 === "function") { | ||
for (let key of __getOwnPropNames(module2)) | ||
if (!__hasOwnProp.call(target, key) && key !== "default") | ||
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | ||
} | ||
return target; | ||
}; | ||
var __toModule = (module2) => { | ||
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | ||
}; | ||
var __publicField = (obj, key, value) => { | ||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
return value; | ||
}; | ||
__export(exports, { | ||
Logger: () => Logger, | ||
logger: () => logger | ||
}); | ||
var import_debug = __toModule(require("debug")); | ||
var import_util = __toModule(require("util")); | ||
class Logger { | ||
constructor(config) { | ||
this.config = { | ||
context: 'sequelize', | ||
debug: true, | ||
...config | ||
}; | ||
constructor(_a = {}) { | ||
__publicField(this, "config"); | ||
var _b = _a, { context = "sequelize" } = _b, rest = __objRest(_b, ["context"]); | ||
this.config = __spreadValues({ | ||
context | ||
}, rest); | ||
} | ||
warn(message) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`(${this.config.context}) Warning: ${message}`); | ||
} | ||
inspect(value) { | ||
return util.inspect(value, false, 3); | ||
return import_util.default.inspect(value, { | ||
showHidden: false, | ||
depth: 1 | ||
}); | ||
} | ||
debugContext(name) { | ||
return debug(`${this.config.context}:${name}`); | ||
return (0, import_debug.default)(`${this.config.context}:${name}`); | ||
} | ||
} | ||
exports.logger = new Logger(); | ||
exports.Logger = Logger; | ||
const logger = new Logger(); | ||
//# sourceMappingURL=logger.js.map |
@@ -1,11 +0,8 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const validator = _.cloneDeep(require('validator')); | ||
const moment = require('moment'); | ||
"use strict"; | ||
const _ = require("lodash"); | ||
const validator = _.cloneDeep(require("validator")); | ||
const moment = require("moment"); | ||
const extensions = { | ||
extend(name, fn) { | ||
this[name] = fn; | ||
return this; | ||
@@ -32,4 +29,4 @@ }, | ||
regex(str, pattern, modifiers) { | ||
str += ''; | ||
if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') { | ||
str += ""; | ||
if (Object.prototype.toString.call(pattern).slice(8, -1) !== "RegExp") { | ||
pattern = new RegExp(pattern, modifiers); | ||
@@ -43,3 +40,3 @@ } | ||
isDecimal(str) { | ||
return str !== '' && !!str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][+-]?(?:[0-9]+))?$/); | ||
return str !== "" && !!str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][+-]?(?:[0-9]+))?$/); | ||
}, | ||
@@ -68,38 +65,21 @@ min(str, val) { | ||
exports.extensions = extensions; | ||
// instance based validators | ||
validator.isImmutable = function(value, validatorArgs, field, modelInstance) { | ||
return modelInstance.isNewRecord || modelInstance.dataValues[field] === modelInstance._previousDataValues[field]; | ||
}; | ||
// extra validators | ||
validator.notNull = function(val) { | ||
return val !== null && val !== undefined; | ||
return val !== null && val !== void 0; | ||
}; | ||
// https://github.com/chriso/validator.js/blob/6.2.0/validator.js | ||
_.forEach(extensions, (extend, key) => { | ||
validator[key] = extend; | ||
}); | ||
// map isNull to isEmpty | ||
// https://github.com/chriso/validator.js/commit/e33d38a26ee2f9666b319adb67c7fc0d3dea7125 | ||
validator.isNull = validator.isEmpty; | ||
// isDate removed in 7.0.0 | ||
// https://github.com/chriso/validator.js/commit/095509fc707a4dc0e99f85131df1176ad6389fc9 | ||
validator.isDate = function(dateString) { | ||
// avoid http://momentjs.com/guides/#/warnings/js-date/ | ||
// by doing a preliminary check on `dateString` | ||
const parsed = Date.parse(dateString); | ||
if (isNaN(parsed)) { | ||
// fail if we can't parse it | ||
return false; | ||
} | ||
// otherwise convert to ISO 8601 as moment prefers | ||
// http://momentjs.com/docs/#/parsing/string/ | ||
const date = new Date(parsed); | ||
return moment(date.toISOString()).isValid(); | ||
}; | ||
exports.validator = validator; | ||
//# sourceMappingURL=validator-extras.js.map |
192
package.json
{ | ||
"name": "@creditiq/sequelize", | ||
"description": "Multi dialect ORM for Node.JS", | ||
"version": "6.6.6", | ||
"maintainers": [ | ||
"Pedro Augusto de Paula Barbosa <papb1996@gmail.com>" | ||
"description": "Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Amazon Redshift and Snowflake’s Data Cloud. It features solid transaction support, relations, eager and lazy loading, read replication and more.", | ||
"version": "6.20.1", | ||
"funding": [ | ||
{ | ||
"type": "opencollective", | ||
"url": "https://opencollective.com/sequelize" | ||
} | ||
], | ||
@@ -16,4 +19,14 @@ "repository": { | ||
"homepage": "https://sequelize.org/", | ||
"main": "index.js", | ||
"types": "types", | ||
"main": "./lib/index.js", | ||
"types": "./types", | ||
"type": "commonjs", | ||
"exports": { | ||
".": { | ||
"import": "./lib/index.mjs", | ||
"require": "./lib/index.js" | ||
}, | ||
"./lib/*": "./lib/*.js", | ||
"./lib/errors": "./lib/errors/index.js", | ||
"./*": "./*" | ||
}, | ||
"engines": { | ||
@@ -24,35 +37,46 @@ "node": ">=10.0.0" | ||
"lib", | ||
"types/index.d.ts", | ||
"types/lib", | ||
"types/type-helpers" | ||
"types", | ||
"index.js" | ||
], | ||
"license": "MIT", | ||
"dependencies": { | ||
"debug": "^4.1.1", | ||
"dottie": "^2.0.0", | ||
"inflection": "1.13.1", | ||
"lodash": "^4.17.20", | ||
"moment": "^2.26.0", | ||
"moment-timezone": "^0.5.31", | ||
"retry-as-promised": "^3.2.0", | ||
"semver": "^7.3.2", | ||
"sequelize-pool": "^6.0.0", | ||
"@types/debug": "^4.1.7", | ||
"@types/validator": "^13.7.1", | ||
"debug": "^4.3.3", | ||
"dottie": "^2.0.2", | ||
"inflection": "^1.13.2", | ||
"lodash": "^4.17.21", | ||
"moment": "^2.29.1", | ||
"moment-timezone": "^0.5.34", | ||
"pg-connection-string": "^2.5.0", | ||
"retry-as-promised": "^5.0.0", | ||
"semver": "^7.3.5", | ||
"sequelize-pool": "^7.1.0", | ||
"toposort-class": "^1.0.1", | ||
"uuid": "^8.1.0", | ||
"validator": "^13.6.0", | ||
"uuid": "^8.3.2", | ||
"validator": "^13.7.0", | ||
"wkx": "^0.5.0" | ||
}, | ||
"devDependencies": { | ||
"@commitlint/cli": "^11.0.0", | ||
"@commitlint/config-angular": "^11.0.0", | ||
"@types/node": "^12.12.42", | ||
"@types/validator": "^13.1.4", | ||
"acorn": "^8.0.4", | ||
"chai": "^4.x", | ||
"chai-as-promised": "^7.x", | ||
"chai-datetime": "^1.6.0", | ||
"cheerio": "^1.0.0-rc.3", | ||
"@commitlint/cli": "^15.0.0", | ||
"@commitlint/config-angular": "^15.0.0", | ||
"@octokit/rest": "^18.12.0", | ||
"@octokit/types": "^6.34.0", | ||
"@types/chai": "^4.3.0", | ||
"@types/lodash": "4.14.182", | ||
"@types/mocha": "^9.0.0", | ||
"@types/node": "^16.11.17", | ||
"@types/sinon": "^10.0.6", | ||
"@typescript-eslint/eslint-plugin": "^5.8.1", | ||
"@typescript-eslint/parser": "^5.8.1", | ||
"acorn": "^8.7.0", | ||
"chai": "^4.3.4", | ||
"chai-as-promised": "^7.1.1", | ||
"chai-datetime": "^1.8.0", | ||
"cheerio": "^1.0.0-rc.10", | ||
"cls-hooked": "^4.2.2", | ||
"cross-env": "^7.0.2", | ||
"delay": "^4.3.0", | ||
"copyfiles": "^2.4.1", | ||
"cross-env": "^7.0.3", | ||
"delay": "^5.0.0", | ||
"esbuild": "0.14.3", | ||
"esdoc": "^1.1.0", | ||
@@ -62,17 +86,20 @@ "esdoc-ecmascript-proposal-plugin": "^1.0.0", | ||
"esdoc-standard-plugin": "^1.0.0", | ||
"eslint": "^6.8.0", | ||
"eslint-plugin-jsdoc": "^20.4.0", | ||
"eslint-plugin-mocha": "^6.2.2", | ||
"expect-type": "^0.11.0", | ||
"fs-jetpack": "^4.1.0", | ||
"husky": "^4.2.5", | ||
"js-combinatorics": "^0.5.5", | ||
"lcov-result-merger": "^3.0.0", | ||
"lint-staged": "^10.2.6", | ||
"mariadb": "^2.3.1", | ||
"markdownlint-cli": "^0.26.0", | ||
"marked": "^1.1.0", | ||
"mocha": "^7.1.2", | ||
"mysql2": "^2.1.0", | ||
"nyc": "^15.0.0", | ||
"eslint": "^8.5.0", | ||
"eslint-plugin-jsdoc": "^37.4.0", | ||
"eslint-plugin-mocha": "^9.0.0", | ||
"expect-type": "^0.12.0", | ||
"fast-glob": "^3.2.7", | ||
"fs-jetpack": "^4.3.0", | ||
"husky": "^7.0.4", | ||
"ibm_db": "^2.8.1", | ||
"js-combinatorics": "^0.6.1", | ||
"lcov-result-merger": "^3.1.0", | ||
"lint-staged": "^12.1.4", | ||
"mariadb": "^2.5.5", | ||
"markdownlint-cli": "^0.30.0", | ||
"mocha": "^7.2.0", | ||
"module-alias": "^2.2.2", | ||
"mysql2": "^2.3.3", | ||
"node-hook": "^1.0.0", | ||
"nyc": "^15.1.0", | ||
"p-map": "^4.0.0", | ||
@@ -82,12 +109,14 @@ "p-props": "^4.0.0", | ||
"p-timeout": "^4.0.0", | ||
"pg": "^8.2.1", | ||
"pg-hstore": "^2.x", | ||
"pg": "^8.7.1", | ||
"pg-hstore": "^2.3.4", | ||
"rimraf": "^3.0.2", | ||
"semantic-release": "^17.3.0", | ||
"semantic-release": "^18.0.1", | ||
"semantic-release-fail-on-major-bump": "^1.0.0", | ||
"sinon": "^9.0.2", | ||
"sinon-chai": "^3.3.0", | ||
"sqlite3": "^4.2.0", | ||
"sinon": "^12.0.1", | ||
"sinon-chai": "^3.7.0", | ||
"snowflake-sdk": "^1.6.6", | ||
"source-map-support": "^0.5.21", | ||
"sqlite3": "npm:@vscode/sqlite3@^5.0.7", | ||
"tedious": "8.3.0", | ||
"typescript": "^4.1.3" | ||
"typescript": "^4.5.4" | ||
}, | ||
@@ -104,2 +133,8 @@ "peerDependenciesMeta": { | ||
}, | ||
"ibm_db": { | ||
"optional": true | ||
}, | ||
"snowflake-sdk": { | ||
"optional": true | ||
}, | ||
"mariadb": { | ||
@@ -123,4 +158,7 @@ "optional": true | ||
"mssql", | ||
"db2", | ||
"ibm_db", | ||
"sql", | ||
"sqlserver", | ||
"snowflake", | ||
"orm", | ||
@@ -157,10 +195,4 @@ "nodejs", | ||
"lint-staged": { | ||
"*.js": "eslint" | ||
"*!(d).[tj]s": "eslint" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged", | ||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS" | ||
} | ||
}, | ||
"release": { | ||
@@ -175,3 +207,7 @@ "plugins": [ | ||
"branches": [ | ||
"v6" | ||
"v6", | ||
{ | ||
"name": "v6-beta", | ||
"prerelease": "beta" | ||
} | ||
] | ||
@@ -184,16 +220,17 @@ }, | ||
"----------------------------------------- static analysis -----------------------------------------": "", | ||
"lint": "eslint lib test --quiet", | ||
"lint": "eslint src test --quiet --fix", | ||
"lint-docs": "markdownlint docs", | ||
"test-typings": "tsc -b types/tsconfig.json && tsc -b types/test/tsconfig.json", | ||
"test-typings": "tsc --noEmit --emitDeclarationOnly false && tsc -b test/tsconfig.json", | ||
"----------------------------------------- documentation -------------------------------------------": "", | ||
"docs": "rimraf esdoc && esdoc -c docs/esdoc-config.js && cp docs/favicon.ico esdoc/favicon.ico && cp docs/ROUTER.txt esdoc/ROUTER && node docs/run-docs-transforms.js && node docs/redirects/create-redirects.js && rimraf esdoc/file esdoc/source.html", | ||
"docs": "sh docs.sh", | ||
"----------------------------------------- tests ---------------------------------------------------": "", | ||
"test-unit": "mocha \"test/unit/**/*.test.js\"", | ||
"test-integration": "mocha \"test/integration/**/*.test.js\"", | ||
"mocha": "mocha -r ./test/registerEsbuild", | ||
"test-unit": "yarn mocha \"test/unit/**/*.test.[tj]s\"", | ||
"test-integration": "yarn mocha \"test/integration/**/*.test.[tj]s\"", | ||
"teaser": "node test/teaser.js", | ||
"test": "npm run teaser && npm run test-unit && npm run test-integration", | ||
"test": "npm run prepare && npm run test-typings && npm run teaser && npm run test-unit && npm run test-integration", | ||
"----------------------------------------- coverage ------------------------------------------------": "", | ||
"cover": "rimraf coverage && npm run teaser && npm run cover-integration && npm run cover-unit && npm run merge-coverage", | ||
"cover-integration": "cross-env COVERAGE=true nyc --reporter=lcovonly mocha \"test/integration/**/*.test.js\" && node -e \"require('fs').renameSync('coverage/lcov.info', 'coverage/integration.info')\"", | ||
"cover-unit": "cross-env COVERAGE=true nyc --reporter=lcovonly mocha \"test/unit/**/*.test.js\" && node -e \"require('fs').renameSync('coverage/lcov.info', 'coverage/unit.info')\"", | ||
"cover-integration": "cross-env COVERAGE=true nyc --reporter=lcovonly yarn mocha \"test/integration/**/*.test.[tj]s\" && node -e \"require('fs').renameSync('coverage/lcov.info', 'coverage/integration.info')\"", | ||
"cover-unit": "cross-env COVERAGE=true nyc --reporter=lcovonly yarn mocha \"test/unit/**/*.test.[tj]s\" && node -e \"require('fs').renameSync('coverage/lcov.info', 'coverage/unit.info')\"", | ||
"merge-coverage": "lcov-result-merger \"coverage/*.info\" \"coverage/lcov.info\"", | ||
@@ -203,8 +240,12 @@ "----------------------------------------- local test dbs ------------------------------------------": "", | ||
"start-mysql": "bash dev/mysql/5.7/start.sh", | ||
"start-mysql-8": "bash dev/mysql/8.0/start.sh", | ||
"start-postgres": "bash dev/postgres/10/start.sh", | ||
"start-mssql": "bash dev/mssql/2019/start.sh", | ||
"start-db2": "bash dev/db2/11.5/start.sh", | ||
"stop-mariadb": "bash dev/mariadb/10.3/stop.sh", | ||
"stop-mysql": "bash dev/mysql/5.7/stop.sh", | ||
"stop-mysql-8": "bash dev/mysql/8.0/stop.sh", | ||
"stop-postgres": "bash dev/postgres/10/stop.sh", | ||
"stop-mssql": "bash dev/mssql/2019/stop.sh", | ||
"stop-db2": "bash dev/db2/11.5/stop.sh", | ||
"restart-mariadb": "npm run start-mariadb", | ||
@@ -214,2 +255,3 @@ "restart-mysql": "npm run start-mysql", | ||
"restart-mssql": "npm run start-mssql", | ||
"restart-db2": "npm run start-db2", | ||
"----------------------------------------- local tests ---------------------------------------------": "", | ||
@@ -222,2 +264,5 @@ "test-unit-mariadb": "cross-env DIALECT=mariadb npm run test-unit", | ||
"test-unit-mssql": "cross-env DIALECT=mssql npm run test-unit", | ||
"test-unit-db2": "cross-env DIALECT=db2 npm run test-unit", | ||
"test-unit-snowflake": "cross-env DIALECT=snowflake npm run test-unit", | ||
"test-unit-all": "npm run test-unit-mariadb && npm run test-unit-mysql && npm run test-unit-postgres && npm run test-unit-postgres-native && npm run test-unit-mssql && npm run test-unit-sqlite && npm run test-unit-snowflake && npm run test-unit-db2", | ||
"test-integration-mariadb": "cross-env DIALECT=mariadb npm run test-integration", | ||
@@ -229,2 +274,4 @@ "test-integration-mysql": "cross-env DIALECT=mysql npm run test-integration", | ||
"test-integration-mssql": "cross-env DIALECT=mssql npm run test-integration", | ||
"test-integration-db2": "cross-env DIALECT=db2 npm run test-integration", | ||
"test-integration-snowflake": "cross-env DIALECT=snowflake npm run test-integration", | ||
"test-mariadb": "cross-env DIALECT=mariadb npm test", | ||
@@ -236,2 +283,3 @@ "test-mysql": "cross-env DIALECT=mysql npm test", | ||
"test-mssql": "cross-env DIALECT=mssql npm test", | ||
"test-db2": "cross-env DIALECT=db2 npm test", | ||
"----------------------------------------- development ---------------------------------------------": "", | ||
@@ -245,4 +293,8 @@ "sscce": "node sscce.js", | ||
"sscce-mssql": "cross-env DIALECT=mssql node sscce.js", | ||
"sscce-db2": "cross-env DIALECT=db2 node sscce.js", | ||
"prepare": "npm run build && husky install", | ||
"build": "node ./build.js", | ||
"---------------------------------------------------------------------------------------------------": "" | ||
} | ||
}, | ||
"support": true | ||
} |
@@ -1,11 +0,14 @@ | ||
# Sequelize | ||
<p align="center"> | ||
<img src="docs/images/logo-small.png" width="100" /> | ||
<h1 align="center">Sequelize</h1> | ||
</p> | ||
[![npm version](https://badgen.net/npm/v/sequelize)](https://www.npmjs.com/package/sequelize) | ||
[![Build Status](https://github.com/sequelize/sequelize/workflows/CI/badge.svg)](https://github.com/sequelize/sequelize/actions?query=workflow%3ACI) | ||
<!-- [![codecov](https://badgen.net/codecov/c/github/sequelize/sequelize/main?icon=codecov)](https://codecov.io/gh/sequelize/sequelize) --> | ||
[![npm downloads](https://badgen.net/npm/dm/sequelize)](https://www.npmjs.com/package/sequelize) | ||
[![sponsor](https://img.shields.io/opencollective/all/sequelize?label=sponsors)](https://opencollective.com/sequelize) | ||
[![Merged PRs](https://badgen.net/github/merged-prs/sequelize/sequelize)](https://github.com/sequelize/sequelize) | ||
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) | ||
Sequelize is a promise-based [Node.js](https://nodejs.org/en/about/) [ORM tool](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Postgres](https://en.wikipedia.org/wiki/PostgreSQL), [MySQL](https://en.wikipedia.org/wiki/MySQL), [MariaDB](https://en.wikipedia.org/wiki/MariaDB), [SQLite](https://en.wikipedia.org/wiki/SQLite) and [Microsoft SQL Server](https://en.wikipedia.org/wiki/Microsoft_SQL_Server). It features solid transaction support, relations, eager and lazy loading, read replication and more. | ||
Sequelize is a promise-based [Node.js](https://nodejs.org/en/about/) [ORM tool](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Postgres](https://en.wikipedia.org/wiki/PostgreSQL), [MySQL](https://en.wikipedia.org/wiki/MySQL), [MariaDB](https://en.wikipedia.org/wiki/MariaDB), [SQLite](https://en.wikipedia.org/wiki/SQLite), [DB2](https://en.wikipedia.org/wiki/IBM_Db2_Family) and [Microsoft SQL Server](https://en.wikipedia.org/wiki/Microsoft_SQL_Server). It features solid transaction support, relations, eager and lazy loading, read replication and more. | ||
@@ -22,10 +25,8 @@ Sequelize follows [Semantic Versioning](http://semver.org) and supports Node v10 and above. | ||
## Note: Looking for maintainers! | ||
## Supporting the project | ||
Recently, a bigger part of the former core maintainers (thanks to all your hard work!) have been rather busy. Hence, the available time to look after our beloved ORM has been shrinking and shrinking drastically, generating a great chance for you: | ||
Do you like Sequelize and would like to give back to the engineering team behind it? | ||
We are looking for more core maintainers who are interested in improving/fixing our TypeScript typings, improving the documentation, organizing issues, reviewing PRs, streamlining the overall code base and planning the future roadmap. | ||
We have recently created an [OpenCollective based money pool](https://opencollective.com/sequelize) which is shared amongst all core maintainers based on their contributions. Every support is wholeheartedly welcome. ❤️ | ||
If that sounds interesting to you, please reach out to us on [our Slack channel](https://sequelize.slack.com/) by sending a direct message to *Pedro A P B*. If you don't have access, get yourself an invite automatically via [this link](http://sequelize-slack.herokuapp.com/). We are looking forward to meet you! | ||
## Installation | ||
@@ -42,2 +43,3 @@ | ||
$ npm i tedious # Microsoft SQL Server | ||
$ npm i ibm_db #DB2 | ||
``` | ||
@@ -69,2 +71,3 @@ | ||
- [Plugins](https://sequelize.org/master/manual/resources.html) | ||
- [For YugabyteDB](https://github.com/yugabyte/sequelize-yugabytedb) | ||
@@ -75,1 +78,2 @@ ### Translations | ||
- [中文文档](https://github.com/demopark/sequelize-docs-Zh-CN) (UNOFFICIAL) | ||
@@ -1,20 +0,20 @@ | ||
import DataTypes = require('./lib/data-types'); | ||
import Deferrable = require('./lib/deferrable'); | ||
import Op = require('./lib/operators'); | ||
import QueryTypes = require('./lib/query-types'); | ||
import TableHints = require('./lib/table-hints'); | ||
import IndexHints = require('./lib/index-hints'); | ||
import Utils = require('./lib/utils'); | ||
import DataTypes = require('./data-types'); | ||
import Deferrable = require('./deferrable'); | ||
import Op from './operators'; | ||
import QueryTypes = require('./query-types'); | ||
import TableHints = require('./table-hints'); | ||
import IndexHints = require('./index-hints'); | ||
import Utils = require('./utils'); | ||
export * from './lib/sequelize'; | ||
export * from './lib/query-interface'; | ||
export * from './lib/data-types'; | ||
export * from './lib/model'; | ||
export * from './lib/transaction'; | ||
export * from './lib/associations/index'; | ||
export * from './lib/errors'; | ||
export { BaseError as Error } from './lib/errors'; | ||
export { useInflection } from './lib/utils'; | ||
export * from './associations/index'; | ||
export * from './data-types'; | ||
export * from './errors/index'; | ||
export { BaseError as Error } from './errors/index'; | ||
export * from './model'; | ||
export * from './dialects/abstract/query-interface'; | ||
export * from './sequelize'; | ||
export * from './transaction'; | ||
export { useInflection } from './utils'; | ||
export { Validator } from './utils/validator-extras'; | ||
export { Utils, QueryTypes, Op, TableHints, IndexHints, DataTypes, Deferrable }; | ||
export { Validator as validator } from './lib/utils/validator-extras'; | ||
@@ -21,0 +21,0 @@ /** |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
2648726
288
30469
76
16
59
1
+ Added@types/debug@^4.1.7
+ Added@types/validator@^13.7.1
+ Addedpg-connection-string@^2.5.0
+ Added@types/debug@4.1.12(transitive)
+ Added@types/ms@0.7.34(transitive)
+ Added@types/validator@13.11.10(transitive)
+ Addedinflection@1.13.4(transitive)
+ Addedpg-connection-string@2.6.4(transitive)
+ Addedretry-as-promised@5.0.0(transitive)
+ Addedsequelize-pool@7.1.0(transitive)
- Removedany-promise@1.3.0(transitive)
- Removedinflection@1.13.1(transitive)
- Removedretry-as-promised@3.2.0(transitive)
- Removedsequelize-pool@6.1.0(transitive)
Updateddebug@^4.3.3
Updateddottie@^2.0.2
Updatedinflection@^1.13.2
Updatedlodash@^4.17.21
Updatedmoment@^2.29.1
Updatedmoment-timezone@^0.5.34
Updatedretry-as-promised@^5.0.0
Updatedsemver@^7.3.5
Updatedsequelize-pool@^7.1.0
Updateduuid@^8.3.2
Updatedvalidator@^13.7.0