'use strict'; const { expect } = require('chai'); const { TEST_TIMESTAMP } = require('../../util/constants'); const { isMssql } = require('../../util/db-helpers'); const { dropTables, createAccounts } = require('../../util/tableCreatorHelper'); const { insertAccounts } = require('../../util/dataInsertHelper'); module.exports = function (knex) { describe('Updates with Triggers', function () { // Trigger options const triggerOptions = { includeTriggerModifications: true }; before(function () { if (!isMssql(knex)) { this.skip('This test is MSSQL only'); } }); before(async () => { await dropTables(knex); await createAccounts(knex); await insertAccounts(knex); }); describe('Trigger Specific Tests', function () { // Reused variables // Table Names const primaryTable = 'test_return_with_trigger_primary'; const secondaryTable = 'test_return_with_trigger_secondary'; // Foreign Key Column Names const primaryLink = 'foreign_id'; const secondaryLink = 'looping_id'; // Trigger Name const triggerName = 'tr_test_insert_with_trigger'; // Create proper environment for tests before(async function () { if (!isMssql(knex)) { this.skip('This test is MSSQL only'); } await knex.schema.hasTable('users').then(async function (exists) { if (exists) { await knex.schema.dropTable(primaryTable); await knex.schema.dropTable(secondaryTable); } // Create tables await knex.schema.createTable(primaryTable, function (table) { table.increments().primary(); table.string('data').defaultsTo(''); table.integer(primaryLink).nullable(); }); await knex.schema.createTable(secondaryTable, function (table) { table.increments().primary(); table.string('data').defaultsTo(''); table.integer(secondaryLink).nullable(); }); await knex.raw(` CREATE TRIGGER [${triggerName}] ON [${secondaryTable}] AFTER UPDATE AS BEGIN SET NOCOUNT ON; BEGIN update pt set pt.${primaryLink} = i.id from Inserted as i inner join ${primaryTable} as pt on pt.id = i.${secondaryLink} END END `); }); }); // Clean-up test specific tables after(async function () { if (!isMssql(knex)) { return; } // Drop table (Trigger is removed with table) await knex.schema.dropTable(primaryTable); await knex.schema.dropTable(secondaryTable); }); // Reset tables for each test beforeEach(async function () { // "Truncate" tables instead of recreate for each test for speed gains await knex.raw(` delete from ${primaryTable} dbcc checkident('${primaryTable}', reseed, 0); delete from ${secondaryTable} dbcc checkident('${secondaryTable}', reseed, 0); `); }); it('#4152 Should allow returns with updates on tables with triggers', async function () { let reachedEnd = false; await knex.transaction(async function () { let updateResults; async function updateWithReturn() { const insertPrimary = { data: 'Testing Data', }; const insertSecondary = { data: 'Test Linking', }; const primaryId = await knex(primaryTable).insert( [insertPrimary], ['id'], triggerOptions ); // Test retrieve with trigger const secondaryId = ( await knex(secondaryTable).insert( [insertSecondary], ['id'], triggerOptions ) )[0]; const updateSecondary = {}; updateSecondary[secondaryLink] = primaryId[0]; updateResults = await knex(secondaryTable) .where('id', '=', secondaryId) .update(updateSecondary, ['id'], triggerOptions); } await updateWithReturn(); expect(Number.parseInt(updateResults)).to.be.finite; reachedEnd = true; }); expect(reachedEnd).to.be.true; }); it('#4152 Should allow returns with updates on tables with triggers using returning function', async function () { let reachedEnd = false; await knex.transaction(async function () { let updateResults; async function updateWithReturn() { const insertPrimary = { data: 'Testing Data', }; const insertSecondary = { data: 'Test Linking', }; const primaryId = await knex(primaryTable).insert( [insertPrimary], ['id'], triggerOptions ); // Test retrieve with trigger const secondaryId = ( await knex(secondaryTable).insert( [insertSecondary], ['id'], triggerOptions ) )[0]; const updateSecondary = {}; updateSecondary[secondaryLink] = primaryId[0]; updateResults = await knex(secondaryTable) .where('id', '=', secondaryId) .returning(['id'], triggerOptions) .update(updateSecondary); } await updateWithReturn(); expect(Number.parseInt(updateResults)).to.be.finite; reachedEnd = true; }); expect(reachedEnd).to.be.true; }); }); describe('Re-test all Update Functions with trigger option and returns', function () { before(async function () { if (!isMssql(knex)) { this.skip('This test is MSSQL only'); } // Reset all table data to original stats of original tests }); it('should allow returning for updates in PostgreSQL and MSSQL', function () { return knex('accounts') .where('id', 1) .update( { email: 'test100@example.com', first_name: 'UpdatedUser', last_name: 'UpdatedTest', }, '*', triggerOptions ) .testSql(function (tester) { tester( 'mysql', 'update `accounts` set `email` = ?, `first_name` = ?, `last_name` = ? where `id` = ?', ['test100@example.com', 'UpdatedUser', 'UpdatedTest', 1], 1 ); tester( 'pg', 'update "accounts" set "email" = ?, "first_name" = ?, "last_name" = ? where "id" = ? returning *', ['test100@example.com', 'UpdatedUser', 'UpdatedTest', 1], [ { id: '1', first_name: 'UpdatedUser', last_name: 'UpdatedTest', email: 'test100@example.com', logins: 1, balance: 0, about: 'Lorem ipsum Dolore labore incididunt enim.', created_at: TEST_TIMESTAMP, updated_at: TEST_TIMESTAMP, phone: null, }, ] ); tester( 'pg-redshift', 'update "accounts" set "email" = ?, "first_name" = ?, "last_name" = ? where "id" = ?', ['test100@example.com', 'UpdatedUser', 'UpdatedTest', 1], 1 ); tester( 'sqlite3', 'update `accounts` set `email` = ?, `first_name` = ?, `last_name` = ? where `id` = ?', ['test100@example.com', 'UpdatedUser', 'UpdatedTest', 1], 1 ); tester( 'oracledb', 'update "accounts" set "email" = ?, "first_name" = ?, "last_name" = ? where "id" = ? returning "ROWID" into ?', [ 'test100@example.com', 'UpdatedUser', 'UpdatedTest', 1, (v) => v.toString() === '[object ReturningHelper:ROWID]', ], 1 ); tester( 'mssql', 'select top(0) [t].* into #out from [accounts] as t left join [accounts] on 0=1;update [accounts] set [email] = ?, [first_name] = ?, [last_name] = ? output inserted.* into #out where [id] = ?; select * from #out; drop table #out;', ['test100@example.com', 'UpdatedUser', 'UpdatedTest', 1], [ { id: '1', first_name: 'UpdatedUser', last_name: 'UpdatedTest', email: 'test100@example.com', logins: 1, balance: 0, about: 'Lorem ipsum Dolore labore incididunt enim.', created_at: TEST_TIMESTAMP, updated_at: TEST_TIMESTAMP, phone: null, }, ] ); }); }); }); }); };