query-interface.js 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  1. 'use strict';
  2. const _ = require('lodash');
  3. const Utils = require('../../utils');
  4. const DataTypes = require('../../data-types');
  5. const Transaction = require('../../transaction');
  6. const QueryTypes = require('../../query-types');
  7. /**
  8. * The interface that Sequelize uses to talk to all databases
  9. */
  10. class QueryInterface {
  11. constructor(sequelize, queryGenerator) {
  12. this.sequelize = sequelize;
  13. this.queryGenerator = queryGenerator;
  14. }
  15. /**
  16. * Create a database
  17. *
  18. * @param {string} database Database name to create
  19. * @param {object} [options] Query options
  20. * @param {string} [options.charset] Database default character set, MYSQL only
  21. * @param {string} [options.collate] Database default collation
  22. * @param {string} [options.encoding] Database default character set, PostgreSQL only
  23. * @param {string} [options.ctype] Database character classification, PostgreSQL only
  24. * @param {string} [options.template] The name of the template from which to create the new database, PostgreSQL only
  25. *
  26. * @returns {Promise}
  27. */
  28. async createDatabase(database, options) {
  29. options = options || {};
  30. const sql = this.queryGenerator.createDatabaseQuery(database, options);
  31. return await this.sequelize.query(sql, options);
  32. }
  33. /**
  34. * Drop a database
  35. *
  36. * @param {string} database Database name to drop
  37. * @param {object} [options] Query options
  38. *
  39. * @returns {Promise}
  40. */
  41. async dropDatabase(database, options) {
  42. options = options || {};
  43. const sql = this.queryGenerator.dropDatabaseQuery(database);
  44. return await this.sequelize.query(sql, options);
  45. }
  46. /**
  47. * Create a schema
  48. *
  49. * @param {string} schema Schema name to create
  50. * @param {object} [options] Query options
  51. *
  52. * @returns {Promise}
  53. */
  54. async createSchema(schema, options) {
  55. options = options || {};
  56. const sql = this.queryGenerator.createSchema(schema);
  57. return await this.sequelize.query(sql, options);
  58. }
  59. /**
  60. * Drop a schema
  61. *
  62. * @param {string} schema Schema name to drop
  63. * @param {object} [options] Query options
  64. *
  65. * @returns {Promise}
  66. */
  67. async dropSchema(schema, options) {
  68. options = options || {};
  69. const sql = this.queryGenerator.dropSchema(schema);
  70. return await this.sequelize.query(sql, options);
  71. }
  72. /**
  73. * Drop all schemas
  74. *
  75. * @param {object} [options] Query options
  76. *
  77. * @returns {Promise}
  78. */
  79. async dropAllSchemas(options) {
  80. options = options || {};
  81. if (!this.queryGenerator._dialect.supports.schemas) {
  82. return this.sequelize.drop(options);
  83. }
  84. const schemas = await this.showAllSchemas(options);
  85. return Promise.all(schemas.map(schemaName => this.dropSchema(schemaName, options)));
  86. }
  87. /**
  88. * Show all schemas
  89. *
  90. * @param {object} [options] Query options
  91. *
  92. * @returns {Promise<Array>}
  93. */
  94. async showAllSchemas(options) {
  95. options = {
  96. ...options,
  97. raw: true,
  98. type: this.sequelize.QueryTypes.SELECT
  99. };
  100. const showSchemasSql = this.queryGenerator.showSchemasQuery(options);
  101. const schemaNames = await this.sequelize.query(showSchemasSql, options);
  102. return _.flatten(schemaNames.map(value => value.schema_name ? value.schema_name : value));
  103. }
  104. /**
  105. * Return database version
  106. *
  107. * @param {object} [options] Query options
  108. * @param {QueryType} [options.type] Query type
  109. *
  110. * @returns {Promise}
  111. * @private
  112. */
  113. async databaseVersion(options) {
  114. return await this.sequelize.query(
  115. this.queryGenerator.versionQuery(),
  116. { ...options, type: QueryTypes.VERSION }
  117. );
  118. }
  119. /**
  120. * Create a table with given set of attributes
  121. *
  122. * ```js
  123. * queryInterface.createTable(
  124. * 'nameOfTheNewTable',
  125. * {
  126. * id: {
  127. * type: Sequelize.INTEGER,
  128. * primaryKey: true,
  129. * autoIncrement: true
  130. * },
  131. * createdAt: {
  132. * type: Sequelize.DATE
  133. * },
  134. * updatedAt: {
  135. * type: Sequelize.DATE
  136. * },
  137. * attr1: Sequelize.STRING,
  138. * attr2: Sequelize.INTEGER,
  139. * attr3: {
  140. * type: Sequelize.BOOLEAN,
  141. * defaultValue: false,
  142. * allowNull: false
  143. * },
  144. * //foreign key usage
  145. * attr4: {
  146. * type: Sequelize.INTEGER,
  147. * references: {
  148. * model: 'another_table_name',
  149. * key: 'id'
  150. * },
  151. * onUpdate: 'cascade',
  152. * onDelete: 'cascade'
  153. * }
  154. * },
  155. * {
  156. * engine: 'MYISAM', // default: 'InnoDB'
  157. * charset: 'latin1', // default: null
  158. * schema: 'public', // default: public, PostgreSQL only.
  159. * comment: 'my table', // comment for table
  160. * collate: 'latin1_danish_ci' // collation, MYSQL only
  161. * }
  162. * )
  163. * ```
  164. *
  165. * @param {string} tableName Name of table to create
  166. * @param {object} attributes Object representing a list of table attributes to create
  167. * @param {object} [options] create table and query options
  168. * @param {Model} [model] model class
  169. *
  170. * @returns {Promise}
  171. */
  172. async createTable(tableName, attributes, options, model) {
  173. let sql = '';
  174. options = { ...options };
  175. if (options && options.uniqueKeys) {
  176. _.forOwn(options.uniqueKeys, uniqueKey => {
  177. if (uniqueKey.customIndex === undefined) {
  178. uniqueKey.customIndex = true;
  179. }
  180. });
  181. }
  182. if (model) {
  183. options.uniqueKeys = options.uniqueKeys || model.uniqueKeys;
  184. }
  185. attributes = _.mapValues(
  186. attributes,
  187. attribute => this.sequelize.normalizeAttribute(attribute)
  188. );
  189. // Postgres requires special SQL commands for ENUM/ENUM[]
  190. await this.ensureEnums(tableName, attributes, options, model);
  191. if (
  192. !tableName.schema &&
  193. (options.schema || !!model && model._schema)
  194. ) {
  195. tableName = this.queryGenerator.addSchema({
  196. tableName,
  197. _schema: !!model && model._schema || options.schema
  198. });
  199. }
  200. attributes = this.queryGenerator.attributesToSQL(attributes, { table: tableName, context: 'createTable' });
  201. sql = this.queryGenerator.createTableQuery(tableName, attributes, options);
  202. return await this.sequelize.query(sql, options);
  203. }
  204. /**
  205. * Drop a table from database
  206. *
  207. * @param {string} tableName Table name to drop
  208. * @param {object} options Query options
  209. *
  210. * @returns {Promise}
  211. */
  212. async dropTable(tableName, options) {
  213. // if we're forcing we should be cascading unless explicitly stated otherwise
  214. options = { ...options };
  215. options.cascade = options.cascade || options.force || false;
  216. const sql = this.queryGenerator.dropTableQuery(tableName, options);
  217. await this.sequelize.query(sql, options);
  218. }
  219. async _dropAllTables(tableNames, skip, options) {
  220. for (const tableName of tableNames) {
  221. // if tableName is not in the Array of tables names then don't drop it
  222. if (!skip.includes(tableName.tableName || tableName)) {
  223. await this.dropTable(tableName, { ...options, cascade: true } );
  224. }
  225. }
  226. }
  227. /**
  228. * Drop all tables from database
  229. *
  230. * @param {object} [options] query options
  231. * @param {Array} [options.skip] List of table to skip
  232. *
  233. * @returns {Promise}
  234. */
  235. async dropAllTables(options) {
  236. options = options || {};
  237. const skip = options.skip || [];
  238. const tableNames = await this.showAllTables(options);
  239. const foreignKeys = await this.getForeignKeysForTables(tableNames, options);
  240. for (const tableName of tableNames) {
  241. let normalizedTableName = tableName;
  242. if (_.isObject(tableName)) {
  243. normalizedTableName = `${tableName.schema}.${tableName.tableName}`;
  244. }
  245. for (const foreignKey of foreignKeys[normalizedTableName]) {
  246. await this.sequelize.query(this.queryGenerator.dropForeignKeyQuery(tableName, foreignKey));
  247. }
  248. }
  249. await this._dropAllTables(tableNames, skip, options);
  250. }
  251. /**
  252. * Rename a table
  253. *
  254. * @param {string} before Current name of table
  255. * @param {string} after New name from table
  256. * @param {object} [options] Query options
  257. *
  258. * @returns {Promise}
  259. */
  260. async renameTable(before, after, options) {
  261. options = options || {};
  262. const sql = this.queryGenerator.renameTableQuery(before, after);
  263. return await this.sequelize.query(sql, options);
  264. }
  265. /**
  266. * Get all tables in current database
  267. *
  268. * @param {object} [options] Query options
  269. * @param {boolean} [options.raw=true] Run query in raw mode
  270. * @param {QueryType} [options.type=QueryType.SHOWTABLE] query type
  271. *
  272. * @returns {Promise<Array>}
  273. * @private
  274. */
  275. async showAllTables(options) {
  276. options = {
  277. ...options,
  278. raw: true,
  279. type: QueryTypes.SHOWTABLES
  280. };
  281. const showTablesSql = this.queryGenerator.showTablesQuery(this.sequelize.config.database);
  282. const tableNames = await this.sequelize.query(showTablesSql, options);
  283. return _.flatten(tableNames);
  284. }
  285. /**
  286. * Describe a table structure
  287. *
  288. * This method returns an array of hashes containing information about all attributes in the table.
  289. *
  290. * ```js
  291. * {
  292. * name: {
  293. * type: 'VARCHAR(255)', // this will be 'CHARACTER VARYING' for pg!
  294. * allowNull: true,
  295. * defaultValue: null
  296. * },
  297. * isBetaMember: {
  298. * type: 'TINYINT(1)', // this will be 'BOOLEAN' for pg!
  299. * allowNull: false,
  300. * defaultValue: false
  301. * }
  302. * }
  303. * ```
  304. *
  305. * @param {string} tableName table name
  306. * @param {object} [options] Query options
  307. *
  308. * @returns {Promise<object>}
  309. */
  310. async describeTable(tableName, options) {
  311. let schema = null;
  312. let schemaDelimiter = null;
  313. if (typeof options === 'string') {
  314. schema = options;
  315. } else if (typeof options === 'object' && options !== null) {
  316. schema = options.schema || null;
  317. schemaDelimiter = options.schemaDelimiter || null;
  318. }
  319. if (typeof tableName === 'object' && tableName !== null) {
  320. schema = tableName.schema;
  321. tableName = tableName.tableName;
  322. }
  323. const sql = this.queryGenerator.describeTableQuery(tableName, schema, schemaDelimiter);
  324. options = { ...options, type: QueryTypes.DESCRIBE };
  325. try {
  326. const data = await this.sequelize.query(sql, options);
  327. /*
  328. * If no data is returned from the query, then the table name may be wrong.
  329. * Query generators that use information_schema for retrieving table info will just return an empty result set,
  330. * it will not throw an error like built-ins do (e.g. DESCRIBE on MySql).
  331. */
  332. if (_.isEmpty(data)) {
  333. throw new Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`);
  334. }
  335. return data;
  336. } catch (e) {
  337. if (e.original && e.original.code === 'ER_NO_SUCH_TABLE') {
  338. throw new Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`);
  339. }
  340. throw e;
  341. }
  342. }
  343. /**
  344. * Add a new column to a table
  345. *
  346. * ```js
  347. * queryInterface.addColumn('tableA', 'columnC', Sequelize.STRING, {
  348. * after: 'columnB' // after option is only supported by MySQL
  349. * });
  350. * ```
  351. *
  352. * @param {string} table Table to add column to
  353. * @param {string} key Column name
  354. * @param {object} attribute Attribute definition
  355. * @param {object} [options] Query options
  356. *
  357. * @returns {Promise}
  358. */
  359. async addColumn(table, key, attribute, options) {
  360. if (!table || !key || !attribute) {
  361. throw new Error('addColumn takes at least 3 arguments (table, attribute name, attribute definition)');
  362. }
  363. options = options || {};
  364. attribute = this.sequelize.normalizeAttribute(attribute);
  365. return await this.sequelize.query(this.queryGenerator.addColumnQuery(table, key, attribute), options);
  366. }
  367. /**
  368. * Remove a column from a table
  369. *
  370. * @param {string} tableName Table to remove column from
  371. * @param {string} attributeName Column name to remove
  372. * @param {object} [options] Query options
  373. */
  374. async removeColumn(tableName, attributeName, options) {
  375. return this.sequelize.query(this.queryGenerator.removeColumnQuery(tableName, attributeName), options);
  376. }
  377. normalizeAttribute(dataTypeOrOptions) {
  378. let attribute;
  379. if (Object.values(DataTypes).includes(dataTypeOrOptions)) {
  380. attribute = { type: dataTypeOrOptions, allowNull: true };
  381. } else {
  382. attribute = dataTypeOrOptions;
  383. }
  384. return this.sequelize.normalizeAttribute(attribute);
  385. }
  386. /**
  387. * Change a column definition
  388. *
  389. * @param {string} tableName Table name to change from
  390. * @param {string} attributeName Column name
  391. * @param {object} dataTypeOrOptions Attribute definition for new column
  392. * @param {object} [options] Query options
  393. */
  394. async changeColumn(tableName, attributeName, dataTypeOrOptions, options) {
  395. options = options || {};
  396. const query = this.queryGenerator.attributesToSQL({
  397. [attributeName]: this.normalizeAttribute(dataTypeOrOptions)
  398. }, {
  399. context: 'changeColumn',
  400. table: tableName
  401. });
  402. const sql = this.queryGenerator.changeColumnQuery(tableName, query);
  403. return this.sequelize.query(sql, options);
  404. }
  405. /**
  406. * Rejects if the table doesn't have the specified column, otherwise returns the column description.
  407. *
  408. * @param {string} tableName
  409. * @param {string} columnName
  410. * @param {object} options
  411. * @private
  412. */
  413. async assertTableHasColumn(tableName, columnName, options) {
  414. const description = await this.describeTable(tableName, options);
  415. if (description[columnName]) {
  416. return description;
  417. }
  418. throw new Error(`Table ${tableName} doesn't have the column ${columnName}`);
  419. }
  420. /**
  421. * Rename a column
  422. *
  423. * @param {string} tableName Table name whose column to rename
  424. * @param {string} attrNameBefore Current column name
  425. * @param {string} attrNameAfter New column name
  426. * @param {object} [options] Query option
  427. *
  428. * @returns {Promise}
  429. */
  430. async renameColumn(tableName, attrNameBefore, attrNameAfter, options) {
  431. options = options || {};
  432. const data = (await this.assertTableHasColumn(tableName, attrNameBefore, options))[attrNameBefore];
  433. const _options = {};
  434. _options[attrNameAfter] = {
  435. attribute: attrNameAfter,
  436. type: data.type,
  437. allowNull: data.allowNull,
  438. defaultValue: data.defaultValue
  439. };
  440. // fix: a not-null column cannot have null as default value
  441. if (data.defaultValue === null && !data.allowNull) {
  442. delete _options[attrNameAfter].defaultValue;
  443. }
  444. const sql = this.queryGenerator.renameColumnQuery(
  445. tableName,
  446. attrNameBefore,
  447. this.queryGenerator.attributesToSQL(_options)
  448. );
  449. return await this.sequelize.query(sql, options);
  450. }
  451. /**
  452. * Add an index to a column
  453. *
  454. * @param {string|object} tableName Table name to add index on, can be a object with schema
  455. * @param {Array} [attributes] Use options.fields instead, List of attributes to add index on
  456. * @param {object} options indexes options
  457. * @param {Array} options.fields List of attributes to add index on
  458. * @param {boolean} [options.concurrently] Pass CONCURRENT so other operations run while the index is created
  459. * @param {boolean} [options.unique] Create a unique index
  460. * @param {string} [options.using] Useful for GIN indexes
  461. * @param {string} [options.operator] Index operator
  462. * @param {string} [options.type] Type of index, available options are UNIQUE|FULLTEXT|SPATIAL
  463. * @param {string} [options.name] Name of the index. Default is <table>_<attr1>_<attr2>
  464. * @param {object} [options.where] Where condition on index, for partial indexes
  465. * @param {string} [rawTablename] table name, this is just for backward compatibiity
  466. *
  467. * @returns {Promise}
  468. */
  469. async addIndex(tableName, attributes, options, rawTablename) {
  470. // Support for passing tableName, attributes, options or tableName, options (with a fields param which is the attributes)
  471. if (!Array.isArray(attributes)) {
  472. rawTablename = options;
  473. options = attributes;
  474. attributes = options.fields;
  475. }
  476. if (!rawTablename) {
  477. // Map for backwards compat
  478. rawTablename = tableName;
  479. }
  480. options = Utils.cloneDeep(options);
  481. options.fields = attributes;
  482. const sql = this.queryGenerator.addIndexQuery(tableName, options, rawTablename);
  483. return await this.sequelize.query(sql, { ...options, supportsSearchPath: false });
  484. }
  485. /**
  486. * Show indexes on a table
  487. *
  488. * @param {string} tableName table name
  489. * @param {object} [options] Query options
  490. *
  491. * @returns {Promise<Array>}
  492. * @private
  493. */
  494. async showIndex(tableName, options) {
  495. const sql = this.queryGenerator.showIndexesQuery(tableName, options);
  496. return await this.sequelize.query(sql, { ...options, type: QueryTypes.SHOWINDEXES });
  497. }
  498. /**
  499. * Returns all foreign key constraints of requested tables
  500. *
  501. * @param {string[]} tableNames table names
  502. * @param {object} [options] Query options
  503. *
  504. * @returns {Promise}
  505. */
  506. async getForeignKeysForTables(tableNames, options) {
  507. if (tableNames.length === 0) {
  508. return {};
  509. }
  510. options = { ...options, type: QueryTypes.FOREIGNKEYS };
  511. const results = await Promise.all(tableNames.map(tableName =>
  512. this.sequelize.query(this.queryGenerator.getForeignKeysQuery(tableName, this.sequelize.config.database), options)));
  513. const result = {};
  514. tableNames.forEach((tableName, i) => {
  515. if (_.isObject(tableName)) {
  516. tableName = `${tableName.schema}.${tableName.tableName}`;
  517. }
  518. result[tableName] = Array.isArray(results[i])
  519. ? results[i].map(r => r.constraint_name)
  520. : [results[i] && results[i].constraint_name];
  521. result[tableName] = result[tableName].filter(_.identity);
  522. });
  523. return result;
  524. }
  525. /**
  526. * Get foreign key references details for the table
  527. *
  528. * Those details contains constraintSchema, constraintName, constraintCatalog
  529. * tableCatalog, tableSchema, tableName, columnName,
  530. * referencedTableCatalog, referencedTableCatalog, referencedTableSchema, referencedTableName, referencedColumnName.
  531. * Remind: constraint informations won't return if it's sqlite.
  532. *
  533. * @param {string} tableName table name
  534. * @param {object} [options] Query options
  535. */
  536. async getForeignKeyReferencesForTable(tableName, options) {
  537. const queryOptions = {
  538. ...options,
  539. type: QueryTypes.FOREIGNKEYS
  540. };
  541. const query = this.queryGenerator.getForeignKeysQuery(tableName, this.sequelize.config.database);
  542. return this.sequelize.query(query, queryOptions);
  543. }
  544. /**
  545. * Remove an already existing index from a table
  546. *
  547. * @param {string} tableName Table name to drop index from
  548. * @param {string|string[]} indexNameOrAttributes Index name or list of attributes that in the index
  549. * @param {object} [options] Query options
  550. *
  551. * @returns {Promise}
  552. */
  553. async removeIndex(tableName, indexNameOrAttributes, options) {
  554. options = options || {};
  555. const sql = this.queryGenerator.removeIndexQuery(tableName, indexNameOrAttributes);
  556. return await this.sequelize.query(sql, options);
  557. }
  558. /**
  559. * Add a constraint to a table
  560. *
  561. * Available constraints:
  562. * - UNIQUE
  563. * - DEFAULT (MSSQL only)
  564. * - CHECK (MySQL - Ignored by the database engine )
  565. * - FOREIGN KEY
  566. * - PRIMARY KEY
  567. *
  568. * @example <caption>UNIQUE</caption>
  569. * queryInterface.addConstraint('Users', {
  570. * fields: ['email'],
  571. * type: 'unique',
  572. * name: 'custom_unique_constraint_name'
  573. * });
  574. *
  575. * @example <caption>CHECK</caption>
  576. * queryInterface.addConstraint('Users', {
  577. * fields: ['roles'],
  578. * type: 'check',
  579. * where: {
  580. * roles: ['user', 'admin', 'moderator', 'guest']
  581. * }
  582. * });
  583. *
  584. * @example <caption>Default - MSSQL only</caption>
  585. * queryInterface.addConstraint('Users', {
  586. * fields: ['roles'],
  587. * type: 'default',
  588. * defaultValue: 'guest'
  589. * });
  590. *
  591. * @example <caption>Primary Key</caption>
  592. * queryInterface.addConstraint('Users', {
  593. * fields: ['username'],
  594. * type: 'primary key',
  595. * name: 'custom_primary_constraint_name'
  596. * });
  597. *
  598. * @example <caption>Foreign Key</caption>
  599. * queryInterface.addConstraint('Posts', {
  600. * fields: ['username'],
  601. * type: 'foreign key',
  602. * name: 'custom_fkey_constraint_name',
  603. * references: { //Required field
  604. * table: 'target_table_name',
  605. * field: 'target_column_name'
  606. * },
  607. * onDelete: 'cascade',
  608. * onUpdate: 'cascade'
  609. * });
  610. *
  611. * @example <caption>Composite Foreign Key</caption>
  612. * queryInterface.addConstraint('TableName', {
  613. * fields: ['source_column_name', 'other_source_column_name'],
  614. * type: 'foreign key',
  615. * name: 'custom_fkey_constraint_name',
  616. * references: { //Required field
  617. * table: 'target_table_name',
  618. * fields: ['target_column_name', 'other_target_column_name']
  619. * },
  620. * onDelete: 'cascade',
  621. * onUpdate: 'cascade'
  622. * });
  623. *
  624. * @param {string} tableName Table name where you want to add a constraint
  625. * @param {object} options An object to define the constraint name, type etc
  626. * @param {string} options.type Type of constraint. One of the values in available constraints(case insensitive)
  627. * @param {Array} options.fields Array of column names to apply the constraint over
  628. * @param {string} [options.name] Name of the constraint. If not specified, sequelize automatically creates a named constraint based on constraint type, table & column names
  629. * @param {string} [options.defaultValue] The value for the default constraint
  630. * @param {object} [options.where] Where clause/expression for the CHECK constraint
  631. * @param {object} [options.references] Object specifying target table, column name to create foreign key constraint
  632. * @param {string} [options.references.table] Target table name
  633. * @param {string} [options.references.field] Target column name
  634. * @param {string} [options.references.fields] Target column names for a composite primary key. Must match the order of fields in options.fields.
  635. * @param {string} [options.deferrable] Sets the constraint to be deferred or immediately checked. See Sequelize.Deferrable. PostgreSQL Only
  636. *
  637. * @returns {Promise}
  638. */
  639. async addConstraint(tableName, options) {
  640. if (!options.fields) {
  641. throw new Error('Fields must be specified through options.fields');
  642. }
  643. if (!options.type) {
  644. throw new Error('Constraint type must be specified through options.type');
  645. }
  646. options = Utils.cloneDeep(options);
  647. const sql = this.queryGenerator.addConstraintQuery(tableName, options);
  648. return await this.sequelize.query(sql, options);
  649. }
  650. async showConstraint(tableName, constraintName, options) {
  651. const sql = this.queryGenerator.showConstraintsQuery(tableName, constraintName);
  652. return await this.sequelize.query(sql, { ...options, type: QueryTypes.SHOWCONSTRAINTS });
  653. }
  654. /**
  655. * Remove a constraint from a table
  656. *
  657. * @param {string} tableName Table name to drop constraint from
  658. * @param {string} constraintName Constraint name
  659. * @param {object} options Query options
  660. */
  661. async removeConstraint(tableName, constraintName, options) {
  662. return this.sequelize.query(this.queryGenerator.removeConstraintQuery(tableName, constraintName), options);
  663. }
  664. async insert(instance, tableName, values, options) {
  665. options = Utils.cloneDeep(options);
  666. options.hasTrigger = instance && instance.constructor.options.hasTrigger;
  667. const sql = this.queryGenerator.insertQuery(tableName, values, instance && instance.constructor.rawAttributes, options);
  668. options.type = QueryTypes.INSERT;
  669. options.instance = instance;
  670. const results = await this.sequelize.query(sql, options);
  671. if (instance) results[0].isNewRecord = false;
  672. return results;
  673. }
  674. /**
  675. * Upsert
  676. *
  677. * @param {string} tableName table to upsert on
  678. * @param {object} insertValues values to be inserted, mapped to field name
  679. * @param {object} updateValues values to be updated, mapped to field name
  680. * @param {object} where where conditions, which can be used for UPDATE part when INSERT fails
  681. * @param {object} options query options
  682. *
  683. * @returns {Promise<boolean,?number>} Resolves an array with <created, primaryKey>
  684. */
  685. async upsert(tableName, insertValues, updateValues, where, options) {
  686. options = { ...options };
  687. const model = options.model;
  688. const primaryKeys = Object.values(model.primaryKeys).map(item => item.field);
  689. const uniqueKeys = Object.values(model.uniqueKeys).filter(c => c.fields.length >= 1).map(c => c.fields);
  690. const indexKeys = Object.values(model._indexes).filter(c => c.unique && c.fields.length >= 1).map(c => c.fields);
  691. options.type = QueryTypes.UPSERT;
  692. options.updateOnDuplicate = Object.keys(updateValues);
  693. options.upsertKeys = [];
  694. // For fields in updateValues, try to find a constraint or unique index
  695. // that includes given field. Only first matching upsert key is used.
  696. for (const field of options.updateOnDuplicate) {
  697. const uniqueKey = uniqueKeys.find(fields => fields.includes(field));
  698. if (uniqueKey) {
  699. options.upsertKeys = uniqueKey;
  700. break;
  701. }
  702. const indexKey = indexKeys.find(fields => fields.includes(field));
  703. if (indexKey) {
  704. options.upsertKeys = indexKey;
  705. break;
  706. }
  707. }
  708. // Always use PK, if no constraint available OR update data contains PK
  709. if (
  710. options.upsertKeys.length === 0
  711. || _.intersection(options.updateOnDuplicate, primaryKeys).length
  712. ) {
  713. options.upsertKeys = primaryKeys;
  714. }
  715. options.upsertKeys = _.uniq(options.upsertKeys);
  716. const sql = this.queryGenerator.insertQuery(tableName, insertValues, model.rawAttributes, options);
  717. return await this.sequelize.query(sql, options);
  718. }
  719. /**
  720. * Insert multiple records into a table
  721. *
  722. * @example
  723. * queryInterface.bulkInsert('roles', [{
  724. * label: 'user',
  725. * createdAt: new Date(),
  726. * updatedAt: new Date()
  727. * }, {
  728. * label: 'admin',
  729. * createdAt: new Date(),
  730. * updatedAt: new Date()
  731. * }]);
  732. *
  733. * @param {string} tableName Table name to insert record to
  734. * @param {Array} records List of records to insert
  735. * @param {object} options Various options, please see Model.bulkCreate options
  736. * @param {object} attributes Various attributes mapped by field name
  737. *
  738. * @returns {Promise}
  739. */
  740. async bulkInsert(tableName, records, options, attributes) {
  741. options = { ...options };
  742. options.type = QueryTypes.INSERT;
  743. const results = await this.sequelize.query(
  744. this.queryGenerator.bulkInsertQuery(tableName, records, options, attributes),
  745. options
  746. );
  747. return results[0];
  748. }
  749. async update(instance, tableName, values, identifier, options) {
  750. options = { ...options };
  751. options.hasTrigger = instance && instance.constructor.options.hasTrigger;
  752. const sql = this.queryGenerator.updateQuery(tableName, values, identifier, options, instance.constructor.rawAttributes);
  753. options.type = QueryTypes.UPDATE;
  754. options.instance = instance;
  755. return await this.sequelize.query(sql, options);
  756. }
  757. /**
  758. * Update multiple records of a table
  759. *
  760. * @example
  761. * queryInterface.bulkUpdate('roles', {
  762. * label: 'admin',
  763. * }, {
  764. * userType: 3,
  765. * },
  766. * );
  767. *
  768. * @param {string} tableName Table name to update
  769. * @param {object} values Values to be inserted, mapped to field name
  770. * @param {object} identifier A hash with conditions OR an ID as integer OR a string with conditions
  771. * @param {object} [options] Various options, please see Model.bulkCreate options
  772. * @param {object} [attributes] Attributes on return objects if supported by SQL dialect
  773. *
  774. * @returns {Promise}
  775. */
  776. async bulkUpdate(tableName, values, identifier, options, attributes) {
  777. options = Utils.cloneDeep(options);
  778. if (typeof identifier === 'object') identifier = Utils.cloneDeep(identifier);
  779. const sql = this.queryGenerator.updateQuery(tableName, values, identifier, options, attributes);
  780. const table = _.isObject(tableName) ? tableName : { tableName };
  781. const model = _.find(this.sequelize.modelManager.models, { tableName: table.tableName });
  782. options.type = QueryTypes.BULKUPDATE;
  783. options.model = model;
  784. return await this.sequelize.query(sql, options);
  785. }
  786. async delete(instance, tableName, identifier, options) {
  787. const cascades = [];
  788. const sql = this.queryGenerator.deleteQuery(tableName, identifier, {}, instance.constructor);
  789. options = { ...options };
  790. // Check for a restrict field
  791. if (!!instance.constructor && !!instance.constructor.associations) {
  792. const keys = Object.keys(instance.constructor.associations);
  793. const length = keys.length;
  794. let association;
  795. for (let i = 0; i < length; i++) {
  796. association = instance.constructor.associations[keys[i]];
  797. if (association.options && association.options.onDelete &&
  798. association.options.onDelete.toLowerCase() === 'cascade' &&
  799. association.options.useHooks === true) {
  800. cascades.push(association.accessors.get);
  801. }
  802. }
  803. }
  804. for (const cascade of cascades) {
  805. let instances = await instance[cascade](options);
  806. // Check for hasOne relationship with non-existing associate ("has zero")
  807. if (!instances) continue;
  808. if (!Array.isArray(instances)) instances = [instances];
  809. for (const _instance of instances) await _instance.destroy(options);
  810. }
  811. options.instance = instance;
  812. return await this.sequelize.query(sql, options);
  813. }
  814. /**
  815. * Delete multiple records from a table
  816. *
  817. * @param {string} tableName table name from where to delete records
  818. * @param {object} where where conditions to find records to delete
  819. * @param {object} [options] options
  820. * @param {boolean} [options.truncate] Use truncate table command
  821. * @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.
  822. * @param {boolean} [options.restartIdentity=false] Only used in conjunction with TRUNCATE. Automatically restart sequences owned by columns of the truncated table.
  823. * @param {Model} [model] Model
  824. *
  825. * @returns {Promise}
  826. */
  827. async bulkDelete(tableName, where, options, model) {
  828. options = Utils.cloneDeep(options);
  829. options = _.defaults(options, { limit: null });
  830. if (options.truncate === true) {
  831. return this.sequelize.query(
  832. this.queryGenerator.truncateTableQuery(tableName, options),
  833. options
  834. );
  835. }
  836. if (typeof identifier === 'object') where = Utils.cloneDeep(where);
  837. return await this.sequelize.query(
  838. this.queryGenerator.deleteQuery(tableName, where, options, model),
  839. options
  840. );
  841. }
  842. async select(model, tableName, optionsArg) {
  843. const options = { ...optionsArg, type: QueryTypes.SELECT, model };
  844. return await this.sequelize.query(
  845. this.queryGenerator.selectQuery(tableName, options, model),
  846. options
  847. );
  848. }
  849. async increment(model, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) {
  850. options = Utils.cloneDeep(options);
  851. const sql = this.queryGenerator.arithmeticQuery('+', tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options);
  852. options.type = QueryTypes.UPDATE;
  853. options.model = model;
  854. return await this.sequelize.query(sql, options);
  855. }
  856. async decrement(model, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) {
  857. options = Utils.cloneDeep(options);
  858. const sql = this.queryGenerator.arithmeticQuery('-', tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options);
  859. options.type = QueryTypes.UPDATE;
  860. options.model = model;
  861. return await this.sequelize.query(sql, options);
  862. }
  863. async rawSelect(tableName, options, attributeSelector, Model) {
  864. options = Utils.cloneDeep(options);
  865. options = _.defaults(options, {
  866. raw: true,
  867. plain: true,
  868. type: QueryTypes.SELECT
  869. });
  870. const sql = this.queryGenerator.selectQuery(tableName, options, Model);
  871. if (attributeSelector === undefined) {
  872. throw new Error('Please pass an attribute selector!');
  873. }
  874. const data = await this.sequelize.query(sql, options);
  875. if (!options.plain) {
  876. return data;
  877. }
  878. const result = data ? data[attributeSelector] : null;
  879. if (!options || !options.dataType) {
  880. return result;
  881. }
  882. const dataType = options.dataType;
  883. if (dataType instanceof DataTypes.DECIMAL || dataType instanceof DataTypes.FLOAT) {
  884. if (result !== null) {
  885. return parseFloat(result);
  886. }
  887. }
  888. if (dataType instanceof DataTypes.INTEGER || dataType instanceof DataTypes.BIGINT) {
  889. return parseInt(result, 10);
  890. }
  891. if (dataType instanceof DataTypes.DATE) {
  892. if (result !== null && !(result instanceof Date)) {
  893. return new Date(result);
  894. }
  895. }
  896. return result;
  897. }
  898. async createTrigger(
  899. tableName,
  900. triggerName,
  901. timingType,
  902. fireOnArray,
  903. functionName,
  904. functionParams,
  905. optionsArray,
  906. options
  907. ) {
  908. const sql = this.queryGenerator.createTrigger(tableName, triggerName, timingType, fireOnArray, functionName, functionParams, optionsArray);
  909. options = options || {};
  910. if (sql) {
  911. return await this.sequelize.query(sql, options);
  912. }
  913. }
  914. async dropTrigger(tableName, triggerName, options) {
  915. const sql = this.queryGenerator.dropTrigger(tableName, triggerName);
  916. options = options || {};
  917. if (sql) {
  918. return await this.sequelize.query(sql, options);
  919. }
  920. }
  921. async renameTrigger(tableName, oldTriggerName, newTriggerName, options) {
  922. const sql = this.queryGenerator.renameTrigger(tableName, oldTriggerName, newTriggerName);
  923. options = options || {};
  924. if (sql) {
  925. return await this.sequelize.query(sql, options);
  926. }
  927. }
  928. /**
  929. * Create an SQL function
  930. *
  931. * @example
  932. * queryInterface.createFunction(
  933. * 'someFunction',
  934. * [
  935. * {type: 'integer', name: 'param', direction: 'IN'}
  936. * ],
  937. * 'integer',
  938. * 'plpgsql',
  939. * 'RETURN param + 1;',
  940. * [
  941. * 'IMMUTABLE',
  942. * 'LEAKPROOF'
  943. * ],
  944. * {
  945. * variables:
  946. * [
  947. * {type: 'integer', name: 'myVar', default: 100}
  948. * ],
  949. * force: true
  950. * };
  951. * );
  952. *
  953. * @param {string} functionName Name of SQL function to create
  954. * @param {Array} params List of parameters declared for SQL function
  955. * @param {string} returnType SQL type of function returned value
  956. * @param {string} language The name of the language that the function is implemented in
  957. * @param {string} body Source code of function
  958. * @param {Array} optionsArray Extra-options for creation
  959. * @param {object} [options] query options
  960. * @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
  961. * @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.
  962. *
  963. * @returns {Promise}
  964. */
  965. async createFunction(functionName, params, returnType, language, body, optionsArray, options) {
  966. const sql = this.queryGenerator.createFunction(functionName, params, returnType, language, body, optionsArray, options);
  967. options = options || {};
  968. if (sql) {
  969. return await this.sequelize.query(sql, options);
  970. }
  971. }
  972. /**
  973. * Drop an SQL function
  974. *
  975. * @example
  976. * queryInterface.dropFunction(
  977. * 'someFunction',
  978. * [
  979. * {type: 'varchar', name: 'param1', direction: 'IN'},
  980. * {type: 'integer', name: 'param2', direction: 'INOUT'}
  981. * ]
  982. * );
  983. *
  984. * @param {string} functionName Name of SQL function to drop
  985. * @param {Array} params List of parameters declared for SQL function
  986. * @param {object} [options] query options
  987. *
  988. * @returns {Promise}
  989. */
  990. async dropFunction(functionName, params, options) {
  991. const sql = this.queryGenerator.dropFunction(functionName, params);
  992. options = options || {};
  993. if (sql) {
  994. return await this.sequelize.query(sql, options);
  995. }
  996. }
  997. /**
  998. * Rename an SQL function
  999. *
  1000. * @example
  1001. * queryInterface.renameFunction(
  1002. * 'fooFunction',
  1003. * [
  1004. * {type: 'varchar', name: 'param1', direction: 'IN'},
  1005. * {type: 'integer', name: 'param2', direction: 'INOUT'}
  1006. * ],
  1007. * 'barFunction'
  1008. * );
  1009. *
  1010. * @param {string} oldFunctionName Current name of function
  1011. * @param {Array} params List of parameters declared for SQL function
  1012. * @param {string} newFunctionName New name of function
  1013. * @param {object} [options] query options
  1014. *
  1015. * @returns {Promise}
  1016. */
  1017. async renameFunction(oldFunctionName, params, newFunctionName, options) {
  1018. const sql = this.queryGenerator.renameFunction(oldFunctionName, params, newFunctionName);
  1019. options = options || {};
  1020. if (sql) {
  1021. return await this.sequelize.query(sql, options);
  1022. }
  1023. }
  1024. // Helper methods useful for querying
  1025. /**
  1026. * @private
  1027. */
  1028. ensureEnums() {
  1029. // noop by default
  1030. }
  1031. async setIsolationLevel(transaction, value, options) {
  1032. if (!transaction || !(transaction instanceof Transaction)) {
  1033. throw new Error('Unable to set isolation level for a transaction without transaction object!');
  1034. }
  1035. if (transaction.parent || !value) {
  1036. // Not possible to set a separate isolation level for savepoints
  1037. return;
  1038. }
  1039. options = { ...options, transaction: transaction.parent || transaction };
  1040. const sql = this.queryGenerator.setIsolationLevelQuery(value, {
  1041. parent: transaction.parent
  1042. });
  1043. if (!sql) return;
  1044. return await this.sequelize.query(sql, options);
  1045. }
  1046. async startTransaction(transaction, options) {
  1047. if (!transaction || !(transaction instanceof Transaction)) {
  1048. throw new Error('Unable to start a transaction without transaction object!');
  1049. }
  1050. options = { ...options, transaction: transaction.parent || transaction };
  1051. options.transaction.name = transaction.parent ? transaction.name : undefined;
  1052. const sql = this.queryGenerator.startTransactionQuery(transaction);
  1053. return await this.sequelize.query(sql, options);
  1054. }
  1055. async deferConstraints(transaction, options) {
  1056. options = { ...options, transaction: transaction.parent || transaction };
  1057. const sql = this.queryGenerator.deferConstraintsQuery(options);
  1058. if (sql) {
  1059. return await this.sequelize.query(sql, options);
  1060. }
  1061. }
  1062. async commitTransaction(transaction, options) {
  1063. if (!transaction || !(transaction instanceof Transaction)) {
  1064. throw new Error('Unable to commit a transaction without transaction object!');
  1065. }
  1066. if (transaction.parent) {
  1067. // Savepoints cannot be committed
  1068. return;
  1069. }
  1070. options = {
  1071. ...options,
  1072. transaction: transaction.parent || transaction,
  1073. supportsSearchPath: false,
  1074. completesTransaction: true
  1075. };
  1076. const sql = this.queryGenerator.commitTransactionQuery(transaction);
  1077. const promise = this.sequelize.query(sql, options);
  1078. transaction.finished = 'commit';
  1079. return await promise;
  1080. }
  1081. async rollbackTransaction(transaction, options) {
  1082. if (!transaction || !(transaction instanceof Transaction)) {
  1083. throw new Error('Unable to rollback a transaction without transaction object!');
  1084. }
  1085. options = {
  1086. ...options,
  1087. transaction: transaction.parent || transaction,
  1088. supportsSearchPath: false,
  1089. completesTransaction: true
  1090. };
  1091. options.transaction.name = transaction.parent ? transaction.name : undefined;
  1092. const sql = this.queryGenerator.rollbackTransactionQuery(transaction);
  1093. const promise = this.sequelize.query(sql, options);
  1094. transaction.finished = 'rollback';
  1095. return await promise;
  1096. }
  1097. }
  1098. exports.QueryInterface = QueryInterface;