12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469 |
- '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) {
- if (this.sequelize.options.dialect === 'sqlite') {
- // sqlite needs some special treatment as it cannot rename a column
- return SQLiteQueryInterface.describeTable(this, 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;
|