select.test.js 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469
  1. 'use strict';
  2. const Support = require('../../support');
  3. const { DataTypes, Op } = require('@sequelize/core');
  4. const util = require('node:util');
  5. const {
  6. _validateIncludedElements,
  7. } = require('@sequelize/core/_non-semver-use-at-your-own-risk_/model-internals.js');
  8. const { beforeAll2, createSequelizeInstance } = require('../../support');
  9. const expectsql = Support.expectsql;
  10. const current = Support.sequelize;
  11. const sql = current.queryGenerator;
  12. const TICK_LEFT = Support.sequelize.dialect.TICK_CHAR_LEFT;
  13. const TICK_RIGHT = Support.sequelize.dialect.TICK_CHAR_RIGHT;
  14. // Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation
  15. describe(Support.getTestDialectTeaser('SQL'), () => {
  16. describe('select', () => {
  17. function expectSelect(options, expectation) {
  18. const model = options.model;
  19. return expectsql(
  20. () => sql.selectQuery(options.table || (model && model.table), options, options.model),
  21. expectation,
  22. );
  23. }
  24. const testsql = function (options, expectation, testFunction = it) {
  25. testFunction(util.inspect(options, { depth: 2 }), () => {
  26. expectSelect(options, expectation);
  27. });
  28. };
  29. testsql.only = (options, expectation) => testsql(options, expectation, it.only);
  30. testsql(
  31. {
  32. table: 'User',
  33. attributes: ['email', ['first_name', 'firstName']],
  34. where: {
  35. email: 'jon.snow@gmail.com',
  36. },
  37. order: [['email', 'DESC']],
  38. limit: 10,
  39. },
  40. {
  41. default:
  42. "SELECT [email], [first_name] AS [firstName] FROM [User] WHERE [User].[email] = 'jon.snow@gmail.com' ORDER BY [email] DESC LIMIT 10;",
  43. db2: 'SELECT "email", "first_name" AS "firstName" FROM "User" WHERE "User"."email" = \'jon.snow@gmail.com\' ORDER BY "email" DESC FETCH NEXT 10 ROWS ONLY;',
  44. mssql:
  45. "SELECT [email], [first_name] AS [firstName] FROM [User] WHERE [User].[email] = N'jon.snow@gmail.com' ORDER BY [email] DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;",
  46. ibmi: 'SELECT "email", "first_name" AS "firstName" FROM "User" WHERE "User"."email" = \'jon.snow@gmail.com\' ORDER BY "email" DESC FETCH NEXT 10 ROWS ONLY',
  47. },
  48. );
  49. testsql(
  50. {
  51. table: 'User',
  52. attributes: ['email', ['first_name', 'firstName'], ['last_name', 'lastName']],
  53. order: [['last_name', 'ASC']],
  54. groupedLimit: {
  55. limit: 3,
  56. on: 'companyId',
  57. values: [1, 5],
  58. },
  59. },
  60. {
  61. default: `SELECT [User].* FROM (${[
  62. `SELECT * FROM (SELECT [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [User] WHERE [User].[companyId] = 1 ORDER BY [last_name] ASC LIMIT 3) AS sub`,
  63. `SELECT * FROM (SELECT [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [User] WHERE [User].[companyId] = 5 ORDER BY [last_name] ASC LIMIT 3) AS sub`,
  64. ].join(current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ')}) AS [User];`,
  65. 'db2 ibmi': `SELECT [User].* FROM (${[
  66. `SELECT * FROM (SELECT "email", "first_name" AS "firstName", "last_name" AS "lastName" FROM "User" WHERE "User"."companyId" = 1 ORDER BY "last_name" ASC FETCH NEXT 3 ROWS ONLY) AS sub`,
  67. `SELECT * FROM (SELECT "email", "first_name" AS "firstName", "last_name" AS "lastName" FROM "User" WHERE "User"."companyId" = 5 ORDER BY "last_name" ASC FETCH NEXT 3 ROWS ONLY) AS sub`,
  68. ].join(current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ')}) AS [User];`,
  69. mssql: `SELECT [User].* FROM (${[
  70. `SELECT * FROM (SELECT [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [User] WHERE [User].[companyId] = 1 ORDER BY [last_name] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY) AS sub`,
  71. `SELECT * FROM (SELECT [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [User] WHERE [User].[companyId] = 5 ORDER BY [last_name] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY) AS sub`,
  72. ].join(current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ')}) AS [User];`,
  73. },
  74. );
  75. describe('With BelongsToMany', () => {
  76. const vars = beforeAll2(() => {
  77. const User = Support.sequelize.define('user', {
  78. id: {
  79. type: DataTypes.INTEGER,
  80. primaryKey: true,
  81. autoIncrement: true,
  82. field: 'id_user',
  83. },
  84. });
  85. const Project = Support.sequelize.define('project', {
  86. title: DataTypes.STRING,
  87. });
  88. const ProjectUser = Support.sequelize.define(
  89. 'project_user',
  90. {
  91. userId: {
  92. type: DataTypes.INTEGER,
  93. field: 'user_id',
  94. },
  95. projectId: {
  96. type: DataTypes.INTEGER,
  97. field: 'project_id',
  98. },
  99. },
  100. { timestamps: false },
  101. );
  102. User.Projects = User.belongsToMany(Project, { through: ProjectUser });
  103. Project.belongsToMany(User, { through: ProjectUser });
  104. return { User, Project, ProjectUser };
  105. });
  106. it('supports groupedLimit', () => {
  107. const { User } = vars;
  108. expectSelect(
  109. {
  110. table: User.table,
  111. model: User,
  112. attributes: [['id_user', 'id']],
  113. order: [['last_name', 'ASC']],
  114. groupedLimit: {
  115. limit: 3,
  116. on: User.Projects,
  117. values: [1, 5],
  118. },
  119. },
  120. {
  121. default: `SELECT [user].* FROM (${[
  122. `SELECT * FROM (
  123. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  124. FROM [users] AS [user]
  125. INNER JOIN [project_users] AS [project_user]
  126. ON [user].[id_user] = [project_user].[user_id]
  127. AND [project_user].[project_id] = 1
  128. ORDER BY [subquery_order_0] ASC LIMIT 3
  129. ) AS sub`,
  130. `SELECT * FROM (
  131. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  132. FROM [users] AS [user]
  133. INNER JOIN [project_users] AS [project_user]
  134. ON [user].[id_user] = [project_user].[user_id]
  135. AND [project_user].[project_id] = 5
  136. ORDER BY [subquery_order_0] ASC LIMIT 3
  137. ) AS sub`,
  138. ].join(
  139. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  140. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  141. 'db2 ibmi': `SELECT [user].* FROM (${[
  142. `SELECT * FROM (
  143. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  144. FROM [users] AS [user]
  145. INNER JOIN [project_users] AS [project_user]
  146. ON [user].[id_user] = [project_user].[user_id]
  147. AND [project_user].[project_id] = 1
  148. ORDER BY [subquery_order_0] ASC FETCH NEXT 3 ROWS ONLY
  149. ) AS sub`,
  150. `SELECT * FROM (
  151. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  152. FROM [users] AS [user]
  153. INNER JOIN [project_users] AS [project_user]
  154. ON [user].[id_user] = [project_user].[user_id]
  155. AND [project_user].[project_id] = 5
  156. ORDER BY [subquery_order_0] ASC FETCH NEXT 3 ROWS ONLY
  157. ) AS sub`,
  158. ].join(
  159. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  160. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  161. mssql: `SELECT [user].* FROM (${[
  162. `SELECT * FROM (
  163. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  164. FROM [users] AS [user]
  165. INNER JOIN [project_users] AS [project_user]
  166. ON [user].[id_user] = [project_user].[user_id]
  167. AND [project_user].[project_id] = 1
  168. ORDER BY [subquery_order_0] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
  169. ) AS sub`,
  170. `SELECT * FROM (
  171. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  172. FROM [users] AS [user]
  173. INNER JOIN [project_users] AS [project_user]
  174. ON [user].[id_user] = [project_user].[user_id]
  175. AND [project_user].[project_id] = 5
  176. ORDER BY [subquery_order_0] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
  177. ) AS sub`,
  178. ].join(
  179. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  180. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  181. },
  182. );
  183. });
  184. it('supports groupedLimit with through', () => {
  185. const { User } = vars;
  186. expectSelect(
  187. {
  188. table: User.table,
  189. model: User,
  190. attributes: [['id_user', 'id']],
  191. order: [['last_name', 'ASC']],
  192. groupedLimit: {
  193. limit: 3,
  194. through: {
  195. where: {
  196. status: 1,
  197. },
  198. },
  199. on: User.Projects,
  200. values: [1, 5],
  201. },
  202. },
  203. {
  204. default: `SELECT [user].* FROM (${[
  205. `SELECT * FROM (
  206. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  207. FROM [users] AS [user]
  208. INNER JOIN [project_users] AS [project_user]
  209. ON [user].[id_user] = [project_user].[user_id]
  210. AND ([project_user].[project_id] = 1
  211. AND [project_user].[status] = 1)
  212. ORDER BY [subquery_order_0] ASC LIMIT 3
  213. ) AS sub`,
  214. `SELECT * FROM (
  215. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  216. FROM [users] AS [user]
  217. INNER JOIN [project_users] AS [project_user]
  218. ON [user].[id_user] = [project_user].[user_id]
  219. AND ([project_user].[project_id] = 5
  220. AND [project_user].[status] = 1)
  221. ORDER BY [subquery_order_0] ASC LIMIT 3
  222. ) AS sub`,
  223. ].join(
  224. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  225. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  226. 'db2 ibmi': `SELECT [user].* FROM (${[
  227. `SELECT * FROM (
  228. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  229. FROM [users] AS [user]
  230. INNER JOIN [project_users] AS [project_user]
  231. ON [user].[id_user] = [project_user].[user_id]
  232. AND ([project_user].[project_id] = 1
  233. AND [project_user].[status] = 1)
  234. ORDER BY [subquery_order_0] ASC FETCH NEXT 3 ROWS ONLY
  235. ) AS sub`,
  236. `SELECT * FROM (
  237. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  238. FROM [users] AS [user]
  239. INNER JOIN [project_users] AS [project_user]
  240. ON [user].[id_user] = [project_user].[user_id]
  241. AND ([project_user].[project_id] = 5
  242. AND [project_user].[status] = 1)
  243. ORDER BY [subquery_order_0] ASC FETCH NEXT 3 ROWS ONLY
  244. ) AS sub`,
  245. ].join(
  246. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  247. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  248. mssql: `SELECT [user].* FROM (${[
  249. `SELECT * FROM (
  250. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  251. FROM [users] AS [user]
  252. INNER JOIN [project_users] AS [project_user]
  253. ON [user].[id_user] = [project_user].[user_id]
  254. AND ([project_user].[project_id] = 1
  255. AND [project_user].[status] = 1)
  256. ORDER BY [subquery_order_0] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
  257. ) AS sub`,
  258. `SELECT * FROM (
  259. SELECT [user].[id_user] AS [id], [user].[last_name] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  260. FROM [users] AS [user]
  261. INNER JOIN [project_users] AS [project_user]
  262. ON [user].[id_user] = [project_user].[user_id]
  263. AND ([project_user].[project_id] = 5
  264. AND [project_user].[status] = 1)
  265. ORDER BY [subquery_order_0] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
  266. ) AS sub`,
  267. ].join(
  268. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  269. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  270. },
  271. );
  272. });
  273. it('supports groupedLimit with through and where', () => {
  274. const { User } = vars;
  275. expectSelect(
  276. {
  277. table: User.table,
  278. model: User,
  279. attributes: [['id_user', 'id']],
  280. order: [['id_user', 'ASC']],
  281. where: {
  282. age: {
  283. [Op.gte]: 21,
  284. },
  285. },
  286. groupedLimit: {
  287. limit: 3,
  288. on: User.Projects,
  289. values: [1, 5],
  290. },
  291. },
  292. {
  293. default: `SELECT [user].* FROM (${[
  294. `SELECT * FROM (
  295. SELECT [user].[id_user] AS [id], [user].[id_user] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  296. FROM [users] AS [user]
  297. INNER JOIN [project_users] AS [project_user]
  298. ON [user].[id_user] = [project_user].[user_id]
  299. AND [project_user].[project_id] = 1
  300. WHERE [user].[age] >= 21
  301. ORDER BY [subquery_order_0] ASC LIMIT 3
  302. ) AS sub`,
  303. `SELECT * FROM (
  304. SELECT [user].[id_user] AS [id], [user].[id_user] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  305. FROM [users] AS [user]
  306. INNER JOIN [project_users] AS [project_user]
  307. ON [user].[id_user] = [project_user].[user_id]
  308. AND [project_user].[project_id] = 5
  309. WHERE [user].[age] >= 21
  310. ORDER BY [subquery_order_0] ASC LIMIT 3
  311. ) AS sub`,
  312. ].join(
  313. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  314. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  315. 'db2 ibmi': `SELECT [user].* FROM (${[
  316. `SELECT * FROM (
  317. SELECT [user].[id_user] AS [id], [user].[id_user] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  318. FROM [users] AS [user]
  319. INNER JOIN [project_users] AS [project_user]
  320. ON [user].[id_user] = [project_user].[user_id]
  321. AND [project_user].[project_id] = 1
  322. WHERE [user].[age] >= 21
  323. ORDER BY [subquery_order_0] ASC FETCH NEXT 3 ROWS ONLY
  324. ) AS sub`,
  325. `SELECT * FROM (
  326. SELECT [user].[id_user] AS [id], [user].[id_user] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  327. FROM [users] AS [user]
  328. INNER JOIN [project_users] AS [project_user]
  329. ON [user].[id_user] = [project_user].[user_id]
  330. AND [project_user].[project_id] = 5
  331. WHERE [user].[age] >= 21
  332. ORDER BY [subquery_order_0] ASC FETCH NEXT 3 ROWS ONLY
  333. ) AS sub`,
  334. ].join(
  335. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  336. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  337. mssql: `SELECT [user].* FROM (${[
  338. `SELECT * FROM (
  339. SELECT [user].[id_user] AS [id], [user].[id_user] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  340. FROM [users] AS [user]
  341. INNER JOIN [project_users] AS [project_user]
  342. ON [user].[id_user] = [project_user].[user_id]
  343. AND [project_user].[project_id] = 1
  344. WHERE [user].[age] >= 21
  345. ORDER BY [subquery_order_0] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
  346. ) AS sub`,
  347. `SELECT * FROM (
  348. SELECT [user].[id_user] AS [id], [user].[id_user] AS [subquery_order_0], [project_user].[user_id] AS [project_user.userId], [project_user].[project_id] AS [project_user.projectId]
  349. FROM [users] AS [user]
  350. INNER JOIN [project_users] AS [project_user]
  351. ON [user].[id_user] = [project_user].[user_id]
  352. AND [project_user].[project_id] = 5
  353. WHERE [user].[age] >= 21
  354. ORDER BY [subquery_order_0] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
  355. ) AS sub`,
  356. ].join(
  357. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  358. )}) AS [user] ORDER BY [subquery_order_0] ASC;`,
  359. },
  360. );
  361. });
  362. });
  363. describe('With HasMany', () => {
  364. const vars = beforeAll2(() => {
  365. const User = Support.sequelize.define(
  366. 'user',
  367. {
  368. id: {
  369. type: DataTypes.INTEGER,
  370. primaryKey: true,
  371. autoIncrement: true,
  372. field: 'id_user',
  373. },
  374. email: DataTypes.STRING,
  375. firstName: {
  376. type: DataTypes.STRING,
  377. field: 'first_name',
  378. },
  379. lastName: {
  380. type: DataTypes.STRING,
  381. field: 'last_name',
  382. },
  383. },
  384. {
  385. tableName: 'users',
  386. },
  387. );
  388. const Post = Support.sequelize.define(
  389. 'Post',
  390. {
  391. title: DataTypes.STRING,
  392. userId: {
  393. type: DataTypes.INTEGER,
  394. field: 'user_id',
  395. },
  396. },
  397. {
  398. tableName: 'post',
  399. },
  400. );
  401. User.Posts = User.hasMany(Post, { foreignKey: 'userId', as: 'POSTS' });
  402. const Comment = Support.sequelize.define(
  403. 'Comment',
  404. {
  405. title: DataTypes.STRING,
  406. postId: {
  407. type: DataTypes.INTEGER,
  408. field: 'post_id',
  409. },
  410. },
  411. {
  412. tableName: 'comment',
  413. },
  414. );
  415. Post.Comments = Post.hasMany(Comment, { foreignKey: 'postId', as: 'COMMENTS' });
  416. const include = _validateIncludedElements({
  417. include: [
  418. {
  419. attributes: ['title'],
  420. association: User.Posts,
  421. },
  422. ],
  423. model: User,
  424. }).include;
  425. return { User, Post, include };
  426. });
  427. it('supports groupedLimit', () => {
  428. const { include, User } = vars;
  429. expectSelect(
  430. {
  431. table: User.table,
  432. model: User,
  433. include,
  434. attributes: [
  435. ['id_user', 'id'],
  436. 'email',
  437. ['first_name', 'firstName'],
  438. ['last_name', 'lastName'],
  439. ],
  440. order: [['last_name', 'ASC']],
  441. groupedLimit: {
  442. limit: 3,
  443. on: 'companyId',
  444. values: [1, 5],
  445. },
  446. },
  447. {
  448. default: `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title] FROM (${[
  449. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 1 ORDER BY [lastName] ASC LIMIT 3) AS sub`,
  450. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 5 ORDER BY [lastName] ASC LIMIT 3) AS sub`,
  451. ].join(
  452. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  453. )}) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id] = [POSTS].[user_id];`,
  454. 'db2 ibmi': `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title] FROM (${[
  455. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 1 ORDER BY [lastName] ASC FETCH NEXT 3 ROWS ONLY) AS sub`,
  456. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 5 ORDER BY [lastName] ASC FETCH NEXT 3 ROWS ONLY) AS sub`,
  457. ].join(
  458. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  459. )}) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id] = [POSTS].[user_id];`,
  460. mssql: `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title] FROM (${[
  461. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 1 ORDER BY [lastName] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY) AS sub`,
  462. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 5 ORDER BY [lastName] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY) AS sub`,
  463. ].join(
  464. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  465. )}) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id] = [POSTS].[user_id];`,
  466. },
  467. );
  468. });
  469. it('supports order, limit, offset', () => {
  470. const { include, User } = vars;
  471. expectSelect(
  472. {
  473. table: User.table,
  474. model: User,
  475. include,
  476. attributes: [
  477. ['id_user', 'id'],
  478. 'email',
  479. ['first_name', 'firstName'],
  480. ['last_name', 'lastName'],
  481. ],
  482. // [last_name] is not wrapped in a literal, so it's a column name and must be escaped
  483. // as [[[last_name]]]
  484. order: [
  485. [
  486. '[last_name]'
  487. .replaceAll('[', Support.sequelize.dialect.TICK_CHAR_LEFT)
  488. .replaceAll(']', Support.sequelize.dialect.TICK_CHAR_RIGHT),
  489. 'ASC',
  490. ],
  491. ],
  492. limit: 30,
  493. offset: 10,
  494. hasMultiAssociation: true, // must be set only for mssql dialect here
  495. subQuery: true,
  496. },
  497. {
  498. default: `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title] FROM (SELECT [user].[id_user] AS [id], [user].[email], [user].[first_name] AS [firstName], [user].[last_name] AS [lastName] FROM [users] AS [user] ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC LIMIT 30 OFFSET 10) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id_user] = [POSTS].[user_id] ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC;`,
  499. 'db2 ibmi mssql': `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title] FROM (SELECT [user].[id_user] AS [id], [user].[email], [user].[first_name] AS [firstName], [user].[last_name] AS [lastName] FROM [users] AS [user] ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC OFFSET 10 ROWS FETCH NEXT 30 ROWS ONLY) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id_user] = [POSTS].[user_id] ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC;`,
  500. },
  501. );
  502. });
  503. it('supports order, limit, offset without subQuery', () => {
  504. const { include, User } = vars;
  505. // By default, SELECT with include of a multi association & limit will be ran as a subQuery
  506. // This checks the result when the query is forced to be ran without a subquery
  507. expectSelect(
  508. {
  509. table: User.table,
  510. model: User,
  511. include,
  512. attributes: [
  513. ['id_user', 'id'],
  514. 'email',
  515. ['first_name', 'firstName'],
  516. ['last_name', 'lastName'],
  517. ],
  518. // [last_name] is not wrapped in a literal, so it's a column name and must be escaped
  519. // as [[[last_name]]]
  520. order: [
  521. [
  522. '[last_name]'
  523. .replaceAll('[', Support.sequelize.dialect.TICK_CHAR_LEFT)
  524. .replaceAll(']', Support.sequelize.dialect.TICK_CHAR_RIGHT),
  525. 'ASC',
  526. ],
  527. ],
  528. limit: 30,
  529. offset: 10,
  530. hasMultiAssociation: true, // must be set only for mssql dialect here
  531. subQuery: false,
  532. },
  533. {
  534. default: `SELECT [user].[id_user] AS [id], [user].[email], [user].[first_name] AS [firstName], [user].[last_name] AS [lastName], [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title]
  535. FROM [users] AS [user] LEFT OUTER JOIN [post] AS [POSTS]
  536. ON [user].[id_user] = [POSTS].[user_id]
  537. ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC LIMIT 30 OFFSET 10;`,
  538. 'db2 ibmi mssql': `SELECT [user].[id_user] AS [id], [user].[email], [user].[first_name] AS [firstName], [user].[last_name] AS [lastName], [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title]
  539. FROM [users] AS [user] LEFT OUTER JOIN [post] AS [POSTS]
  540. ON [user].[id_user] = [POSTS].[user_id]
  541. ORDER BY [user].${TICK_LEFT}${TICK_LEFT}${TICK_LEFT}last_name${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} ASC OFFSET 10 ROWS FETCH NEXT 30 ROWS ONLY;`,
  542. },
  543. );
  544. });
  545. it('supports nested includes', () => {
  546. const { Post, User } = vars;
  547. const nestedInclude = _validateIncludedElements({
  548. include: [
  549. {
  550. attributes: ['title'],
  551. association: User.Posts,
  552. include: [
  553. {
  554. attributes: ['title'],
  555. association: Post.Comments,
  556. },
  557. ],
  558. },
  559. ],
  560. model: User,
  561. }).include;
  562. expectSelect(
  563. {
  564. table: User.table,
  565. model: User,
  566. include: nestedInclude,
  567. attributes: [
  568. ['id_user', 'id'],
  569. 'email',
  570. ['first_name', 'firstName'],
  571. ['last_name', 'lastName'],
  572. ],
  573. order: [['last_name', 'ASC']],
  574. groupedLimit: {
  575. limit: 3,
  576. on: 'companyId',
  577. values: [1, 5],
  578. },
  579. },
  580. {
  581. default: `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title], [POSTS->COMMENTS].[id] AS [POSTS.COMMENTS.id], [POSTS->COMMENTS].[title] AS [POSTS.COMMENTS.title] FROM (${[
  582. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 1 ORDER BY [lastName] ASC LIMIT 3) AS sub`,
  583. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 5 ORDER BY [lastName] ASC LIMIT 3) AS sub`,
  584. ].join(
  585. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  586. )}) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id] = [POSTS].[user_id] LEFT OUTER JOIN [comment] AS [POSTS->COMMENTS] ON [POSTS].[id] = [POSTS->COMMENTS].[post_id];`,
  587. 'db2 ibmi': `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title], [POSTS->COMMENTS].[id] AS [POSTS.COMMENTS.id], [POSTS->COMMENTS].[title] AS [POSTS.COMMENTS.title] FROM (${[
  588. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 1 ORDER BY [lastName] ASC FETCH NEXT 3 ROWS ONLY) AS sub`,
  589. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 5 ORDER BY [lastName] ASC FETCH NEXT 3 ROWS ONLY) AS sub`,
  590. ].join(
  591. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  592. )}) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id] = [POSTS].[user_id] LEFT OUTER JOIN [comment] AS [POSTS->COMMENTS] ON [POSTS].[id] = [POSTS->COMMENTS].[post_id];`,
  593. mssql: `SELECT [user].*, [POSTS].[id] AS [POSTS.id], [POSTS].[title] AS [POSTS.title], [POSTS->COMMENTS].[id] AS [POSTS.COMMENTS.id], [POSTS->COMMENTS].[title] AS [POSTS.COMMENTS.title] FROM (${[
  594. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 1 ORDER BY [lastName] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY) AS sub`,
  595. `SELECT * FROM (SELECT [id_user] AS [id], [email], [first_name] AS [firstName], [last_name] AS [lastName] FROM [users] AS [user] WHERE [user].[companyId] = 5 ORDER BY [lastName] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY) AS sub`,
  596. ].join(
  597. current.dialect.supports['UNION ALL'] ? ' UNION ALL ' : ' UNION ',
  598. )}) AS [user] LEFT OUTER JOIN [post] AS [POSTS] ON [user].[id] = [POSTS].[user_id] LEFT OUTER JOIN [comment] AS [POSTS->COMMENTS] ON [POSTS].[id] = [POSTS->COMMENTS].[post_id];`,
  599. },
  600. );
  601. });
  602. });
  603. it('include (left outer join)', () => {
  604. const User = Support.sequelize.define(
  605. 'User',
  606. {
  607. name: DataTypes.STRING,
  608. age: DataTypes.INTEGER,
  609. },
  610. {
  611. freezeTableName: true,
  612. },
  613. );
  614. const Post = Support.sequelize.define(
  615. 'Post',
  616. {
  617. title: DataTypes.STRING,
  618. },
  619. {
  620. freezeTableName: true,
  621. },
  622. );
  623. User.Posts = User.hasMany(Post, { foreignKey: 'user_id' });
  624. expectsql(
  625. sql.selectQuery(
  626. 'User',
  627. {
  628. attributes: ['name', 'age'],
  629. include: _validateIncludedElements({
  630. include: [
  631. {
  632. attributes: ['title'],
  633. association: User.Posts,
  634. },
  635. ],
  636. model: User,
  637. }).include,
  638. model: User,
  639. },
  640. User,
  641. ),
  642. {
  643. ibmi: 'SELECT "User"."name", "User"."age", "posts"."id" AS "posts.id", "posts"."title" AS "posts.title" FROM "User" AS "User" LEFT OUTER JOIN "Post" AS "posts" ON "User"."id" = "posts"."user_id"',
  644. default:
  645. 'SELECT [User].[name], [User].[age], [posts].[id] AS [posts.id], [posts].[title] AS [posts.title] FROM [User] AS [User] LEFT OUTER JOIN [Post] AS [posts] ON [User].[id] = [posts].[user_id];',
  646. },
  647. );
  648. });
  649. it('include (right outer join)', () => {
  650. const User = Support.sequelize.define(
  651. 'User',
  652. {
  653. name: DataTypes.STRING,
  654. age: DataTypes.INTEGER,
  655. },
  656. {
  657. freezeTableName: true,
  658. },
  659. );
  660. const Post = Support.sequelize.define(
  661. 'Post',
  662. {
  663. title: DataTypes.STRING,
  664. },
  665. {
  666. freezeTableName: true,
  667. },
  668. );
  669. User.Posts = User.hasMany(Post, { foreignKey: 'user_id' });
  670. expectsql(
  671. sql.selectQuery(
  672. 'User',
  673. {
  674. attributes: ['name', 'age'],
  675. include: _validateIncludedElements({
  676. include: [
  677. {
  678. attributes: ['title'],
  679. association: User.Posts,
  680. right: true,
  681. },
  682. ],
  683. model: User,
  684. }).include,
  685. model: User,
  686. },
  687. User,
  688. ),
  689. {
  690. default: `SELECT [User].[name], [User].[age], [posts].[id] AS [posts.id], [posts].[title] AS [posts.title] FROM [User] AS [User] ${current.dialect.supports['RIGHT JOIN'] ? 'RIGHT' : 'LEFT'} OUTER JOIN [Post] AS [posts] ON [User].[id] = [posts].[user_id];`,
  691. },
  692. );
  693. });
  694. it('include through (right outer join)', () => {
  695. const User = Support.sequelize.define('user', {
  696. id: {
  697. type: DataTypes.INTEGER,
  698. primaryKey: true,
  699. autoIncrement: true,
  700. field: 'id_user',
  701. },
  702. });
  703. const Project = Support.sequelize.define('project', {
  704. title: DataTypes.STRING,
  705. });
  706. const ProjectUser = Support.sequelize.define(
  707. 'project_user',
  708. {
  709. userId: {
  710. type: DataTypes.INTEGER,
  711. field: 'user_id',
  712. },
  713. projectId: {
  714. type: DataTypes.INTEGER,
  715. field: 'project_id',
  716. },
  717. },
  718. { timestamps: false },
  719. );
  720. User.belongsToMany(Project, { through: ProjectUser });
  721. Project.belongsToMany(User, { through: ProjectUser });
  722. expectsql(
  723. sql.selectQuery(
  724. 'User',
  725. {
  726. attributes: ['id_user', 'id'],
  727. include: _validateIncludedElements({
  728. include: [
  729. {
  730. model: Project,
  731. right: true,
  732. },
  733. ],
  734. model: User,
  735. }).include,
  736. model: User,
  737. // the order here is important, because a different piece of code is responsible for naming the through table name in ORDER BY
  738. // than in LEFT JOIN
  739. order: [['projects', ProjectUser, 'userId', 'ASC']],
  740. },
  741. User,
  742. ),
  743. {
  744. default: `
  745. SELECT [user].[id_user],
  746. [user].[id],
  747. [projects].[id] AS [projects.id],
  748. [projects].[title] AS [projects.title],
  749. [projects].[createdAt] AS [projects.createdAt],
  750. [projects].[updatedAt] AS [projects.updatedAt],
  751. [projects->project_user].[user_id] AS [projects.project_user.userId],
  752. [projects->project_user].[project_id] AS [projects.project_user.projectId]
  753. FROM [User] AS [user]
  754. ${current.dialect.supports['RIGHT JOIN'] ? 'RIGHT' : 'LEFT'} OUTER JOIN (
  755. [project_users] AS [projects->project_user]
  756. INNER JOIN [projects] AS [projects]
  757. ON [projects].[id] = [projects->project_user].[project_id]
  758. )
  759. ON [user].[id_user] = [projects->project_user].[user_id]
  760. ORDER BY [projects->project_user].[user_id] ASC;`,
  761. },
  762. );
  763. });
  764. describe('include (subQuery alias)', () => {
  765. const vars = beforeAll2(() => {
  766. const User = Support.sequelize.define(
  767. 'User',
  768. {
  769. name: DataTypes.STRING,
  770. age: DataTypes.INTEGER,
  771. },
  772. {
  773. freezeTableName: true,
  774. },
  775. );
  776. const Post = Support.sequelize.define(
  777. 'Post',
  778. {
  779. title: DataTypes.STRING,
  780. },
  781. {
  782. freezeTableName: true,
  783. },
  784. );
  785. User.Posts = User.hasMany(Post, { foreignKey: 'user_id', as: 'postaliasname' });
  786. return { User };
  787. });
  788. it('w/o filters', () => {
  789. const { User } = vars;
  790. expectsql(
  791. sql.selectQuery(
  792. 'User',
  793. {
  794. table: User.table,
  795. model: User,
  796. attributes: ['name', 'age'],
  797. include: _validateIncludedElements({
  798. model: User,
  799. include: [
  800. {
  801. attributes: ['title'],
  802. association: User.Posts,
  803. subQuery: true,
  804. required: true,
  805. },
  806. ],
  807. as: 'User',
  808. }).include,
  809. subQuery: true,
  810. },
  811. User,
  812. ),
  813. {
  814. default:
  815. 'SELECT [User].* FROM ' +
  816. '(SELECT [User].[name], [User].[age], [User].[id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' +
  817. 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' +
  818. `WHERE EXISTS ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE [postaliasname].[user_id] = [User].[id]) ) AS [User];`,
  819. },
  820. );
  821. });
  822. it('w/ nested column filter', () => {
  823. const { User } = vars;
  824. expectsql(
  825. () =>
  826. sql.selectQuery(
  827. 'User',
  828. {
  829. table: User.table,
  830. model: User,
  831. attributes: ['name', 'age'],
  832. where: { '$postaliasname.title$': 'test' },
  833. include: _validateIncludedElements({
  834. model: User,
  835. include: [
  836. {
  837. attributes: ['title'],
  838. association: User.Posts,
  839. subQuery: true,
  840. required: true,
  841. },
  842. ],
  843. as: 'User',
  844. }).include,
  845. subQuery: true,
  846. },
  847. User,
  848. ),
  849. {
  850. default:
  851. 'SELECT [User].* FROM ' +
  852. '(SELECT [User].[name], [User].[age], [User].[id], [postaliasname].[id] AS [postaliasname.id], [postaliasname].[title] AS [postaliasname.title] FROM [User] AS [User] ' +
  853. 'INNER JOIN [Post] AS [postaliasname] ON [User].[id] = [postaliasname].[user_id] ' +
  854. `WHERE [postaliasname].[title] = ${sql.escape('test')} AND EXISTS ( SELECT [user_id] FROM [Post] AS [postaliasname] WHERE [postaliasname].[user_id] = [User].[id]) ) AS [User];`,
  855. },
  856. );
  857. });
  858. });
  859. it('include w/ subQuery + nested filter + paging', () => {
  860. const User = Support.sequelize.define('User', {
  861. scopeId: DataTypes.INTEGER,
  862. });
  863. const Company = Support.sequelize.define('Company', {
  864. name: DataTypes.STRING,
  865. public: DataTypes.BOOLEAN,
  866. scopeId: DataTypes.INTEGER,
  867. });
  868. const Profession = Support.sequelize.define('Profession', {
  869. name: DataTypes.STRING,
  870. scopeId: DataTypes.INTEGER,
  871. });
  872. User.Company = User.belongsTo(Company, { foreignKey: 'companyId' });
  873. User.Profession = User.belongsTo(Profession, { foreignKey: 'professionId' });
  874. Company.Users = Company.hasMany(User, { as: 'Users', foreignKey: 'companyId' });
  875. Profession.Users = Profession.hasMany(User, { as: 'Users', foreignKey: 'professionId' });
  876. expectsql(
  877. sql.selectQuery(
  878. 'Company',
  879. {
  880. table: Company.table,
  881. model: Company,
  882. attributes: ['name', 'public'],
  883. where: { '$Users.profession.name$': 'test', [Op.and]: { scopeId: [42] } },
  884. include: _validateIncludedElements({
  885. include: [
  886. {
  887. association: Company.Users,
  888. attributes: [],
  889. include: [
  890. {
  891. association: User.Profession,
  892. attributes: [],
  893. required: true,
  894. },
  895. ],
  896. subQuery: true,
  897. required: true,
  898. },
  899. ],
  900. model: Company,
  901. }).include,
  902. limit: 5,
  903. offset: 0,
  904. subQuery: true,
  905. },
  906. Company,
  907. ),
  908. {
  909. default:
  910. 'SELECT [Company].* FROM (' +
  911. 'SELECT [Company].[name], [Company].[public], [Company].[id] FROM [Company] AS [Company] ' +
  912. 'INNER JOIN [Users] AS [Users] ON [Company].[id] = [Users].[companyId] ' +
  913. 'INNER JOIN [Professions] AS [Users->profession] ON [Users].[professionId] = [Users->profession].[id] ' +
  914. `WHERE ([Company].[scopeId] IN (42) AND [Users->profession].[name] = ${sql.escape('test')}) AND ` +
  915. 'EXISTS ( SELECT [Users].[companyId] FROM [Users] AS [Users] ' +
  916. 'INNER JOIN [Professions] AS [profession] ON [Users].[professionId] = [profession].[id] ' +
  917. `WHERE [Users].[companyId] = [Company].[id] ) ORDER BY [Company].[id] LIMIT 5) AS [Company];`,
  918. 'db2 ibmi':
  919. 'SELECT [Company].* FROM (' +
  920. 'SELECT [Company].[name], [Company].[public], [Company].[id] FROM [Company] AS [Company] ' +
  921. 'INNER JOIN [Users] AS [Users] ON [Company].[id] = [Users].[companyId] ' +
  922. 'INNER JOIN [Professions] AS [Users->profession] ON [Users].[professionId] = [Users->profession].[id] ' +
  923. `WHERE ([Company].[scopeId] IN (42) AND [Users->profession].[name] = ${sql.escape('test')}) AND ` +
  924. 'EXISTS ( SELECT [Users].[companyId] FROM [Users] AS [Users] ' +
  925. 'INNER JOIN [Professions] AS [profession] ON [Users].[professionId] = [profession].[id] ' +
  926. `WHERE [Users].[companyId] = [Company].[id] ) ` +
  927. `ORDER BY [Company].[id] FETCH NEXT 5 ROWS ONLY) AS [Company];`,
  928. mssql:
  929. 'SELECT [Company].* FROM (' +
  930. 'SELECT [Company].[name], [Company].[public], [Company].[id] FROM [Company] AS [Company] ' +
  931. 'INNER JOIN [Users] AS [Users] ON [Company].[id] = [Users].[companyId] ' +
  932. 'INNER JOIN [Professions] AS [Users->profession] ON [Users].[professionId] = [Users->profession].[id] ' +
  933. `WHERE ([Company].[scopeId] IN (42) AND [Users->profession].[name] = ${sql.escape('test')}) AND ` +
  934. 'EXISTS ( SELECT [Users].[companyId] FROM [Users] AS [Users] ' +
  935. 'INNER JOIN [Professions] AS [profession] ON [Users].[professionId] = [profession].[id] ' +
  936. `WHERE [Users].[companyId] = [Company].[id] ) ` +
  937. `ORDER BY [Company].[id] OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY) AS [Company];`,
  938. },
  939. );
  940. });
  941. it('properly stringify IN values as per field definition', () => {
  942. const User = Support.sequelize.define(
  943. 'User',
  944. {
  945. name: DataTypes.STRING,
  946. age: DataTypes.INTEGER,
  947. data: DataTypes.BLOB,
  948. },
  949. {
  950. freezeTableName: true,
  951. },
  952. );
  953. expectsql(
  954. sql.selectQuery(
  955. 'User',
  956. {
  957. attributes: ['name', 'age', 'data'],
  958. where: {
  959. data: ['123'],
  960. },
  961. },
  962. User,
  963. ),
  964. {
  965. ibmi: `SELECT "name", "age", "data" FROM "User" AS "User" WHERE "User"."data" IN (BLOB(X'313233'))`,
  966. db2: `SELECT "name", "age", "data" FROM "User" AS "User" WHERE "User"."data" IN (BLOB('123'));`,
  967. postgres: `SELECT "name", "age", "data" FROM "User" AS "User" WHERE "User"."data" IN ('\\x313233');`,
  968. snowflake: `SELECT "name", "age", "data" FROM "User" AS "User" WHERE "User"."data" IN (X'313233');`,
  969. 'mariadb mysql sqlite3':
  970. "SELECT `name`, `age`, `data` FROM `User` AS `User` WHERE `User`.`data` IN (X'313233');",
  971. mssql:
  972. 'SELECT [name], [age], [data] FROM [User] AS [User] WHERE [User].[data] IN (0x313233);',
  973. },
  974. );
  975. });
  976. describe('attribute escaping', () => {
  977. it('plain attributes (1)', () => {
  978. expectsql(
  979. sql.selectQuery('User', {
  980. attributes: [
  981. '* FROM [User];'
  982. .replaceAll('[', Support.sequelize.dialect.TICK_CHAR_LEFT)
  983. .replaceAll(']', Support.sequelize.dialect.TICK_CHAR_RIGHT),
  984. ],
  985. }),
  986. {
  987. default: `SELECT ${TICK_LEFT}* FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT};${TICK_RIGHT} FROM ${TICK_LEFT}User${TICK_RIGHT};`,
  988. },
  989. );
  990. });
  991. it('plain attributes (2)', () => {
  992. expectsql(
  993. sql.selectQuery('User', {
  994. attributes: ['* FROM User; DELETE FROM User;SELECT id'],
  995. }),
  996. {
  997. default: 'SELECT [* FROM User; DELETE FROM User;SELECT id] FROM [User];',
  998. ibmi: 'SELECT "* FROM User; DELETE FROM User;SELECT id" FROM "User"',
  999. },
  1000. );
  1001. });
  1002. it('plain attributes (3)', () => {
  1003. expectsql(
  1004. sql.selectQuery('User', {
  1005. attributes: [`a', * FROM User; DELETE FROM User;SELECT id`],
  1006. }),
  1007. {
  1008. default: `SELECT [a', * FROM User; DELETE FROM User;SELECT id] FROM [User];`,
  1009. mssql: `SELECT [a', * FROM User; DELETE FROM User;SELECT id] FROM [User];`,
  1010. ibmi: `SELECT "a', * FROM User; DELETE FROM User;SELECT id" FROM "User"`,
  1011. },
  1012. );
  1013. });
  1014. it('plain attributes (4)', () => {
  1015. expectsql(
  1016. sql.selectQuery('User', {
  1017. attributes: ['*, COUNT(*) FROM User; DELETE FROM User;SELECT id'],
  1018. }),
  1019. {
  1020. default: 'SELECT [*, COUNT(*) FROM User; DELETE FROM User;SELECT id] FROM [User];',
  1021. ibmi: 'SELECT "*, COUNT(*) FROM User; DELETE FROM User;SELECT id" FROM "User"',
  1022. },
  1023. );
  1024. });
  1025. it('aliased attributes (1)', () => {
  1026. expectsql(
  1027. sql.selectQuery('User', {
  1028. attributes: [
  1029. // this is not wrapped in `literal()`, so it's a column name.
  1030. // [ & ] will be escaped as [[ & ]]
  1031. [
  1032. '* FROM [User]; DELETE FROM [User];SELECT [id]'
  1033. .replaceAll('[', Support.sequelize.dialect.TICK_CHAR_LEFT)
  1034. .replaceAll(']', Support.sequelize.dialect.TICK_CHAR_RIGHT),
  1035. 'myCol',
  1036. ],
  1037. ],
  1038. }),
  1039. {
  1040. default: `SELECT [* FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT}; DELETE FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT};SELECT ${TICK_LEFT}${TICK_LEFT}id${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} AS ${TICK_LEFT}myCol] FROM [User];`,
  1041. ibmi: 'SELECT "* FROM ""User""; DELETE FROM ""User"";SELECT ""id""" AS "myCol" FROM "User"',
  1042. },
  1043. );
  1044. });
  1045. it('aliased attributes (2)', () => {
  1046. expectsql(
  1047. sql.selectQuery('User', {
  1048. attributes: [['* FROM User; DELETE FROM User;SELECT id', 'myCol']],
  1049. }),
  1050. {
  1051. default: 'SELECT [* FROM User; DELETE FROM User;SELECT id] AS [myCol] FROM [User];',
  1052. ibmi: 'SELECT "* FROM User; DELETE FROM User;SELECT id" AS "myCol" FROM "User"',
  1053. },
  1054. );
  1055. });
  1056. it('aliased attributes (3)', () => {
  1057. expectsql(
  1058. sql.selectQuery('User', {
  1059. attributes: [['id', '* FROM User; DELETE FROM User;SELECT id']],
  1060. }),
  1061. {
  1062. default: 'SELECT [id] AS [* FROM User; DELETE FROM User;SELECT id] FROM [User];',
  1063. ibmi: 'SELECT "id" AS "* FROM User; DELETE FROM User;SELECT id" FROM "User"',
  1064. },
  1065. );
  1066. });
  1067. it('attributes from includes', () => {
  1068. const User = Support.sequelize.define(
  1069. 'User',
  1070. {
  1071. name: DataTypes.STRING,
  1072. age: DataTypes.INTEGER,
  1073. },
  1074. {
  1075. freezeTableName: true,
  1076. },
  1077. );
  1078. const Post = Support.sequelize.define(
  1079. 'Post',
  1080. {
  1081. title: DataTypes.STRING,
  1082. },
  1083. {
  1084. freezeTableName: true,
  1085. },
  1086. );
  1087. // association name is Pascal case to test quoteIdentifier: false
  1088. User.Posts = User.hasMany(Post, { foreignKey: 'user_id', as: 'Posts' });
  1089. expectsql(
  1090. sql.selectQuery(
  1091. 'User',
  1092. {
  1093. attributes: ['name', 'age'],
  1094. include: _validateIncludedElements({
  1095. include: [
  1096. {
  1097. attributes: [
  1098. // this is not wrapped in `literal()`, so it's a column name.
  1099. // [ & ] will be escaped as [[ & ]]
  1100. '* FROM [User]; DELETE FROM [User];SELECT [id]'
  1101. .replaceAll('[', TICK_LEFT)
  1102. .replaceAll(']', TICK_RIGHT),
  1103. ],
  1104. association: User.Posts,
  1105. },
  1106. ],
  1107. model: User,
  1108. }).include,
  1109. model: User,
  1110. },
  1111. User,
  1112. ),
  1113. {
  1114. // expectsql fails with consecutive TICKS so we add the dialect-specific one ourself
  1115. default: `SELECT [User].[name], [User].[age], [Posts].[id] AS [Posts.id], [Posts].[* FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT}; DELETE FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT};SELECT ${TICK_LEFT}${TICK_LEFT}id${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} AS ${TICK_LEFT}Posts.* FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT}; DELETE FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT};SELECT ${TICK_LEFT}${TICK_LEFT}id${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} FROM ${TICK_LEFT}User] AS [User] LEFT OUTER JOIN [Post] AS [Posts] ON [User].[id] = [Posts].[user_id];`,
  1116. ibmi: 'SELECT "User"."name", "User"."age", "Posts"."id" AS "Posts.id", "Posts"."* FROM ""User""; DELETE FROM ""User"";SELECT ""id""" AS "Posts.* FROM ""User""; DELETE FROM ""User"";SELECT ""id""" FROM "User" AS "User" LEFT OUTER JOIN "Post" AS "Posts" ON "User"."id" = "Posts"."user_id"',
  1117. },
  1118. );
  1119. expectsql(
  1120. sql.selectQuery(
  1121. 'User',
  1122. {
  1123. attributes: ['name', 'age'],
  1124. include: _validateIncludedElements({
  1125. include: [
  1126. {
  1127. attributes: [
  1128. // this is not wrapped in `literal()`, so it's a column name.
  1129. // [ & ] will be escaped as [[ & ]]
  1130. [
  1131. '* FROM [User]; DELETE FROM [User];SELECT [id]'
  1132. .replaceAll('[', Support.sequelize.dialect.TICK_CHAR_LEFT)
  1133. .replaceAll(']', Support.sequelize.dialect.TICK_CHAR_RIGHT),
  1134. 'data',
  1135. ],
  1136. ],
  1137. association: User.Posts,
  1138. },
  1139. ],
  1140. model: User,
  1141. }).include,
  1142. model: User,
  1143. },
  1144. User,
  1145. ),
  1146. {
  1147. // expectsql fails with consecutive TICKS so we add the dialect-specific one ourself
  1148. default: `SELECT [User].[name], [User].[age], [Posts].[id] AS [Posts.id], [Posts].[* FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT}; DELETE FROM ${TICK_LEFT}${TICK_LEFT}User${TICK_RIGHT}${TICK_RIGHT};SELECT ${TICK_LEFT}${TICK_LEFT}id${TICK_RIGHT}${TICK_RIGHT}${TICK_RIGHT} AS ${TICK_LEFT}Posts.data] FROM [User] AS [User] LEFT OUTER JOIN [Post] AS [Posts] ON [User].[id] = [Posts].[user_id];`,
  1149. ibmi: 'SELECT "User"."name", "User"."age", "Posts"."id" AS "Posts.id", "Posts"."* FROM ""User""; DELETE FROM ""User"";SELECT ""id""" AS "Posts.data" FROM "User" AS "User" LEFT OUTER JOIN "Post" AS "Posts" ON "User"."id" = "Posts"."user_id"',
  1150. },
  1151. );
  1152. expectsql(
  1153. sql.selectQuery(
  1154. 'User',
  1155. {
  1156. attributes: ['name', 'age'],
  1157. include: _validateIncludedElements({
  1158. include: [
  1159. {
  1160. attributes: [['* FROM User; DELETE FROM User;SELECT id', 'data']],
  1161. association: User.Posts,
  1162. },
  1163. ],
  1164. model: User,
  1165. }).include,
  1166. model: User,
  1167. },
  1168. User,
  1169. ),
  1170. {
  1171. ibmi: 'SELECT "User"."name", "User"."age", "Posts"."id" AS "Posts.id", "Posts"."* FROM User; DELETE FROM User;SELECT id" AS "Posts.data" FROM "User" AS "User" LEFT OUTER JOIN "Post" AS "Posts" ON "User"."id" = "Posts"."user_id"',
  1172. default:
  1173. 'SELECT [User].[name], [User].[age], [Posts].[id] AS [Posts.id], [Posts].[* FROM User; DELETE FROM User;SELECT id] AS [Posts.data] FROM [User] AS [User] LEFT OUTER JOIN [Post] AS [Posts] ON [User].[id] = [Posts].[user_id];',
  1174. },
  1175. );
  1176. });
  1177. });
  1178. });
  1179. describe('quoteIdentifiers: false', () => {
  1180. let sql;
  1181. beforeEach(() => {
  1182. sql = createSequelizeInstance({
  1183. quoteIdentifiers: false,
  1184. }).queryGenerator;
  1185. });
  1186. it('*', () => {
  1187. expectsql(sql.selectQuery('User'), {
  1188. default: 'SELECT * FROM [User];',
  1189. ibmi: 'SELECT * FROM "User"',
  1190. postgres: 'SELECT * FROM "User";',
  1191. snowflake: 'SELECT * FROM User;',
  1192. });
  1193. });
  1194. it('with attributes', () => {
  1195. expectsql(
  1196. sql.selectQuery('User', {
  1197. attributes: ['name', 'age'],
  1198. }),
  1199. {
  1200. default: 'SELECT [name], [age] FROM [User];',
  1201. ibmi: 'SELECT "name", "age" FROM "User"',
  1202. postgres: 'SELECT name, age FROM "User";',
  1203. snowflake: 'SELECT name, age FROM User;',
  1204. },
  1205. );
  1206. });
  1207. it('include (left outer join)', () => {
  1208. const User = Support.sequelize.define(
  1209. 'User',
  1210. {
  1211. name: DataTypes.STRING,
  1212. age: DataTypes.INTEGER,
  1213. },
  1214. {
  1215. freezeTableName: true,
  1216. },
  1217. );
  1218. const Post = Support.sequelize.define(
  1219. 'Post',
  1220. {
  1221. title: DataTypes.STRING,
  1222. },
  1223. {
  1224. freezeTableName: true,
  1225. },
  1226. );
  1227. // association name is Pascal case to test quoteIdentifier: false
  1228. User.Posts = User.hasMany(Post, { foreignKey: 'user_id', as: 'Posts' });
  1229. expectsql(
  1230. sql.selectQuery(
  1231. 'User',
  1232. {
  1233. attributes: ['name', 'age'],
  1234. include: _validateIncludedElements({
  1235. include: [
  1236. {
  1237. attributes: ['title'],
  1238. association: User.Posts,
  1239. },
  1240. ],
  1241. model: User,
  1242. }).include,
  1243. model: User,
  1244. },
  1245. User,
  1246. ),
  1247. {
  1248. default:
  1249. 'SELECT [User].[name], [User].[age], [Posts].[id] AS [Posts.id], [Posts].[title] AS [Posts.title] FROM [User] AS [User] LEFT OUTER JOIN [Post] AS [Posts] ON [User].[id] = [Posts].[user_id];',
  1250. ibmi: 'SELECT "User"."name", "User"."age", "Posts"."id" AS "Posts.id", "Posts"."title" AS "Posts.title" FROM "User" AS "User" LEFT OUTER JOIN "Post" AS "Posts" ON "User"."id" = "Posts"."user_id"',
  1251. postgres:
  1252. 'SELECT "User".name, "User".age, Posts.id AS "Posts.id", Posts.title AS "Posts.title" FROM "User" AS "User" LEFT OUTER JOIN Post AS Posts ON "User".id = Posts.user_id;',
  1253. snowflake:
  1254. 'SELECT User.name, User.age, Posts.id AS "Posts.id", Posts.title AS "Posts.title" FROM User AS User LEFT OUTER JOIN Post AS Posts ON User.id = Posts.user_id;',
  1255. },
  1256. );
  1257. });
  1258. it('nested include (left outer join)', () => {
  1259. const User = Support.sequelize.define(
  1260. 'User',
  1261. {
  1262. name: DataTypes.STRING,
  1263. age: DataTypes.INTEGER,
  1264. },
  1265. {
  1266. freezeTableName: true,
  1267. },
  1268. );
  1269. const Post = Support.sequelize.define(
  1270. 'Post',
  1271. {
  1272. title: DataTypes.STRING,
  1273. },
  1274. {
  1275. freezeTableName: true,
  1276. },
  1277. );
  1278. const Comment = Support.sequelize.define(
  1279. 'Comment',
  1280. {
  1281. title: DataTypes.STRING,
  1282. },
  1283. {
  1284. freezeTableName: true,
  1285. },
  1286. );
  1287. // association names are Pascal case to test quoteIdentifier: false
  1288. User.Posts = User.hasMany(Post, { foreignKey: 'user_id', as: 'Posts' });
  1289. Post.Comments = Post.hasMany(Comment, { foreignKey: 'post_id', as: 'Comments' });
  1290. expectsql(
  1291. sql.selectQuery(
  1292. 'User',
  1293. {
  1294. attributes: ['name', 'age'],
  1295. include: _validateIncludedElements({
  1296. include: [
  1297. {
  1298. attributes: ['title'],
  1299. association: User.Posts,
  1300. include: [
  1301. {
  1302. model: Comment,
  1303. },
  1304. ],
  1305. },
  1306. ],
  1307. model: User,
  1308. }).include,
  1309. model: User,
  1310. },
  1311. User,
  1312. ),
  1313. {
  1314. default:
  1315. 'SELECT [User].[name], [User].[age], [Posts].[id] AS [Posts.id], [Posts].[title] AS [Posts.title], [Posts->Comments].[id] AS [Posts.Comments.id], [Posts->Comments].[title] AS [Posts.Comments.title], [Posts->Comments].[createdAt] AS [Posts.Comments.createdAt], [Posts->Comments].[updatedAt] AS [Posts.Comments.updatedAt], [Posts->Comments].[post_id] AS [Posts.Comments.post_id] FROM [User] AS [User] LEFT OUTER JOIN [Post] AS [Posts] ON [User].[id] = [Posts].[user_id] LEFT OUTER JOIN [Comment] AS [Posts->Comments] ON [Posts].[id] = [Posts->Comments].[post_id];',
  1316. ibmi: 'SELECT "User"."name", "User"."age", "Posts"."id" AS "Posts.id", "Posts"."title" AS "Posts.title", "Posts->Comments"."id" AS "Posts.Comments.id", "Posts->Comments"."title" AS "Posts.Comments.title", "Posts->Comments"."createdAt" AS "Posts.Comments.createdAt", "Posts->Comments"."updatedAt" AS "Posts.Comments.updatedAt", "Posts->Comments"."post_id" AS "Posts.Comments.post_id" FROM "User" AS "User" LEFT OUTER JOIN "Post" AS "Posts" ON "User"."id" = "Posts"."user_id" LEFT OUTER JOIN "Comment" AS "Posts->Comments" ON "Posts"."id" = "Posts->Comments"."post_id"',
  1317. postgres:
  1318. 'SELECT "User".name, "User".age, Posts.id AS "Posts.id", Posts.title AS "Posts.title", "Posts->Comments".id AS "Posts.Comments.id", "Posts->Comments".title AS "Posts.Comments.title", "Posts->Comments".createdAt AS "Posts.Comments.createdAt", "Posts->Comments".updatedAt AS "Posts.Comments.updatedAt", "Posts->Comments".post_id AS "Posts.Comments.post_id" FROM "User" AS "User" LEFT OUTER JOIN Post AS Posts ON "User".id = Posts.user_id LEFT OUTER JOIN Comment AS "Posts->Comments" ON Posts.id = "Posts->Comments".post_id;',
  1319. snowflake:
  1320. 'SELECT User.name, User.age, Posts.id AS "Posts.id", Posts.title AS "Posts.title", "Posts->Comments".id AS "Posts.Comments.id", "Posts->Comments".title AS "Posts.Comments.title", "Posts->Comments".createdAt AS "Posts.Comments.createdAt", "Posts->Comments".updatedAt AS "Posts.Comments.updatedAt", "Posts->Comments".post_id AS "Posts.Comments.post_id" FROM User AS User LEFT OUTER JOIN Post AS Posts ON User.id = Posts.user_id LEFT OUTER JOIN Comment AS "Posts->Comments" ON Posts.id = "Posts->Comments".post_id;',
  1321. },
  1322. );
  1323. });
  1324. it('attributes with dot notation', () => {
  1325. const User = Support.sequelize.define(
  1326. 'User',
  1327. {
  1328. name: DataTypes.STRING,
  1329. age: DataTypes.INTEGER,
  1330. statuslabel: {
  1331. field: 'status.label',
  1332. type: DataTypes.STRING,
  1333. },
  1334. },
  1335. {
  1336. freezeTableName: true,
  1337. },
  1338. );
  1339. const Post = Support.sequelize.define(
  1340. 'Post',
  1341. {
  1342. title: DataTypes.STRING,
  1343. statuslabel: {
  1344. field: 'status.label',
  1345. type: DataTypes.STRING,
  1346. },
  1347. },
  1348. {
  1349. freezeTableName: true,
  1350. },
  1351. );
  1352. // association name is Pascal case to test quoteIdentifier: false
  1353. User.Posts = User.hasMany(Post, { foreignKey: 'user_id', as: 'Posts' });
  1354. expectsql(
  1355. sql.selectQuery(
  1356. 'User',
  1357. {
  1358. attributes: ['name', 'age', ['status.label', 'statuslabel']],
  1359. include: _validateIncludedElements({
  1360. include: [
  1361. {
  1362. attributes: ['title', ['status.label', 'statuslabel']],
  1363. association: User.Posts,
  1364. },
  1365. ],
  1366. model: User,
  1367. }).include,
  1368. model: User,
  1369. dotNotation: true,
  1370. },
  1371. User,
  1372. ),
  1373. {
  1374. default:
  1375. 'SELECT [User].[name], [User].[age], [User].[status.label] AS [statuslabel], [Posts].[id] AS [Posts.id], [Posts].[title] AS [Posts.title], [Posts].[status.label] AS [Posts.statuslabel] FROM [User] AS [User] LEFT OUTER JOIN [Post] AS [Posts] ON [User].[id] = [Posts].[user_id];',
  1376. postgres:
  1377. 'SELECT "User".name, "User".age, "User"."status.label" AS statuslabel, Posts.id AS "Posts.id", Posts.title AS "Posts.title", Posts."status.label" AS "Posts.statuslabel" FROM "User" AS "User" LEFT OUTER JOIN Post AS Posts ON "User".id = Posts.user_id;',
  1378. snowflake:
  1379. 'SELECT User.name, User.age, User."status.label" AS statuslabel, Posts.id AS "Posts.id", Posts.title AS "Posts.title", Posts."status.label" AS "Posts.statuslabel" FROM User AS User LEFT OUTER JOIN Post AS Posts ON User.id = Posts.user_id;',
  1380. },
  1381. );
  1382. });
  1383. });
  1384. });