From 2d92219f1923c8028b9d6e9c4d8dbe8f3a35d406 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:04:27 +0000 Subject: [PATCH 1/5] Initial plan From 67f07f3a0883d96f9c34a7cec07a03c6ccca6980 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:16:41 +0000 Subject: [PATCH 2/5] Fix trigger CALL syntax to pass row data - all test1119 tests passing Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/60createtable.js | 56 +++++++++++++++++++++++++++++++++++++------- src/72delete.js | 29 +++++++++++++++-------- test/test1119.js | 2 +- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/60createtable.js b/src/60createtable.js index 35811f916d..50532001e7 100755 --- a/src/60createtable.js +++ b/src/60createtable.js @@ -335,7 +335,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { if (alasql.fn[trigger.funcid](r) === false) prevent = prevent || true; } else if (trigger.statement) { - if (trigger.statement.execute(databaseid) === false) prevent = prevent || true; + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + if (alasql.fn[trigger.statement.expression.funcid](r) === false) prevent = prevent || true; + } else if (trigger.statement.execute(databaseid) === false) { + prevent = prevent || true; + } } } } @@ -350,7 +355,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { alasql.fn[trigger.funcid](r); } else if (trigger.statement) { - trigger.statement.execute(databaseid); + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + alasql.fn[trigger.statement.expression.funcid](r); + } else { + trigger.statement.execute(databaseid); + } } } } @@ -491,7 +501,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { alasql.fn[trigger.funcid](r); } else if (trigger.statement) { - trigger.statement.execute(databaseid); + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + alasql.fn[trigger.statement.expression.funcid](r); + } else { + trigger.statement.execute(databaseid); + } } } } @@ -510,7 +525,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { if (alasql.fn[trigger.funcid](r) === false) prevent = prevent || true; } else if (trigger.statement) { - if (trigger.statement.execute(databaseid) === false) prevent = prevent || true; + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + if (alasql.fn[trigger.statement.expression.funcid](r) === false) prevent = prevent || true; + } else if (trigger.statement.execute(databaseid) === false) { + prevent = prevent || true; + } } } } @@ -525,7 +545,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { alasql.fn[trigger.funcid](r); } else if (trigger.statement) { - trigger.statement.execute(databaseid); + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + alasql.fn[trigger.statement.expression.funcid](r); + } else { + trigger.statement.execute(databaseid); + } } } } @@ -616,7 +641,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { if (alasql.fn[trigger.funcid](this.data[i], r) === false) prevent = prevent || true; } else if (trigger.statement) { - if (trigger.statement.execute(databaseid) === false) prevent = prevent || true; + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + if (alasql.fn[trigger.statement.expression.funcid](this.data[i], r) === false) prevent = prevent || true; + } else if (trigger.statement.execute(databaseid) === false) { + prevent = prevent || true; + } } } } @@ -631,7 +661,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { alasql.fn[trigger.funcid](this.data[i], r); } else if (trigger.statement) { - trigger.statement.execute(databaseid); + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + alasql.fn[trigger.statement.expression.funcid](this.data[i], r); + } else { + trigger.statement.execute(databaseid); + } } } } @@ -688,7 +723,12 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (trigger.funcid) { alasql.fn[trigger.funcid](this.data[i], r); } else if (trigger.statement) { - trigger.statement.execute(databaseid); + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + alasql.fn[trigger.statement.expression.funcid](this.data[i], r); + } else { + trigger.statement.execute(databaseid); + } } } } diff --git a/src/72delete.js b/src/72delete.js index 277a8052c3..da92ea5ee4 100755 --- a/src/72delete.js +++ b/src/72delete.js @@ -88,8 +88,8 @@ yy.Delete.prototype.compile = function (databaseid) { var deletedRows = []; for (var i = 0, ilen = table.data.length; i < ilen; i++) { if (wherefn(table.data[i], params, alasql)) { - // Track deleted row for OUTPUT clause - if (self.output) { + // Track deleted row for OUTPUT clause and AFTER DELETE trigger + if (self.output || table.afterdelete) { deletedRows.push(cloneDeep(table.data[i])); } // Check for transaction - if it is not possible then return all back @@ -104,14 +104,23 @@ yy.Delete.prototype.compile = function (databaseid) { } table.data = newtable; - // Trigger prevent functionality - for (var tr in table.afterdelete) { - var trigger = table.afterdelete[tr]; - if (trigger) { - if (trigger.funcid) { - alasql.fn[trigger.funcid](); - } else if (trigger.statement) { - trigger.statement.execute(databaseid); + // AFTER DELETE triggers - call for each deleted row + if (table.afterdelete) { + for (var i = 0; i < deletedRows.length; i++) { + for (var tr in table.afterdelete) { + var trigger = table.afterdelete[tr]; + if (trigger) { + if (trigger.funcid) { + alasql.fn[trigger.funcid](deletedRows[i]); + } else if (trigger.statement) { + // Check if statement is CALL with a function + if (trigger.statement.expression && trigger.statement.expression.funcid) { + alasql.fn[trigger.statement.expression.funcid](deletedRows[i]); + } else { + trigger.statement.execute(databaseid); + } + } + } } } } diff --git a/test/test1119.js b/test/test1119.js index 01ca1d96bf..6bfbc53417 100644 --- a/test/test1119.js +++ b/test/test1119.js @@ -3,7 +3,7 @@ if (typeof exports === 'object') { var alasql = require('..'); // You might need to adjust the path depending on where you save the test file } -describe.skip('Test 1119 - Trigger callback parameter', function () { +describe('Test 1119 - Trigger callback parameter', function () { const test = '1119'; // Test file number before(function () { From b6187fec0ab321540138218a677ec384d9c4539b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:21:02 +0000 Subject: [PATCH 3/5] Refactor: Extract trigger execution logic into helper function Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/60createtable.js | 88 ++++++-------------------------------------- src/71trigger.js | 24 ++++++++++++ src/72delete.js | 11 +----- 3 files changed, 36 insertions(+), 87 deletions(-) diff --git a/src/60createtable.js b/src/60createtable.js index 50532001e7..a91229388e 100755 --- a/src/60createtable.js +++ b/src/60createtable.js @@ -332,15 +332,8 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { for (var tr in table.beforeinsert) { var trigger = table.beforeinsert[tr]; if (trigger) { - if (trigger.funcid) { - if (alasql.fn[trigger.funcid](r) === false) prevent = prevent || true; - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - if (alasql.fn[trigger.statement.expression.funcid](r) === false) prevent = prevent || true; - } else if (trigger.statement.execute(databaseid) === false) { - prevent = prevent || true; - } + if (alasql.executeTrigger(trigger, databaseid, r) === false) { + prevent = prevent || true; } } } @@ -352,16 +345,7 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { escape = true; trigger = table.insteadofinsert[tr]; if (trigger) { - if (trigger.funcid) { - alasql.fn[trigger.funcid](r); - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - alasql.fn[trigger.statement.expression.funcid](r); - } else { - trigger.statement.execute(databaseid); - } - } + alasql.executeTrigger(trigger, databaseid, r); } } if (escape) return; @@ -498,16 +482,7 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { for (var tr in table.afterinsert) { var trigger = table.afterinsert[tr]; if (trigger) { - if (trigger.funcid) { - alasql.fn[trigger.funcid](r); - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - alasql.fn[trigger.statement.expression.funcid](r); - } else { - trigger.statement.execute(databaseid); - } - } + alasql.executeTrigger(trigger, databaseid, r); } } alasql.inserted = oldinserted; @@ -522,15 +497,8 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { for (var tr in table.beforedelete) { var trigger = table.beforedelete[tr]; if (trigger) { - if (trigger.funcid) { - if (alasql.fn[trigger.funcid](r) === false) prevent = prevent || true; - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - if (alasql.fn[trigger.statement.expression.funcid](r) === false) prevent = prevent || true; - } else if (trigger.statement.execute(databaseid) === false) { - prevent = prevent || true; - } + if (alasql.executeTrigger(trigger, databaseid, r) === false) { + prevent = prevent || true; } } } @@ -542,16 +510,7 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { escape = true; var trigger = table.insteadofdelete[tr]; if (trigger) { - if (trigger.funcid) { - alasql.fn[trigger.funcid](r); - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - alasql.fn[trigger.statement.expression.funcid](r); - } else { - trigger.statement.execute(databaseid); - } - } + alasql.executeTrigger(trigger, databaseid, r); } } if (escape) return; @@ -638,15 +597,8 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { for (var tr in table.beforeupdate) { var trigger = table.beforeupdate[tr]; if (trigger) { - if (trigger.funcid) { - if (alasql.fn[trigger.funcid](this.data[i], r) === false) prevent = prevent || true; - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - if (alasql.fn[trigger.statement.expression.funcid](this.data[i], r) === false) prevent = prevent || true; - } else if (trigger.statement.execute(databaseid) === false) { - prevent = prevent || true; - } + if (alasql.executeTrigger(trigger, databaseid, this.data[i], r) === false) { + prevent = prevent || true; } } } @@ -658,16 +610,7 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { escape = true; var trigger = table.insteadofupdate[tr]; if (trigger) { - if (trigger.funcid) { - alasql.fn[trigger.funcid](this.data[i], r); - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - alasql.fn[trigger.statement.expression.funcid](this.data[i], r); - } else { - trigger.statement.execute(databaseid); - } - } + alasql.executeTrigger(trigger, databaseid, this.data[i], r); } } if (escape) return; @@ -720,16 +663,7 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { for (var tr in table.afterupdate) { var trigger = table.afterupdate[tr]; if (trigger) { - if (trigger.funcid) { - alasql.fn[trigger.funcid](this.data[i], r); - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - alasql.fn[trigger.statement.expression.funcid](this.data[i], r); - } else { - trigger.statement.execute(databaseid); - } - } + alasql.executeTrigger(trigger, databaseid, this.data[i], r); } } }; diff --git a/src/71trigger.js b/src/71trigger.js index 10a705e9cb..0bd230b0de 100755 --- a/src/71trigger.js +++ b/src/71trigger.js @@ -108,3 +108,27 @@ yy.DropTrigger.prototype.execute = function (databaseid, params, cb) { if (cb) res = cb(res); return res; }; + +/** + * Helper function to execute a trigger with proper parameter passing + * @param {object} trigger - The trigger object + * @param {string} databaseid - Database ID for statement execution + * @param {...any} args - Arguments to pass to the trigger function + * @return {*} Result from trigger execution (typically boolean or undefined) + */ +alasql.executeTrigger = function (trigger, databaseid, ...args) { + if (!trigger) return; + + if (trigger.funcid) { + // Direct function ID (older syntax: CREATE TRIGGER ... tablename funcname) + return alasql.fn[trigger.funcid](...args); + } else if (trigger.statement) { + // CALL syntax: CREATE TRIGGER ... CALL funcname() + if (trigger.statement.expression && trigger.statement.expression.funcid) { + return alasql.fn[trigger.statement.expression.funcid](...args); + } else { + // Fallback: execute statement without parameters (for non-CALL statements) + return trigger.statement.execute(databaseid); + } + } +}; diff --git a/src/72delete.js b/src/72delete.js index da92ea5ee4..59f8f02787 100755 --- a/src/72delete.js +++ b/src/72delete.js @@ -110,16 +110,7 @@ yy.Delete.prototype.compile = function (databaseid) { for (var tr in table.afterdelete) { var trigger = table.afterdelete[tr]; if (trigger) { - if (trigger.funcid) { - alasql.fn[trigger.funcid](deletedRows[i]); - } else if (trigger.statement) { - // Check if statement is CALL with a function - if (trigger.statement.expression && trigger.statement.expression.funcid) { - alasql.fn[trigger.statement.expression.funcid](deletedRows[i]); - } else { - trigger.statement.execute(databaseid); - } - } + alasql.executeTrigger(trigger, databaseid, deletedRows[i]); } } } From 987c1648291df235da0c86cdfa36367390511551 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:26:33 +0000 Subject: [PATCH 4/5] Changes before error encountered Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/72delete.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/72delete.js b/src/72delete.js index 59f8f02787..03a6f33ed7 100755 --- a/src/72delete.js +++ b/src/72delete.js @@ -105,6 +105,8 @@ yy.Delete.prototype.compile = function (databaseid) { table.data = newtable; // AFTER DELETE triggers - call for each deleted row + // Note: Triggers are called once per row per trigger (row-level triggers) + // For N deleted rows and M triggers, this results in N×M trigger calls if (table.afterdelete) { for (var i = 0; i < deletedRows.length; i++) { for (var tr in table.afterdelete) { From 4093c48b4176259cca611e8f9b0c0328dc2b73d8 Mon Sep 17 00:00:00 2001 From: "M. Wulff" Date: Wed, 17 Dec 2025 23:48:00 +1100 Subject: [PATCH 5/5] fmt --- src/71trigger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/71trigger.js b/src/71trigger.js index 0bd230b0de..a31bab454a 100755 --- a/src/71trigger.js +++ b/src/71trigger.js @@ -118,7 +118,7 @@ yy.DropTrigger.prototype.execute = function (databaseid, params, cb) { */ alasql.executeTrigger = function (trigger, databaseid, ...args) { if (!trigger) return; - + if (trigger.funcid) { // Direct function ID (older syntax: CREATE TRIGGER ... tablename funcname) return alasql.fn[trigger.funcid](...args);