upsert.test.js 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. 'use strict';
  2. const { expect } = require('chai');
  3. const sinon = require('sinon');
  4. const { beforeEach2, sequelize } = require('../support');
  5. const { DataTypes, Sequelize, sql } = require('@sequelize/core');
  6. const dialectName = sequelize.dialect.name;
  7. describe('Model', () => {
  8. before(function () {
  9. this.clock = sinon.useFakeTimers();
  10. });
  11. after(function () {
  12. this.clock.restore();
  13. });
  14. beforeEach(function () {
  15. this.clock.reset();
  16. });
  17. beforeEach(async function () {
  18. this.User = this.sequelize.define('user', {
  19. username: DataTypes.STRING,
  20. foo: {
  21. unique: 'foobar',
  22. type: DataTypes.STRING,
  23. },
  24. bar: {
  25. unique: 'foobar',
  26. type: DataTypes.INTEGER,
  27. },
  28. counter: {
  29. type: DataTypes.INTEGER,
  30. defaultValue: 0,
  31. },
  32. baz: {
  33. type: DataTypes.STRING,
  34. field: 'zab',
  35. defaultValue: 'BAZ_DEFAULT_VALUE',
  36. },
  37. blob: DataTypes.BLOB,
  38. });
  39. this.ModelWithFieldPK = this.sequelize.define('ModelWithFieldPK', {
  40. userId: {
  41. field: 'user_id',
  42. type: DataTypes.INTEGER,
  43. autoIncrement: true,
  44. primaryKey: true,
  45. },
  46. foo: {
  47. type: DataTypes.STRING,
  48. unique: true,
  49. },
  50. });
  51. await this.sequelize.sync({ force: true });
  52. });
  53. if (sequelize.dialect.supports.upserts) {
  54. describe('upsert', () => {
  55. it('works with upsert on id', async function () {
  56. const [, created0] = await this.User.upsert({ id: 42, username: 'john' });
  57. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  58. expect(created0).to.be.null;
  59. } else {
  60. expect(created0).to.be.true;
  61. }
  62. this.clock.tick(1000);
  63. const [, created] = await this.User.upsert({ id: 42, username: 'doe' });
  64. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  65. expect(created).to.be.null;
  66. } else {
  67. expect(created).to.be.false;
  68. }
  69. const user = await this.User.findByPk(42);
  70. expect(user.createdAt).to.be.ok;
  71. expect(user.username).to.equal('doe');
  72. expect(user.updatedAt).to.be.afterTime(user.createdAt);
  73. });
  74. it('works with upsert on a composite key', async function () {
  75. const [, created0] = await this.User.upsert({ foo: 'baz', bar: 19, username: 'john' });
  76. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  77. expect(created0).to.be.null;
  78. } else {
  79. expect(created0).to.be.true;
  80. }
  81. this.clock.tick(1000);
  82. const [, created] = await this.User.upsert({ foo: 'baz', bar: 19, username: 'doe' });
  83. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  84. expect(created).to.be.null;
  85. } else {
  86. expect(created).to.be.false;
  87. }
  88. const user = await this.User.findOne({ where: { foo: 'baz', bar: 19 } });
  89. expect(user.createdAt).to.be.ok;
  90. expect(user.username).to.equal('doe');
  91. expect(user.updatedAt).to.be.afterTime(user.createdAt);
  92. });
  93. it('should work with UUIDs wth default values', async function () {
  94. const User = this.sequelize.define('User', {
  95. id: {
  96. primaryKey: true,
  97. allowNull: false,
  98. unique: true,
  99. type: DataTypes.UUID,
  100. defaultValue: sql.uuidV4,
  101. },
  102. name: {
  103. type: DataTypes.STRING,
  104. },
  105. });
  106. await User.sync({ force: true });
  107. await User.upsert({ name: 'John Doe' });
  108. });
  109. it('works with upsert on a composite primary key', async function () {
  110. const User = this.sequelize.define('user', {
  111. a: {
  112. type: DataTypes.STRING,
  113. primaryKey: true,
  114. },
  115. b: {
  116. type: DataTypes.STRING,
  117. primaryKey: true,
  118. },
  119. username: DataTypes.STRING,
  120. });
  121. await User.sync({ force: true });
  122. const [created1, created2] = await Promise.all([
  123. // Create two users
  124. User.upsert({ a: 'a', b: 'b', username: 'john' }),
  125. User.upsert({ a: 'a', b: 'a', username: 'curt' }),
  126. ]);
  127. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  128. expect(created1[1]).to.be.null;
  129. expect(created2[1]).to.be.null;
  130. } else {
  131. expect(created1[1]).to.be.true;
  132. expect(created2[1]).to.be.true;
  133. }
  134. this.clock.tick(1000);
  135. // Update the first one
  136. const [, created] = await User.upsert({ a: 'a', b: 'b', username: 'doe' });
  137. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  138. expect(created).to.be.null;
  139. } else {
  140. expect(created).to.be.false;
  141. }
  142. const user1 = await User.findOne({ where: { a: 'a', b: 'b' } });
  143. expect(user1.createdAt).to.be.ok;
  144. expect(user1.username).to.equal('doe');
  145. expect(user1.updatedAt).to.be.afterTime(user1.createdAt);
  146. const user2 = await User.findOne({ where: { a: 'a', b: 'a' } });
  147. // The second one should not be updated
  148. expect(user2.createdAt).to.be.ok;
  149. expect(user2.username).to.equal('curt');
  150. expect(user2.updatedAt).to.equalTime(user2.createdAt);
  151. });
  152. it('supports validations', async function () {
  153. const User = this.sequelize.define('user', {
  154. email: {
  155. type: DataTypes.STRING,
  156. validate: {
  157. isEmail: true,
  158. },
  159. },
  160. });
  161. await expect(User.upsert({ email: 'notanemail' })).to.eventually.be.rejectedWith(
  162. Sequelize.ValidationError,
  163. );
  164. });
  165. it('supports skipping validations', async function () {
  166. const User = this.sequelize.define('user', {
  167. email: {
  168. type: DataTypes.STRING,
  169. validate: {
  170. isEmail: true,
  171. },
  172. },
  173. });
  174. const options = { validate: false };
  175. await User.sync({ force: true });
  176. const [, created] = await User.upsert({ id: 1, email: 'notanemail' }, options);
  177. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  178. expect(created).to.be.null;
  179. } else {
  180. expect(created).to.be.true;
  181. }
  182. });
  183. it('works with BLOBs', async function () {
  184. const [, created0] = await this.User.upsert({
  185. id: 42,
  186. username: 'john',
  187. blob: Buffer.from('kaj'),
  188. });
  189. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  190. expect(created0).to.be.null;
  191. } else {
  192. expect(created0).to.be.ok;
  193. }
  194. this.clock.tick(1000);
  195. const [, created] = await this.User.upsert({
  196. id: 42,
  197. username: 'doe',
  198. blob: Buffer.from('andrea'),
  199. });
  200. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  201. expect(created).to.be.null;
  202. } else {
  203. expect(created).to.be.false;
  204. }
  205. const user = await this.User.findByPk(42);
  206. expect(user.createdAt).to.be.ok;
  207. expect(user.username).to.equal('doe');
  208. expect(user.blob.toString()).to.equal('andrea');
  209. expect(user.updatedAt).to.be.afterTime(user.createdAt);
  210. });
  211. it('works with .field', async function () {
  212. const [, created0] = await this.User.upsert({ id: 42, baz: 'foo' });
  213. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  214. expect(created0).to.be.null;
  215. } else {
  216. expect(created0).to.be.ok;
  217. }
  218. const [, created] = await this.User.upsert({ id: 42, baz: 'oof' });
  219. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  220. expect(created).to.be.null;
  221. } else {
  222. expect(created).to.be.false;
  223. }
  224. const user = await this.User.findByPk(42);
  225. expect(user.baz).to.equal('oof');
  226. });
  227. it('works with primary key using .field', async function () {
  228. const [, created0] = await this.ModelWithFieldPK.upsert({ userId: 42, foo: 'first' });
  229. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  230. expect(created0).to.be.null;
  231. } else {
  232. expect(created0).to.be.ok;
  233. }
  234. this.clock.tick(1000);
  235. const [, created] = await this.ModelWithFieldPK.upsert({ userId: 42, foo: 'second' });
  236. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  237. expect(created).to.be.null;
  238. } else {
  239. expect(created).to.be.false;
  240. }
  241. const instance = await this.ModelWithFieldPK.findOne({ where: { userId: 42 } });
  242. expect(instance.foo).to.equal('second');
  243. });
  244. it('works with database functions', async function () {
  245. const [, created0] = await this.User.upsert({
  246. id: 42,
  247. username: 'john',
  248. foo: this.sequelize.fn('upper', 'mixedCase1'),
  249. });
  250. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  251. expect(created0).to.be.null;
  252. } else {
  253. expect(created0).to.be.ok;
  254. }
  255. this.clock.tick(1000);
  256. const [, created] = await this.User.upsert({
  257. id: 42,
  258. username: 'doe',
  259. foo: this.sequelize.fn('upper', 'mixedCase2'),
  260. });
  261. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  262. expect(created).to.be.null;
  263. } else {
  264. expect(created).to.be.false;
  265. }
  266. const user = await this.User.findByPk(42);
  267. expect(user.createdAt).to.be.ok;
  268. expect(user.username).to.equal('doe');
  269. expect(user.foo).to.equal('MIXEDCASE2');
  270. });
  271. it('does not overwrite createdAt time on update', async function () {
  272. await this.User.create({ id: 42, username: 'john' });
  273. const user0 = await this.User.findByPk(42);
  274. const originalCreatedAt = user0.createdAt;
  275. const originalUpdatedAt = user0.updatedAt;
  276. this.clock.tick(5000);
  277. await this.User.upsert({ id: 42, username: 'doe' });
  278. const user = await this.User.findByPk(42);
  279. expect(user.updatedAt).to.be.gt(originalUpdatedAt);
  280. expect(user.createdAt).to.deep.equal(originalCreatedAt);
  281. this.clock.restore();
  282. });
  283. it('does not overwrite createdAt when supplied as an explicit insert value when using fields', async function () {
  284. const originalCreatedAt = new Date('2010-01-01T12:00:00.000Z');
  285. await this.User.upsert(
  286. { id: 42, username: 'john', createdAt: originalCreatedAt },
  287. { fields: ['id', 'username'] },
  288. );
  289. const user = await this.User.findByPk(42);
  290. expect(user.createdAt).to.deep.equal(originalCreatedAt);
  291. this.clock.restore();
  292. });
  293. it('falls back to a noop if no update values are found in the upsert data', async function () {
  294. const User = this.sequelize.define(
  295. 'user',
  296. {
  297. username: DataTypes.STRING,
  298. email: {
  299. type: DataTypes.STRING,
  300. field: 'email_address',
  301. defaultValue: 'xxx@yyy.zzz',
  302. },
  303. },
  304. {
  305. // note, timestamps: false is important here because this test is attempting to see what happens
  306. // if there are NO updatable fields (including timestamp values).
  307. timestamps: false,
  308. },
  309. );
  310. await User.sync({ force: true });
  311. // notice how the data does not actually have the update fields.
  312. await User.upsert({ id: 42, username: 'jack' }, { fields: ['email'] });
  313. await User.upsert({ id: 42, username: 'jill' }, { fields: ['email'] });
  314. const user = await User.findByPk(42);
  315. // just making sure the user exists, i.e. the insert happened.
  316. expect(user).to.be.ok;
  317. expect(user.username).to.equal('jack'); // second upsert should not have updated username.
  318. });
  319. it('does not update using default values', async function () {
  320. await this.User.create({ id: 42, username: 'john', baz: 'new baz value' });
  321. const user0 = await this.User.findByPk(42);
  322. // 'username' should be 'john' since it was set
  323. expect(user0.username).to.equal('john');
  324. // 'baz' should be 'new baz value' since it was set
  325. expect(user0.baz).to.equal('new baz value');
  326. await this.User.upsert({ id: 42, username: 'doe' });
  327. const user = await this.User.findByPk(42);
  328. // 'username' was updated
  329. expect(user.username).to.equal('doe');
  330. // 'baz' should still be 'new baz value' since it was not updated
  331. expect(user.baz).to.equal('new baz value');
  332. });
  333. it('does not update when setting current values', async function () {
  334. await this.User.create({ id: 42, username: 'john' });
  335. const user = await this.User.findByPk(42);
  336. const [, created] = await this.User.upsert({ id: user.id, username: user.username });
  337. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  338. expect(created).to.be.null;
  339. } else {
  340. // After set node-mysql flags = '-FOUND_ROWS' / foundRows=false
  341. // result from upsert should be false when upsert a row to its current value
  342. // https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html
  343. expect(created).to.equal(false);
  344. }
  345. });
  346. it('works when two separate uniqueKeys are passed', async function () {
  347. const User = this.sequelize.define('User', {
  348. username: {
  349. type: DataTypes.STRING,
  350. unique: true,
  351. },
  352. email: {
  353. type: DataTypes.STRING,
  354. unique: true,
  355. },
  356. city: {
  357. type: DataTypes.STRING,
  358. },
  359. });
  360. await User.sync({ force: true });
  361. const [, created0] = await User.upsert({
  362. username: 'user1',
  363. email: 'user1@domain.ext',
  364. city: 'City',
  365. });
  366. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  367. expect(created0).to.be.null;
  368. } else {
  369. expect(created0).to.be.ok;
  370. }
  371. const [, created] = await User.upsert({
  372. username: 'user1',
  373. email: 'user1@domain.ext',
  374. city: 'New City',
  375. });
  376. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  377. expect(created).to.be.null;
  378. } else {
  379. expect(created).to.be.false;
  380. }
  381. const user = await User.findOne({
  382. where: { username: 'user1', email: 'user1@domain.ext' },
  383. });
  384. expect(user.createdAt).to.be.ok;
  385. expect(user.city).to.equal('New City');
  386. });
  387. it('works when indexes are created via indexes array', async function () {
  388. const User = this.sequelize.define(
  389. 'User',
  390. {
  391. username: DataTypes.STRING,
  392. email: DataTypes.STRING,
  393. city: DataTypes.STRING,
  394. },
  395. {
  396. indexes: [
  397. {
  398. unique: true,
  399. fields: ['username'],
  400. },
  401. {
  402. unique: true,
  403. fields: ['email'],
  404. },
  405. ],
  406. },
  407. );
  408. await User.sync({ force: true });
  409. const [, created0] = await User.upsert({
  410. username: 'user1',
  411. email: 'user1@domain.ext',
  412. city: 'City',
  413. });
  414. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  415. expect(created0).to.be.null;
  416. } else {
  417. expect(created0).to.be.ok;
  418. }
  419. const [, created] = await User.upsert({
  420. username: 'user1',
  421. email: 'user1@domain.ext',
  422. city: 'New City',
  423. });
  424. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  425. expect(created).to.be.null;
  426. } else {
  427. expect(created).to.be.false;
  428. }
  429. const user = await User.findOne({
  430. where: { username: 'user1', email: 'user1@domain.ext' },
  431. });
  432. expect(user.createdAt).to.be.ok;
  433. expect(user.city).to.equal('New City');
  434. });
  435. it('works when composite indexes are created via indexes array', async () => {
  436. const User = sequelize.define(
  437. 'User',
  438. {
  439. name: DataTypes.STRING,
  440. address: DataTypes.STRING,
  441. city: DataTypes.STRING,
  442. },
  443. {
  444. indexes: [
  445. {
  446. unique: 'users_name_address',
  447. fields: ['name', 'address'],
  448. },
  449. ],
  450. },
  451. );
  452. await User.sync({ force: true });
  453. const [, created0] = await User.upsert({ name: 'user1', address: 'address', city: 'City' });
  454. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  455. expect(created0).to.be.null;
  456. } else {
  457. expect(created0).to.be.ok;
  458. }
  459. const [, created] = await User.upsert({
  460. name: 'user1',
  461. address: 'address',
  462. city: 'New City',
  463. });
  464. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  465. expect(created).to.be.null;
  466. } else {
  467. expect(created).not.to.be.ok;
  468. }
  469. const user = await User.findOne({ where: { name: 'user1', address: 'address' } });
  470. expect(user.createdAt).to.be.ok;
  471. expect(user.city).to.equal('New City');
  472. });
  473. if (dialectName === 'mssql') {
  474. it('Should throw foreignKey violation for MERGE statement as ForeignKeyConstraintError', async function () {
  475. const User = this.sequelize.define('User', {
  476. username: {
  477. type: DataTypes.STRING,
  478. primaryKey: true,
  479. },
  480. });
  481. const Posts = this.sequelize.define('Posts', {
  482. title: {
  483. type: DataTypes.STRING,
  484. primaryKey: true,
  485. },
  486. username: DataTypes.STRING,
  487. });
  488. Posts.belongsTo(User, { foreignKey: 'username' });
  489. await this.sequelize.sync({ force: true });
  490. await User.create({ username: 'user1' });
  491. await expect(
  492. Posts.upsert({ title: 'Title', username: 'user2' }),
  493. ).to.eventually.be.rejectedWith(Sequelize.ForeignKeyConstraintError);
  494. });
  495. }
  496. if (dialectName.startsWith('postgres')) {
  497. it('works when deletedAt is Infinity and part of primary key', async function () {
  498. const User = this.sequelize.define(
  499. 'User',
  500. {
  501. name: {
  502. type: DataTypes.STRING,
  503. primaryKey: true,
  504. },
  505. address: DataTypes.STRING,
  506. deletedAt: {
  507. type: DataTypes.DATE,
  508. primaryKey: true,
  509. allowNull: true,
  510. defaultValue: Number.POSITIVE_INFINITY,
  511. },
  512. },
  513. {
  514. paranoid: true,
  515. },
  516. );
  517. await User.sync({ force: true });
  518. await Promise.all([
  519. User.create({ name: 'user1' }),
  520. User.create({ name: 'user2', deletedAt: Number.POSITIVE_INFINITY }),
  521. // this record is soft deleted
  522. User.create({ name: 'user3', deletedAt: Number.NEGATIVE_INFINITY }),
  523. ]);
  524. await User.upsert({ name: 'user1', address: 'address' });
  525. const users = await User.findAll({
  526. where: { address: null },
  527. });
  528. expect(users).to.have.lengthOf(2);
  529. });
  530. }
  531. if (dialectName === 'mysql' || dialectName === 'mariadb') {
  532. it('should allow to use calculated values on duplicate', async function () {
  533. await this.User.upsert({
  534. id: 1,
  535. counter: this.sequelize.literal('`counter` + 1'),
  536. });
  537. await this.User.upsert({
  538. id: 1,
  539. counter: this.sequelize.literal('`counter` + 1'),
  540. });
  541. const user = await this.User.findByPk(1);
  542. expect(user.counter).to.equal(2);
  543. });
  544. }
  545. if (sequelize.dialect.supports.returnValues) {
  546. describe('returns values', () => {
  547. it('works with upsert on id', async function () {
  548. const [user0, created0] = await this.User.upsert(
  549. { id: 42, username: 'john' },
  550. { returning: true },
  551. );
  552. expect(user0.get('id')).to.equal(42);
  553. expect(user0.get('username')).to.equal('john');
  554. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  555. expect(created0).to.be.null;
  556. } else {
  557. expect(created0).to.be.true;
  558. }
  559. const [user, created] = await this.User.upsert(
  560. { id: 42, username: 'doe' },
  561. { returning: true },
  562. );
  563. expect(user.get('id')).to.equal(42);
  564. expect(user.get('username')).to.equal('doe');
  565. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  566. expect(created).to.be.null;
  567. } else {
  568. expect(created).to.be.false;
  569. }
  570. });
  571. it('works for table with custom primary key field', async function () {
  572. const User = this.sequelize.define('User', {
  573. id: {
  574. type: DataTypes.INTEGER,
  575. autoIncrement: true,
  576. primaryKey: true,
  577. field: 'id_the_primary',
  578. },
  579. username: {
  580. type: DataTypes.STRING,
  581. },
  582. });
  583. await User.sync({ force: true });
  584. const [user0, created0] = await User.upsert(
  585. { id: 42, username: 'john' },
  586. { returning: true },
  587. );
  588. expect(user0.get('id')).to.equal(42);
  589. expect(user0.get('username')).to.equal('john');
  590. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  591. expect(created0).to.be.null;
  592. } else {
  593. expect(created0).to.be.true;
  594. }
  595. const [user, created] = await User.upsert(
  596. { id: 42, username: 'doe' },
  597. { returning: true },
  598. );
  599. expect(user.get('id')).to.equal(42);
  600. expect(user.get('username')).to.equal('doe');
  601. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  602. expect(created).to.be.null;
  603. } else {
  604. expect(created).to.be.false;
  605. }
  606. });
  607. it('works for non incrementing primaryKey', async function () {
  608. const User = this.sequelize.define('User', {
  609. id: {
  610. type: DataTypes.STRING,
  611. primaryKey: true,
  612. field: 'id_the_primary',
  613. },
  614. username: {
  615. type: DataTypes.STRING,
  616. },
  617. });
  618. await User.sync({ force: true });
  619. const [user0, created0] = await User.upsert(
  620. { id: 'surya', username: 'john' },
  621. { returning: true },
  622. );
  623. expect(user0.get('id')).to.equal('surya');
  624. expect(user0.get('username')).to.equal('john');
  625. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  626. expect(created0).to.be.null;
  627. } else {
  628. expect(created0).to.be.true;
  629. }
  630. const [user, created] = await User.upsert(
  631. { id: 'surya', username: 'doe' },
  632. { returning: true },
  633. );
  634. expect(user.get('id')).to.equal('surya');
  635. expect(user.get('username')).to.equal('doe');
  636. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  637. expect(created).to.be.null;
  638. } else {
  639. expect(created).to.be.false;
  640. }
  641. });
  642. it('should return default value set by the database (upsert)', async function () {
  643. const User = this.sequelize.define('User', {
  644. name: { type: DataTypes.STRING, primaryKey: true },
  645. code: { type: DataTypes.INTEGER, defaultValue: Sequelize.literal(2020) },
  646. });
  647. await User.sync({ force: true });
  648. const [user, created] = await User.upsert(
  649. { name: 'Test default value' },
  650. { returning: true },
  651. );
  652. expect(user.name).to.equal('Test default value');
  653. expect(user.code).to.equal(2020);
  654. if (['db2', 'sqlite3', 'postgres'].includes(dialectName)) {
  655. expect(created).to.be.null;
  656. } else {
  657. expect(created).to.be.true;
  658. }
  659. });
  660. });
  661. }
  662. if (sequelize.dialect.supports.inserts.conflictFields) {
  663. describe('conflictFields', () => {
  664. const vars = beforeEach2(async () => {
  665. // An Abstract joiner table. Unique constraint deliberately removed
  666. // to ensure that `conflictFields` is actually respected, not inferred.
  667. const Memberships = sequelize.define('memberships', {
  668. user_id: DataTypes.INTEGER,
  669. group_id: DataTypes.INTEGER,
  670. permissions: DataTypes.ENUM('admin', 'member'),
  671. });
  672. await Memberships.sync({ force: true });
  673. await sequelize.queryInterface.addConstraint('memberships', {
  674. type: 'UNIQUE',
  675. fields: ['user_id', 'group_id'],
  676. });
  677. return { Memberships };
  678. });
  679. it('should insert with no other rows', async () => {
  680. const { Memberships } = vars;
  681. const [newRow] = await Memberships.upsert(
  682. {
  683. user_id: 1,
  684. group_id: 1,
  685. permissions: 'member',
  686. },
  687. {
  688. conflictFields: ['user_id', 'group_id'],
  689. },
  690. );
  691. expect(newRow).to.not.eq(null);
  692. expect(newRow.permissions).to.eq('member');
  693. });
  694. it('should use conflictFields as upsertKeys', async () => {
  695. const { Memberships } = vars;
  696. const [originalMembership] = await Memberships.upsert(
  697. {
  698. user_id: 1,
  699. group_id: 1,
  700. permissions: 'member',
  701. },
  702. {
  703. conflictFields: ['user_id', 'group_id'],
  704. },
  705. );
  706. expect(originalMembership).to.not.eq(null);
  707. expect(originalMembership.permissions).to.eq('member');
  708. const [updatedMembership] = await Memberships.upsert(
  709. {
  710. user_id: 1,
  711. group_id: 1,
  712. permissions: 'admin',
  713. },
  714. {
  715. conflictFields: ['user_id', 'group_id'],
  716. },
  717. );
  718. expect(updatedMembership).to.not.eq(null);
  719. expect(updatedMembership.permissions).to.eq('admin');
  720. expect(updatedMembership.id).to.eq(originalMembership.id);
  721. const [otherMembership] = await Memberships.upsert(
  722. {
  723. user_id: 2,
  724. group_id: 1,
  725. permissions: 'member',
  726. },
  727. {
  728. conflictFields: ['user_id', 'group_id'],
  729. },
  730. );
  731. expect(otherMembership).to.not.eq(null);
  732. expect(otherMembership.permissions).to.eq('member');
  733. expect(otherMembership.id).to.not.eq(originalMembership.id);
  734. });
  735. it('should map conflictFields to column names', async () => {
  736. const Employees = sequelize.define('employees', {
  737. employeeId: {
  738. type: DataTypes.INTEGER,
  739. field: 'Employee_ID',
  740. },
  741. departmentId: {
  742. type: DataTypes.INTEGER,
  743. field: 'Department_ID',
  744. },
  745. position: DataTypes.ENUM('junior', 'senior'),
  746. });
  747. await Employees.sync({ force: true });
  748. await sequelize.queryInterface.addConstraint('employees', {
  749. type: 'UNIQUE',
  750. fields: ['Employee_ID', 'Department_ID'],
  751. });
  752. const [originalEmployee] = await Employees.upsert(
  753. {
  754. employeeId: 1,
  755. departmentId: 1,
  756. position: 'junior',
  757. },
  758. {
  759. conflictFields: ['employeeId', 'departmentId'],
  760. },
  761. );
  762. expect(originalEmployee).to.not.eq(null);
  763. expect(originalEmployee.position).to.eq('junior');
  764. const [updatedEmployee] = await Employees.upsert(
  765. {
  766. employeeId: 1,
  767. departmentId: 1,
  768. position: 'senior',
  769. },
  770. {
  771. conflictFields: ['employeeId', 'departmentId'],
  772. },
  773. );
  774. expect(updatedEmployee).to.not.eq(null);
  775. expect(updatedEmployee.position).to.eq('senior');
  776. expect(updatedEmployee.id).to.eq(originalEmployee.id);
  777. const [otherEmployee] = await Employees.upsert(
  778. {
  779. employeeId: 2,
  780. departmentId: 1,
  781. position: 'senior',
  782. },
  783. {
  784. conflictFields: ['employeeId', 'departmentId'],
  785. },
  786. );
  787. expect(otherEmployee).to.not.eq(null);
  788. expect(otherEmployee.position).to.eq('senior');
  789. expect(otherEmployee.id).to.not.eq(originalEmployee.id);
  790. });
  791. });
  792. }
  793. if (sequelize.dialect.supports.inserts.onConflictWhere) {
  794. describe('conflictWhere', () => {
  795. const vars = beforeEach2(async () => {
  796. const User = sequelize.define(
  797. 'users',
  798. {
  799. name: DataTypes.STRING,
  800. bio: DataTypes.STRING,
  801. isUnique: DataTypes.BOOLEAN,
  802. },
  803. {
  804. indexes: [
  805. {
  806. unique: true,
  807. fields: ['name'],
  808. where: { isUnique: true },
  809. },
  810. ],
  811. },
  812. );
  813. await User.sync({ force: true });
  814. return { User };
  815. });
  816. it('should insert with no other rows', async () => {
  817. const { User } = vars;
  818. const [newRow] = await User.upsert(
  819. {
  820. name: 'John',
  821. isUnique: true,
  822. },
  823. {
  824. conflictWhere: {
  825. isUnique: true,
  826. },
  827. },
  828. );
  829. expect(newRow).to.not.eq(null);
  830. expect(newRow.name).to.eq('John');
  831. });
  832. it('should update with another unique user', async () => {
  833. const { User } = vars;
  834. let [newRow] = await User.upsert(
  835. {
  836. name: 'John',
  837. isUnique: true,
  838. bio: 'before',
  839. },
  840. {
  841. conflictWhere: {
  842. isUnique: true,
  843. },
  844. },
  845. );
  846. expect(newRow).to.not.eq(null);
  847. expect(newRow.name).to.eq('John');
  848. expect(newRow.bio).to.eq('before');
  849. [newRow] = await User.upsert(
  850. {
  851. name: 'John',
  852. isUnique: true,
  853. bio: 'after',
  854. },
  855. {
  856. conflictWhere: {
  857. isUnique: true,
  858. },
  859. },
  860. );
  861. expect(newRow).to.not.eq(null);
  862. expect(newRow.name).to.eq('John');
  863. expect(newRow.bio).to.eq('after');
  864. const rowCount = await User.count();
  865. expect(rowCount).to.eq(1);
  866. });
  867. it('allows both unique and non-unique users with the same name', async () => {
  868. const { User } = vars;
  869. let [newRow] = await User.upsert(
  870. {
  871. name: 'John',
  872. isUnique: true,
  873. bio: 'first',
  874. },
  875. {
  876. conflictWhere: {
  877. isUnique: true,
  878. },
  879. },
  880. );
  881. expect(newRow).to.not.eq(null);
  882. expect(newRow.name).to.eq('John');
  883. expect(newRow.bio).to.eq('first');
  884. [newRow] = await User.upsert(
  885. {
  886. name: 'John',
  887. isUnique: false,
  888. bio: 'second',
  889. },
  890. {
  891. conflictWhere: {
  892. isUnique: true,
  893. },
  894. },
  895. );
  896. expect(newRow).to.not.eq(null);
  897. expect(newRow.name).to.eq('John');
  898. expect(newRow.bio).to.eq('second');
  899. const rowCount = await User.count();
  900. expect(rowCount).to.eq(2);
  901. });
  902. it('allows for multiple unique users with different names', async () => {
  903. const { User } = vars;
  904. let [newRow] = await User.upsert(
  905. {
  906. name: 'John',
  907. isUnique: true,
  908. bio: 'first',
  909. },
  910. {
  911. conflictWhere: {
  912. isUnique: true,
  913. },
  914. },
  915. );
  916. expect(newRow).to.not.eq(null);
  917. expect(newRow.name).to.eq('John');
  918. expect(newRow.bio).to.eq('first');
  919. [newRow] = await User.upsert(
  920. {
  921. name: 'Bob',
  922. isUnique: false,
  923. bio: 'second',
  924. },
  925. {
  926. conflictWhere: {
  927. isUnique: true,
  928. },
  929. },
  930. );
  931. expect(newRow).to.not.eq(null);
  932. expect(newRow.name).to.eq('Bob');
  933. expect(newRow.bio).to.eq('second');
  934. const rowCount = await User.count();
  935. expect(rowCount).to.eq(2);
  936. });
  937. });
  938. }
  939. });
  940. }
  941. });