sequelize
Version:
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.
646 lines (645 loc) • 23.3 kB
JavaScript
"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").default;
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 {
constructor(database, username, password, options) {
let config;
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") {
config = {};
options = username || {};
const urlParts = url.parse(arguments[0], true);
options.dialect = urlParts.protocol.replace(/:$/, "");
options.host = urlParts.hostname;
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(/^\//, "");
}
if (urlParts.port) {
options.port = urlParts.port;
}
if (urlParts.auth) {
const authParts = urlParts.auth.split(":");
config.username = authParts[0];
if (authParts.length > 1)
config.password = authParts.slice(1).join(":");
}
if (urlParts.query) {
if (urlParts.query.host) {
options.host = urlParts.query.host;
}
if (options.dialectOptions) {
Object.assign(options.dialectOptions, urlParts.query);
} else {
options.dialectOptions = urlParts.query;
if (urlParts.query.options) {
try {
const o = JSON.parse(urlParts.query.options);
options.dialectOptions.options = o;
} catch (e) {
}
}
}
}
if (["postgres", "postgresql"].includes(options.dialect)) {
Object.assign(options.dialectOptions, pgConnectionString.parse(arguments[0]));
}
} else {
options = options || {};
config = { database, username, password };
}
Sequelize.runHooks("beforeInit", config, options);
this.options = __spreadValues({
dialect: null,
dialectModule: null,
dialectModulePath: null,
host: "localhost",
protocol: "tcp",
define: {},
query: {},
sync: {},
timezone: "+00:00",
standardConformingStrings: true,
logging: console.log,
omitNull: false,
native: false,
replication: false,
ssl: void 0,
pool: {},
quoteIdentifiers: true,
hooks: {},
retry: {
max: 5,
match: [
"SQLITE_BUSY: database is locked"
]
},
transactionType: Transaction.TYPES.DEFERRED,
isolationLevel: null,
databaseVersion: 0,
typeValidation: false,
benchmark: false,
minifyAliases: false,
logQueryParameters: false,
attributeBehavior: "throw"
}, options);
if (!this.options.dialect) {
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 === "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();
this.options.logging = console.log;
}
this._setupHooks(options.hooks);
this.config = {
database: config.database || this.options.database,
username: config.username || this.options.username,
password: config.password || this.options.password || null,
host: config.host || this.options.host,
port: config.port || this.options.port,
pool: this.options.pool,
protocol: this.options.protocol,
native: this.options.native,
ssl: this.options.ssl,
replication: this.options.replication,
dialectModule: this.options.dialectModule,
dialectModulePath: this.options.dialectModulePath,
keepDefaultTimezone: this.options.keepDefaultTimezone,
dialectOptions: this.options.dialectOptions
};
let Dialect;
switch (this.getDialect()) {
case "mariadb":
Dialect = require("./dialects/mariadb");
break;
case "mssql":
Dialect = require("./dialects/mssql");
break;
case "mysql":
Dialect = require("./dialects/mysql");
break;
case "oracle":
Dialect = require("./dialects/oracle");
break;
case "postgres":
Dialect = require("./dialects/postgres");
break;
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, oracle, 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") {
deprecations.noBoolOperatorAliases();
}
this.queryInterface = this.dialect.queryInterface;
this.models = {};
this.modelManager = new ModelManager(this);
this.connectionManager = this.dialect.connectionManager;
Sequelize.runHooks("afterInit", this);
}
refreshTypes() {
this.connectionManager.refreshTypeParser(DataTypes);
}
getDialect() {
return this.options.dialect;
}
getDatabaseName() {
return this.config.database;
}
getQueryInterface() {
return this.queryInterface;
}
define(modelName, attributes, options = {}) {
options.modelName = modelName;
options.sequelize = this;
const model = class extends Model {
};
model.init(attributes, options);
return model;
}
model(modelName) {
if (!this.isDefined(modelName)) {
throw new Error(`${modelName} has not been defined`);
}
return this.modelManager.getModel(modelName);
}
isDefined(modelName) {
return !!this.modelManager.models.find((model) => model.name === modelName);
}
async query(sql, 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;
}
if (options.mapToModel) {
options.fieldMap = _.get(options, "model.fieldAttributeMap", {});
}
options = _.defaults(options, {
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) {
if (options.model || options.nest || options.plain) {
options.type = QueryTypes.SELECT;
} else {
options.type = QueryTypes.RAW;
}
}
if (!this.dialect.supports.searchPath || !this.options.dialectOptions || !this.options.dialectOptions.prependSearchPath || options.supportsSearchPath === false) {
delete options.searchPath;
} else if (!options.searchPath) {
options.searchPath = "DEFAULT";
}
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 !== 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 !== 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");
}
if (options.replacements) {
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 = () => {
if (options.transaction && options.transaction.finished && !options.completesTransaction) {
const error = new Error(`${options.transaction.finished} has been called on this transaction(${options.transaction.id}), you can no longer use it. (The rejected query is attached as the 'sql' property of this error)`);
error.sql = sql;
throw error;
}
};
const retryOptions = __spreadValues(__spreadValues({}, this.options.retry), options.retry);
return retry(async () => {
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);
checkTransaction();
return await query.run(sql, bindParameters);
} finally {
await this.runHooks("afterQuery", options, query);
if (!options.transaction) {
this.connectionManager.releaseConnection(connection);
}
}
}, retryOptions);
}
async set(variables, options) {
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");
}
options.raw = true;
options.plain = true;
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) {
return this.dialect.queryGenerator.escape(value);
}
async createSchema(schema, options) {
return await this.getQueryInterface().createSchema(schema, options);
}
async showAllSchemas(options) {
return await this.getQueryInterface().showAllSchemas(options);
}
async dropSchema(schema, options) {
return await this.getQueryInterface().dropSchema(schema, options);
}
async dropAllSchemas(options) {
return await this.getQueryInterface().dropAllSchemas(options);
}
async sync(options) {
options = __spreadProps(__spreadValues(__spreadValues(__spreadValues({}, this.options), this.options.sync), options), {
hooks: options ? options.hooks !== false : true
});
if (options.match) {
if (!options.match.test(this.config.database)) {
throw new Error(`Database "${this.config.database}" does not match sync match parameter "${options.match}"`);
}
}
if (options.hooks) {
await this.runHooks("beforeBulkSync", options);
}
if (options.force) {
await this.drop(options);
}
if (this.modelManager.models.length === 0) {
await this.authenticate(options);
} else {
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);
}
return this;
}
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 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);
} else {
await Promise.all(models.map((model) => model.truncate(options)));
}
}
async drop(options) {
if (options && options.cascade) {
for (const model of this.modelManager.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);
}
}
async authenticate(options) {
options = __spreadValues({
raw: true,
plain: true,
type: QueryTypes.SELECT
}, options);
await this.query(this.dialect.queryGenerator.authTestQuery(), options);
return;
}
async databaseVersion(options) {
return await this.getQueryInterface().databaseVersion(options);
}
random() {
if (["postgres", "sqlite", "snowflake"].includes(this.getDialect())) {
return this.fn("RANDOM");
}
return this.fn("RAND");
}
static fn(fn, ...args) {
return new Utils.Fn(fn, args);
}
static col(col) {
return new Utils.Col(col);
}
static cast(val, type) {
return new Utils.Cast(val, type);
}
static literal(val) {
return new Utils.Literal(val);
}
static and(...args) {
return { [Op.and]: args };
}
static or(...args) {
return { [Op.or]: args };
}
static json(conditionsOrPath, value) {
return new Utils.Json(conditionsOrPath, value);
}
static where(attr, comparator, logic) {
return new Utils.Where(attr, comparator, logic);
}
async transaction(options, autoCallback) {
if (typeof options === "function") {
autoCallback = options;
options = void 0;
}
const transaction = new Transaction(this, options);
if (!autoCallback) {
await transaction.prepareEnvironment(false);
return transaction;
}
return Sequelize._clsRun(async () => {
await transaction.prepareEnvironment(true);
let result;
try {
result = await autoCallback(transaction);
} catch (err) {
try {
await transaction.rollback();
} catch (ignore) {
}
throw err;
}
await transaction.commit();
return result;
});
}
static useCLS(ns) {
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;
}
static _clsRun(fn) {
const ns = Sequelize._cls;
if (!ns)
return fn();
let res;
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")) {
options = last;
if (options.logging === console.log) {
args.splice(args.length - 1, 1);
}
} else {
options = this.options;
}
if (options.logging) {
if (options.logging === true) {
deprecations.noTrueLogging();
options.logging = console.log;
}
if ((this.options.benchmark || options.benchmark) && options.logging === console.log) {
args = [`${args[0]} Elapsed time: ${args[1]}ms`];
}
options.logging(...args);
}
}
close() {
return this.connectionManager.close();
}
normalizeDataType(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.");
}
if (dialectTypes[type.type.key]) {
type.type = dialectTypes[type.type.key].extend(type.type);
}
}
return type;
}
normalizeAttribute(attribute) {
if (!_.isPlainObject(attribute)) {
attribute = { type: attribute };
}
if (!attribute.type)
return attribute;
attribute.type = this.normalizeDataType(attribute.type);
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) {
if (attribute.values) {
attribute.type.values = attribute.type.options.values = attribute.values;
} else {
attribute.values = attribute.type.values;
}
if (!attribute.values.length) {
throw new Error("Values for ENUM have not been defined.");
}
}
return attribute;
}
}
Sequelize.prototype.fn = Sequelize.fn;
Sequelize.prototype.col = Sequelize.col;
Sequelize.prototype.cast = Sequelize.cast;
Sequelize.prototype.literal = Sequelize.literal;
Sequelize.prototype.and = Sequelize.and;
Sequelize.prototype.or = Sequelize.or;
Sequelize.prototype.json = Sequelize.json;
Sequelize.prototype.where = Sequelize.where;
Sequelize.prototype.validate = Sequelize.prototype.authenticate;
Object.defineProperty(Sequelize, "version", {
enumerable: true,
get() {
return require("../package.json").version;
}
});
Sequelize.options = { hooks: {} };
Sequelize.Utils = Utils;
Sequelize.Op = Op;
Sequelize.TableHints = TableHints;
Sequelize.IndexHints = IndexHints;
Sequelize.Transaction = Transaction;
Sequelize.prototype.Sequelize = Sequelize;
Sequelize.prototype.QueryTypes = Sequelize.QueryTypes = QueryTypes;
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;
for (const dataType in DataTypes) {
Sequelize[dataType] = DataTypes[dataType];
}
Sequelize.Deferrable = Deferrable;
Sequelize.prototype.Association = Sequelize.Association = Association;
Sequelize.useInflection = Utils.useInflection;
Hooks.applyTo(Sequelize);
Hooks.applyTo(Sequelize.prototype);
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