1287 lines
41 KiB
JavaScript
1287 lines
41 KiB
JavaScript
|
'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
|
||
|
*/
|
||
|
class QueryInterface {
|
||
|
constructor(sequelize, queryGenerator) {
|
||
|
this.sequelize = sequelize;
|
||
|
this.queryGenerator = 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) {
|
||
|
options = options || {};
|
||
|
const sql = this.queryGenerator.createDatabaseQuery(database, options);
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Drop a database
|
||
|
*
|
||
|
* @param {string} database Database name to drop
|
||
|
* @param {object} [options] Query options
|
||
|
*
|
||
|
* @returns {Promise}
|
||
|
*/
|
||
|
async dropDatabase(database, options) {
|
||
|
options = options || {};
|
||
|
const sql = this.queryGenerator.dropDatabaseQuery(database);
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a schema
|
||
|
*
|
||
|
* @param {string} schema Schema name to create
|
||
|
* @param {object} [options] Query options
|
||
|
*
|
||
|
* @returns {Promise}
|
||
|
*/
|
||
|
async createSchema(schema, options) {
|
||
|
options = options || {};
|
||
|
const sql = this.queryGenerator.createSchema(schema);
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Drop a schema
|
||
|
*
|
||
|
* @param {string} schema Schema name to drop
|
||
|
* @param {object} [options] Query options
|
||
|
*
|
||
|
* @returns {Promise}
|
||
|
*/
|
||
|
async dropSchema(schema, options) {
|
||
|
options = options || {};
|
||
|
const sql = this.queryGenerator.dropSchema(schema);
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Drop all schemas
|
||
|
*
|
||
|
* @param {object} [options] Query options
|
||
|
*
|
||
|
* @returns {Promise}
|
||
|
*/
|
||
|
async dropAllSchemas(options) {
|
||
|
options = options || {};
|
||
|
|
||
|
if (!this.queryGenerator._dialect.supports.schemas) {
|
||
|
return this.sequelize.drop(options);
|
||
|
}
|
||
|
const schemas = await this.showAllSchemas(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,
|
||
|
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 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 }
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 };
|
||
|
|
||
|
if (options && options.uniqueKeys) {
|
||
|
_.forOwn(options.uniqueKeys, uniqueKey => {
|
||
|
if (uniqueKey.customIndex === undefined) {
|
||
|
uniqueKey.customIndex = true;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (model) {
|
||
|
options.uniqueKeys = options.uniqueKeys || model.uniqueKeys;
|
||
|
}
|
||
|
|
||
|
attributes = _.mapValues(
|
||
|
attributes,
|
||
|
attribute => this.sequelize.normalizeAttribute(attribute)
|
||
|
);
|
||
|
|
||
|
// Postgres requires special SQL commands for ENUM/ENUM[]
|
||
|
await this.ensureEnums(tableName, attributes, options, model);
|
||
|
|
||
|
if (
|
||
|
!tableName.schema &&
|
||
|
(options.schema || !!model && model._schema)
|
||
|
) {
|
||
|
tableName = this.queryGenerator.addSchema({
|
||
|
tableName,
|
||
|
_schema: !!model && model._schema || options.schema
|
||
|
});
|
||
|
}
|
||
|
|
||
|
attributes = this.queryGenerator.attributesToSQL(attributes, { table: tableName, context: 'createTable' });
|
||
|
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 dropTable(tableName, options) {
|
||
|
// if we're forcing we should be cascading unless explicitly stated otherwise
|
||
|
options = { ...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 } );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
let normalizedTableName = tableName;
|
||
|
if (_.isObject(tableName)) {
|
||
|
normalizedTableName = `${tableName.schema}.${tableName.tableName}`;
|
||
|
}
|
||
|
|
||
|
for (const foreignKey of foreignKeys[normalizedTableName]) {
|
||
|
await this.sequelize.query(this.queryGenerator.dropForeignKeyQuery(tableName, foreignKey));
|
||
|
}
|
||
|
}
|
||
|
await this._dropAllTables(tableNames, skip, options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
options = options || {};
|
||
|
const sql = this.queryGenerator.renameTableQuery(before, after);
|
||
|
return await this.sequelize.query(sql, 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,
|
||
|
raw: true,
|
||
|
type: QueryTypes.SHOWTABLES
|
||
|
};
|
||
|
|
||
|
const showTablesSql = this.queryGenerator.showTablesQuery(this.sequelize.config.database);
|
||
|
const tableNames = await this.sequelize.query(showTablesSql, options);
|
||
|
return _.flatten(tableNames);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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') {
|
||
|
schema = options;
|
||
|
} else if (typeof options === 'object' && options !== null) {
|
||
|
schema = options.schema || null;
|
||
|
schemaDelimiter = options.schemaDelimiter || 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 };
|
||
|
|
||
|
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') {
|
||
|
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)');
|
||
|
}
|
||
|
|
||
|
options = options || {};
|
||
|
attribute = this.sequelize.normalizeAttribute(attribute);
|
||
|
return await this.sequelize.query(this.queryGenerator.addColumnQuery(table, key, attribute), options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
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 = options || {};
|
||
|
|
||
|
const query = this.queryGenerator.attributesToSQL({
|
||
|
[attributeName]: this.normalizeAttribute(dataTypeOrOptions)
|
||
|
}, {
|
||
|
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) {
|
||
|
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 = 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
|
||
|
};
|
||
|
|
||
|
// 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)
|
||
|
);
|
||
|
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)) {
|
||
|
rawTablename = options;
|
||
|
options = attributes;
|
||
|
attributes = options.fields;
|
||
|
}
|
||
|
|
||
|
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 });
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 });
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns all foreign key constraints of requested tables
|
||
|
*
|
||
|
* @param {string[]} tableNames table names
|
||
|
* @param {object} [options] Query options
|
||
|
*
|
||
|
* @returns {Promise}
|
||
|
*/
|
||
|
async getForeignKeysForTables(tableNames, options) {
|
||
|
if (tableNames.length === 0) {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
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)));
|
||
|
|
||
|
const result = {};
|
||
|
|
||
|
tableNames.forEach((tableName, i) => {
|
||
|
if (_.isObject(tableName)) {
|
||
|
tableName = `${tableName.schema}.${tableName.tableName}`;
|
||
|
}
|
||
|
|
||
|
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,
|
||
|
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
|
||
|
* @param {boolean} [options.concurrently] Pass CONCURRENTLY so other operations run while the index is created
|
||
|
*
|
||
|
* @returns {Promise}
|
||
|
*/
|
||
|
async removeIndex(tableName, indexNameOrAttributes, options) {
|
||
|
options = options || {};
|
||
|
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');
|
||
|
}
|
||
|
|
||
|
if (!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 });
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
options = Utils.cloneDeep(options);
|
||
|
options.hasTrigger = instance && instance.constructor.options.hasTrigger;
|
||
|
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;
|
||
|
|
||
|
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 };
|
||
|
|
||
|
const model = options.model;
|
||
|
|
||
|
options.type = QueryTypes.UPSERT;
|
||
|
options.updateOnDuplicate = Object.keys(updateValues);
|
||
|
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 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;
|
||
|
}
|
||
|
|
||
|
const indexKey = indexKeys.find(fields => fields.includes(field));
|
||
|
if (indexKey) {
|
||
|
options.upsertKeys = indexKey;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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.type = QueryTypes.INSERT;
|
||
|
|
||
|
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 };
|
||
|
options.hasTrigger = instance && instance.constructor.options.hasTrigger;
|
||
|
|
||
|
const sql = this.queryGenerator.updateQuery(tableName, values, identifier, 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) {
|
||
|
options = Utils.cloneDeep(options);
|
||
|
if (typeof identifier === 'object') identifier = Utils.cloneDeep(identifier);
|
||
|
|
||
|
const sql = this.queryGenerator.updateQuery(tableName, values, identifier, options, attributes);
|
||
|
const table = _.isObject(tableName) ? tableName : { tableName };
|
||
|
const model = _.find(this.sequelize.modelManager.models, { tableName: table.tableName });
|
||
|
|
||
|
options.type = QueryTypes.BULKUPDATE;
|
||
|
options.model = model;
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
async delete(instance, tableName, identifier, options) {
|
||
|
const cascades = [];
|
||
|
const sql = this.queryGenerator.deleteQuery(tableName, identifier, {}, instance.constructor);
|
||
|
|
||
|
options = { ...options };
|
||
|
|
||
|
// Check for a restrict field
|
||
|
if (!!instance.constructor && !!instance.constructor.associations) {
|
||
|
const keys = Object.keys(instance.constructor.associations);
|
||
|
const length = keys.length;
|
||
|
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) {
|
||
|
cascades.push(association.accessors.get);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
options.instance = instance;
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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
|
||
|
);
|
||
|
}
|
||
|
|
||
|
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
|
||
|
);
|
||
|
}
|
||
|
|
||
|
async increment(model, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) {
|
||
|
options = Utils.cloneDeep(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);
|
||
|
|
||
|
options.type = QueryTypes.UPDATE;
|
||
|
options.model = model;
|
||
|
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
async rawSelect(tableName, options, attributeSelector, Model) {
|
||
|
options = Utils.cloneDeep(options);
|
||
|
options = _.defaults(options, {
|
||
|
raw: true,
|
||
|
plain: true,
|
||
|
type: QueryTypes.SELECT
|
||
|
});
|
||
|
|
||
|
const sql = this.queryGenerator.selectQuery(tableName, options, Model);
|
||
|
|
||
|
if (attributeSelector === undefined) {
|
||
|
throw new Error('Please pass an attribute selector!');
|
||
|
}
|
||
|
|
||
|
const data = await this.sequelize.query(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) {
|
||
|
if (result !== null) {
|
||
|
return parseFloat(result);
|
||
|
}
|
||
|
}
|
||
|
if (dataType instanceof DataTypes.INTEGER || dataType instanceof DataTypes.BIGINT) {
|
||
|
if (result !== null) {
|
||
|
return parseInt(result, 10);
|
||
|
}
|
||
|
}
|
||
|
if (dataType instanceof DataTypes.DATE) {
|
||
|
if (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 = options || {};
|
||
|
if (sql) {
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async dropTrigger(tableName, triggerName, options) {
|
||
|
const sql = this.queryGenerator.dropTrigger(tableName, triggerName);
|
||
|
options = options || {};
|
||
|
|
||
|
if (sql) {
|
||
|
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) {
|
||
|
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) {
|
||
|
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) {
|
||
|
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) {
|
||
|
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!');
|
||
|
}
|
||
|
|
||
|
if (transaction.parent || !value) {
|
||
|
// Not possible to set a separate isolation level for savepoints
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
options = { ...options, transaction: transaction.parent || transaction };
|
||
|
|
||
|
const sql = this.queryGenerator.setIsolationLevelQuery(value, {
|
||
|
parent: transaction.parent
|
||
|
});
|
||
|
|
||
|
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!');
|
||
|
}
|
||
|
|
||
|
options = { ...options, transaction: transaction.parent || transaction };
|
||
|
options.transaction.name = transaction.parent ? transaction.name : undefined;
|
||
|
const sql = this.queryGenerator.startTransactionQuery(transaction);
|
||
|
|
||
|
return await this.sequelize.query(sql, options);
|
||
|
}
|
||
|
|
||
|
async deferConstraints(transaction, options) {
|
||
|
options = { ...options, transaction: transaction.parent || transaction };
|
||
|
|
||
|
const sql = this.queryGenerator.deferConstraintsQuery(options);
|
||
|
|
||
|
if (sql) {
|
||
|
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!');
|
||
|
}
|
||
|
if (transaction.parent) {
|
||
|
// Savepoints cannot be committed
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
options = {
|
||
|
...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';
|
||
|
|
||
|
return await promise;
|
||
|
}
|
||
|
|
||
|
async rollbackTransaction(transaction, options) {
|
||
|
if (!transaction || !(transaction instanceof Transaction)) {
|
||
|
throw new Error('Unable to rollback a transaction without transaction object!');
|
||
|
}
|
||
|
|
||
|
options = {
|
||
|
...options,
|
||
|
transaction: transaction.parent || transaction,
|
||
|
supportsSearchPath: false,
|
||
|
completesTransaction: true
|
||
|
};
|
||
|
options.transaction.name = transaction.parent ? transaction.name : undefined;
|
||
|
const sql = this.queryGenerator.rollbackTransactionQuery(transaction);
|
||
|
const promise = this.sequelize.query(sql, options);
|
||
|
|
||
|
transaction.finished = 'rollback';
|
||
|
|
||
|
return await promise;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.QueryInterface = QueryInterface;
|