query-generator.js 95 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733
  1. 'use strict';
  2. const util = require('util');
  3. const _ = require('lodash');
  4. const uuidv4 = require('uuid').v4;
  5. const Utils = require('../../utils');
  6. const deprecations = require('../../utils/deprecations');
  7. const SqlString = require('../../sql-string');
  8. const DataTypes = require('../../data-types');
  9. const Model = require('../../model');
  10. const Association = require('../../associations/base');
  11. const BelongsTo = require('../../associations/belongs-to');
  12. const BelongsToMany = require('../../associations/belongs-to-many');
  13. const HasMany = require('../../associations/has-many');
  14. const Op = require('../../operators');
  15. const sequelizeError = require('../../errors');
  16. const IndexHints = require('../../index-hints');
  17. const QuoteHelper = require('./query-generator/helpers/quote');
  18. /**
  19. * Abstract Query Generator
  20. *
  21. * @private
  22. */
  23. class QueryGenerator {
  24. constructor(options) {
  25. if (!options.sequelize) throw new Error('QueryGenerator initialized without options.sequelize');
  26. if (!options._dialect) throw new Error('QueryGenerator initialized without options._dialect');
  27. this.sequelize = options.sequelize;
  28. this.options = options.sequelize.options;
  29. // dialect name
  30. this.dialect = options._dialect.name;
  31. this._dialect = options._dialect;
  32. }
  33. extractTableDetails(tableName, options) {
  34. options = options || {};
  35. tableName = tableName || {};
  36. return {
  37. schema: tableName.schema || options.schema || 'public',
  38. tableName: _.isPlainObject(tableName) ? tableName.tableName : tableName,
  39. delimiter: tableName.delimiter || options.delimiter || '.'
  40. };
  41. }
  42. addSchema(param) {
  43. if (!param._schema) return param.tableName || param;
  44. const self = this;
  45. return {
  46. tableName: param.tableName || param,
  47. table: param.tableName || param,
  48. name: param.name || param,
  49. schema: param._schema,
  50. delimiter: param._schemaDelimiter || '.',
  51. toString() {
  52. return self.quoteTable(this);
  53. }
  54. };
  55. }
  56. dropSchema(tableName, options) {
  57. return this.dropTableQuery(tableName, options);
  58. }
  59. describeTableQuery(tableName, schema, schemaDelimiter) {
  60. const table = this.quoteTable(
  61. this.addSchema({
  62. tableName,
  63. _schema: schema,
  64. _schemaDelimiter: schemaDelimiter
  65. })
  66. );
  67. return `DESCRIBE ${table};`;
  68. }
  69. dropTableQuery(tableName) {
  70. return `DROP TABLE IF EXISTS ${this.quoteTable(tableName)};`;
  71. }
  72. renameTableQuery(before, after) {
  73. return `ALTER TABLE ${this.quoteTable(before)} RENAME TO ${this.quoteTable(after)};`;
  74. }
  75. /**
  76. * Returns an insert into command
  77. *
  78. * @param {string} table
  79. * @param {object} valueHash attribute value pairs
  80. * @param {object} modelAttributes
  81. * @param {object} [options]
  82. *
  83. * @private
  84. */
  85. insertQuery(table, valueHash, modelAttributes, options) {
  86. options = options || {};
  87. _.defaults(options, this.options);
  88. const modelAttributeMap = {};
  89. const bind = [];
  90. const fields = [];
  91. const returningModelAttributes = [];
  92. const values = [];
  93. const quotedTable = this.quoteTable(table);
  94. const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
  95. let query;
  96. let valueQuery = '';
  97. let emptyQuery = '';
  98. let outputFragment = '';
  99. let returningFragment = '';
  100. let identityWrapperRequired = false;
  101. let tmpTable = ''; //tmpTable declaration for trigger
  102. if (modelAttributes) {
  103. _.each(modelAttributes, (attribute, key) => {
  104. modelAttributeMap[key] = attribute;
  105. if (attribute.field) {
  106. modelAttributeMap[attribute.field] = attribute;
  107. }
  108. });
  109. }
  110. if (this._dialect.supports['DEFAULT VALUES']) {
  111. emptyQuery += ' DEFAULT VALUES';
  112. } else if (this._dialect.supports['VALUES ()']) {
  113. emptyQuery += ' VALUES ()';
  114. }
  115. if (this._dialect.supports.returnValues && options.returning) {
  116. const returnValues = this.generateReturnValues(modelAttributes, options);
  117. returningModelAttributes.push(...returnValues.returnFields);
  118. returningFragment = returnValues.returningFragment;
  119. tmpTable = returnValues.tmpTable || '';
  120. outputFragment = returnValues.outputFragment || '';
  121. }
  122. if (_.get(this, ['sequelize', 'options', 'dialectOptions', 'prependSearchPath']) || options.searchPath) {
  123. // Not currently supported with search path (requires output of multiple queries)
  124. options.bindParam = false;
  125. }
  126. if (this._dialect.supports.EXCEPTION && options.exception) {
  127. // Not currently supported with bind parameters (requires output of multiple queries)
  128. options.bindParam = false;
  129. }
  130. valueHash = Utils.removeNullValuesFromHash(valueHash, this.options.omitNull);
  131. for (const key in valueHash) {
  132. if (Object.prototype.hasOwnProperty.call(valueHash, key)) {
  133. const value = valueHash[key];
  134. fields.push(this.quoteIdentifier(key));
  135. // SERIALS' can't be NULL in postgresql, use DEFAULT where supported
  136. if (modelAttributeMap && modelAttributeMap[key] && modelAttributeMap[key].autoIncrement === true && !value) {
  137. if (!this._dialect.supports.autoIncrement.defaultValue) {
  138. fields.splice(-1, 1);
  139. } else if (this._dialect.supports.DEFAULT) {
  140. values.push('DEFAULT');
  141. } else {
  142. values.push(this.escape(null));
  143. }
  144. } else {
  145. if (modelAttributeMap && modelAttributeMap[key] && modelAttributeMap[key].autoIncrement === true) {
  146. identityWrapperRequired = true;
  147. }
  148. if (value instanceof Utils.SequelizeMethod || options.bindParam === false) {
  149. values.push(this.escape(value, modelAttributeMap && modelAttributeMap[key] || undefined, { context: 'INSERT' }));
  150. } else {
  151. values.push(this.format(value, modelAttributeMap && modelAttributeMap[key] || undefined, { context: 'INSERT' }, bindParam));
  152. }
  153. }
  154. }
  155. }
  156. let onDuplicateKeyUpdate = '';
  157. if (this._dialect.supports.inserts.updateOnDuplicate && options.updateOnDuplicate) {
  158. if (this._dialect.supports.inserts.updateOnDuplicate == ' ON CONFLICT DO UPDATE SET') { // postgres / sqlite
  159. // If no conflict target columns were specified, use the primary key names from options.upsertKeys
  160. const conflictKeys = options.upsertKeys.map(attr => this.quoteIdentifier(attr));
  161. const updateKeys = options.updateOnDuplicate.map(attr => `${this.quoteIdentifier(attr)}=EXCLUDED.${this.quoteIdentifier(attr)}`);
  162. onDuplicateKeyUpdate = ` ON CONFLICT (${conflictKeys.join(',')}) DO UPDATE SET ${updateKeys.join(',')}`;
  163. } else {
  164. const valueKeys = options.updateOnDuplicate.map(attr => `${this.quoteIdentifier(attr)}=VALUES(${this.quoteIdentifier(attr)})`);
  165. onDuplicateKeyUpdate += `${this._dialect.supports.inserts.updateOnDuplicate} ${valueKeys.join(',')}`;
  166. }
  167. }
  168. const replacements = {
  169. ignoreDuplicates: options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '',
  170. onConflictDoNothing: options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : '',
  171. attributes: fields.join(','),
  172. output: outputFragment,
  173. values: values.join(','),
  174. tmpTable
  175. };
  176. valueQuery = `${tmpTable}INSERT${replacements.ignoreDuplicates} INTO ${quotedTable} (${replacements.attributes})${replacements.output} VALUES (${replacements.values})${onDuplicateKeyUpdate}${replacements.onConflictDoNothing}${valueQuery}`;
  177. emptyQuery = `${tmpTable}INSERT${replacements.ignoreDuplicates} INTO ${quotedTable}${replacements.output}${onDuplicateKeyUpdate}${replacements.onConflictDoNothing}${emptyQuery}`;
  178. // Mostly for internal use, so we expect the user to know what he's doing!
  179. // pg_temp functions are private per connection, so we never risk this function interfering with another one.
  180. if (this._dialect.supports.EXCEPTION && options.exception) {
  181. const dropFunction = 'DROP FUNCTION IF EXISTS pg_temp.testfunc()';
  182. if (returningModelAttributes.length === 0) {
  183. returningModelAttributes.push('*');
  184. }
  185. const delimiter = `$func_${uuidv4().replace(/-/g, '')}$`;
  186. const selectQuery = `SELECT (testfunc.response).${returningModelAttributes.join(', (testfunc.response).')}, testfunc.sequelize_caught_exception FROM pg_temp.testfunc();`;
  187. options.exception = 'WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL;';
  188. valueQuery = `CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response ${quotedTable}, OUT sequelize_caught_exception text) RETURNS RECORD AS ${delimiter} BEGIN ${valueQuery} RETURNING * INTO response; EXCEPTION ${options.exception} END ${delimiter} LANGUAGE plpgsql; ${selectQuery} ${dropFunction}`;
  189. } else {
  190. valueQuery += returningFragment;
  191. emptyQuery += returningFragment;
  192. }
  193. query = `${replacements.attributes.length ? valueQuery : emptyQuery};`;
  194. if (identityWrapperRequired && this._dialect.supports.autoIncrement.identityInsert) {
  195. query = `SET IDENTITY_INSERT ${quotedTable} ON; ${query} SET IDENTITY_INSERT ${quotedTable} OFF;`;
  196. }
  197. // Used by Postgres upsertQuery and calls to here with options.exception set to true
  198. const result = { query };
  199. if (options.bindParam !== false) {
  200. result.bind = bind;
  201. }
  202. return result;
  203. }
  204. /**
  205. * Returns an insert into command for multiple values.
  206. *
  207. * @param {string} tableName
  208. * @param {object} fieldValueHashes
  209. * @param {object} options
  210. * @param {object} fieldMappedAttributes
  211. *
  212. * @private
  213. */
  214. bulkInsertQuery(tableName, fieldValueHashes, options, fieldMappedAttributes) {
  215. options = options || {};
  216. fieldMappedAttributes = fieldMappedAttributes || {};
  217. const tuples = [];
  218. const serials = {};
  219. const allAttributes = [];
  220. let onDuplicateKeyUpdate = '';
  221. for (const fieldValueHash of fieldValueHashes) {
  222. _.forOwn(fieldValueHash, (value, key) => {
  223. if (!allAttributes.includes(key)) {
  224. allAttributes.push(key);
  225. }
  226. if (
  227. fieldMappedAttributes[key]
  228. && fieldMappedAttributes[key].autoIncrement === true
  229. ) {
  230. serials[key] = true;
  231. }
  232. });
  233. }
  234. for (const fieldValueHash of fieldValueHashes) {
  235. const values = allAttributes.map(key => {
  236. if (
  237. this._dialect.supports.bulkDefault
  238. && serials[key] === true
  239. ) {
  240. return fieldValueHash[key] || 'DEFAULT';
  241. }
  242. return this.escape(fieldValueHash[key], fieldMappedAttributes[key], { context: 'INSERT' });
  243. });
  244. tuples.push(`(${values.join(',')})`);
  245. }
  246. if (this._dialect.supports.inserts.updateOnDuplicate && options.updateOnDuplicate) {
  247. if (this._dialect.supports.inserts.updateOnDuplicate == ' ON CONFLICT DO UPDATE SET') { // postgres / sqlite
  248. // If no conflict target columns were specified, use the primary key names from options.upsertKeys
  249. const conflictKeys = options.upsertKeys.map(attr => this.quoteIdentifier(attr));
  250. const updateKeys = options.updateOnDuplicate.map(attr => `${this.quoteIdentifier(attr)}=EXCLUDED.${this.quoteIdentifier(attr)}`);
  251. onDuplicateKeyUpdate = ` ON CONFLICT (${conflictKeys.join(',')}) DO UPDATE SET ${updateKeys.join(',')}`;
  252. } else { // mysql / maria
  253. const valueKeys = options.updateOnDuplicate.map(attr => `${this.quoteIdentifier(attr)}=VALUES(${this.quoteIdentifier(attr)})`);
  254. onDuplicateKeyUpdate = `${this._dialect.supports.inserts.updateOnDuplicate} ${valueKeys.join(',')}`;
  255. }
  256. }
  257. const ignoreDuplicates = options.ignoreDuplicates ? this._dialect.supports.inserts.ignoreDuplicates : '';
  258. const attributes = allAttributes.map(attr => this.quoteIdentifier(attr)).join(',');
  259. const onConflictDoNothing = options.ignoreDuplicates ? this._dialect.supports.inserts.onConflictDoNothing : '';
  260. let returning = '';
  261. if (this._dialect.supports.returnValues && options.returning) {
  262. const returnValues = this.generateReturnValues(fieldMappedAttributes, options);
  263. returning += returnValues.returningFragment;
  264. }
  265. return Utils.joinSQLFragments([
  266. 'INSERT',
  267. ignoreDuplicates,
  268. 'INTO',
  269. this.quoteTable(tableName),
  270. `(${attributes})`,
  271. 'VALUES',
  272. tuples.join(','),
  273. onDuplicateKeyUpdate,
  274. onConflictDoNothing,
  275. returning,
  276. ';'
  277. ]);
  278. }
  279. /**
  280. * Returns an update query
  281. *
  282. * @param {string} tableName
  283. * @param {object} attrValueHash
  284. * @param {object} where A hash with conditions (e.g. {name: 'foo'}) OR an ID as integer
  285. * @param {object} options
  286. * @param {object} attributes
  287. *
  288. * @private
  289. */
  290. updateQuery(tableName, attrValueHash, where, options, attributes) {
  291. options = options || {};
  292. _.defaults(options, this.options);
  293. attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, options.omitNull, options);
  294. const values = [];
  295. const bind = [];
  296. const modelAttributeMap = {};
  297. let outputFragment = '';
  298. let tmpTable = ''; // tmpTable declaration for trigger
  299. let suffix = '';
  300. if (_.get(this, ['sequelize', 'options', 'dialectOptions', 'prependSearchPath']) || options.searchPath) {
  301. // Not currently supported with search path (requires output of multiple queries)
  302. options.bindParam = false;
  303. }
  304. const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
  305. if (this._dialect.supports['LIMIT ON UPDATE'] && options.limit) {
  306. if (this.dialect !== 'mssql') {
  307. suffix = ` LIMIT ${this.escape(options.limit)} `;
  308. }
  309. }
  310. if (this._dialect.supports.returnValues && options.returning) {
  311. const returnValues = this.generateReturnValues(attributes, options);
  312. suffix += returnValues.returningFragment;
  313. tmpTable = returnValues.tmpTable || '';
  314. outputFragment = returnValues.outputFragment || '';
  315. // ensure that the return output is properly mapped to model fields.
  316. if (!this._dialect.supports.returnValues.output && options.returning) {
  317. options.mapToModel = true;
  318. }
  319. }
  320. if (attributes) {
  321. _.each(attributes, (attribute, key) => {
  322. modelAttributeMap[key] = attribute;
  323. if (attribute.field) {
  324. modelAttributeMap[attribute.field] = attribute;
  325. }
  326. });
  327. }
  328. for (const key in attrValueHash) {
  329. if (modelAttributeMap && modelAttributeMap[key] &&
  330. modelAttributeMap[key].autoIncrement === true &&
  331. !this._dialect.supports.autoIncrement.update) {
  332. // not allowed to update identity column
  333. continue;
  334. }
  335. const value = attrValueHash[key];
  336. if (value instanceof Utils.SequelizeMethod || options.bindParam === false) {
  337. values.push(`${this.quoteIdentifier(key)}=${this.escape(value, modelAttributeMap && modelAttributeMap[key] || undefined, { context: 'UPDATE' })}`);
  338. } else {
  339. values.push(`${this.quoteIdentifier(key)}=${this.format(value, modelAttributeMap && modelAttributeMap[key] || undefined, { context: 'UPDATE' }, bindParam)}`);
  340. }
  341. }
  342. const whereOptions = { ...options, bindParam };
  343. if (values.length === 0) {
  344. return '';
  345. }
  346. const query = `${tmpTable}UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')}${outputFragment} ${this.whereQuery(where, whereOptions)}${suffix}`.trim();
  347. // Used by Postgres upsertQuery and calls to here with options.exception set to true
  348. const result = { query };
  349. if (options.bindParam !== false) {
  350. result.bind = bind;
  351. }
  352. return result;
  353. }
  354. /**
  355. * Returns an update query using arithmetic operator
  356. *
  357. * @param {string} operator String with the arithmetic operator (e.g. '+' or '-')
  358. * @param {string} tableName Name of the table
  359. * @param {object} where A plain-object with conditions (e.g. {name: 'foo'}) OR an ID as integer
  360. * @param {object} incrementAmountsByField A plain-object with attribute-value-pairs
  361. * @param {object} extraAttributesToBeUpdated A plain-object with attribute-value-pairs
  362. * @param {object} options
  363. *
  364. * @private
  365. */
  366. arithmeticQuery(operator, tableName, where, incrementAmountsByField, extraAttributesToBeUpdated, options) {
  367. options = options || {};
  368. _.defaults(options, { returning: true });
  369. extraAttributesToBeUpdated = Utils.removeNullValuesFromHash(extraAttributesToBeUpdated, this.options.omitNull);
  370. let outputFragment = '';
  371. let returningFragment = '';
  372. if (this._dialect.supports.returnValues && options.returning) {
  373. const returnValues = this.generateReturnValues(null, options);
  374. outputFragment = returnValues.outputFragment;
  375. returningFragment = returnValues.returningFragment;
  376. }
  377. const updateSetSqlFragments = [];
  378. for (const field in incrementAmountsByField) {
  379. const incrementAmount = incrementAmountsByField[field];
  380. const quotedField = this.quoteIdentifier(field);
  381. const escapedAmount = this.escape(incrementAmount);
  382. updateSetSqlFragments.push(`${quotedField}=${quotedField}${operator} ${escapedAmount}`);
  383. }
  384. for (const field in extraAttributesToBeUpdated) {
  385. const newValue = extraAttributesToBeUpdated[field];
  386. const quotedField = this.quoteIdentifier(field);
  387. const escapedValue = this.escape(newValue);
  388. updateSetSqlFragments.push(`${quotedField}=${escapedValue}`);
  389. }
  390. return Utils.joinSQLFragments([
  391. 'UPDATE',
  392. this.quoteTable(tableName),
  393. 'SET',
  394. updateSetSqlFragments.join(','),
  395. outputFragment,
  396. this.whereQuery(where),
  397. returningFragment
  398. ]);
  399. }
  400. /*
  401. Returns an add index query.
  402. Parameters:
  403. - tableName -> Name of an existing table, possibly with schema.
  404. - options:
  405. - type: UNIQUE|FULLTEXT|SPATIAL
  406. - name: The name of the index. Default is <table>_<attr1>_<attr2>
  407. - fields: An array of attributes as string or as hash.
  408. If the attribute is a hash, it must have the following content:
  409. - name: The name of the attribute/column
  410. - length: An integer. Optional
  411. - order: 'ASC' or 'DESC'. Optional
  412. - parser
  413. - using
  414. - operator
  415. - concurrently: Pass CONCURRENT so other operations run while the index is created
  416. - rawTablename, the name of the table, without schema. Used to create the name of the index
  417. @private
  418. */
  419. addIndexQuery(tableName, attributes, options, rawTablename) {
  420. options = options || {};
  421. if (!Array.isArray(attributes)) {
  422. options = attributes;
  423. attributes = undefined;
  424. } else {
  425. options.fields = attributes;
  426. }
  427. options.prefix = options.prefix || rawTablename || tableName;
  428. if (options.prefix && typeof options.prefix === 'string') {
  429. options.prefix = options.prefix.replace(/\./g, '_');
  430. options.prefix = options.prefix.replace(/("|')/g, '');
  431. }
  432. const fieldsSql = options.fields.map(field => {
  433. if (field instanceof Utils.SequelizeMethod) {
  434. return this.handleSequelizeMethod(field);
  435. }
  436. if (typeof field === 'string') {
  437. field = {
  438. name: field
  439. };
  440. }
  441. let result = '';
  442. if (field.attribute) {
  443. field.name = field.attribute;
  444. }
  445. if (!field.name) {
  446. throw new Error(`The following index field has no name: ${util.inspect(field)}`);
  447. }
  448. result += this.quoteIdentifier(field.name);
  449. if (this._dialect.supports.index.collate && field.collate) {
  450. result += ` COLLATE ${this.quoteIdentifier(field.collate)}`;
  451. }
  452. if (this._dialect.supports.index.operator) {
  453. const operator = field.operator || options.operator;
  454. if (operator) {
  455. result += ` ${operator}`;
  456. }
  457. }
  458. if (this._dialect.supports.index.length && field.length) {
  459. result += `(${field.length})`;
  460. }
  461. if (field.order) {
  462. result += ` ${field.order}`;
  463. }
  464. return result;
  465. });
  466. if (!options.name) {
  467. // Mostly for cases where addIndex is called directly by the user without an options object (for example in migrations)
  468. // All calls that go through sequelize should already have a name
  469. options = Utils.nameIndex(options, options.prefix);
  470. }
  471. options = Model._conformIndex(options);
  472. if (!this._dialect.supports.index.type) {
  473. delete options.type;
  474. }
  475. if (options.where) {
  476. options.where = this.whereQuery(options.where);
  477. }
  478. if (typeof tableName === 'string') {
  479. tableName = this.quoteIdentifiers(tableName);
  480. } else {
  481. tableName = this.quoteTable(tableName);
  482. }
  483. const concurrently = this._dialect.supports.index.concurrently && options.concurrently ? 'CONCURRENTLY' : undefined;
  484. let ind;
  485. if (this._dialect.supports.indexViaAlter) {
  486. ind = [
  487. 'ALTER TABLE',
  488. tableName,
  489. concurrently,
  490. 'ADD'
  491. ];
  492. } else {
  493. ind = ['CREATE'];
  494. }
  495. ind = ind.concat(
  496. options.unique ? 'UNIQUE' : '',
  497. options.type, 'INDEX',
  498. !this._dialect.supports.indexViaAlter ? concurrently : undefined,
  499. this.quoteIdentifiers(options.name),
  500. this._dialect.supports.index.using === 1 && options.using ? `USING ${options.using}` : '',
  501. !this._dialect.supports.indexViaAlter ? `ON ${tableName}` : undefined,
  502. this._dialect.supports.index.using === 2 && options.using ? `USING ${options.using}` : '',
  503. `(${fieldsSql.join(', ')})`,
  504. this._dialect.supports.index.parser && options.parser ? `WITH PARSER ${options.parser}` : undefined,
  505. this._dialect.supports.index.where && options.where ? options.where : undefined
  506. );
  507. return _.compact(ind).join(' ');
  508. }
  509. addConstraintQuery(tableName, options) {
  510. if (typeof tableName === 'string') {
  511. tableName = this.quoteIdentifiers(tableName);
  512. } else {
  513. tableName = this.quoteTable(tableName);
  514. }
  515. return Utils.joinSQLFragments([
  516. 'ALTER TABLE',
  517. tableName,
  518. 'ADD',
  519. this.getConstraintSnippet(tableName, options || {}),
  520. ';'
  521. ]);
  522. }
  523. getConstraintSnippet(tableName, options) {
  524. let constraintSnippet, constraintName;
  525. const fieldsSql = options.fields.map(field => {
  526. if (typeof field === 'string') {
  527. return this.quoteIdentifier(field);
  528. }
  529. if (field instanceof Utils.SequelizeMethod) {
  530. return this.handleSequelizeMethod(field);
  531. }
  532. if (field.attribute) {
  533. field.name = field.attribute;
  534. }
  535. if (!field.name) {
  536. throw new Error(`The following index field has no name: ${field}`);
  537. }
  538. return this.quoteIdentifier(field.name);
  539. });
  540. const fieldsSqlQuotedString = fieldsSql.join(', ');
  541. const fieldsSqlString = fieldsSql.join('_');
  542. switch (options.type.toUpperCase()) {
  543. case 'UNIQUE':
  544. constraintName = this.quoteIdentifier(options.name || `${tableName}_${fieldsSqlString}_uk`);
  545. constraintSnippet = `CONSTRAINT ${constraintName} UNIQUE (${fieldsSqlQuotedString})`;
  546. break;
  547. case 'CHECK':
  548. options.where = this.whereItemsQuery(options.where);
  549. constraintName = this.quoteIdentifier(options.name || `${tableName}_${fieldsSqlString}_ck`);
  550. constraintSnippet = `CONSTRAINT ${constraintName} CHECK (${options.where})`;
  551. break;
  552. case 'DEFAULT':
  553. if (options.defaultValue === undefined) {
  554. throw new Error('Default value must be specifed for DEFAULT CONSTRAINT');
  555. }
  556. if (this._dialect.name !== 'mssql') {
  557. throw new Error('Default constraints are supported only for MSSQL dialect.');
  558. }
  559. constraintName = this.quoteIdentifier(options.name || `${tableName}_${fieldsSqlString}_df`);
  560. constraintSnippet = `CONSTRAINT ${constraintName} DEFAULT (${this.escape(options.defaultValue)}) FOR ${fieldsSql[0]}`;
  561. break;
  562. case 'PRIMARY KEY':
  563. constraintName = this.quoteIdentifier(options.name || `${tableName}_${fieldsSqlString}_pk`);
  564. constraintSnippet = `CONSTRAINT ${constraintName} PRIMARY KEY (${fieldsSqlQuotedString})`;
  565. break;
  566. case 'FOREIGN KEY':
  567. const references = options.references;
  568. if (!references || !references.table || !references.field) {
  569. throw new Error('references object with table and field must be specified');
  570. }
  571. constraintName = this.quoteIdentifier(options.name || `${tableName}_${fieldsSqlString}_${references.table}_fk`);
  572. const referencesSnippet = `${this.quoteTable(references.table)} (${this.quoteIdentifier(references.field)})`;
  573. constraintSnippet = `CONSTRAINT ${constraintName} `;
  574. constraintSnippet += `FOREIGN KEY (${fieldsSqlQuotedString}) REFERENCES ${referencesSnippet}`;
  575. if (options.onUpdate) {
  576. constraintSnippet += ` ON UPDATE ${options.onUpdate.toUpperCase()}`;
  577. }
  578. if (options.onDelete) {
  579. constraintSnippet += ` ON DELETE ${options.onDelete.toUpperCase()}`;
  580. }
  581. break;
  582. default: throw new Error(`${options.type} is invalid.`);
  583. }
  584. return constraintSnippet;
  585. }
  586. removeConstraintQuery(tableName, constraintName) {
  587. if (typeof tableName === 'string') {
  588. tableName = this.quoteIdentifiers(tableName);
  589. } else {
  590. tableName = this.quoteTable(tableName);
  591. }
  592. return Utils.joinSQLFragments([
  593. 'ALTER TABLE',
  594. tableName,
  595. 'DROP CONSTRAINT',
  596. this.quoteIdentifiers(constraintName)
  597. ]);
  598. }
  599. /*
  600. Quote an object based on its type. This is a more general version of quoteIdentifiers
  601. Strings: should proxy to quoteIdentifiers
  602. Arrays:
  603. * Expects array in the form: [<model> (optional), <model> (optional),... String, String (optional)]
  604. Each <model> can be a model, or an object {model: Model, as: String}, matching include, or an
  605. association object, or the name of an association.
  606. * Zero or more models can be included in the array and are used to trace a path through the tree of
  607. included nested associations. This produces the correct table name for the ORDER BY/GROUP BY SQL
  608. and quotes it.
  609. * If a single string is appended to end of array, it is quoted.
  610. If two strings appended, the 1st string is quoted, the 2nd string unquoted.
  611. Objects:
  612. * If raw is set, that value should be returned verbatim, without quoting
  613. * If fn is set, the string should start with the value of fn, starting paren, followed by
  614. the values of cols (which is assumed to be an array), quoted and joined with ', ',
  615. unless they are themselves objects
  616. * If direction is set, should be prepended
  617. Currently this function is only used for ordering / grouping columns and Sequelize.col(), but it could
  618. potentially also be used for other places where we want to be able to call SQL functions (e.g. as default values)
  619. @private
  620. */
  621. quote(collection, parent, connector) {
  622. // init
  623. const validOrderOptions = [
  624. 'ASC',
  625. 'DESC',
  626. 'ASC NULLS LAST',
  627. 'DESC NULLS LAST',
  628. 'ASC NULLS FIRST',
  629. 'DESC NULLS FIRST',
  630. 'NULLS FIRST',
  631. 'NULLS LAST'
  632. ];
  633. // default
  634. connector = connector || '.';
  635. // just quote as identifiers if string
  636. if (typeof collection === 'string') {
  637. return this.quoteIdentifiers(collection);
  638. }
  639. if (Array.isArray(collection)) {
  640. // iterate through the collection and mutate objects into associations
  641. collection.forEach((item, index) => {
  642. const previous = collection[index - 1];
  643. let previousAssociation;
  644. let previousModel;
  645. // set the previous as the parent when previous is undefined or the target of the association
  646. if (!previous && parent !== undefined) {
  647. previousModel = parent;
  648. } else if (previous && previous instanceof Association) {
  649. previousAssociation = previous;
  650. previousModel = previous.target;
  651. }
  652. // if the previous item is a model, then attempt getting an association
  653. if (previousModel && previousModel.prototype instanceof Model) {
  654. let model;
  655. let as;
  656. if (typeof item === 'function' && item.prototype instanceof Model) {
  657. // set
  658. model = item;
  659. } else if (_.isPlainObject(item) && item.model && item.model.prototype instanceof Model) {
  660. // set
  661. model = item.model;
  662. as = item.as;
  663. }
  664. if (model) {
  665. // set the as to either the through name or the model name
  666. if (!as && previousAssociation && previousAssociation instanceof Association && previousAssociation.through && previousAssociation.through.model === model) {
  667. // get from previous association
  668. item = new Association(previousModel, model, {
  669. as: model.name
  670. });
  671. } else {
  672. // get association from previous model
  673. item = previousModel.getAssociationForAlias(model, as);
  674. // attempt to use the model name if the item is still null
  675. if (!item) {
  676. item = previousModel.getAssociationForAlias(model, model.name);
  677. }
  678. }
  679. // make sure we have an association
  680. if (!(item instanceof Association)) {
  681. throw new Error(util.format('Unable to find a valid association for model, \'%s\'', model.name));
  682. }
  683. }
  684. }
  685. if (typeof item === 'string') {
  686. // get order index
  687. const orderIndex = validOrderOptions.indexOf(item.toUpperCase());
  688. // see if this is an order
  689. if (index > 0 && orderIndex !== -1) {
  690. item = this.sequelize.literal(` ${validOrderOptions[orderIndex]}`);
  691. } else if (previousModel && previousModel.prototype instanceof Model) {
  692. // only go down this path if we have preivous model and check only once
  693. if (previousModel.associations !== undefined && previousModel.associations[item]) {
  694. // convert the item to an association
  695. item = previousModel.associations[item];
  696. } else if (previousModel.rawAttributes !== undefined && previousModel.rawAttributes[item] && item !== previousModel.rawAttributes[item].field) {
  697. // convert the item attribute from its alias
  698. item = previousModel.rawAttributes[item].field;
  699. } else if (
  700. item.includes('.')
  701. && previousModel.rawAttributes !== undefined
  702. ) {
  703. const itemSplit = item.split('.');
  704. if (previousModel.rawAttributes[itemSplit[0]].type instanceof DataTypes.JSON) {
  705. // just quote identifiers for now
  706. const identifier = this.quoteIdentifiers(`${previousModel.name}.${previousModel.rawAttributes[itemSplit[0]].field}`);
  707. // get path
  708. const path = itemSplit.slice(1);
  709. // extract path
  710. item = this.jsonPathExtractionQuery(identifier, path);
  711. // literal because we don't want to append the model name when string
  712. item = this.sequelize.literal(item);
  713. }
  714. }
  715. }
  716. }
  717. collection[index] = item;
  718. }, this);
  719. // loop through array, adding table names of models to quoted
  720. const collectionLength = collection.length;
  721. const tableNames = [];
  722. let item;
  723. let i = 0;
  724. for (i = 0; i < collectionLength - 1; i++) {
  725. item = collection[i];
  726. if (typeof item === 'string' || item._modelAttribute || item instanceof Utils.SequelizeMethod) {
  727. break;
  728. } else if (item instanceof Association) {
  729. tableNames[i] = item.as;
  730. }
  731. }
  732. // start building sql
  733. let sql = '';
  734. if (i > 0) {
  735. sql += `${this.quoteIdentifier(tableNames.join(connector))}.`;
  736. } else if (typeof collection[0] === 'string' && parent) {
  737. sql += `${this.quoteIdentifier(parent.name)}.`;
  738. }
  739. // loop through everything past i and append to the sql
  740. collection.slice(i).forEach(collectionItem => {
  741. sql += this.quote(collectionItem, parent, connector);
  742. }, this);
  743. return sql;
  744. }
  745. if (collection._modelAttribute) {
  746. return `${this.quoteTable(collection.Model.name)}.${this.quoteIdentifier(collection.fieldName)}`;
  747. }
  748. if (collection instanceof Utils.SequelizeMethod) {
  749. return this.handleSequelizeMethod(collection);
  750. }
  751. if (_.isPlainObject(collection) && collection.raw) {
  752. // simple objects with raw is no longer supported
  753. throw new Error('The `{raw: "..."}` syntax is no longer supported. Use `sequelize.literal` instead.');
  754. }
  755. throw new Error(`Unknown structure passed to order / group: ${util.inspect(collection)}`);
  756. }
  757. /**
  758. * Split a list of identifiers by "." and quote each part
  759. *
  760. * @param {string} identifier
  761. * @param {boolean} force
  762. *
  763. * @returns {string}
  764. */
  765. quoteIdentifier(identifier, force) {
  766. return QuoteHelper.quoteIdentifier(this.dialect, identifier, {
  767. force,
  768. quoteIdentifiers: this.options.quoteIdentifiers
  769. });
  770. }
  771. quoteIdentifiers(identifiers) {
  772. if (identifiers.includes('.')) {
  773. identifiers = identifiers.split('.');
  774. const head = identifiers.slice(0, identifiers.length - 1).join('->');
  775. const tail = identifiers[identifiers.length - 1];
  776. return `${this.quoteIdentifier(head)}.${this.quoteIdentifier(tail)}`;
  777. }
  778. return this.quoteIdentifier(identifiers);
  779. }
  780. quoteAttribute(attribute, model) {
  781. if (model && attribute in model.rawAttributes) {
  782. return this.quoteIdentifier(attribute);
  783. }
  784. return this.quoteIdentifiers(attribute);
  785. }
  786. /**
  787. * Quote table name with optional alias and schema attribution
  788. *
  789. * @param {string|object} param table string or object
  790. * @param {string|boolean} alias alias name
  791. *
  792. * @returns {string}
  793. */
  794. quoteTable(param, alias) {
  795. let table = '';
  796. if (alias === true) {
  797. alias = param.as || param.name || param;
  798. }
  799. if (_.isObject(param)) {
  800. if (this._dialect.supports.schemas) {
  801. if (param.schema) {
  802. table += `${this.quoteIdentifier(param.schema)}.`;
  803. }
  804. table += this.quoteIdentifier(param.tableName);
  805. } else {
  806. if (param.schema) {
  807. table += param.schema + (param.delimiter || '.');
  808. }
  809. table += param.tableName;
  810. table = this.quoteIdentifier(table);
  811. }
  812. } else {
  813. table = this.quoteIdentifier(param);
  814. }
  815. if (alias) {
  816. table += ` AS ${this.quoteIdentifier(alias)}`;
  817. }
  818. return table;
  819. }
  820. /*
  821. Escape a value (e.g. a string, number or date)
  822. @private
  823. */
  824. escape(value, field, options) {
  825. options = options || {};
  826. if (value !== null && value !== undefined) {
  827. if (value instanceof Utils.SequelizeMethod) {
  828. return this.handleSequelizeMethod(value);
  829. }
  830. if (field && field.type) {
  831. this.validate(value, field, options);
  832. if (field.type.stringify) {
  833. // Users shouldn't have to worry about these args - just give them a function that takes a single arg
  834. const simpleEscape = escVal => SqlString.escape(escVal, this.options.timezone, this.dialect);
  835. value = field.type.stringify(value, { escape: simpleEscape, field, timezone: this.options.timezone, operation: options.operation });
  836. if (field.type.escape === false) {
  837. // The data-type already did the required escaping
  838. return value;
  839. }
  840. }
  841. }
  842. }
  843. return SqlString.escape(value, this.options.timezone, this.dialect);
  844. }
  845. bindParam(bind) {
  846. return value => {
  847. bind.push(value);
  848. return `$${bind.length}`;
  849. };
  850. }
  851. /*
  852. Returns a bind parameter representation of a value (e.g. a string, number or date)
  853. @private
  854. */
  855. format(value, field, options, bindParam) {
  856. options = options || {};
  857. if (value !== null && value !== undefined) {
  858. if (value instanceof Utils.SequelizeMethod) {
  859. throw new Error('Cannot pass SequelizeMethod as a bind parameter - use escape instead');
  860. }
  861. if (field && field.type) {
  862. this.validate(value, field, options);
  863. if (field.type.bindParam) {
  864. return field.type.bindParam(value, { escape: _.identity, field, timezone: this.options.timezone, operation: options.operation, bindParam });
  865. }
  866. }
  867. }
  868. return bindParam(value);
  869. }
  870. /*
  871. Validate a value against a field specification
  872. @private
  873. */
  874. validate(value, field, options) {
  875. if (this.typeValidation && field.type.validate && value) {
  876. try {
  877. if (options.isList && Array.isArray(value)) {
  878. for (const item of value) {
  879. field.type.validate(item, options);
  880. }
  881. } else {
  882. field.type.validate(value, options);
  883. }
  884. } catch (error) {
  885. if (error instanceof sequelizeError.ValidationError) {
  886. error.errors.push(new sequelizeError.ValidationErrorItem(
  887. error.message,
  888. 'Validation error',
  889. field.fieldName,
  890. value,
  891. null,
  892. `${field.type.key} validator`
  893. ));
  894. }
  895. throw error;
  896. }
  897. }
  898. }
  899. isIdentifierQuoted(identifier) {
  900. return QuoteHelper.isIdentifierQuoted(identifier);
  901. }
  902. /**
  903. * Generates an SQL query that extract JSON property of given path.
  904. *
  905. * @param {string} column The JSON column
  906. * @param {string|Array<string>} [path] The path to extract (optional)
  907. * @returns {string} The generated sql query
  908. * @private
  909. */
  910. jsonPathExtractionQuery(column, path) {
  911. let paths = _.toPath(path);
  912. let pathStr;
  913. const quotedColumn = this.isIdentifierQuoted(column)
  914. ? column
  915. : this.quoteIdentifier(column);
  916. switch (this.dialect) {
  917. case 'mysql':
  918. case 'mariadb':
  919. case 'sqlite':
  920. /**
  921. * Non digit sub paths need to be quoted as ECMAScript identifiers
  922. * https://bugs.mysql.com/bug.php?id=81896
  923. */
  924. if (this.dialect === 'mysql') {
  925. paths = paths.map(subPath => {
  926. return /\D/.test(subPath)
  927. ? Utils.addTicks(subPath, '"')
  928. : subPath;
  929. });
  930. }
  931. pathStr = this.escape(['$']
  932. .concat(paths)
  933. .join('.')
  934. .replace(/\.(\d+)(?:(?=\.)|$)/g, (__, digit) => `[${digit}]`));
  935. if (this.dialect === 'sqlite') {
  936. return `json_extract(${quotedColumn},${pathStr})`;
  937. }
  938. return `json_unquote(json_extract(${quotedColumn},${pathStr}))`;
  939. case 'postgres':
  940. pathStr = this.escape(`{${paths.join(',')}}`);
  941. return `(${quotedColumn}#>>${pathStr})`;
  942. default:
  943. throw new Error(`Unsupported ${this.dialect} for JSON operations`);
  944. }
  945. }
  946. /*
  947. Returns a query for selecting elements in the table <tableName>.
  948. Options:
  949. - attributes -> An array of attributes (e.g. ['name', 'birthday']). Default: *
  950. - where -> A hash with conditions (e.g. {name: 'foo'})
  951. OR an ID as integer
  952. - order -> e.g. 'id DESC'
  953. - group
  954. - limit -> The maximum count you want to get.
  955. - offset -> An offset value to start from. Only useable with limit!
  956. @private
  957. */
  958. selectQuery(tableName, options, model) {
  959. options = options || {};
  960. const limit = options.limit;
  961. const mainQueryItems = [];
  962. const subQueryItems = [];
  963. const subQuery = options.subQuery === undefined ? limit && options.hasMultiAssociation : options.subQuery;
  964. const attributes = {
  965. main: options.attributes && options.attributes.slice(),
  966. subQuery: null
  967. };
  968. const mainTable = {
  969. name: tableName,
  970. quotedName: null,
  971. as: null,
  972. model
  973. };
  974. const topLevelInfo = {
  975. names: mainTable,
  976. options,
  977. subQuery
  978. };
  979. let mainJoinQueries = [];
  980. let subJoinQueries = [];
  981. let query;
  982. // Aliases can be passed through subqueries and we don't want to reset them
  983. if (this.options.minifyAliases && !options.aliasesMapping) {
  984. options.aliasesMapping = new Map();
  985. options.aliasesByTable = {};
  986. options.includeAliases = new Map();
  987. }
  988. // resolve table name options
  989. if (options.tableAs) {
  990. mainTable.as = this.quoteIdentifier(options.tableAs);
  991. } else if (!Array.isArray(mainTable.name) && mainTable.model) {
  992. mainTable.as = this.quoteIdentifier(mainTable.model.name);
  993. }
  994. mainTable.quotedName = !Array.isArray(mainTable.name) ? this.quoteTable(mainTable.name) : tableName.map(t => {
  995. return Array.isArray(t) ? this.quoteTable(t[0], t[1]) : this.quoteTable(t, true);
  996. }).join(', ');
  997. if (subQuery && attributes.main) {
  998. for (const keyAtt of mainTable.model.primaryKeyAttributes) {
  999. // Check if mainAttributes contain the primary key of the model either as a field or an aliased field
  1000. if (!attributes.main.some(attr => keyAtt === attr || keyAtt === attr[0] || keyAtt === attr[1])) {
  1001. attributes.main.push(mainTable.model.rawAttributes[keyAtt].field ? [keyAtt, mainTable.model.rawAttributes[keyAtt].field] : keyAtt);
  1002. }
  1003. }
  1004. }
  1005. attributes.main = this.escapeAttributes(attributes.main, options, mainTable.as);
  1006. attributes.main = attributes.main || (options.include ? [`${mainTable.as}.*`] : ['*']);
  1007. // If subquery, we add the mainAttributes to the subQuery and set the mainAttributes to select * from subquery
  1008. if (subQuery || options.groupedLimit) {
  1009. // We need primary keys
  1010. attributes.subQuery = attributes.main;
  1011. attributes.main = [`${mainTable.as || mainTable.quotedName}.*`];
  1012. }
  1013. if (options.include) {
  1014. for (const include of options.include) {
  1015. if (include.separate) {
  1016. continue;
  1017. }
  1018. const joinQueries = this.generateInclude(include, { externalAs: mainTable.as, internalAs: mainTable.as }, topLevelInfo);
  1019. subJoinQueries = subJoinQueries.concat(joinQueries.subQuery);
  1020. mainJoinQueries = mainJoinQueries.concat(joinQueries.mainQuery);
  1021. if (joinQueries.attributes.main.length > 0) {
  1022. attributes.main = _.uniq(attributes.main.concat(joinQueries.attributes.main));
  1023. }
  1024. if (joinQueries.attributes.subQuery.length > 0) {
  1025. attributes.subQuery = _.uniq(attributes.subQuery.concat(joinQueries.attributes.subQuery));
  1026. }
  1027. }
  1028. }
  1029. if (subQuery) {
  1030. subQueryItems.push(this.selectFromTableFragment(options, mainTable.model, attributes.subQuery, mainTable.quotedName, mainTable.as));
  1031. subQueryItems.push(subJoinQueries.join(''));
  1032. } else {
  1033. if (options.groupedLimit) {
  1034. if (!mainTable.as) {
  1035. mainTable.as = mainTable.quotedName;
  1036. }
  1037. const where = { ...options.where };
  1038. let groupedLimitOrder,
  1039. whereKey,
  1040. include,
  1041. groupedTableName = mainTable.as;
  1042. if (typeof options.groupedLimit.on === 'string') {
  1043. whereKey = options.groupedLimit.on;
  1044. } else if (options.groupedLimit.on instanceof HasMany) {
  1045. whereKey = options.groupedLimit.on.foreignKeyField;
  1046. }
  1047. if (options.groupedLimit.on instanceof BelongsToMany) {
  1048. // BTM includes needs to join the through table on to check ID
  1049. groupedTableName = options.groupedLimit.on.manyFromSource.as;
  1050. const groupedLimitOptions = Model._validateIncludedElements({
  1051. include: [{
  1052. association: options.groupedLimit.on.manyFromSource,
  1053. duplicating: false, // The UNION'ed query may contain duplicates, but each sub-query cannot
  1054. required: true,
  1055. where: {
  1056. [Op.placeholder]: true,
  1057. ...options.groupedLimit.through && options.groupedLimit.through.where
  1058. }
  1059. }],
  1060. model
  1061. });
  1062. // Make sure attributes from the join table are mapped back to models
  1063. options.hasJoin = true;
  1064. options.hasMultiAssociation = true;
  1065. options.includeMap = Object.assign(groupedLimitOptions.includeMap, options.includeMap);
  1066. options.includeNames = groupedLimitOptions.includeNames.concat(options.includeNames || []);
  1067. include = groupedLimitOptions.include;
  1068. if (Array.isArray(options.order)) {
  1069. // We need to make sure the order by attributes are available to the parent query
  1070. options.order.forEach((order, i) => {
  1071. if (Array.isArray(order)) {
  1072. order = order[0];
  1073. }
  1074. let alias = `subquery_order_${i}`;
  1075. options.attributes.push([order, alias]);
  1076. // We don't want to prepend model name when we alias the attributes, so quote them here
  1077. alias = this.sequelize.literal(this.quote(alias));
  1078. if (Array.isArray(options.order[i])) {
  1079. options.order[i][0] = alias;
  1080. } else {
  1081. options.order[i] = alias;
  1082. }
  1083. });
  1084. groupedLimitOrder = options.order;
  1085. }
  1086. } else {
  1087. // Ordering is handled by the subqueries, so ordering the UNION'ed result is not needed
  1088. groupedLimitOrder = options.order;
  1089. delete options.order;
  1090. where[Op.placeholder] = true;
  1091. }
  1092. // Caching the base query and splicing the where part into it is consistently > twice
  1093. // as fast than generating from scratch each time for values.length >= 5
  1094. const baseQuery = `SELECT * FROM (${this.selectQuery(
  1095. tableName,
  1096. {
  1097. attributes: options.attributes,
  1098. offset: options.offset,
  1099. limit: options.groupedLimit.limit,
  1100. order: groupedLimitOrder,
  1101. aliasesMapping: options.aliasesMapping,
  1102. aliasesByTable: options.aliasesByTable,
  1103. where,
  1104. include,
  1105. model
  1106. },
  1107. model
  1108. ).replace(/;$/, '')}) AS sub`; // Every derived table must have its own alias
  1109. const placeHolder = this.whereItemQuery(Op.placeholder, true, { model });
  1110. const splicePos = baseQuery.indexOf(placeHolder);
  1111. mainQueryItems.push(this.selectFromTableFragment(options, mainTable.model, attributes.main, `(${
  1112. options.groupedLimit.values.map(value => {
  1113. let groupWhere;
  1114. if (whereKey) {
  1115. groupWhere = {
  1116. [whereKey]: value
  1117. };
  1118. }
  1119. if (include) {
  1120. groupWhere = {
  1121. [options.groupedLimit.on.foreignIdentifierField]: value
  1122. };
  1123. }
  1124. return Utils.spliceStr(baseQuery, splicePos, placeHolder.length, this.getWhereConditions(groupWhere, groupedTableName));
  1125. }).join(
  1126. this._dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION '
  1127. )
  1128. })`, mainTable.as));
  1129. } else {
  1130. mainQueryItems.push(this.selectFromTableFragment(options, mainTable.model, attributes.main, mainTable.quotedName, mainTable.as));
  1131. }
  1132. mainQueryItems.push(mainJoinQueries.join(''));
  1133. }
  1134. // Add WHERE to sub or main query
  1135. if (Object.prototype.hasOwnProperty.call(options, 'where') && !options.groupedLimit) {
  1136. options.where = this.getWhereConditions(options.where, mainTable.as || tableName, model, options);
  1137. if (options.where) {
  1138. if (subQuery) {
  1139. subQueryItems.push(` WHERE ${options.where}`);
  1140. } else {
  1141. mainQueryItems.push(` WHERE ${options.where}`);
  1142. // Walk the main query to update all selects
  1143. mainQueryItems.forEach((value, key) => {
  1144. if (value.startsWith('SELECT')) {
  1145. mainQueryItems[key] = this.selectFromTableFragment(options, model, attributes.main, mainTable.quotedName, mainTable.as, options.where);
  1146. }
  1147. });
  1148. }
  1149. }
  1150. }
  1151. // Add GROUP BY to sub or main query
  1152. if (options.group) {
  1153. options.group = Array.isArray(options.group) ? options.group.map(t => this.aliasGrouping(t, model, mainTable.as, options)).join(', ') : this.aliasGrouping(options.group, model, mainTable.as, options);
  1154. if (subQuery && options.group) {
  1155. subQueryItems.push(` GROUP BY ${options.group}`);
  1156. } else if (options.group) {
  1157. mainQueryItems.push(` GROUP BY ${options.group}`);
  1158. }
  1159. }
  1160. // Add HAVING to sub or main query
  1161. if (Object.prototype.hasOwnProperty.call(options, 'having')) {
  1162. options.having = this.getWhereConditions(options.having, tableName, model, options, false);
  1163. if (options.having) {
  1164. if (subQuery) {
  1165. subQueryItems.push(` HAVING ${options.having}`);
  1166. } else {
  1167. mainQueryItems.push(` HAVING ${options.having}`);
  1168. }
  1169. }
  1170. }
  1171. // Add ORDER to sub or main query
  1172. if (options.order) {
  1173. const orders = this.getQueryOrders(options, model, subQuery);
  1174. if (orders.mainQueryOrder.length) {
  1175. mainQueryItems.push(` ORDER BY ${orders.mainQueryOrder.join(', ')}`);
  1176. }
  1177. if (orders.subQueryOrder.length) {
  1178. subQueryItems.push(` ORDER BY ${orders.subQueryOrder.join(', ')}`);
  1179. }
  1180. }
  1181. // Add LIMIT, OFFSET to sub or main query
  1182. const limitOrder = this.addLimitAndOffset(options, mainTable.model);
  1183. if (limitOrder && !options.groupedLimit) {
  1184. if (subQuery) {
  1185. subQueryItems.push(limitOrder);
  1186. } else {
  1187. mainQueryItems.push(limitOrder);
  1188. }
  1189. }
  1190. if (subQuery) {
  1191. this._throwOnEmptyAttributes(attributes.main, { modelName: model && model.name, as: mainTable.as });
  1192. query = `SELECT ${attributes.main.join(', ')} FROM (${subQueryItems.join('')}) AS ${mainTable.as}${mainJoinQueries.join('')}${mainQueryItems.join('')}`;
  1193. } else {
  1194. query = mainQueryItems.join('');
  1195. }
  1196. if (options.lock && this._dialect.supports.lock) {
  1197. let lock = options.lock;
  1198. if (typeof options.lock === 'object') {
  1199. lock = options.lock.level;
  1200. }
  1201. if (this._dialect.supports.lockKey && (lock === 'KEY SHARE' || lock === 'NO KEY UPDATE')) {
  1202. query += ` FOR ${lock}`;
  1203. } else if (lock === 'SHARE') {
  1204. query += ` ${this._dialect.supports.forShare}`;
  1205. } else {
  1206. query += ' FOR UPDATE';
  1207. }
  1208. if (this._dialect.supports.lockOf && options.lock.of && options.lock.of.prototype instanceof Model) {
  1209. query += ` OF ${this.quoteTable(options.lock.of.name)}`;
  1210. }
  1211. if (this._dialect.supports.skipLocked && options.skipLocked) {
  1212. query += ' SKIP LOCKED';
  1213. }
  1214. }
  1215. return `${query};`;
  1216. }
  1217. aliasGrouping(field, model, tableName, options) {
  1218. const src = Array.isArray(field) ? field[0] : field;
  1219. return this.quote(this._getAliasForField(tableName, src, options) || src, model);
  1220. }
  1221. escapeAttributes(attributes, options, mainTableAs) {
  1222. return attributes && attributes.map(attr => {
  1223. let addTable = true;
  1224. if (attr instanceof Utils.SequelizeMethod) {
  1225. return this.handleSequelizeMethod(attr);
  1226. }
  1227. if (Array.isArray(attr)) {
  1228. if (attr.length !== 2) {
  1229. throw new Error(`${JSON.stringify(attr)} is not a valid attribute definition. Please use the following format: ['attribute definition', 'alias']`);
  1230. }
  1231. attr = attr.slice();
  1232. if (attr[0] instanceof Utils.SequelizeMethod) {
  1233. attr[0] = this.handleSequelizeMethod(attr[0]);
  1234. addTable = false;
  1235. } else if (!attr[0].includes('(') && !attr[0].includes(')')) {
  1236. attr[0] = this.quoteIdentifier(attr[0]);
  1237. } else {
  1238. deprecations.noRawAttributes();
  1239. }
  1240. let alias = attr[1];
  1241. if (this.options.minifyAliases) {
  1242. alias = this._getMinifiedAlias(alias, mainTableAs, options);
  1243. }
  1244. attr = [attr[0], this.quoteIdentifier(alias)].join(' AS ');
  1245. } else {
  1246. attr = !attr.includes(Utils.TICK_CHAR) && !attr.includes('"')
  1247. ? this.quoteAttribute(attr, options.model)
  1248. : this.escape(attr);
  1249. }
  1250. if (!_.isEmpty(options.include) && !attr.includes('.') && addTable) {
  1251. attr = `${mainTableAs}.${attr}`;
  1252. }
  1253. return attr;
  1254. });
  1255. }
  1256. generateInclude(include, parentTableName, topLevelInfo) {
  1257. const joinQueries = {
  1258. mainQuery: [],
  1259. subQuery: []
  1260. };
  1261. const mainChildIncludes = [];
  1262. const subChildIncludes = [];
  1263. let requiredMismatch = false;
  1264. const includeAs = {
  1265. internalAs: include.as,
  1266. externalAs: include.as
  1267. };
  1268. const attributes = {
  1269. main: [],
  1270. subQuery: []
  1271. };
  1272. let joinQuery;
  1273. topLevelInfo.options.keysEscaped = true;
  1274. if (topLevelInfo.names.name !== parentTableName.externalAs && topLevelInfo.names.as !== parentTableName.externalAs) {
  1275. includeAs.internalAs = `${parentTableName.internalAs}->${include.as}`;
  1276. includeAs.externalAs = `${parentTableName.externalAs}.${include.as}`;
  1277. }
  1278. // includeIgnoreAttributes is used by aggregate functions
  1279. if (topLevelInfo.options.includeIgnoreAttributes !== false) {
  1280. include.model._expandAttributes(include);
  1281. Utils.mapFinderOptions(include, include.model);
  1282. const includeAttributes = include.attributes.map(attr => {
  1283. let attrAs = attr;
  1284. let verbatim = false;
  1285. if (Array.isArray(attr) && attr.length === 2) {
  1286. if (attr[0] instanceof Utils.SequelizeMethod && (
  1287. attr[0] instanceof Utils.Literal ||
  1288. attr[0] instanceof Utils.Cast ||
  1289. attr[0] instanceof Utils.Fn
  1290. )) {
  1291. verbatim = true;
  1292. }
  1293. attr = attr.map(attr => attr instanceof Utils.SequelizeMethod ? this.handleSequelizeMethod(attr) : attr);
  1294. attrAs = attr[1];
  1295. attr = attr[0];
  1296. }
  1297. if (attr instanceof Utils.Literal) {
  1298. return attr.val; // We trust the user to rename the field correctly
  1299. }
  1300. if (attr instanceof Utils.Cast || attr instanceof Utils.Fn) {
  1301. throw new Error(
  1302. 'Tried to select attributes using Sequelize.cast or Sequelize.fn without specifying an alias for the result, during eager loading. ' +
  1303. 'This means the attribute will not be added to the returned instance'
  1304. );
  1305. }
  1306. let prefix;
  1307. if (verbatim === true) {
  1308. prefix = attr;
  1309. } else if (/#>>|->>/.test(attr)) {
  1310. prefix = `(${this.quoteIdentifier(includeAs.internalAs)}.${attr.replace(/\(|\)/g, '')})`;
  1311. } else if (/json_extract\(/.test(attr)) {
  1312. prefix = attr.replace(/json_extract\(/i, `json_extract(${this.quoteIdentifier(includeAs.internalAs)}.`);
  1313. } else {
  1314. prefix = `${this.quoteIdentifier(includeAs.internalAs)}.${this.quoteIdentifier(attr)}`;
  1315. }
  1316. let alias = `${includeAs.externalAs}.${attrAs}`;
  1317. if (this.options.minifyAliases) {
  1318. alias = this._getMinifiedAlias(alias, includeAs.internalAs, topLevelInfo.options);
  1319. }
  1320. return Utils.joinSQLFragments([
  1321. prefix,
  1322. 'AS',
  1323. this.quoteIdentifier(alias, true)
  1324. ]);
  1325. });
  1326. if (include.subQuery && topLevelInfo.subQuery) {
  1327. for (const attr of includeAttributes) {
  1328. attributes.subQuery.push(attr);
  1329. }
  1330. } else {
  1331. for (const attr of includeAttributes) {
  1332. attributes.main.push(attr);
  1333. }
  1334. }
  1335. }
  1336. //through
  1337. if (include.through) {
  1338. joinQuery = this.generateThroughJoin(include, includeAs, parentTableName.internalAs, topLevelInfo);
  1339. } else {
  1340. this._generateSubQueryFilter(include, includeAs, topLevelInfo);
  1341. joinQuery = this.generateJoin(include, topLevelInfo);
  1342. }
  1343. // handle possible new attributes created in join
  1344. if (joinQuery.attributes.main.length > 0) {
  1345. attributes.main = attributes.main.concat(joinQuery.attributes.main);
  1346. }
  1347. if (joinQuery.attributes.subQuery.length > 0) {
  1348. attributes.subQuery = attributes.subQuery.concat(joinQuery.attributes.subQuery);
  1349. }
  1350. if (include.include) {
  1351. for (const childInclude of include.include) {
  1352. if (childInclude.separate || childInclude._pseudo) {
  1353. continue;
  1354. }
  1355. const childJoinQueries = this.generateInclude(childInclude, includeAs, topLevelInfo);
  1356. if (include.required === false && childInclude.required === true) {
  1357. requiredMismatch = true;
  1358. }
  1359. // if the child is a sub query we just give it to the
  1360. if (childInclude.subQuery && topLevelInfo.subQuery) {
  1361. subChildIncludes.push(childJoinQueries.subQuery);
  1362. }
  1363. if (childJoinQueries.mainQuery) {
  1364. mainChildIncludes.push(childJoinQueries.mainQuery);
  1365. }
  1366. if (childJoinQueries.attributes.main.length > 0) {
  1367. attributes.main = attributes.main.concat(childJoinQueries.attributes.main);
  1368. }
  1369. if (childJoinQueries.attributes.subQuery.length > 0) {
  1370. attributes.subQuery = attributes.subQuery.concat(childJoinQueries.attributes.subQuery);
  1371. }
  1372. }
  1373. }
  1374. if (include.subQuery && topLevelInfo.subQuery) {
  1375. if (requiredMismatch && subChildIncludes.length > 0) {
  1376. joinQueries.subQuery.push(` ${joinQuery.join} ( ${joinQuery.body}${subChildIncludes.join('')} ) ON ${joinQuery.condition}`);
  1377. } else {
  1378. joinQueries.subQuery.push(` ${joinQuery.join} ${joinQuery.body} ON ${joinQuery.condition}`);
  1379. if (subChildIncludes.length > 0) {
  1380. joinQueries.subQuery.push(subChildIncludes.join(''));
  1381. }
  1382. }
  1383. joinQueries.mainQuery.push(mainChildIncludes.join(''));
  1384. } else {
  1385. if (requiredMismatch && mainChildIncludes.length > 0) {
  1386. joinQueries.mainQuery.push(` ${joinQuery.join} ( ${joinQuery.body}${mainChildIncludes.join('')} ) ON ${joinQuery.condition}`);
  1387. } else {
  1388. joinQueries.mainQuery.push(` ${joinQuery.join} ${joinQuery.body} ON ${joinQuery.condition}`);
  1389. if (mainChildIncludes.length > 0) {
  1390. joinQueries.mainQuery.push(mainChildIncludes.join(''));
  1391. }
  1392. }
  1393. joinQueries.subQuery.push(subChildIncludes.join(''));
  1394. }
  1395. return {
  1396. mainQuery: joinQueries.mainQuery.join(''),
  1397. subQuery: joinQueries.subQuery.join(''),
  1398. attributes
  1399. };
  1400. }
  1401. _getMinifiedAlias(alias, tableName, options) {
  1402. // We do not want to re-alias in case of a subquery
  1403. if (options.aliasesByTable[`${tableName}${alias}`]) {
  1404. return options.aliasesByTable[`${tableName}${alias}`];
  1405. }
  1406. // Do not alias custom suquery_orders
  1407. if (alias.match(/subquery_order_[0-9]/)) {
  1408. return alias;
  1409. }
  1410. const minifiedAlias = `_${options.aliasesMapping.size}`;
  1411. options.aliasesMapping.set(minifiedAlias, alias);
  1412. options.aliasesByTable[`${tableName}${alias}`] = minifiedAlias;
  1413. return minifiedAlias;
  1414. }
  1415. _getAliasForField(tableName, field, options) {
  1416. if (this.options.minifyAliases) {
  1417. if (options.aliasesByTable[`${tableName}${field}`]) {
  1418. return options.aliasesByTable[`${tableName}${field}`];
  1419. }
  1420. }
  1421. return null;
  1422. }
  1423. generateJoin(include, topLevelInfo) {
  1424. const association = include.association;
  1425. const parent = include.parent;
  1426. const parentIsTop = !!parent && !include.parent.association && include.parent.model.name === topLevelInfo.options.model.name;
  1427. let $parent;
  1428. let joinWhere;
  1429. /* Attributes for the left side */
  1430. const left = association.source;
  1431. const attrLeft = association instanceof BelongsTo ?
  1432. association.identifier :
  1433. association.sourceKeyAttribute || left.primaryKeyAttribute;
  1434. const fieldLeft = association instanceof BelongsTo ?
  1435. association.identifierField :
  1436. left.rawAttributes[association.sourceKeyAttribute || left.primaryKeyAttribute].field;
  1437. let asLeft;
  1438. /* Attributes for the right side */
  1439. const right = include.model;
  1440. const tableRight = right.getTableName();
  1441. const fieldRight = association instanceof BelongsTo ?
  1442. right.rawAttributes[association.targetIdentifier || right.primaryKeyAttribute].field :
  1443. association.identifierField;
  1444. let asRight = include.as;
  1445. while (($parent = $parent && $parent.parent || include.parent) && $parent.association) {
  1446. if (asLeft) {
  1447. asLeft = `${$parent.as}->${asLeft}`;
  1448. } else {
  1449. asLeft = $parent.as;
  1450. }
  1451. }
  1452. if (!asLeft) asLeft = parent.as || parent.model.name;
  1453. else asRight = `${asLeft}->${asRight}`;
  1454. let joinOn = `${this.quoteTable(asLeft)}.${this.quoteIdentifier(fieldLeft)}`;
  1455. const subqueryAttributes = [];
  1456. if (topLevelInfo.options.groupedLimit && parentIsTop || topLevelInfo.subQuery && include.parent.subQuery && !include.subQuery) {
  1457. if (parentIsTop) {
  1458. // The main model attributes is not aliased to a prefix
  1459. const tableName = this.quoteTable(parent.as || parent.model.name);
  1460. // Check for potential aliased JOIN condition
  1461. joinOn = this._getAliasForField(tableName, attrLeft, topLevelInfo.options) || `${tableName}.${this.quoteIdentifier(attrLeft)}`;
  1462. if (topLevelInfo.subQuery) {
  1463. subqueryAttributes.push(`${tableName}.${this.quoteIdentifier(fieldLeft)}`);
  1464. }
  1465. } else {
  1466. const joinSource = `${asLeft.replace(/->/g, '.')}.${attrLeft}`;
  1467. // Check for potential aliased JOIN condition
  1468. joinOn = this._getAliasForField(asLeft, joinSource, topLevelInfo.options) || this.quoteIdentifier(joinSource);
  1469. }
  1470. }
  1471. joinOn += ` = ${this.quoteIdentifier(asRight)}.${this.quoteIdentifier(fieldRight)}`;
  1472. if (include.on) {
  1473. joinOn = this.whereItemsQuery(include.on, {
  1474. prefix: this.sequelize.literal(this.quoteIdentifier(asRight)),
  1475. model: include.model
  1476. });
  1477. }
  1478. if (include.where) {
  1479. joinWhere = this.whereItemsQuery(include.where, {
  1480. prefix: this.sequelize.literal(this.quoteIdentifier(asRight)),
  1481. model: include.model
  1482. });
  1483. if (joinWhere) {
  1484. if (include.or) {
  1485. joinOn += ` OR ${joinWhere}`;
  1486. } else {
  1487. joinOn += ` AND ${joinWhere}`;
  1488. }
  1489. }
  1490. }
  1491. if (this.options.minifyAliases && asRight.length > 63) {
  1492. const alias = `%${topLevelInfo.options.includeAliases.size}`;
  1493. topLevelInfo.options.includeAliases.set(alias, asRight);
  1494. }
  1495. return {
  1496. join: include.required ? 'INNER JOIN' : include.right && this._dialect.supports['RIGHT JOIN'] ? 'RIGHT OUTER JOIN' : 'LEFT OUTER JOIN',
  1497. body: this.quoteTable(tableRight, asRight),
  1498. condition: joinOn,
  1499. attributes: {
  1500. main: [],
  1501. subQuery: subqueryAttributes
  1502. }
  1503. };
  1504. }
  1505. /**
  1506. * Returns the SQL fragments to handle returning the attributes from an insert/update query.
  1507. *
  1508. * @param {object} modelAttributes An object with the model attributes.
  1509. * @param {object} options An object with options.
  1510. *
  1511. * @private
  1512. */
  1513. generateReturnValues(modelAttributes, options) {
  1514. const returnFields = [];
  1515. const returnTypes = [];
  1516. let outputFragment = '';
  1517. let returningFragment = '';
  1518. let tmpTable = '';
  1519. if (Array.isArray(options.returning)) {
  1520. returnFields.push(...options.returning.map(field => this.quoteIdentifier(field)));
  1521. } else if (modelAttributes) {
  1522. _.each(modelAttributes, attribute => {
  1523. if (!(attribute.type instanceof DataTypes.VIRTUAL)) {
  1524. returnFields.push(this.quoteIdentifier(attribute.field));
  1525. returnTypes.push(attribute.type);
  1526. }
  1527. });
  1528. }
  1529. if (_.isEmpty(returnFields)) {
  1530. returnFields.push('*');
  1531. }
  1532. if (this._dialect.supports.returnValues.returning) {
  1533. returningFragment = ` RETURNING ${returnFields.join(',')}`;
  1534. } else if (this._dialect.supports.returnValues.output) {
  1535. outputFragment = ` OUTPUT ${returnFields.map(field => `INSERTED.${field}`).join(',')}`;
  1536. //To capture output rows when there is a trigger on MSSQL DB
  1537. if (options.hasTrigger && this._dialect.supports.tmpTableTrigger) {
  1538. const tmpColumns = returnFields.map((field, i) => `${field} ${returnTypes[i].toSql()}`);
  1539. tmpTable = `DECLARE @tmp TABLE (${tmpColumns.join(',')}); `;
  1540. outputFragment += ' INTO @tmp';
  1541. returningFragment = '; SELECT * FROM @tmp';
  1542. }
  1543. }
  1544. return { outputFragment, returnFields, returningFragment, tmpTable };
  1545. }
  1546. generateThroughJoin(include, includeAs, parentTableName, topLevelInfo) {
  1547. const through = include.through;
  1548. const throughTable = through.model.getTableName();
  1549. const throughAs = `${includeAs.internalAs}->${through.as}`;
  1550. const externalThroughAs = `${includeAs.externalAs}.${through.as}`;
  1551. const throughAttributes = through.attributes.map(attr => {
  1552. let alias = `${externalThroughAs}.${Array.isArray(attr) ? attr[1] : attr}`;
  1553. if (this.options.minifyAliases) {
  1554. alias = this._getMinifiedAlias(alias, throughAs, topLevelInfo.options);
  1555. }
  1556. return Utils.joinSQLFragments([
  1557. `${this.quoteIdentifier(throughAs)}.${this.quoteIdentifier(Array.isArray(attr) ? attr[0] : attr)}`,
  1558. 'AS',
  1559. this.quoteIdentifier(alias)
  1560. ]);
  1561. });
  1562. const association = include.association;
  1563. const parentIsTop = !include.parent.association && include.parent.model.name === topLevelInfo.options.model.name;
  1564. const tableSource = parentTableName;
  1565. const identSource = association.identifierField;
  1566. const tableTarget = includeAs.internalAs;
  1567. const identTarget = association.foreignIdentifierField;
  1568. const attrTarget = association.targetKeyField;
  1569. const joinType = include.required ? 'INNER JOIN' : include.right && this._dialect.supports['RIGHT JOIN'] ? 'RIGHT OUTER JOIN' : 'LEFT OUTER JOIN';
  1570. let joinBody;
  1571. let joinCondition;
  1572. const attributes = {
  1573. main: [],
  1574. subQuery: []
  1575. };
  1576. let attrSource = association.sourceKey;
  1577. let sourceJoinOn;
  1578. let targetJoinOn;
  1579. let throughWhere;
  1580. let targetWhere;
  1581. if (topLevelInfo.options.includeIgnoreAttributes !== false) {
  1582. // Through includes are always hasMany, so we need to add the attributes to the mainAttributes no matter what (Real join will never be executed in subquery)
  1583. for (const attr of throughAttributes) {
  1584. attributes.main.push(attr);
  1585. }
  1586. }
  1587. // Figure out if we need to use field or attribute
  1588. if (!topLevelInfo.subQuery) {
  1589. attrSource = association.sourceKeyField;
  1590. }
  1591. if (topLevelInfo.subQuery && !include.subQuery && !include.parent.subQuery && include.parent.model !== topLevelInfo.options.mainModel) {
  1592. attrSource = association.sourceKeyField;
  1593. }
  1594. // Filter statement for left side of through
  1595. // Used by both join and subquery where
  1596. // If parent include was in a subquery need to join on the aliased attribute
  1597. if (topLevelInfo.subQuery && !include.subQuery && include.parent.subQuery && !parentIsTop) {
  1598. // If we are minifying aliases and our JOIN target has been minified, we need to use the alias instead of the original column name
  1599. const joinSource = this._getAliasForField(tableSource, `${tableSource}.${attrSource}`, topLevelInfo.options) || `${tableSource}.${attrSource}`;
  1600. sourceJoinOn = `${this.quoteIdentifier(joinSource)} = `;
  1601. } else {
  1602. // If we are minifying aliases and our JOIN target has been minified, we need to use the alias instead of the original column name
  1603. const aliasedSource = this._getAliasForField(tableSource, attrSource, topLevelInfo.options) || attrSource;
  1604. sourceJoinOn = `${this.quoteTable(tableSource)}.${this.quoteIdentifier(aliasedSource)} = `;
  1605. }
  1606. sourceJoinOn += `${this.quoteIdentifier(throughAs)}.${this.quoteIdentifier(identSource)}`;
  1607. // Filter statement for right side of through
  1608. // Used by both join and subquery where
  1609. targetJoinOn = `${this.quoteIdentifier(tableTarget)}.${this.quoteIdentifier(attrTarget)} = `;
  1610. targetJoinOn += `${this.quoteIdentifier(throughAs)}.${this.quoteIdentifier(identTarget)}`;
  1611. if (through.where) {
  1612. throughWhere = this.getWhereConditions(through.where, this.sequelize.literal(this.quoteIdentifier(throughAs)), through.model);
  1613. }
  1614. if (this._dialect.supports.joinTableDependent) {
  1615. // Generate a wrapped join so that the through table join can be dependent on the target join
  1616. joinBody = `( ${this.quoteTable(throughTable, throughAs)} INNER JOIN ${this.quoteTable(include.model.getTableName(), includeAs.internalAs)} ON ${targetJoinOn}`;
  1617. if (throughWhere) {
  1618. joinBody += ` AND ${throughWhere}`;
  1619. }
  1620. joinBody += ')';
  1621. joinCondition = sourceJoinOn;
  1622. } else {
  1623. // Generate join SQL for left side of through
  1624. joinBody = `${this.quoteTable(throughTable, throughAs)} ON ${sourceJoinOn} ${joinType} ${this.quoteTable(include.model.getTableName(), includeAs.internalAs)}`;
  1625. joinCondition = targetJoinOn;
  1626. if (throughWhere) {
  1627. joinCondition += ` AND ${throughWhere}`;
  1628. }
  1629. }
  1630. if (include.where || include.through.where) {
  1631. if (include.where) {
  1632. targetWhere = this.getWhereConditions(include.where, this.sequelize.literal(this.quoteIdentifier(includeAs.internalAs)), include.model, topLevelInfo.options);
  1633. if (targetWhere) {
  1634. joinCondition += ` AND ${targetWhere}`;
  1635. }
  1636. }
  1637. }
  1638. this._generateSubQueryFilter(include, includeAs, topLevelInfo);
  1639. return {
  1640. join: joinType,
  1641. body: joinBody,
  1642. condition: joinCondition,
  1643. attributes
  1644. };
  1645. }
  1646. /*
  1647. * Generates subQueryFilter - a select nested in the where clause of the subQuery.
  1648. * For a given include a query is generated that contains all the way from the subQuery
  1649. * table to the include table plus everything that's in required transitive closure of the
  1650. * given include.
  1651. */
  1652. _generateSubQueryFilter(include, includeAs, topLevelInfo) {
  1653. if (!topLevelInfo.subQuery || !include.subQueryFilter) {
  1654. return;
  1655. }
  1656. if (!topLevelInfo.options.where) {
  1657. topLevelInfo.options.where = {};
  1658. }
  1659. let parent = include;
  1660. let child = include;
  1661. let nestedIncludes = this._getRequiredClosure(include).include;
  1662. let query;
  1663. while ((parent = parent.parent)) { // eslint-disable-line
  1664. if (parent.parent && !parent.required) {
  1665. return; // only generate subQueryFilter if all the parents of this include are required
  1666. }
  1667. if (parent.subQueryFilter) {
  1668. // the include is already handled as this parent has the include on its required closure
  1669. // skip to prevent duplicate subQueryFilter
  1670. return;
  1671. }
  1672. nestedIncludes = [{ ...child, include: nestedIncludes, attributes: [] }];
  1673. child = parent;
  1674. }
  1675. const topInclude = nestedIncludes[0];
  1676. const topParent = topInclude.parent;
  1677. const topAssociation = topInclude.association;
  1678. topInclude.association = undefined;
  1679. if (topInclude.through && Object(topInclude.through.model) === topInclude.through.model) {
  1680. query = this.selectQuery(topInclude.through.model.getTableName(), {
  1681. attributes: [topInclude.through.model.primaryKeyField],
  1682. include: Model._validateIncludedElements({
  1683. model: topInclude.through.model,
  1684. include: [{
  1685. association: topAssociation.toTarget,
  1686. required: true,
  1687. where: topInclude.where,
  1688. include: topInclude.include
  1689. }]
  1690. }).include,
  1691. model: topInclude.through.model,
  1692. where: {
  1693. [Op.and]: [
  1694. this.sequelize.literal([
  1695. `${this.quoteTable(topParent.model.name)}.${this.quoteIdentifier(topParent.model.primaryKeyField)}`,
  1696. `${this.quoteIdentifier(topInclude.through.model.name)}.${this.quoteIdentifier(topAssociation.identifierField)}`
  1697. ].join(' = ')),
  1698. topInclude.through.where
  1699. ]
  1700. },
  1701. limit: 1,
  1702. includeIgnoreAttributes: false
  1703. }, topInclude.through.model);
  1704. } else {
  1705. const isBelongsTo = topAssociation.associationType === 'BelongsTo';
  1706. const sourceField = isBelongsTo ? topAssociation.identifierField : topAssociation.sourceKeyField || topParent.model.primaryKeyField;
  1707. const targetField = isBelongsTo ? topAssociation.sourceKeyField || topInclude.model.primaryKeyField : topAssociation.identifierField;
  1708. const join = [
  1709. `${this.quoteIdentifier(topInclude.as)}.${this.quoteIdentifier(targetField)}`,
  1710. `${this.quoteTable(topParent.as || topParent.model.name)}.${this.quoteIdentifier(sourceField)}`
  1711. ].join(' = ');
  1712. query = this.selectQuery(topInclude.model.getTableName(), {
  1713. attributes: [targetField],
  1714. include: Model._validateIncludedElements(topInclude).include,
  1715. model: topInclude.model,
  1716. where: {
  1717. [Op.and]: [
  1718. topInclude.where,
  1719. { [Op.join]: this.sequelize.literal(join) }
  1720. ]
  1721. },
  1722. limit: 1,
  1723. tableAs: topInclude.as,
  1724. includeIgnoreAttributes: false
  1725. }, topInclude.model);
  1726. }
  1727. if (!topLevelInfo.options.where[Op.and]) {
  1728. topLevelInfo.options.where[Op.and] = [];
  1729. }
  1730. topLevelInfo.options.where[`__${includeAs.internalAs}`] = this.sequelize.literal([
  1731. '(',
  1732. query.replace(/;$/, ''),
  1733. ')',
  1734. 'IS NOT NULL'
  1735. ].join(' '));
  1736. }
  1737. /*
  1738. * For a given include hierarchy creates a copy of it where only the required includes
  1739. * are preserved.
  1740. */
  1741. _getRequiredClosure(include) {
  1742. const copy = { ...include, attributes: [], include: [] };
  1743. if (Array.isArray(include.include)) {
  1744. copy.include = include.include
  1745. .filter(i => i.required)
  1746. .map(inc => this._getRequiredClosure(inc));
  1747. }
  1748. return copy;
  1749. }
  1750. getQueryOrders(options, model, subQuery) {
  1751. const mainQueryOrder = [];
  1752. const subQueryOrder = [];
  1753. if (Array.isArray(options.order)) {
  1754. for (let order of options.order) {
  1755. // wrap if not array
  1756. if (!Array.isArray(order)) {
  1757. order = [order];
  1758. }
  1759. if (
  1760. subQuery
  1761. && Array.isArray(order)
  1762. && order[0]
  1763. && !(order[0] instanceof Association)
  1764. && !(typeof order[0] === 'function' && order[0].prototype instanceof Model)
  1765. && !(typeof order[0].model === 'function' && order[0].model.prototype instanceof Model)
  1766. && !(typeof order[0] === 'string' && model && model.associations !== undefined && model.associations[order[0]])
  1767. ) {
  1768. subQueryOrder.push(this.quote(order, model, '->'));
  1769. }
  1770. if (subQuery) {
  1771. // Handle case where sub-query renames attribute we want to order by,
  1772. // see https://github.com/sequelize/sequelize/issues/8739
  1773. const subQueryAttribute = options.attributes.find(a => Array.isArray(a) && a[0] === order[0] && a[1]);
  1774. if (subQueryAttribute) {
  1775. const modelName = this.quoteIdentifier(model.name);
  1776. order[0] = new Utils.Col(this._getAliasForField(modelName, subQueryAttribute[1], options) || subQueryAttribute[1]);
  1777. }
  1778. }
  1779. mainQueryOrder.push(this.quote(order, model, '->'));
  1780. }
  1781. } else if (options.order instanceof Utils.SequelizeMethod) {
  1782. const sql = this.quote(options.order, model, '->');
  1783. if (subQuery) {
  1784. subQueryOrder.push(sql);
  1785. }
  1786. mainQueryOrder.push(sql);
  1787. } else {
  1788. throw new Error('Order must be type of array or instance of a valid sequelize method.');
  1789. }
  1790. return { mainQueryOrder, subQueryOrder };
  1791. }
  1792. _throwOnEmptyAttributes(attributes, extraInfo = {}) {
  1793. if (attributes.length > 0) return;
  1794. const asPart = extraInfo.as && `as ${extraInfo.as}` || '';
  1795. const namePart = extraInfo.modelName && `for model '${extraInfo.modelName}'` || '';
  1796. const message = `Attempted a SELECT query ${namePart} ${asPart} without selecting any columns`;
  1797. throw new sequelizeError.QueryError(message.replace(/ +/g, ' '));
  1798. }
  1799. selectFromTableFragment(options, model, attributes, tables, mainTableAs) {
  1800. this._throwOnEmptyAttributes(attributes, { modelName: model && model.name, as: mainTableAs });
  1801. let fragment = `SELECT ${attributes.join(', ')} FROM ${tables}`;
  1802. if (mainTableAs) {
  1803. fragment += ` AS ${mainTableAs}`;
  1804. }
  1805. if (options.indexHints && this._dialect.supports.indexHints) {
  1806. for (const hint of options.indexHints) {
  1807. if (IndexHints[hint.type]) {
  1808. fragment += ` ${IndexHints[hint.type]} INDEX (${hint.values.map(indexName => this.quoteIdentifiers(indexName)).join(',')})`;
  1809. }
  1810. }
  1811. }
  1812. return fragment;
  1813. }
  1814. /**
  1815. * Returns an SQL fragment for adding result constraints.
  1816. *
  1817. * @param {object} options An object with selectQuery options.
  1818. * @returns {string} The generated sql query.
  1819. * @private
  1820. */
  1821. addLimitAndOffset(options) {
  1822. let fragment = '';
  1823. /* eslint-disable */
  1824. if (options.offset != null && options.limit == null) {
  1825. fragment += ' LIMIT ' + this.escape(options.offset) + ', ' + 10000000000000;
  1826. } else if (options.limit != null) {
  1827. if (options.offset != null) {
  1828. fragment += ' LIMIT ' + this.escape(options.offset) + ', ' + this.escape(options.limit);
  1829. } else {
  1830. fragment += ' LIMIT ' + this.escape(options.limit);
  1831. }
  1832. }
  1833. /* eslint-enable */
  1834. return fragment;
  1835. }
  1836. handleSequelizeMethod(smth, tableName, factory, options, prepend) {
  1837. let result;
  1838. if (Object.prototype.hasOwnProperty.call(this.OperatorMap, smth.comparator)) {
  1839. smth.comparator = this.OperatorMap[smth.comparator];
  1840. }
  1841. if (smth instanceof Utils.Where) {
  1842. let value = smth.logic;
  1843. let key;
  1844. if (smth.attribute instanceof Utils.SequelizeMethod) {
  1845. key = this.getWhereConditions(smth.attribute, tableName, factory, options, prepend);
  1846. } else {
  1847. key = `${this.quoteTable(smth.attribute.Model.name)}.${this.quoteIdentifier(smth.attribute.field || smth.attribute.fieldName)}`;
  1848. }
  1849. if (value && value instanceof Utils.SequelizeMethod) {
  1850. value = this.getWhereConditions(value, tableName, factory, options, prepend);
  1851. if (value === 'NULL') {
  1852. if (smth.comparator === '=') {
  1853. smth.comparator = 'IS';
  1854. }
  1855. if (smth.comparator === '!=') {
  1856. smth.comparator = 'IS NOT';
  1857. }
  1858. }
  1859. return [key, value].join(` ${smth.comparator} `);
  1860. }
  1861. if (_.isPlainObject(value)) {
  1862. return this.whereItemQuery(smth.attribute, value, {
  1863. model: factory
  1864. });
  1865. }
  1866. if (typeof value === 'boolean') {
  1867. value = this.booleanValue(value);
  1868. } else {
  1869. value = this.escape(value);
  1870. }
  1871. if (value === 'NULL') {
  1872. if (smth.comparator === '=') {
  1873. smth.comparator = 'IS';
  1874. }
  1875. if (smth.comparator === '!=') {
  1876. smth.comparator = 'IS NOT';
  1877. }
  1878. }
  1879. return [key, value].join(` ${smth.comparator} `);
  1880. }
  1881. if (smth instanceof Utils.Literal) {
  1882. return smth.val;
  1883. }
  1884. if (smth instanceof Utils.Cast) {
  1885. if (smth.val instanceof Utils.SequelizeMethod) {
  1886. result = this.handleSequelizeMethod(smth.val, tableName, factory, options, prepend);
  1887. } else if (_.isPlainObject(smth.val)) {
  1888. result = this.whereItemsQuery(smth.val);
  1889. } else {
  1890. result = this.escape(smth.val);
  1891. }
  1892. return `CAST(${result} AS ${smth.type.toUpperCase()})`;
  1893. }
  1894. if (smth instanceof Utils.Fn) {
  1895. return `${smth.fn}(${
  1896. smth.args.map(arg => {
  1897. if (arg instanceof Utils.SequelizeMethod) {
  1898. return this.handleSequelizeMethod(arg, tableName, factory, options, prepend);
  1899. }
  1900. if (_.isPlainObject(arg)) {
  1901. return this.whereItemsQuery(arg);
  1902. }
  1903. return this.escape(typeof arg === 'string' ? arg.replace('$', '$$$') : arg);
  1904. }).join(', ')
  1905. })`;
  1906. }
  1907. if (smth instanceof Utils.Col) {
  1908. if (Array.isArray(smth.col) && !factory) {
  1909. throw new Error('Cannot call Sequelize.col() with array outside of order / group clause');
  1910. }
  1911. if (smth.col.startsWith('*')) {
  1912. return '*';
  1913. }
  1914. return this.quote(smth.col, factory);
  1915. }
  1916. return smth.toString(this, factory);
  1917. }
  1918. whereQuery(where, options) {
  1919. const query = this.whereItemsQuery(where, options);
  1920. if (query && query.length) {
  1921. return `WHERE ${query}`;
  1922. }
  1923. return '';
  1924. }
  1925. whereItemsQuery(where, options, binding) {
  1926. if (
  1927. where === null ||
  1928. where === undefined ||
  1929. Utils.getComplexSize(where) === 0
  1930. ) {
  1931. // NO OP
  1932. return '';
  1933. }
  1934. if (typeof where === 'string') {
  1935. throw new Error('Support for `{where: \'raw query\'}` has been removed.');
  1936. }
  1937. const items = [];
  1938. binding = binding || 'AND';
  1939. if (binding[0] !== ' ') binding = ` ${binding} `;
  1940. if (_.isPlainObject(where)) {
  1941. Utils.getComplexKeys(where).forEach(prop => {
  1942. const item = where[prop];
  1943. items.push(this.whereItemQuery(prop, item, options));
  1944. });
  1945. } else {
  1946. items.push(this.whereItemQuery(undefined, where, options));
  1947. }
  1948. return items.length && items.filter(item => item && item.length).join(binding) || '';
  1949. }
  1950. whereItemQuery(key, value, options = {}) {
  1951. if (value === undefined) {
  1952. throw new Error(`WHERE parameter "${key}" has invalid "undefined" value`);
  1953. }
  1954. if (typeof key === 'string' && key.includes('.') && options.model) {
  1955. const keyParts = key.split('.');
  1956. if (options.model.rawAttributes[keyParts[0]] && options.model.rawAttributes[keyParts[0]].type instanceof DataTypes.JSON) {
  1957. const tmp = {};
  1958. const field = options.model.rawAttributes[keyParts[0]];
  1959. _.set(tmp, keyParts.slice(1), value);
  1960. return this.whereItemQuery(field.field || keyParts[0], tmp, { field, ...options });
  1961. }
  1962. }
  1963. const field = this._findField(key, options);
  1964. const fieldType = field && field.type || options.type;
  1965. const isPlainObject = _.isPlainObject(value);
  1966. const isArray = !isPlainObject && Array.isArray(value);
  1967. key = this.OperatorsAliasMap && this.OperatorsAliasMap[key] || key;
  1968. if (isPlainObject) {
  1969. value = this._replaceAliases(value);
  1970. }
  1971. const valueKeys = isPlainObject && Utils.getComplexKeys(value);
  1972. if (key === undefined) {
  1973. if (typeof value === 'string') {
  1974. return value;
  1975. }
  1976. if (isPlainObject && valueKeys.length === 1) {
  1977. return this.whereItemQuery(valueKeys[0], value[valueKeys[0]], options);
  1978. }
  1979. }
  1980. if (value === null) {
  1981. const opValue = options.bindParam ? 'NULL' : this.escape(value, field);
  1982. return this._joinKeyValue(key, opValue, this.OperatorMap[Op.is], options.prefix);
  1983. }
  1984. if (!value) {
  1985. const opValue = options.bindParam ? this.format(value, field, options, options.bindParam) : this.escape(value, field);
  1986. return this._joinKeyValue(key, opValue, this.OperatorMap[Op.eq], options.prefix);
  1987. }
  1988. if (value instanceof Utils.SequelizeMethod && !(key !== undefined && value instanceof Utils.Fn)) {
  1989. return this.handleSequelizeMethod(value);
  1990. }
  1991. // Convert where: [] to Op.and if possible, else treat as literal/replacements
  1992. if (key === undefined && isArray) {
  1993. if (Utils.canTreatArrayAsAnd(value)) {
  1994. key = Op.and;
  1995. } else {
  1996. throw new Error('Support for literal replacements in the `where` object has been removed.');
  1997. }
  1998. }
  1999. if (key === Op.or || key === Op.and || key === Op.not) {
  2000. return this._whereGroupBind(key, value, options);
  2001. }
  2002. if (value[Op.or]) {
  2003. return this._whereBind(this.OperatorMap[Op.or], key, value[Op.or], options);
  2004. }
  2005. if (value[Op.and]) {
  2006. return this._whereBind(this.OperatorMap[Op.and], key, value[Op.and], options);
  2007. }
  2008. if (isArray && fieldType instanceof DataTypes.ARRAY) {
  2009. const opValue = options.bindParam ? this.format(value, field, options, options.bindParam) : this.escape(value, field);
  2010. return this._joinKeyValue(key, opValue, this.OperatorMap[Op.eq], options.prefix);
  2011. }
  2012. if (isPlainObject && fieldType instanceof DataTypes.JSON && options.json !== false) {
  2013. return this._whereJSON(key, value, options);
  2014. }
  2015. // If multiple keys we combine the different logic conditions
  2016. if (isPlainObject && valueKeys.length > 1) {
  2017. return this._whereBind(this.OperatorMap[Op.and], key, value, options);
  2018. }
  2019. if (isArray) {
  2020. return this._whereParseSingleValueObject(key, field, Op.in, value, options);
  2021. }
  2022. if (isPlainObject) {
  2023. if (this.OperatorMap[valueKeys[0]]) {
  2024. return this._whereParseSingleValueObject(key, field, valueKeys[0], value[valueKeys[0]], options);
  2025. }
  2026. return this._whereParseSingleValueObject(key, field, this.OperatorMap[Op.eq], value, options);
  2027. }
  2028. if (key === Op.placeholder) {
  2029. const opValue = options.bindParam ? this.format(value, field, options, options.bindParam) : this.escape(value, field);
  2030. return this._joinKeyValue(this.OperatorMap[key], opValue, this.OperatorMap[Op.eq], options.prefix);
  2031. }
  2032. const opValue = options.bindParam ? this.format(value, field, options, options.bindParam) : this.escape(value, field);
  2033. return this._joinKeyValue(key, opValue, this.OperatorMap[Op.eq], options.prefix);
  2034. }
  2035. _findField(key, options) {
  2036. if (options.field) {
  2037. return options.field;
  2038. }
  2039. if (options.model && options.model.rawAttributes && options.model.rawAttributes[key]) {
  2040. return options.model.rawAttributes[key];
  2041. }
  2042. if (options.model && options.model.fieldRawAttributesMap && options.model.fieldRawAttributesMap[key]) {
  2043. return options.model.fieldRawAttributesMap[key];
  2044. }
  2045. }
  2046. // OR/AND/NOT grouping logic
  2047. _whereGroupBind(key, value, options) {
  2048. const binding = key === Op.or ? this.OperatorMap[Op.or] : this.OperatorMap[Op.and];
  2049. const outerBinding = key === Op.not ? 'NOT ' : '';
  2050. if (Array.isArray(value)) {
  2051. value = value.map(item => {
  2052. let itemQuery = this.whereItemsQuery(item, options, this.OperatorMap[Op.and]);
  2053. if (itemQuery && itemQuery.length && (Array.isArray(item) || _.isPlainObject(item)) && Utils.getComplexSize(item) > 1) {
  2054. itemQuery = `(${itemQuery})`;
  2055. }
  2056. return itemQuery;
  2057. }).filter(item => item && item.length);
  2058. value = value.length && value.join(binding);
  2059. } else {
  2060. value = this.whereItemsQuery(value, options, binding);
  2061. }
  2062. // Op.or: [] should return no data.
  2063. // Op.not of no restriction should also return no data
  2064. if ((key === Op.or || key === Op.not) && !value) {
  2065. return '0 = 1';
  2066. }
  2067. return value ? `${outerBinding}(${value})` : undefined;
  2068. }
  2069. _whereBind(binding, key, value, options) {
  2070. if (_.isPlainObject(value)) {
  2071. value = Utils.getComplexKeys(value).map(prop => {
  2072. const item = value[prop];
  2073. return this.whereItemQuery(key, { [prop]: item }, options);
  2074. });
  2075. } else {
  2076. value = value.map(item => this.whereItemQuery(key, item, options));
  2077. }
  2078. value = value.filter(item => item && item.length);
  2079. return value.length ? `(${value.join(binding)})` : undefined;
  2080. }
  2081. _whereJSON(key, value, options) {
  2082. const items = [];
  2083. let baseKey = this.quoteIdentifier(key);
  2084. if (options.prefix) {
  2085. if (options.prefix instanceof Utils.Literal) {
  2086. baseKey = `${this.handleSequelizeMethod(options.prefix)}.${baseKey}`;
  2087. } else {
  2088. baseKey = `${this.quoteTable(options.prefix)}.${baseKey}`;
  2089. }
  2090. }
  2091. Utils.getOperators(value).forEach(op => {
  2092. const where = {
  2093. [op]: value[op]
  2094. };
  2095. items.push(this.whereItemQuery(key, where, { ...options, json: false }));
  2096. });
  2097. _.forOwn(value, (item, prop) => {
  2098. this._traverseJSON(items, baseKey, prop, item, [prop]);
  2099. });
  2100. const result = items.join(this.OperatorMap[Op.and]);
  2101. return items.length > 1 ? `(${result})` : result;
  2102. }
  2103. _traverseJSON(items, baseKey, prop, item, path) {
  2104. let cast;
  2105. if (path[path.length - 1].includes('::')) {
  2106. const tmp = path[path.length - 1].split('::');
  2107. cast = tmp[1];
  2108. path[path.length - 1] = tmp[0];
  2109. }
  2110. const pathKey = this.jsonPathExtractionQuery(baseKey, path);
  2111. if (_.isPlainObject(item)) {
  2112. Utils.getOperators(item).forEach(op => {
  2113. const value = this._toJSONValue(item[op]);
  2114. items.push(this.whereItemQuery(this._castKey(pathKey, value, cast), { [op]: value }));
  2115. });
  2116. _.forOwn(item, (value, itemProp) => {
  2117. this._traverseJSON(items, baseKey, itemProp, value, path.concat([itemProp]));
  2118. });
  2119. return;
  2120. }
  2121. item = this._toJSONValue(item);
  2122. items.push(this.whereItemQuery(this._castKey(pathKey, item, cast), { [Op.eq]: item }));
  2123. }
  2124. _toJSONValue(value) {
  2125. return value;
  2126. }
  2127. _castKey(key, value, cast, json) {
  2128. cast = cast || this._getJsonCast(Array.isArray(value) ? value[0] : value);
  2129. if (cast) {
  2130. return new Utils.Literal(this.handleSequelizeMethod(new Utils.Cast(new Utils.Literal(key), cast, json)));
  2131. }
  2132. return new Utils.Literal(key);
  2133. }
  2134. _getJsonCast(value) {
  2135. if (typeof value === 'number') {
  2136. return 'double precision';
  2137. }
  2138. if (value instanceof Date) {
  2139. return 'timestamptz';
  2140. }
  2141. if (typeof value === 'boolean') {
  2142. return 'boolean';
  2143. }
  2144. return;
  2145. }
  2146. _joinKeyValue(key, value, comparator, prefix) {
  2147. if (!key) {
  2148. return value;
  2149. }
  2150. if (comparator === undefined) {
  2151. throw new Error(`${key} and ${value} has no comparator`);
  2152. }
  2153. key = this._getSafeKey(key, prefix);
  2154. return [key, value].join(` ${comparator} `);
  2155. }
  2156. _getSafeKey(key, prefix) {
  2157. if (key instanceof Utils.SequelizeMethod) {
  2158. key = this.handleSequelizeMethod(key);
  2159. return this._prefixKey(this.handleSequelizeMethod(key), prefix);
  2160. }
  2161. if (Utils.isColString(key)) {
  2162. key = key.substr(1, key.length - 2).split('.');
  2163. if (key.length > 2) {
  2164. key = [
  2165. // join the tables by -> to match out internal namings
  2166. key.slice(0, -1).join('->'),
  2167. key[key.length - 1]
  2168. ];
  2169. }
  2170. return key.map(identifier => this.quoteIdentifier(identifier)).join('.');
  2171. }
  2172. return this._prefixKey(this.quoteIdentifier(key), prefix);
  2173. }
  2174. _prefixKey(key, prefix) {
  2175. if (prefix) {
  2176. if (prefix instanceof Utils.Literal) {
  2177. return [this.handleSequelizeMethod(prefix), key].join('.');
  2178. }
  2179. return [this.quoteTable(prefix), key].join('.');
  2180. }
  2181. return key;
  2182. }
  2183. _whereParseSingleValueObject(key, field, prop, value, options) {
  2184. if (prop === Op.not) {
  2185. if (Array.isArray(value)) {
  2186. prop = Op.notIn;
  2187. } else if (value !== null && value !== true && value !== false) {
  2188. prop = Op.ne;
  2189. }
  2190. }
  2191. let comparator = this.OperatorMap[prop] || this.OperatorMap[Op.eq];
  2192. switch (prop) {
  2193. case Op.in:
  2194. case Op.notIn:
  2195. if (value instanceof Utils.Literal) {
  2196. return this._joinKeyValue(key, value.val, comparator, options.prefix);
  2197. }
  2198. if (value.length) {
  2199. return this._joinKeyValue(key, `(${value.map(item => this.escape(item, field)).join(', ')})`, comparator, options.prefix);
  2200. }
  2201. if (comparator === this.OperatorMap[Op.in]) {
  2202. return this._joinKeyValue(key, '(NULL)', comparator, options.prefix);
  2203. }
  2204. return '';
  2205. case Op.any:
  2206. case Op.all:
  2207. comparator = `${this.OperatorMap[Op.eq]} ${comparator}`;
  2208. if (value[Op.values]) {
  2209. return this._joinKeyValue(key, `(VALUES ${value[Op.values].map(item => `(${this.escape(item)})`).join(', ')})`, comparator, options.prefix);
  2210. }
  2211. return this._joinKeyValue(key, `(${this.escape(value, field)})`, comparator, options.prefix);
  2212. case Op.between:
  2213. case Op.notBetween:
  2214. return this._joinKeyValue(key, `${this.escape(value[0], field)} AND ${this.escape(value[1], field)}`, comparator, options.prefix);
  2215. case Op.raw:
  2216. throw new Error('The `$raw` where property is no longer supported. Use `sequelize.literal` instead.');
  2217. case Op.col:
  2218. comparator = this.OperatorMap[Op.eq];
  2219. value = value.split('.');
  2220. if (value.length > 2) {
  2221. value = [
  2222. // join the tables by -> to match out internal namings
  2223. value.slice(0, -1).join('->'),
  2224. value[value.length - 1]
  2225. ];
  2226. }
  2227. return this._joinKeyValue(key, value.map(identifier => this.quoteIdentifier(identifier)).join('.'), comparator, options.prefix);
  2228. case Op.startsWith:
  2229. case Op.endsWith:
  2230. case Op.substring:
  2231. comparator = this.OperatorMap[Op.like];
  2232. if (value instanceof Utils.Literal) {
  2233. value = value.val;
  2234. }
  2235. let pattern = `${value}%`;
  2236. if (prop === Op.endsWith) pattern = `%${value}`;
  2237. if (prop === Op.substring) pattern = `%${value}%`;
  2238. return this._joinKeyValue(key, this.escape(pattern), comparator, options.prefix);
  2239. }
  2240. const escapeOptions = {
  2241. acceptStrings: comparator.includes(this.OperatorMap[Op.like])
  2242. };
  2243. if (_.isPlainObject(value)) {
  2244. if (value[Op.col]) {
  2245. return this._joinKeyValue(key, this.whereItemQuery(null, value), comparator, options.prefix);
  2246. }
  2247. if (value[Op.any]) {
  2248. escapeOptions.isList = true;
  2249. return this._joinKeyValue(key, `(${this.escape(value[Op.any], field, escapeOptions)})`, `${comparator} ${this.OperatorMap[Op.any]}`, options.prefix);
  2250. }
  2251. if (value[Op.all]) {
  2252. escapeOptions.isList = true;
  2253. return this._joinKeyValue(key, `(${this.escape(value[Op.all], field, escapeOptions)})`, `${comparator} ${this.OperatorMap[Op.all]}`, options.prefix);
  2254. }
  2255. }
  2256. if (value === null && comparator === this.OperatorMap[Op.eq]) {
  2257. return this._joinKeyValue(key, this.escape(value, field, escapeOptions), this.OperatorMap[Op.is], options.prefix);
  2258. }
  2259. if (value === null && comparator === this.OperatorMap[Op.ne]) {
  2260. return this._joinKeyValue(key, this.escape(value, field, escapeOptions), this.OperatorMap[Op.not], options.prefix);
  2261. }
  2262. return this._joinKeyValue(key, this.escape(value, field, escapeOptions), comparator, options.prefix);
  2263. }
  2264. /*
  2265. Takes something and transforms it into values of a where condition.
  2266. @private
  2267. */
  2268. getWhereConditions(smth, tableName, factory, options, prepend) {
  2269. const where = {};
  2270. if (Array.isArray(tableName)) {
  2271. tableName = tableName[0];
  2272. if (Array.isArray(tableName)) {
  2273. tableName = tableName[1];
  2274. }
  2275. }
  2276. options = options || {};
  2277. if (prepend === undefined) {
  2278. prepend = true;
  2279. }
  2280. if (smth && smth instanceof Utils.SequelizeMethod) { // Checking a property is cheaper than a lot of instanceof calls
  2281. return this.handleSequelizeMethod(smth, tableName, factory, options, prepend);
  2282. }
  2283. if (_.isPlainObject(smth)) {
  2284. return this.whereItemsQuery(smth, {
  2285. model: factory,
  2286. prefix: prepend && tableName,
  2287. type: options.type
  2288. });
  2289. }
  2290. if (typeof smth === 'number') {
  2291. let primaryKeys = factory ? Object.keys(factory.primaryKeys) : [];
  2292. if (primaryKeys.length > 0) {
  2293. // Since we're just a number, assume only the first key
  2294. primaryKeys = primaryKeys[0];
  2295. } else {
  2296. primaryKeys = 'id';
  2297. }
  2298. where[primaryKeys] = smth;
  2299. return this.whereItemsQuery(where, {
  2300. model: factory,
  2301. prefix: prepend && tableName
  2302. });
  2303. }
  2304. if (typeof smth === 'string') {
  2305. return this.whereItemsQuery(smth, {
  2306. model: factory,
  2307. prefix: prepend && tableName
  2308. });
  2309. }
  2310. if (Buffer.isBuffer(smth)) {
  2311. return this.escape(smth);
  2312. }
  2313. if (Array.isArray(smth)) {
  2314. if (smth.length === 0 || smth.length > 0 && smth[0].length === 0) return '1=1';
  2315. if (Utils.canTreatArrayAsAnd(smth)) {
  2316. const _smth = { [Op.and]: smth };
  2317. return this.getWhereConditions(_smth, tableName, factory, options, prepend);
  2318. }
  2319. throw new Error('Support for literal replacements in the `where` object has been removed.');
  2320. }
  2321. if (smth === null) {
  2322. return this.whereItemsQuery(smth, {
  2323. model: factory,
  2324. prefix: prepend && tableName
  2325. });
  2326. }
  2327. return '1=1';
  2328. }
  2329. // A recursive parser for nested where conditions
  2330. parseConditionObject(conditions, path) {
  2331. path = path || [];
  2332. return _.reduce(conditions, (result, value, key) => {
  2333. if (_.isObject(value)) {
  2334. return result.concat(this.parseConditionObject(value, path.concat(key))); // Recursively parse objects
  2335. }
  2336. result.push({ path: path.concat(key), value });
  2337. return result;
  2338. }, []);
  2339. }
  2340. booleanValue(value) {
  2341. return value;
  2342. }
  2343. }
  2344. Object.assign(QueryGenerator.prototype, require('./query-generator/operators'));
  2345. Object.assign(QueryGenerator.prototype, require('./query-generator/transaction'));
  2346. module.exports = QueryGenerator;