@sequelize/core
Version:
Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Amazon Redshift, Snowflake’s Data Cloud, Db2, and IBM i. It features solid transaction support, relations, eager and lazy loading, read replication a
725 lines (724 loc) • 26.7 kB
JavaScript
;
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 __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var query_interface_exports = {};
__export(query_interface_exports, {
AbstractQueryInterface: () => AbstractQueryInterface
});
module.exports = __toCommonJS(query_interface_exports);
var import_utils = require("@sequelize/utils");
var import_defaults = __toESM(require("lodash/defaults"));
var import_find = __toESM(require("lodash/find"));
var import_intersection = __toESM(require("lodash/intersection"));
var import_isObject = __toESM(require("lodash/isObject"));
var import_mapValues = __toESM(require("lodash/mapValues"));
var import_uniq = __toESM(require("lodash/uniq"));
var DataTypes = __toESM(require("../data-types"));
var import_query_types = require("../query-types");
var import_object = require("../utils/object");
var import_sql = require("../utils/sql");
var import_data_types = require("./data-types");
var import_query_interface_typescript = require("./query-interface-typescript");
class AbstractQueryInterface extends import_query_interface_typescript.AbstractQueryInterfaceTypeScript {
/**
* Create a table with given set of attributes
*
* ```js
* queryInterface.createTable(
* 'nameOfTheNewTable',
* {
* id: {
* type: DataTypes.INTEGER,
* primaryKey: true,
* autoIncrement: true
* },
* createdAt: {
* type: DataTypes.DATE
* },
* updatedAt: {
* type: DataTypes.DATE
* },
* attr1: DataTypes.STRING,
* attr2: DataTypes.INTEGER,
* attr3: {
* type: DataTypes.BOOLEAN,
* defaultValue: false,
* allowNull: false
* },
* //foreign key usage
* attr4: {
* type: DataTypes.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}
*/
// TODO: remove "schema" option from the option bag, it must be passed as part of "tableName" instead
async createTable(tableName, attributes, options, model) {
options = { ...options };
if (model && this.sequelize.dialect.name !== "sqlite3") {
options.uniqueKeys = options.uniqueKeys || model.uniqueKeys;
}
attributes = (0, import_mapValues.default)(attributes, (attribute) => this.sequelize.normalizeAttribute(attribute));
await this.ensureEnums(tableName, attributes, options, model);
await this.ensureSequences(tableName, attributes, options);
const modelTable = model?.table;
if (!tableName.schema && (options.schema || modelTable?.schema)) {
tableName = this.queryGenerator.extractTableDetails(tableName);
tableName.schema = modelTable?.schema || options.schema;
}
attributes = this.queryGenerator.attributesToSQL(attributes, {
table: tableName,
context: "createTable",
withoutForeignKeyConstraints: options.withoutForeignKeyConstraints,
// schema override for multi-tenancy
schema: options.schema
});
const sql = this.queryGenerator.createTableQuery(tableName, attributes, options);
return await this.sequelize.queryRaw(sql, options);
}
/**
* Add a new column to a table
*
* ```js
* queryInterface.addColumn('tableA', 'columnC', DataTypes.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)"
);
}
attribute = this.sequelize.normalizeAttribute(attribute);
if (attribute.type instanceof import_data_types.AbstractDataType && // we don't give a context if it already has one, because it could come from a Model.
!attribute.type.usageContext) {
attribute.type.attachUsageContext({
tableName: table,
columnName: key,
sequelize: this.sequelize
});
}
const { ifNotExists, ...rawQueryOptions } = options;
const addColumnQueryOptions = ifNotExists ? { ifNotExists } : void 0;
return await this.sequelize.queryRaw(
this.queryGenerator.addColumnQuery(table, key, attribute, addColumnQueryOptions),
rawQueryOptions
);
}
/**
* 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
*/
normalizeAttribute(dataTypeOrOptions) {
let attribute;
if (Object.values(DataTypes).includes(dataTypeOrOptions)) {
attribute = { type: dataTypeOrOptions, allowNull: true };
} else {
attribute = dataTypeOrOptions;
}
return this.sequelize.normalizeAttribute(attribute);
}
/**
* Split a list of identifiers by "." and quote each part
*
* @param {string} identifier
* @param {boolean} force
*
* @returns {string}
*/
quoteIdentifier(identifier, force) {
return this.queryGenerator.quoteIdentifier(identifier, force);
}
/**
* Split a list of identifiers by "." and quote each part.
*
* @param {string} identifiers
*
* @returns {string}
*/
quoteIdentifiers(identifiers) {
return this.queryGenerator.quoteIdentifiers(identifiers);
}
/**
* 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
*/
async changeColumn(tableName, attributeName, dataTypeOrOptions, options) {
options ||= {};
const query = this.queryGenerator.attributesToSQL(
{
[attributeName]: this.normalizeAttribute(dataTypeOrOptions)
},
{
context: "changeColumn",
table: tableName
}
);
const sql = this.queryGenerator.changeColumnQuery(tableName, query);
return this.sequelize.queryRaw(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
*/
// TODO: rename to "describeColumn"
async assertTableHasColumn(tableName, columnName, options) {
const description = await this.describeTable(tableName, options);
if (description[columnName]) {
return description;
}
throw new Error(`Table ${tableName} doesn't have the column ${columnName}`);
}
/**
* 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 ||= {};
const data = (await this.assertTableHasColumn(tableName, attrNameBefore, options))[attrNameBefore];
const _options = {};
_options[attrNameAfter] = {
attribute: attrNameAfter,
type: data.type,
allowNull: data.allowNull,
defaultValue: data.defaultValue
};
if (data.defaultValue === null && !data.allowNull) {
delete _options[attrNameAfter].defaultValue;
}
const sql = this.queryGenerator.renameColumnQuery(
tableName,
attrNameBefore,
this.queryGenerator.attributesToSQL(_options)
);
return await this.sequelize.queryRaw(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) {
if (!Array.isArray(attributes)) {
rawTablename = options;
options = attributes;
attributes = options.fields;
}
if (!rawTablename) {
rawTablename = tableName;
}
options = (0, import_object.cloneDeep)(options) ?? {};
options.fields = attributes;
const sql = this.queryGenerator.addIndexQuery(tableName, options, rawTablename);
return await this.sequelize.queryRaw(sql, { ...options, supportsSearchPath: false });
}
/**
* Show indexes on a table
*
* @param {TableOrModel} tableName
* @param {object} [options] Query options
*
* @returns {Promise<Array>}
* @private
*/
async showIndex(tableName, options) {
const sql = this.queryGenerator.showIndexesQuery(tableName, options);
return await this.sequelize.queryRaw(sql, { ...options, type: import_query_types.QueryTypes.SHOWINDEXES });
}
/**
* 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
* @param {boolean} [options.concurrently] Pass CONCURRENTLY so other operations run while the index is created
*
* @returns {Promise}
*/
async removeIndex(tableName, indexNameOrAttributes, options) {
options ||= {};
const sql = this.queryGenerator.removeIndexQuery(tableName, indexNameOrAttributes, options);
return await this.sequelize.queryRaw(sql, options);
}
async insert(instance, tableName, values, options) {
if (options?.bind) {
(0, import_sql.assertNoReservedBind)(options.bind);
}
options = (0, import_object.cloneDeep)(options) ?? {};
const modelDefinition = instance?.modelDefinition;
options.hasTrigger = modelDefinition?.options.hasTrigger;
const { bind, query } = this.queryGenerator.insertQuery(
tableName,
values,
modelDefinition && (0, import_object.getObjectFromMap)(modelDefinition.attributes),
options
);
options.type = import_query_types.QueryTypes.INSERT;
options.instance = instance;
delete options.replacements;
options.bind = (0, import_sql.combineBinds)(options.bind, bind);
const results = await this.sequelize.queryRaw(query, options);
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>
*/
// Note: "where" is only used by DB2 and MSSQL. This is because these dialects do not propose any "ON CONFLICT UPDATE" mechanisms
// The UPSERT pattern in SQL server requires providing a WHERE clause
// TODO: the user should be able to configure the WHERE clause for upsert instead of the current default which
// is using the primary keys.
async upsert(tableName, insertValues, updateValues, where, options) {
if (!this.dialect.supports.upserts) {
throw new Error(`Upserts are not supported by the ${this.dialect.name} dialect.`);
}
if (options?.bind) {
(0, import_sql.assertNoReservedBind)(options.bind);
}
options = { ...options };
const model = options.model;
const modelDefinition = model.modelDefinition;
options.type = import_query_types.QueryTypes.UPSERT;
options.updateOnDuplicate = Object.keys(updateValues);
options.upsertKeys = options.conflictFields || [];
if (options.upsertKeys.length === 0) {
const primaryKeys = Array.from(
(0, import_utils.map)(
modelDefinition.primaryKeysAttributeNames,
(pkAttrName) => modelDefinition.attributes.get(pkAttrName).columnName
)
);
const uniqueColumnNames = Object.values(model.getIndexes()).filter((c) => c.unique && c.fields.length > 0).map((c) => c.fields);
for (const field of options.updateOnDuplicate) {
const indexKey = uniqueColumnNames.find((fields) => fields.includes(field));
if (indexKey) {
options.upsertKeys = indexKey;
break;
}
}
if (options.upsertKeys.length === 0 || (0, import_intersection.default)(options.updateOnDuplicate, primaryKeys).length > 0) {
options.upsertKeys = primaryKeys;
}
options.upsertKeys = (0, import_uniq.default)(options.upsertKeys);
}
const { bind, query } = this.queryGenerator.insertQuery(
tableName,
insertValues,
(0, import_object.getObjectFromMap)(modelDefinition.attributes),
options
);
delete options.replacement;
options.bind = (0, import_sql.combineBinds)(options.bind, bind);
return await this.sequelize.queryRaw(query, 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, type: import_query_types.QueryTypes.INSERT };
const sql = this.queryGenerator.bulkInsertQuery(tableName, records, options, attributes);
delete options.replacements;
const results = await this.sequelize.queryRaw(sql, options);
return results[0];
}
async update(instance, tableName, values, where, options) {
if (options?.bind) {
(0, import_sql.assertNoReservedBind)(options.bind);
}
const modelDefinition = instance?.modelDefinition;
options = { ...options, model: instance?.constructor };
options.hasTrigger = modelDefinition?.options.hasTrigger;
const { bind, query } = this.queryGenerator.updateQuery(
tableName,
values,
where,
options,
modelDefinition && (0, import_object.getObjectFromMap)(modelDefinition.attributes)
);
options.type = import_query_types.QueryTypes.UPDATE;
options.instance = instance;
delete options.replacements;
options.bind = (0, import_sql.combineBinds)(options.bind, bind);
return await this.sequelize.queryRaw(query, 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} where 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} [columnDefinitions] Attributes on return objects if supported by SQL dialect
*
* @returns {Promise}
*/
async bulkUpdate(tableName, values, where, options, columnDefinitions) {
if (options?.bind) {
(0, import_sql.assertNoReservedBind)(options.bind);
}
options = (0, import_object.cloneDeep)(options) ?? {};
if (typeof where === "object") {
where = (0, import_object.cloneDeep)(where) ?? {};
}
const { bind, query } = this.queryGenerator.updateQuery(
tableName,
values,
where,
options,
columnDefinitions
);
const table = (0, import_isObject.default)(tableName) ? tableName : { tableName };
const model = options.model ? options.model : (0, import_find.default)(this.sequelize.models, { tableName: table.tableName });
options.type = import_query_types.QueryTypes.BULKUPDATE;
options.model = model;
options.bind = (0, import_sql.combineBinds)(options.bind, bind);
return await this.sequelize.queryRaw(query, options);
}
async select(model, tableName, optionsArg) {
const minifyAliases = optionsArg.minifyAliases ?? this.sequelize.options.minifyAliases;
const options = { ...optionsArg, type: import_query_types.QueryTypes.SELECT, model, minifyAliases };
const sql = this.queryGenerator.selectQuery(tableName, options, model);
delete options.replacements;
return await this.sequelize.queryRaw(sql, options);
}
async increment(model, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) {
return this.#arithmeticQuery(
"+",
model,
tableName,
where,
incrementAmountsByField,
extraAttributesToBeUpdated,
options
);
}
async decrement(model, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) {
return this.#arithmeticQuery(
"-",
model,
tableName,
where,
incrementAmountsByField,
extraAttributesToBeUpdated,
options
);
}
async #arithmeticQuery(operator, model, tableName, where, incrementAmountsByAttribute, extraAttributesToBeUpdated, options) {
options = (0, import_object.cloneDeep)(options) ?? {};
options.model = model;
const sql = this.queryGenerator.arithmeticQuery(
operator,
tableName,
where,
incrementAmountsByAttribute,
extraAttributesToBeUpdated,
options
);
options.type = import_query_types.QueryTypes.UPDATE;
delete options.replacements;
return await this.sequelize.queryRaw(sql, options);
}
async rawSelect(tableName, options, attributeSelector, Model) {
options = (0, import_object.cloneDeep)(options) ?? {};
options = (0, import_defaults.default)(options, {
raw: true,
plain: true,
type: import_query_types.QueryTypes.SELECT
});
const sql = this.queryGenerator.selectQuery(tableName, options, Model);
if (attributeSelector === void 0) {
throw new Error("Please pass an attribute selector!");
}
delete options.replacements;
const data = await this.sequelize.queryRaw(sql, options);
if (!options.plain) {
return data;
}
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) && result !== null) {
return Number.parseFloat(result);
}
if ((dataType instanceof DataTypes.INTEGER || dataType instanceof DataTypes.BIGINT) && result !== null) {
return Number.parseInt(result, 10);
}
if (dataType instanceof DataTypes.DATE && result !== null && !(result instanceof Date)) {
return new Date(result);
}
return result;
}
async createTrigger(tableName, triggerName, timingType, fireOnArray, functionName, functionParams, optionsArray, options) {
const sql = this.queryGenerator.createTrigger(
tableName,
triggerName,
timingType,
fireOnArray,
functionName,
functionParams,
optionsArray
);
options ||= {};
if (sql) {
return await this.sequelize.queryRaw(sql, options);
}
}
async dropTrigger(tableName, triggerName, options) {
const sql = this.queryGenerator.dropTrigger(tableName, triggerName);
options ||= {};
if (sql) {
return await this.sequelize.queryRaw(sql, options);
}
}
async renameTrigger(tableName, oldTriggerName, newTriggerName, options) {
const sql = this.queryGenerator.renameTrigger(tableName, oldTriggerName, newTriggerName);
options ||= {};
if (sql) {
return await this.sequelize.queryRaw(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 ||= {};
if (sql) {
return await this.sequelize.queryRaw(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 ||= {};
if (sql) {
return await this.sequelize.queryRaw(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 ||= {};
if (sql) {
return await this.sequelize.queryRaw(sql, options);
}
}
// Helper methods useful for querying
/**
* @private
*/
ensureEnums() {
}
/**
* @private
*/
ensureSequences() {
}
/**
* @private
*/
getNextPrimaryKeyValue() {
}
}
//# sourceMappingURL=query-interface.js.map