1465 lines
47 KiB
JavaScript
1465 lines
47 KiB
JavaScript
'use strict';
|
|
|
|
const _ = require('lodash');
|
|
|
|
const Utils = require('./utils');
|
|
const DataTypes = require('./data-types');
|
|
const SQLiteQueryInterface = require('./dialects/sqlite/query-interface');
|
|
const MSSQLQueryInterface = require('./dialects/mssql/query-interface');
|
|
const MySQLQueryInterface = require('./dialects/mysql/query-interface');
|
|
const PostgresQueryInterface = require('./dialects/postgres/query-interface');
|
|
const Transaction = require('./transaction');
|
|
const Promise = require('./promise');
|
|
const QueryTypes = require('./query-types');
|
|
const Op = require('./operators');
|
|
|
|
/**
|
|
* The interface that Sequelize uses to talk to all databases
|
|
*
|
|
* @class QueryInterface
|
|
*/
|
|
class QueryInterface {
|
|
constructor(sequelize) {
|
|
this.sequelize = sequelize;
|
|
this.QueryGenerator = this.sequelize.dialect.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}
|
|
*/
|
|
createDatabase(database, options) {
|
|
options = options || {};
|
|
const sql = this.QueryGenerator.createDatabaseQuery(database, options);
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
/**
|
|
* Drop a database
|
|
*
|
|
* @param {string} database Database name to drop
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
dropDatabase(database, options) {
|
|
options = options || {};
|
|
const sql = this.QueryGenerator.dropDatabaseQuery(database);
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
/**
|
|
* Create a schema
|
|
*
|
|
* @param {string} schema Schema name to create
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
createSchema(schema, options) {
|
|
options = options || {};
|
|
const sql = this.QueryGenerator.createSchema(schema);
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
/**
|
|
* Drop a schema
|
|
*
|
|
* @param {string} schema Schema name to drop
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
dropSchema(schema, options) {
|
|
options = options || {};
|
|
const sql = this.QueryGenerator.dropSchema(schema);
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
/**
|
|
* Drop all schemas
|
|
*
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
dropAllSchemas(options) {
|
|
options = options || {};
|
|
|
|
if (!this.QueryGenerator._dialect.supports.schemas) {
|
|
return this.sequelize.drop(options);
|
|
}
|
|
return this.showAllSchemas(options).map(schemaName => this.dropSchema(schemaName, options));
|
|
}
|
|
|
|
/**
|
|
* Show all schemas
|
|
*
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise<Array>}
|
|
*/
|
|
showAllSchemas(options) {
|
|
options = Object.assign({}, options, {
|
|
raw: true,
|
|
type: this.sequelize.QueryTypes.SELECT
|
|
});
|
|
|
|
const showSchemasSql = this.QueryGenerator.showSchemasQuery(options);
|
|
|
|
return this.sequelize.query(showSchemasSql, options).then(schemaNames => _.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
|
|
*/
|
|
databaseVersion(options) {
|
|
return this.sequelize.query(
|
|
this.QueryGenerator.versionQuery(),
|
|
Object.assign({}, 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}
|
|
*/
|
|
createTable(tableName, attributes, options, model) {
|
|
let sql = '';
|
|
let promise;
|
|
|
|
options = _.clone(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[]
|
|
if (this.sequelize.options.dialect === 'postgres') {
|
|
promise = PostgresQueryInterface.ensureEnums(this, tableName, attributes, options, model);
|
|
} else {
|
|
promise = Promise.resolve();
|
|
}
|
|
|
|
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 promise.then(() => this.sequelize.query(sql, options));
|
|
}
|
|
|
|
/**
|
|
* Drop a table from database
|
|
*
|
|
* @param {string} tableName Table name to drop
|
|
* @param {Object} options Query options
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
dropTable(tableName, options) {
|
|
// if we're forcing we should be cascading unless explicitly stated otherwise
|
|
options = _.clone(options) || {};
|
|
options.cascade = options.cascade || options.force || false;
|
|
|
|
let sql = this.QueryGenerator.dropTableQuery(tableName, options);
|
|
|
|
return this.sequelize.query(sql, options).then(() => {
|
|
const promises = [];
|
|
|
|
// Since postgres has a special case for enums, we should drop the related
|
|
// enum type within the table and attribute
|
|
if (this.sequelize.options.dialect === 'postgres') {
|
|
const instanceTable = this.sequelize.modelManager.getModel(tableName, { attribute: 'tableName' });
|
|
|
|
if (instanceTable) {
|
|
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++) {
|
|
if (instanceTable.rawAttributes[keys[i]].type instanceof DataTypes.ENUM) {
|
|
sql = this.QueryGenerator.pgEnumDrop(getTableName, keys[i]);
|
|
options.supportsSearchPath = false;
|
|
promises.push(this.sequelize.query(sql, Object.assign({}, options, { raw: true })));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Promise.all(promises).get(0);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Drop all tables from database
|
|
*
|
|
* @param {Object} [options] query options
|
|
* @param {Array} [options.skip] List of table to skip
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
dropAllTables(options) {
|
|
options = options || {};
|
|
const skip = options.skip || [];
|
|
|
|
const dropAllTables = tableNames => Promise.each(tableNames, tableName => {
|
|
// if tableName is not in the Array of tables names then don't drop it
|
|
if (!skip.includes(tableName.tableName || tableName)) {
|
|
return this.dropTable(tableName, Object.assign({}, options, { cascade: true }) );
|
|
}
|
|
});
|
|
|
|
return this.showAllTables(options).then(tableNames => {
|
|
if (this.sequelize.options.dialect === 'sqlite') {
|
|
return this.sequelize.query('PRAGMA foreign_keys;', options).then(result => {
|
|
const foreignKeysAreEnabled = result.foreign_keys === 1;
|
|
|
|
if (foreignKeysAreEnabled) {
|
|
return this.sequelize.query('PRAGMA foreign_keys = OFF', options)
|
|
.then(() => dropAllTables(tableNames))
|
|
.then(() => this.sequelize.query('PRAGMA foreign_keys = ON', options));
|
|
}
|
|
return dropAllTables(tableNames);
|
|
});
|
|
}
|
|
return this.getForeignKeysForTables(tableNames, options).then(foreignKeys => {
|
|
const queries = [];
|
|
|
|
tableNames.forEach(tableName => {
|
|
let normalizedTableName = tableName;
|
|
if (_.isObject(tableName)) {
|
|
normalizedTableName = `${tableName.schema}.${tableName.tableName}`;
|
|
}
|
|
|
|
foreignKeys[normalizedTableName].forEach(foreignKey => {
|
|
queries.push(this.QueryGenerator.dropForeignKeyQuery(tableName, foreignKey));
|
|
});
|
|
});
|
|
|
|
return Promise.each(queries, q => this.sequelize.query(q, options))
|
|
.then(() => dropAllTables(tableNames));
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Drop specified enum from database (Postgres only)
|
|
*
|
|
* @param {string} [enumName] Enum name to drop
|
|
* @param {Object} options Query options
|
|
*
|
|
* @returns {Promise}
|
|
* @private
|
|
*/
|
|
dropEnum(enumName, options) {
|
|
if (this.sequelize.getDialect() !== 'postgres') {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
options = options || {};
|
|
|
|
return this.sequelize.query(
|
|
this.QueryGenerator.pgEnumDrop(null, null, this.QueryGenerator.pgEscapeAndQuote(enumName)),
|
|
Object.assign({}, options, { raw: true })
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Drop all enums from database (Postgres only)
|
|
*
|
|
* @param {Object} options Query options
|
|
*
|
|
* @returns {Promise}
|
|
* @private
|
|
*/
|
|
dropAllEnums(options) {
|
|
if (this.sequelize.getDialect() !== 'postgres') {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
options = options || {};
|
|
|
|
return this.pgListEnums(null, options).map(result => this.sequelize.query(
|
|
this.QueryGenerator.pgEnumDrop(null, null, this.QueryGenerator.pgEscapeAndQuote(result.enum_name)),
|
|
Object.assign({}, options, { raw: true })
|
|
));
|
|
}
|
|
|
|
/**
|
|
* List all enums (Postgres only)
|
|
*
|
|
* @param {string} [tableName] Table whose enum to list
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise}
|
|
* @private
|
|
*/
|
|
pgListEnums(tableName, options) {
|
|
options = options || {};
|
|
const sql = this.QueryGenerator.pgListEnums(tableName);
|
|
return this.sequelize.query(sql, Object.assign({}, options, { plain: false, raw: true, type: QueryTypes.SELECT }));
|
|
}
|
|
|
|
/**
|
|
* Rename a table
|
|
*
|
|
* @param {string} before Current name of table
|
|
* @param {string} after New name from table
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
renameTable(before, after, options) {
|
|
options = options || {};
|
|
const sql = this.QueryGenerator.renameTableQuery(before, after);
|
|
return 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
|
|
*/
|
|
showAllTables(options) {
|
|
options = Object.assign({}, options, {
|
|
raw: true,
|
|
type: QueryTypes.SHOWTABLES
|
|
});
|
|
|
|
const showTablesSql = this.QueryGenerator.showTablesQuery(this.sequelize.config.database);
|
|
return this.sequelize.query(showTablesSql, options).then(tableNames => _.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>}
|
|
*/
|
|
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 = Object.assign({}, options, { type: QueryTypes.DESCRIBE });
|
|
|
|
return this.sequelize.query(sql, options).then(data => {
|
|
/*
|
|
* 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 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}
|
|
*/
|
|
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 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
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
removeColumn(tableName, attributeName, options) {
|
|
options = options || {};
|
|
switch (this.sequelize.options.dialect) {
|
|
case 'sqlite':
|
|
// sqlite needs some special treatment as it cannot drop a column
|
|
return SQLiteQueryInterface.removeColumn(this, tableName, attributeName, options);
|
|
case 'mssql':
|
|
// mssql needs special treatment as it cannot drop a column with a default or foreign key constraint
|
|
return MSSQLQueryInterface.removeColumn(this, tableName, attributeName, options);
|
|
case 'mysql':
|
|
case 'mariadb':
|
|
// mysql/mariadb need special treatment as it cannot drop a column with a foreign key constraint
|
|
return MySQLQueryInterface.removeColumn(this, tableName, attributeName, options);
|
|
default:
|
|
return this.sequelize.query(this.QueryGenerator.removeColumnQuery(tableName, attributeName), options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
changeColumn(tableName, attributeName, dataTypeOrOptions, options) {
|
|
const attributes = {};
|
|
options = options || {};
|
|
|
|
if (_.values(DataTypes).includes(dataTypeOrOptions)) {
|
|
attributes[attributeName] = { type: dataTypeOrOptions, allowNull: true };
|
|
} else {
|
|
attributes[attributeName] = dataTypeOrOptions;
|
|
}
|
|
|
|
attributes[attributeName] = this.sequelize.normalizeAttribute(attributes[attributeName]);
|
|
|
|
if (this.sequelize.options.dialect === 'sqlite') {
|
|
// sqlite needs some special treatment as it cannot change a column
|
|
return SQLiteQueryInterface.changeColumn(this, tableName, attributes, options);
|
|
}
|
|
const query = this.QueryGenerator.attributesToSQL(attributes, {
|
|
context: 'changeColumn',
|
|
table: tableName
|
|
});
|
|
const sql = this.QueryGenerator.changeColumnQuery(tableName, query);
|
|
|
|
return this.sequelize.query(sql, 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}
|
|
*/
|
|
renameColumn(tableName, attrNameBefore, attrNameAfter, options) {
|
|
options = options || {};
|
|
return this.describeTable(tableName, options).then(data => {
|
|
if (!data[attrNameBefore]) {
|
|
throw new Error(`Table ${tableName} doesn't have the column ${attrNameBefore}`);
|
|
}
|
|
|
|
data = data[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;
|
|
}
|
|
|
|
if (this.sequelize.options.dialect === 'sqlite') {
|
|
// sqlite needs some special treatment as it cannot rename a column
|
|
return SQLiteQueryInterface.renameColumn(this, tableName, attrNameBefore, attrNameAfter, options);
|
|
}
|
|
const sql = this.QueryGenerator.renameColumnQuery(
|
|
tableName,
|
|
attrNameBefore,
|
|
this.QueryGenerator.attributesToSQL(_options)
|
|
);
|
|
return 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}
|
|
*/
|
|
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 this.sequelize.query(sql, Object.assign({}, options, { supportsSearchPath: false }));
|
|
}
|
|
|
|
/**
|
|
* Show indexes on a table
|
|
*
|
|
* @param {string} tableName table name
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise<Array>}
|
|
* @private
|
|
*/
|
|
showIndex(tableName, options) {
|
|
const sql = this.QueryGenerator.showIndexesQuery(tableName, options);
|
|
return this.sequelize.query(sql, Object.assign({}, options, { type: QueryTypes.SHOWINDEXES }));
|
|
}
|
|
|
|
getForeignKeysForTables(tableNames, options) {
|
|
if (tableNames.length === 0) {
|
|
return Promise.resolve({});
|
|
}
|
|
|
|
options = Object.assign({}, options || {}, { type: QueryTypes.FOREIGNKEYS });
|
|
|
|
return Promise.map(tableNames, tableName =>
|
|
this.sequelize.query(this.QueryGenerator.getForeignKeysQuery(tableName, this.sequelize.config.database), options)
|
|
).then(results => {
|
|
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
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
getForeignKeyReferencesForTable(tableName, options) {
|
|
const queryOptions = Object.assign({}, options, {
|
|
type: QueryTypes.FOREIGNKEYS
|
|
});
|
|
const catalogName = this.sequelize.config.database;
|
|
switch (this.sequelize.options.dialect) {
|
|
case 'sqlite':
|
|
// sqlite needs some special treatment.
|
|
return SQLiteQueryInterface.getForeignKeyReferencesForTable(this, tableName, queryOptions);
|
|
case 'postgres':
|
|
{
|
|
// 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, catalogName);
|
|
return this.sequelize.query(query, queryOptions)
|
|
.then(result => result.map(Utils.camelizeObjectKeys));
|
|
}
|
|
case 'mssql':
|
|
case 'mysql':
|
|
case 'mariadb':
|
|
default: {
|
|
const query = this.QueryGenerator.getForeignKeysQuery(tableName, catalogName);
|
|
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} indexNameOrAttributes Index name
|
|
* @param {Object} [options] Query options
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
removeIndex(tableName, indexNameOrAttributes, options) {
|
|
options = options || {};
|
|
const sql = this.QueryGenerator.removeIndexQuery(tableName, indexNameOrAttributes);
|
|
return 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', ['email'], {
|
|
* type: 'unique',
|
|
* name: 'custom_unique_constraint_name'
|
|
* });
|
|
*
|
|
* @example <caption>CHECK</caption>
|
|
* queryInterface.addConstraint('Users', ['roles'], {
|
|
* type: 'check',
|
|
* where: {
|
|
* roles: ['user', 'admin', 'moderator', 'guest']
|
|
* }
|
|
* });
|
|
*
|
|
* @example <caption>Default - MSSQL only</caption>
|
|
* queryInterface.addConstraint('Users', ['roles'], {
|
|
* type: 'default',
|
|
* defaultValue: 'guest'
|
|
* });
|
|
*
|
|
* @example <caption>Primary Key</caption>
|
|
* queryInterface.addConstraint('Users', ['username'], {
|
|
* type: 'primary key',
|
|
* name: 'custom_primary_constraint_name'
|
|
* });
|
|
*
|
|
* @example <caption>Foreign Key</caption>
|
|
* queryInterface.addConstraint('Posts', ['username'], {
|
|
* type: 'foreign key',
|
|
* name: 'custom_fkey_constraint_name',
|
|
* references: { //Required field
|
|
* table: 'target_table_name',
|
|
* field: 'target_column_name'
|
|
* },
|
|
* onDelete: 'cascade',
|
|
* onUpdate: 'cascade'
|
|
* });
|
|
*
|
|
* @param {string} tableName Table name where you want to add a constraint
|
|
* @param {Array} attributes Array of column names to apply the constraint over
|
|
* @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 {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} [rawTablename] Table name, for backward compatibility
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
addConstraint(tableName, attributes, options, rawTablename) {
|
|
if (!Array.isArray(attributes)) {
|
|
rawTablename = options;
|
|
options = attributes;
|
|
attributes = options.fields;
|
|
}
|
|
|
|
if (!options.type) {
|
|
throw new Error('Constraint type must be specified through options.type');
|
|
}
|
|
|
|
if (!rawTablename) {
|
|
// Map for backwards compat
|
|
rawTablename = tableName;
|
|
}
|
|
|
|
options = Utils.cloneDeep(options);
|
|
options.fields = attributes;
|
|
|
|
if (this.sequelize.dialect.name === 'sqlite') {
|
|
return SQLiteQueryInterface.addConstraint(this, tableName, options, rawTablename);
|
|
}
|
|
const sql = this.QueryGenerator.addConstraintQuery(tableName, options, rawTablename);
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
showConstraint(tableName, constraintName, options) {
|
|
const sql = this.QueryGenerator.showConstraintsQuery(tableName, constraintName);
|
|
return this.sequelize.query(sql, Object.assign({}, 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
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
removeConstraint(tableName, constraintName, options) {
|
|
options = options || {};
|
|
|
|
switch (this.sequelize.options.dialect) {
|
|
case 'mysql':
|
|
case 'mariadb':
|
|
//does not support DROP CONSTRAINT. Instead DROP PRIMARY, FOREIGN KEY, INDEX should be used
|
|
return MySQLQueryInterface.removeConstraint(this, tableName, constraintName, options);
|
|
case 'sqlite':
|
|
return SQLiteQueryInterface.removeConstraint(this, tableName, constraintName, options);
|
|
default:
|
|
const sql = this.QueryGenerator.removeConstraintQuery(tableName, constraintName);
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
return this.sequelize.query(sql, options).then(results => {
|
|
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 various conditions
|
|
* @param {Model} model Model to upsert on
|
|
* @param {Object} options query options
|
|
*
|
|
* @returns {Promise<boolean,?number>} Resolves an array with <created, primaryKey>
|
|
*/
|
|
upsert(tableName, insertValues, updateValues, where, model, options) {
|
|
const wheres = [];
|
|
const attributes = Object.keys(insertValues);
|
|
let indexes = [];
|
|
let indexFields;
|
|
|
|
options = _.clone(options);
|
|
|
|
if (!Utils.isWhereEmpty(where)) {
|
|
wheres.push(where);
|
|
}
|
|
|
|
// Lets combine unique keys and indexes into one
|
|
indexes = _.map(model.uniqueKeys, value => {
|
|
return value.fields;
|
|
});
|
|
|
|
model._indexes.forEach(value => {
|
|
if (value.unique) {
|
|
// fields in the index may both the strings or objects with an attribute property - lets sanitize that
|
|
indexFields = value.fields.map(field => {
|
|
if (_.isPlainObject(field)) {
|
|
return field.attribute;
|
|
}
|
|
return field;
|
|
});
|
|
indexes.push(indexFields);
|
|
}
|
|
});
|
|
|
|
for (const index of indexes) {
|
|
if (_.intersection(attributes, index).length === index.length) {
|
|
where = {};
|
|
for (const field of index) {
|
|
where[field] = insertValues[field];
|
|
}
|
|
wheres.push(where);
|
|
}
|
|
}
|
|
|
|
where = { [Op.or]: wheres };
|
|
|
|
options.type = QueryTypes.UPSERT;
|
|
options.raw = true;
|
|
|
|
const sql = this.QueryGenerator.upsertQuery(tableName, insertValues, updateValues, where, model, options);
|
|
return this.sequelize.query(sql, options).then(result => {
|
|
switch (this.sequelize.options.dialect) {
|
|
case 'postgres':
|
|
return [result.created, result.primary_key];
|
|
|
|
case 'mssql':
|
|
return [
|
|
result.$action === 'INSERT',
|
|
result[model.primaryKeyField]
|
|
];
|
|
|
|
// MySQL returns 1 for inserted, 2 for updated
|
|
// http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html.
|
|
case 'mysql':
|
|
case 'mariadb':
|
|
return [result === 1, undefined];
|
|
|
|
default:
|
|
return [result, undefined];
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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}
|
|
*/
|
|
bulkInsert(tableName, records, options, attributes) {
|
|
options = _.clone(options) || {};
|
|
options.type = QueryTypes.INSERT;
|
|
|
|
return this.sequelize.query(
|
|
this.QueryGenerator.bulkInsertQuery(tableName, records, options, attributes),
|
|
options
|
|
).then(results => results[0]);
|
|
}
|
|
|
|
update(instance, tableName, values, identifier, options) {
|
|
options = _.clone(options || {});
|
|
options.hasTrigger = !!(instance && instance._modelOptions && instance._modelOptions.hasTrigger);
|
|
|
|
const sql = this.QueryGenerator.updateQuery(tableName, values, identifier, options, instance.constructor.rawAttributes);
|
|
|
|
options.type = QueryTypes.UPDATE;
|
|
|
|
options.instance = instance;
|
|
return 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}
|
|
*/
|
|
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 this.sequelize.query(sql, options);
|
|
}
|
|
|
|
delete(instance, tableName, identifier, options) {
|
|
const cascades = [];
|
|
const sql = this.QueryGenerator.deleteQuery(tableName, identifier, {}, instance.constructor);
|
|
|
|
options = _.clone(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Promise.each(cascades, cascade => {
|
|
return instance[cascade](options).then(instances => {
|
|
// Check for hasOne relationship with non-existing associate ("has zero")
|
|
if (!instances) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
if (!Array.isArray(instances)) instances = [instances];
|
|
|
|
return Promise.each(instances, instance => instance.destroy(options));
|
|
});
|
|
}).then(() => {
|
|
options.instance = instance;
|
|
return 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}
|
|
*/
|
|
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 this.sequelize.query(
|
|
this.QueryGenerator.deleteQuery(tableName, where, options, model),
|
|
options
|
|
);
|
|
}
|
|
|
|
select(model, tableName, optionsArg) {
|
|
const options = Object.assign({}, optionsArg, { type: QueryTypes.SELECT, model });
|
|
|
|
return this.sequelize.query(
|
|
this.QueryGenerator.selectQuery(tableName, options, model),
|
|
options
|
|
);
|
|
}
|
|
|
|
increment(model, tableName, values, identifier, options) {
|
|
options = Utils.cloneDeep(options);
|
|
|
|
const sql = this.QueryGenerator.arithmeticQuery('+', tableName, values, identifier, options, options.attributes);
|
|
|
|
options.type = QueryTypes.UPDATE;
|
|
options.model = model;
|
|
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
decrement(model, tableName, values, identifier, options) {
|
|
options = Utils.cloneDeep(options);
|
|
|
|
const sql = this.QueryGenerator.arithmeticQuery('-', tableName, values, identifier, options, options.attributes);
|
|
|
|
options.type = QueryTypes.UPDATE;
|
|
options.model = model;
|
|
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
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!');
|
|
}
|
|
|
|
return this.sequelize.query(sql, options).then(data => {
|
|
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) {
|
|
return parseInt(result, 10);
|
|
}
|
|
if (dataType instanceof DataTypes.DATE) {
|
|
if (result !== null && !(result instanceof Date)) {
|
|
return new Date(result);
|
|
}
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
|
|
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 this.sequelize.query(sql, options);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
dropTrigger(tableName, triggerName, options) {
|
|
const sql = this.QueryGenerator.dropTrigger(tableName, triggerName);
|
|
options = options || {};
|
|
|
|
if (sql) {
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
renameTrigger(tableName, oldTriggerName, newTriggerName, options) {
|
|
const sql = this.QueryGenerator.renameTrigger(tableName, oldTriggerName, newTriggerName);
|
|
options = options || {};
|
|
|
|
if (sql) {
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
/**
|
|
* 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}
|
|
*/
|
|
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 this.sequelize.query(sql, options);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
/**
|
|
* 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}
|
|
*/
|
|
dropFunction(functionName, params, options) {
|
|
const sql = this.QueryGenerator.dropFunction(functionName, params);
|
|
options = options || {};
|
|
|
|
if (sql) {
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
/**
|
|
* 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}
|
|
*/
|
|
renameFunction(oldFunctionName, params, newFunctionName, options) {
|
|
const sql = this.QueryGenerator.renameFunction(oldFunctionName, params, newFunctionName);
|
|
options = options || {};
|
|
|
|
if (sql) {
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
// Helper methods useful for querying
|
|
|
|
/**
|
|
* Escape an identifier (e.g. a table or attribute name)
|
|
*
|
|
* @param {string} identifier identifier to quote
|
|
* @param {boolean} [force] If force is true,the identifier will be quoted even if the `quoteIdentifiers` option is false.
|
|
*
|
|
* @private
|
|
*/
|
|
quoteIdentifier(identifier, force) {
|
|
return this.QueryGenerator.quoteIdentifier(identifier, force);
|
|
}
|
|
|
|
quoteTable(identifier) {
|
|
return this.QueryGenerator.quoteTable(identifier);
|
|
}
|
|
|
|
/**
|
|
* Quote array of identifiers at once
|
|
*
|
|
* @param {string[]} identifiers array of identifiers to quote
|
|
* @param {boolean} [force] If force is true,the identifier will be quoted even if the `quoteIdentifiers` option is false.
|
|
*
|
|
* @private
|
|
*/
|
|
quoteIdentifiers(identifiers, force) {
|
|
return this.QueryGenerator.quoteIdentifiers(identifiers, force);
|
|
}
|
|
|
|
/**
|
|
* Escape a value (e.g. a string, number or date)
|
|
*
|
|
* @param {string} value string to escape
|
|
*
|
|
* @private
|
|
*/
|
|
escape(value) {
|
|
return this.QueryGenerator.escape(value);
|
|
}
|
|
|
|
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 Promise.resolve();
|
|
}
|
|
|
|
options = Object.assign({}, options, {
|
|
transaction: transaction.parent || transaction
|
|
});
|
|
|
|
const sql = this.QueryGenerator.setIsolationLevelQuery(value, {
|
|
parent: transaction.parent
|
|
});
|
|
|
|
if (!sql) return Promise.resolve();
|
|
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
startTransaction(transaction, options) {
|
|
if (!transaction || !(transaction instanceof Transaction)) {
|
|
throw new Error('Unable to start a transaction without transaction object!');
|
|
}
|
|
|
|
options = Object.assign({}, options, {
|
|
transaction: transaction.parent || transaction
|
|
});
|
|
options.transaction.name = transaction.parent ? transaction.name : undefined;
|
|
const sql = this.QueryGenerator.startTransactionQuery(transaction);
|
|
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
deferConstraints(transaction, options) {
|
|
options = Object.assign({}, options, {
|
|
transaction: transaction.parent || transaction
|
|
});
|
|
|
|
const sql = this.QueryGenerator.deferConstraintsQuery(options);
|
|
|
|
if (sql) {
|
|
return this.sequelize.query(sql, options);
|
|
}
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
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 Promise.resolve();
|
|
}
|
|
|
|
options = Object.assign({}, 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 promise;
|
|
}
|
|
|
|
rollbackTransaction(transaction, options) {
|
|
if (!transaction || !(transaction instanceof Transaction)) {
|
|
throw new Error('Unable to rollback a transaction without transaction object!');
|
|
}
|
|
|
|
options = Object.assign({}, 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 promise;
|
|
}
|
|
}
|
|
|
|
module.exports = QueryInterface;
|
|
module.exports.QueryInterface = QueryInterface;
|
|
module.exports.default = QueryInterface;
|