Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 46 additions & 18 deletions src/69while.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
/*
//
// CREATE VIEW for Alasql.js
// WHILE, BREAK, CONTINUE, and BEGIN...END for Alasql.js
// Date: 03.11.2014
// (c) 2014, Andrey Gershun
//
*/

// Control flow exception types
yy.BreakException = function () {
this.message = 'BREAK';
};
yy.BreakException.prototype = Object.create(Error.prototype);
yy.BreakException.prototype.constructor = yy.BreakException;

yy.ContinueException = function () {
this.message = 'CONTINUE';
};
yy.ContinueException.prototype = Object.create(Error.prototype);
yy.ContinueException.prototype.constructor = yy.ContinueException;

yy.While = function (params) {
return Object.assign(this, params);
};
Expand Down Expand Up @@ -41,8 +54,18 @@ yy.While.prototype.execute = function (databaseid, params, cb) {
loop();
} else {
while (fn(params, alasql)) {
var res1 = self.loopstat.execute(databaseid, params);
res.push(res1);
try {
var res1 = self.loopstat.execute(databaseid, params);
res.push(res1);
} catch (err) {
if (err instanceof yy.BreakException) {
break;
} else if (err instanceof yy.ContinueException) {
continue;
} else {
throw err;
}
}
}
}
return res;
Expand All @@ -57,9 +80,7 @@ yy.Break.prototype.toString = function () {
};

yy.Break.prototype.execute = function (databaseid, params, cb, scope) {
var res = 1;
if (cb) res = cb(res);
return res;
throw new yy.BreakException();
};

yy.Continue = function (params) {
Expand All @@ -71,9 +92,7 @@ yy.Continue.prototype.toString = function () {
};

yy.Continue.prototype.execute = function (databaseid, params, cb, scope) {
var res = 1;
if (cb) res = cb(res);
return res;
throw new yy.ContinueException();
};

yy.BeginEnd = function (params) {
Expand All @@ -88,15 +107,24 @@ yy.BeginEnd.prototype.execute = function (databaseid, params, cb, scope) {
var self = this;
var res = [];

var idx = 0;
runone();
function runone() {
self.statements[idx].execute(databaseid, params, function (data) {
res.push(data);
idx++;
if (idx < self.statements.length) return runone();
if (cb) res = cb(res);
});
if (cb) {
// Asynchronous execution with callback
var idx = 0;
runone();
function runone() {
self.statements[idx].execute(databaseid, params, function (data) {
res.push(data);
idx++;
if (idx < self.statements.length) return runone();
if (cb) res = cb(res);
});
}
} else {
// Synchronous execution
for (var i = 0; i < self.statements.length; i++) {
var res1 = self.statements[i].execute(databaseid, params);
res.push(res1);
}
}
return res;
};
148 changes: 148 additions & 0 deletions test/test37.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
if (typeof exports === 'object') {
var assert = require('assert');
var alasql = require('..');
}

describe('Test 37 - WHILE with BREAK and CONTINUE statements', function () {
/**
* SQL-99 standard defines:
* - WHILE condition DO statement END WHILE
* - LOOP can be terminated with BREAK/LEAVE
* - CONTINUE/ITERATE skips to next iteration
*
* For compatibility with T-SQL syntax:
* - WHILE condition BEGIN statements END
* - BREAK exits the loop
* - CONTINUE skips to next iteration
*/

it('1. Simple WHILE loop without BREAK/CONTINUE', function () {
var res = alasql('SET @a = 0; WHILE @a < 3 BEGIN SET @a = @a + 1; END; SELECT @a as a');
assert.deepEqual(res[res.length - 1], [{a: 3}]);
});

it('2. WHILE with BREAK - exit loop early', function () {
var res = alasql(
'SET @a = 0; WHILE @a < 10 BEGIN SET @a = @a + 1; IF @a = 5 BREAK; END; SELECT @a as a'
);
assert.deepEqual(res[res.length - 1], [{a: 5}]);
});

it('3. WHILE with CONTINUE - skip iteration', function () {
var res = alasql(
'SET @mysum = 0; SET @i = 0; WHILE @i < 5 BEGIN SET @i = @i + 1; IF @i = 3 CONTINUE; SET @mysum = @mysum + @i; END; SELECT @mysum as mysum'
);
// Should sum: 1 + 2 + 4 + 5 = 12 (skipping 3)
assert.deepEqual(res[res.length - 1], [{mysum: 12}]);
});

it('4. BREAK immediately (condition true, break on first iteration)', function () {
var res = alasql(
'SET @a = 1; WHILE @a < 10 BEGIN IF @a = 1 BREAK; SET @a = @a + 1; END; SELECT @a as a'
);
assert.deepEqual(res[res.length - 1], [{a: 1}]);
});

it('5. Example from issue - WHILE with BREAK and CONTINUE (original logic)', function () {
// Using the exact example from the issue
var res = alasql(`
SET @a = 1;
WHILE @a < 10
BEGIN
IF @a = 8 BREAK;
SET @a = @a + 1;
IF @a = 5 CONTINUE;
SET @a = @a + 1;
END;
SELECT @a as a
`);
// This example demonstrates the syntax but the CONTINUE never actually triggers
// because by the time @a is checked against 5, it's been incremented past 5
// The BREAK does trigger when @a reaches 8
assert.deepEqual(res[res.length - 1], [{a: 11}]);
});

it('5b. Better example - WHILE with BREAK and CONTINUE that actually triggers', function () {
// Modified to show CONTINUE actually working
var res = alasql(`
SET @a = 0;
SET @result = 0;
WHILE @a < 10
BEGIN
SET @a = @a + 1;
IF @a = 8 BREAK;
IF @a % 2 = 0 CONTINUE;
SET @result = @result + @a;
END;
SELECT @result as result
`);
// Adds odd numbers: 1 + 3 + 5 + 7 = 16
assert.deepEqual(res[res.length - 1], [{result: 16}]);
});

it('6. CONTINUE multiple times in one loop', function () {
var res = alasql(`
SET @cnt = 0;
SET @i = 0;
WHILE @i < 10
BEGIN
SET @i = @i + 1;
IF @i % 2 = 0 CONTINUE;
SET @cnt = @cnt + 1;
END;
SELECT @cnt as cnt
`);
// Count only odd numbers: 1, 3, 5, 7, 9 = 5
assert.deepEqual(res[res.length - 1], [{cnt: 5}]);
});

it('7. WHILE with nested IF and BREAK', function () {
var res = alasql(`
SET @x = 0;
WHILE @x < 100
BEGIN
SET @x = @x + 1;
IF @x > 5
BEGIN
IF @x = 7 BREAK;
END;
END;
SELECT @x as x
`);
assert.deepEqual(res[res.length - 1], [{x: 7}]);
});

it('8. WHILE loop that completes without BREAK', function () {
var res = alasql(`
SET @mysum = 0;
SET @i = 1;
WHILE @i <= 5
BEGIN
SET @mysum = @mysum + @i;
SET @i = @i + 1;
IF @i > 100 BREAK;
END;
SELECT @mysum as mysum
`);
// Sum: 1 + 2 + 3 + 4 + 5 = 15
assert.deepEqual(res[res.length - 1], [{mysum: 15}]);
});

it('9. Empty loop with immediate BREAK', function () {
var res = alasql('SET @a = 1; WHILE @a < 100 BEGIN BREAK; END; SELECT @a as a');
assert.deepEqual(res[res.length - 1], [{a: 1}]);
});

it('10. CONTINUE at end of loop (should work like normal iteration)', function () {
var res = alasql(`
SET @a = 0;
WHILE @a < 3
BEGIN
SET @a = @a + 1;
CONTINUE;
END;
SELECT @a as a
`);
assert.deepEqual(res[res.length - 1], [{a: 3}]);
});
});