insert-query.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import { DataTypes, literal } from '@sequelize/core';
  2. import { expect } from 'chai';
  3. import { beforeAll2, expectsql, sequelize } from '../../support';
  4. describe('QueryGenerator#insertQuery', () => {
  5. const queryGenerator = sequelize.queryGenerator;
  6. const vars = beforeAll2(() => {
  7. const User = sequelize.define(
  8. 'User',
  9. {
  10. firstName: DataTypes.STRING,
  11. },
  12. { timestamps: false },
  13. );
  14. return { User };
  15. });
  16. // you'll find more replacement tests in query-generator tests
  17. it('parses named replacements in literals', () => {
  18. const { User } = vars;
  19. const { query, bind } = queryGenerator.insertQuery(
  20. User.table,
  21. {
  22. firstName: literal(':name'),
  23. },
  24. {},
  25. {
  26. replacements: {
  27. name: 'Zoe',
  28. },
  29. },
  30. );
  31. expectsql(query, {
  32. default: `INSERT INTO [Users] ([firstName]) VALUES ('Zoe');`,
  33. mssql: `INSERT INTO [Users] ([firstName]) VALUES (N'Zoe');`,
  34. db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ('Zoe'));`,
  35. ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ('Zoe'))`,
  36. });
  37. expect(bind).to.deep.eq({});
  38. });
  39. it('supports named bind parameters in literals', () => {
  40. const { User } = vars;
  41. const { query, bind } = queryGenerator.insertQuery(User.table, {
  42. firstName: 'John',
  43. lastName: literal('$lastName'),
  44. username: 'jd',
  45. });
  46. expectsql(query, {
  47. default: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES ($sequelize_1,$lastName,$sequelize_2);`,
  48. db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$lastName,$sequelize_2));`,
  49. ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$lastName,$sequelize_2))`,
  50. });
  51. expect(bind).to.deep.eq({
  52. sequelize_1: 'John',
  53. sequelize_2: 'jd',
  54. });
  55. });
  56. it('parses positional bind parameters in literals', () => {
  57. const { User } = vars;
  58. const { query, bind } = queryGenerator.insertQuery(User.table, {
  59. firstName: 'John',
  60. lastName: literal('$1'),
  61. username: 'jd',
  62. });
  63. // lastName's bind position being changed from $1 to $2 is intentional: bind array order must match their order in the query in some dialects.
  64. expectsql(query, {
  65. default: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES ($sequelize_1,$1,$sequelize_2);`,
  66. db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$1,$sequelize_2));`,
  67. ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$1,$sequelize_2))`,
  68. });
  69. expect(bind).to.deep.eq({
  70. sequelize_1: 'John',
  71. sequelize_2: 'jd',
  72. });
  73. });
  74. it('parses bind parameters in literals even with bindParams: false', () => {
  75. const { User } = vars;
  76. const { query, bind } = queryGenerator.insertQuery(
  77. User.table,
  78. {
  79. firstName: 'John',
  80. lastName: literal('$1'),
  81. username: 'jd',
  82. },
  83. {},
  84. {
  85. bindParam: false,
  86. },
  87. );
  88. expectsql(query, {
  89. default: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES ('John',$1,'jd');`,
  90. mssql: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES (N'John',$1,N'jd');`,
  91. db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ('John',$1,'jd'));`,
  92. ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ('John',$1,'jd'))`,
  93. });
  94. expect(bind).to.be.undefined;
  95. });
  96. // This test was added due to a regression where these values were being converted to strings
  97. it('binds number values', () => {
  98. if (!sequelize.dialect.supports.dataTypes.ARRAY) {
  99. return;
  100. }
  101. const { User } = vars;
  102. const { query, bind } = queryGenerator.insertQuery(User.tableName, {
  103. numbers: [1, 2, 3],
  104. });
  105. expectsql(query, {
  106. default: `INSERT INTO "Users" ([numbers]) VALUES ($sequelize_1);`,
  107. db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("numbers") VALUES ($sequelize_1));`,
  108. ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("numbers") VALUES ($sequelize_1))`,
  109. });
  110. expect(bind).to.deep.eq({
  111. sequelize_1: [1, 2, 3],
  112. });
  113. });
  114. describe('returning', () => {
  115. it('supports returning: true', () => {
  116. const { User } = vars;
  117. const { query } = queryGenerator.insertQuery(
  118. User.table,
  119. {
  120. firstName: 'John',
  121. },
  122. User.getAttributes(),
  123. {
  124. returning: true,
  125. },
  126. );
  127. expectsql(query, {
  128. default: `INSERT INTO [Users] ([firstName]) VALUES ($sequelize_1) RETURNING [id], [firstName];`,
  129. // TODO: insertQuery should throw if returning is not supported
  130. 'mysql mariadb': `INSERT INTO \`Users\` (\`firstName\`) VALUES ($sequelize_1);`,
  131. // TODO: insertQuery should throw if returning is not supported
  132. snowflake: `INSERT INTO "Users" ("firstName") VALUES ($sequelize_1);`,
  133. mssql:
  134. 'INSERT INTO [Users] ([firstName]) OUTPUT INSERTED.[id], INSERTED.[firstName] VALUES ($sequelize_1);',
  135. db2: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1));',
  136. ibmi: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1))',
  137. });
  138. });
  139. it('supports array of strings (column names)', () => {
  140. const { User } = vars;
  141. const { query } = queryGenerator.insertQuery(
  142. User.table,
  143. {
  144. firstName: 'John',
  145. },
  146. User.getAttributes(),
  147. {
  148. returning: ['*', 'myColumn'],
  149. },
  150. );
  151. expectsql(query, {
  152. default: `INSERT INTO [Users] ([firstName]) VALUES ($sequelize_1) RETURNING [*], [myColumn];`,
  153. // TODO: insertQuery should throw if returning is not supported
  154. 'mysql mariadb': `INSERT INTO \`Users\` (\`firstName\`) VALUES ($sequelize_1);`,
  155. // TODO: insertQuery should throw if returning is not supported
  156. snowflake: `INSERT INTO "Users" ("firstName") VALUES ($sequelize_1);`,
  157. mssql:
  158. 'INSERT INTO [Users] ([firstName]) OUTPUT INSERTED.[*], INSERTED.[myColumn] VALUES ($sequelize_1);',
  159. // TODO: should only select specified columns
  160. db2: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1));',
  161. ibmi: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1))',
  162. });
  163. });
  164. it('supports array of literals', () => {
  165. const { User } = vars;
  166. expectsql(
  167. () => {
  168. return queryGenerator.insertQuery(
  169. User.table,
  170. {
  171. firstName: 'John',
  172. },
  173. User.getAttributes(),
  174. {
  175. returning: [literal('*')],
  176. },
  177. ).query;
  178. },
  179. {
  180. default: `INSERT INTO [Users] ([firstName]) VALUES ($sequelize_1) RETURNING *;`,
  181. // TODO: insertQuery should throw if returning is not supported
  182. 'mysql mariadb': `INSERT INTO \`Users\` (\`firstName\`) VALUES ($sequelize_1);`,
  183. // TODO: insertQuery should throw if returning is not supported
  184. snowflake: `INSERT INTO "Users" ("firstName") VALUES ($sequelize_1);`,
  185. mssql: new Error(
  186. 'literal() cannot be used in the "returning" option array in mssql. Use col(), or a string instead.',
  187. ),
  188. // TODO: should only select specified columns
  189. db2: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1));',
  190. ibmi: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1))',
  191. },
  192. );
  193. });
  194. it('binds date values', () => {
  195. const result = queryGenerator.insertQuery('myTable', {
  196. birthday: new Date('2011-03-27T10:01:55Z'),
  197. });
  198. expectsql(result, {
  199. query: {
  200. default: 'INSERT INTO [myTable] ([birthday]) VALUES ($sequelize_1);',
  201. 'db2 ibmi':
  202. 'SELECT * FROM FINAL TABLE (INSERT INTO "myTable" ("birthday") VALUES ($sequelize_1));',
  203. },
  204. bind: {
  205. mysql: {
  206. sequelize_1: '2011-03-27 10:01:55.000',
  207. },
  208. mariadb: {
  209. sequelize_1: '2011-03-27 10:01:55.000',
  210. },
  211. db2: {
  212. sequelize_1: '2011-03-27 10:01:55.000',
  213. },
  214. ibmi: {
  215. sequelize_1: '2011-03-27 10:01:55.000',
  216. },
  217. snowflake: {
  218. sequelize_1: '2011-03-27 10:01:55.000',
  219. },
  220. sqlite3: {
  221. sequelize_1: '2011-03-27 10:01:55.000 +00:00',
  222. },
  223. postgres: {
  224. sequelize_1: '2011-03-27 10:01:55.000 +00:00',
  225. },
  226. mssql: {
  227. sequelize_1: '2011-03-27 10:01:55.000 +00:00',
  228. },
  229. },
  230. });
  231. });
  232. it('binds boolean values', () => {
  233. const result = queryGenerator.insertQuery('myTable', { positive: true, negative: false });
  234. expectsql(result, {
  235. query: {
  236. default:
  237. 'INSERT INTO [myTable] ([positive],[negative]) VALUES ($sequelize_1,$sequelize_2);',
  238. 'db2 ibmi':
  239. 'SELECT * FROM FINAL TABLE (INSERT INTO "myTable" ("positive","negative") VALUES ($sequelize_1,$sequelize_2));',
  240. },
  241. bind: {
  242. sqlite3: {
  243. sequelize_1: 1,
  244. sequelize_2: 0,
  245. },
  246. mysql: {
  247. sequelize_1: 1,
  248. sequelize_2: 0,
  249. },
  250. mariadb: {
  251. sequelize_1: 1,
  252. sequelize_2: 0,
  253. },
  254. mssql: {
  255. sequelize_1: 1,
  256. sequelize_2: 0,
  257. },
  258. postgres: {
  259. sequelize_1: true,
  260. sequelize_2: false,
  261. },
  262. db2: {
  263. sequelize_1: true,
  264. sequelize_2: false,
  265. },
  266. ibmi: {
  267. sequelize_1: 1,
  268. sequelize_2: 0,
  269. },
  270. snowflake: {
  271. sequelize_1: true,
  272. sequelize_2: false,
  273. },
  274. },
  275. });
  276. });
  277. // TODO: Should we ignore undefined values instead? undefined is closer to "missing property" than null
  278. it('treats undefined as null', () => {
  279. const { query, bind } = queryGenerator.insertQuery('myTable', {
  280. value: undefined,
  281. name: 'bar',
  282. });
  283. expectsql(query, {
  284. default: 'INSERT INTO [myTable] ([value],[name]) VALUES ($sequelize_1,$sequelize_2);',
  285. 'db2 ibmi':
  286. 'SELECT * FROM FINAL TABLE (INSERT INTO "myTable" ("value","name") VALUES ($sequelize_1,$sequelize_2));',
  287. });
  288. expect(bind).to.deep.eq({
  289. sequelize_1: null,
  290. sequelize_2: 'bar',
  291. });
  292. });
  293. });
  294. });