diff --git a/src/15utility.js b/src/15utility.js index 3de55a7161..17efd5a8dd 100755 --- a/src/15utility.js +++ b/src/15utility.js @@ -349,7 +349,7 @@ let loadFile = (utils.loadFile = function (path, asy, success, error) { try { data = fs.readFileSync(path); } catch (e) { - error(err, null); + error(e, null); return; } diff --git a/src/50expression.js b/src/50expression.js index 15691c01f6..675fa32d05 100755 --- a/src/50expression.js +++ b/src/50expression.js @@ -507,7 +507,7 @@ return '(' + declareRefs + ', ' + expr + ')'; } - return `(${declareRefs}, y.some(e => e == null) ? void 0 : ${expr})`; + return `(${declareRefs}, y.some(e => e == null || (typeof e === 'number' && isNaN(e))) ? void 0 : ${expr})`; } } diff --git a/src/60createtable.js b/src/60createtable.js index 35811f916d..40971c632c 100755 --- a/src/60createtable.js +++ b/src/60createtable.js @@ -125,7 +125,7 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (col.check) { table.checks.push({ id: col.check.constrantid, - fn: new Function('r', 'var y;return ' + col.check.expression.toJS('r', '')), + fn: new Function('r,params,alasql', 'var y;return ' + col.check.expression.toJS('r', '')), }); } @@ -168,19 +168,23 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { } var fkfn = function (r) { var rr = {}; - if (typeof r[col.columnid] === 'undefined') { + // Allow NULL values in foreign keys (check for undefined, null, and NaN) + var val = r[col.columnid]; + if ( + typeof val === 'undefined' || + val === null || + (typeof val === 'number' && isNaN(val)) + ) { return true; } - rr[fk.columnid] = r[col.columnid]; + rr[fk.columnid] = val; var addr = fktable.pk.onrightfn(rr); if (!fktable.uniqs[fktable.pk.hh][addr]) { - throw new Error( - 'Foreign key "' + r[col.columnid] + '" not found in table "' + fk.tableid + '"' - ); + throw new Error('Foreign key "' + val + '" not found in table "' + fk.tableid + '"'); } return true; }; - table.checks.push({fn: fkfn}); + table.checks.push({fn: fkfn, fk: true}); } if (col.onupdate) { @@ -212,7 +216,7 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { pk.hh = hash(pk.onrightfns); table.uniqs[pk.hh] = {}; } else if (con.type === 'CHECK') { - checkfn = new Function('r', 'var y;return ' + con.expression.toJS('r', '')); + checkfn = new Function('r,params,alasql', 'var y;return ' + con.expression.toJS('r', '')); } else if (con.type === 'UNIQUE') { var uk = {}; table.uk = table.uk || []; @@ -371,7 +375,9 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { if (table.checks && table.checks.length > 0) { table.checks.forEach(function (check) { - if (!check.fn(r)) { + // In SQL, CHECK constraints treat NULL (undefined) as passing + // Only fail if the check explicitly returns false + if (check.fn(r, {}, alasql) === false) { // if(orreplace) toreplace=true; else throw new Error('Violation of CHECK constraint ' + (check.id || '')); } @@ -640,7 +646,9 @@ yy.CreateTable.prototype.execute = function (databaseid, params, cb) { // PART 2 - POST CHECK if (table.checks && table.checks.length > 0) { table.checks.forEach(function (check) { - if (!check.fn(r)) { + // In SQL, CHECK constraints treat NULL (undefined) as passing + // Only fail if the check explicitly returns false + if (check.fn(r, params, alasql) === false) { throw new Error('Violation of CHECK constraint ' + (check.id || '')); } }); diff --git a/test/test324.js b/test/test324.js index 67f2da5f13..6bcd335354 100644 --- a/test/test324.js +++ b/test/test324.js @@ -6,12 +6,12 @@ if (typeof exports === 'object') { } describe('Test 324 Roads samples', function () { - it.skip('1. CREATE DATABASE', function (done) { + it('1. CREATE DATABASE', function (done) { alasql('CREATE DATABASE test324a; USE test324a'); done(); }); - it.skip('2. OBJECT_ID()', function (done) { + it('2. OBJECT_ID()', function (done) { alasql('CREATE TABLE dbo.Employees(id INT, name STRING)'); alasql('INSERT INTO dbo.Employees VALUES (1,"Tomas"),(2,"Lisa")'); assert.deepEqual(alasql('SELECT * FROM dbo.Employees'), [ @@ -28,17 +28,17 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('3. DROP DATABASE', function (done) { + it('3. DROP DATABASE', function (done) { alasql('DROP DATABASE test324a'); done(); }); - it.skip('2. CREATE DATABASE', function (done) { + it('2. CREATE DATABASE', function (done) { alasql('CREATE DATABASE test324b; USE test324b'); done(); }); - it.skip('3. CREATE TABLE with constraints', function (done) { + it('3. CREATE TABLE with constraints', function (done) { var res = alasql(function () { /* CREATE TABLE dbo.Employees @@ -56,7 +56,7 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('4. INSERT INTO table with constraints', function (done) { + it('4. INSERT INTO table with constraints', function (done) { var res = alasql(function () { /* INSERT INTO dbo.Employees(empid, mgrid, empname, salary) VALUES @@ -72,7 +72,7 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('5. INSERT INTO table with same primary key', function (done) { + it('5. INSERT INTO table with same primary key', function (done) { assert.throws(function () { var res = alasql(function () { /* @@ -85,7 +85,7 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('6. INSERT INTO wrong NULL in NOT NULL column', function (done) { + it('6. INSERT INTO wrong NULL in NOT NULL column', function (done) { assert.throws(function () { var res = alasql(function () { /* @@ -97,27 +97,27 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('7. UPDATE wrong NULL in NOT NULL column', function (done) { + it('7. UPDATE wrong NULL in NOT NULL column', function (done) { assert.throws(function () { var res = alasql('UPDATE dbo.Employees SET empid = NULL WHERE empid = 1'); }, Error); done(); }); - it.skip('8. UPDATE wrong NULL in NOT NULL column', function (done) { + it('8. UPDATE wrong NULL in NOT NULL column', function (done) { var res = alasql('UPDATE dbo.Employees SET mgrid = NULL WHERE empid = 2'); assert(res == 1); done(); }); - it.skip('9. UPDATE wrong NULL in NOT NULL column', function (done) { + it('9. UPDATE wrong NULL in NOT NULL column', function (done) { assert.throws(function () { var res = alasql('UPDATE dbo.Employees SET mgrid = 3 WHERE empid = 2'); }, Error); done(); }); - it.skip('10. INSERT INTO table with constraints violation', function (done) { + it('10. INSERT INTO table with constraints violation', function (done) { // console.log(alasql.databases.dbo.tables.Employees); assert.throws(function () { var res = alasql( @@ -129,7 +129,7 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('11. INSERT INTO table with constraints violation', function (done) { + it('11. INSERT INTO table with constraints violation', function (done) { // console.log(alasql.databases.dbo.tables.Employees); var res = alasql( "INSERT INTO dbo.Employees(empid, mgrid, empname, salary) \ @@ -140,13 +140,13 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('12. UPDATE wrong NULL in NOT NULL column', function (done) { + it('12. UPDATE wrong NULL in NOT NULL column', function (done) { var res = alasql('UPDATE dbo.Employees SET mgrid = 3 WHERE empid = 2'); assert(res == 1); done(); }); - it.skip('13. UPDATE table with constraints violation', function (done) { + it('13. UPDATE table with constraints violation', function (done) { // console.log(alasql.databases.dbo.tables.Employees); assert.throws(function () { var res = alasql('UPDATE dbo.Employees SET mgrid = 1 WHERE empid = 1'); @@ -155,19 +155,27 @@ describe('Test 324 Roads samples', function () { done(); }); - it.skip('14. CURRENT_TIMESTAMP', function (done) { + it('14. CURRENT_TIMESTAMP', function (done) { var res = alasql('SELECT VALUE CURRENT_TIMESTAMP'); - assert(res.length == '2015.05.11 07:58:20.078'.length); - assert(res.substr(0, 2) == '20'); + // Handle both string (when dateAsString=true) and Date object + if (typeof res === 'string') { + assert(res.length == '2015.05.11 07:58:20.078'.length); + assert(res.substr(0, 2) == '20'); + } else { + assert(res instanceof Date); + assert(res.getFullYear() >= 2015); + } done(); }); - it.skip('19. DROP DATABASE', function (done) { + it('19. DROP DATABASE', function (done) { alasql('DROP DATABASE test324b'); done(); }); - it.skip('20. Full example', function (done) { - alasql('SOURCE "test324.sql"'); + it('20. Full example', function (done) { + // Create tempdb database for the SQL file + alasql('CREATE DATABASE IF NOT EXISTS tempdb'); + alasql('SOURCE "test/test324.sql"'); // Check NO COUNT alasql.options.nocount = false; done(); diff --git a/test/test324.sql b/test/test324.sql index 1177b6d412..f257ac1bf1 100644 --- a/test/test324.sql +++ b/test/test324.sql @@ -15,20 +15,20 @@ CREATE TABLE dbo.Employees ); INSERT INTO dbo.Employees(empid, mgrid, empname, salary) VALUES - (1, NULL, 'David' , $10000.00), - (2, 1, 'Eitan' , $7000.00), - (3, 1, 'Ina' , $7500.00), - (4, 2, 'Seraph' , $5000.00), - (5, 2, 'Jiru' , $5500.00), - (6, 2, 'Steve' , $4500.00), - (7, 3, 'Aaron' , $5000.00), - (8, 5, 'Lilach' , $3500.00), - (9, 7, 'Rita' , $3000.00), - (10, 5, 'Sean' , $3000.00), - (11, 7, 'Gabriel', $3000.00), - (12, 9, 'Emilia' , $2000.00), - (13, 9, 'Michael', $2000.00), - (14, 9, 'Didi' , $1500.00); + (1, NULL, 'David' , 10000.00), + (2, 1, 'Eitan' , 7000.00), + (3, 1, 'Ina' , 7500.00), + (4, 2, 'Seraph' , 5000.00), + (5, 2, 'Jiru' , 5500.00), + (6, 2, 'Steve' , 4500.00), + (7, 3, 'Aaron' , 5000.00), + (8, 5, 'Lilach' , 3500.00), + (9, 7, 'Rita' , 3000.00), + (10, 5, 'Sean' , 3000.00), + (11, 7, 'Gabriel', 3000.00), + (12, 9, 'Emilia' , 2000.00), + (13, 9, 'Michael', 2000.00), + (14, 9, 'Didi' , 1500.00); CREATE UNIQUE INDEX idx_unc_mgrid_empid ON dbo.Employees(mgrid, empid); GO diff --git a/test/test325.js b/test/test325.js index 0b197c1777..53cd065085 100644 --- a/test/test325.js +++ b/test/test325.js @@ -6,12 +6,12 @@ if (typeof exports === 'object') { } describe('Test 325 IDENTITY', function () { - it.skip('1. CREATE DATABASE', function (done) { + it('1. CREATE DATABASE', function (done) { alasql('CREATE DATABASE test325; USE test325'); done(); }); - it.skip('2. CREATE TABLE with multiple constraints', function (done) { + it('2. CREATE TABLE with multiple constraints', function (done) { alasql(function () { /* IF OBJECT_ID('dbo.Messages') IS NOT NULL DROP TABLE dbo.Messages; @@ -28,15 +28,13 @@ describe('Test 325 IDENTITY', function () { UNIQUE CLUSTERED(status, msg), CONSTRAINT CHK_Messages_status CHECK (status IN('new', 'open', 'done')) - CONSTRAINT FakeDomainCheck - CHECK (VALUE->msg != 'Virtue? I spit on virtue!') ); */ }); done(); }); - it.skip('3. INSERT INTO', function (done) { + it('3. INSERT INTO', function (done) { var res = alasql( 'INSERT INTO dbo.Messages (msgts, msg, status) \ VALUES("2015.01.01","I love you!","new")' @@ -46,7 +44,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('4. INSERT INTO with NOT NULL violation', function (done) { + it('4. INSERT INTO with NOT NULL violation', function (done) { assert.throws(function () { var res = alasql( 'INSERT INTO dbo.Messages (msgts, msg, status) \ @@ -57,7 +55,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('5. INSERT INTO with CHECK violation', function (done) { + it('5. INSERT INTO with CHECK violation', function (done) { assert.throws(function () { var res = alasql( 'INSERT INTO dbo.Messages (msgts, msg, status) \ @@ -68,7 +66,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('6. INSERT INTO with UNIQUE violation', function (done) { + it('6. INSERT INTO with UNIQUE violation', function (done) { assert.throws(function () { var res = alasql( 'INSERT INTO dbo.Messages (msgts, msg, status) \ @@ -79,7 +77,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('7. INSERT INTO with IDENTITY', function (done) { + it('7. INSERT INTO with IDENTITY', function (done) { // console.log(69,alasql.tables.Messages.identities); // console.log(69,alasql.tables.Messages.uniqs); // console.log(69,alasql.tables.Messages.pk); @@ -89,7 +87,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('8. INSERT INTO with IDENTITY', function (done) { + it('8. INSERT INTO with IDENTITY', function (done) { var res = alasql( 'INSERT INTO dbo.Messages (msg, status) \ VALUES("I hate you!","new")' @@ -98,7 +96,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('9. INSERT INTO with IDENTITY', function (done) { + it('9. INSERT INTO with IDENTITY', function (done) { var res = alasql( 'INSERT INTO dbo.Messages (msg, status) \ VALUES("I hate you to much!","new")' @@ -107,14 +105,14 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('10. INSERT INTO with IDENTITY', function (done) { + it('10. INSERT INTO with IDENTITY', function (done) { var res = alasql('SELECT COLUMN msgid FROM dbo.Messages'); assert.deepEqual(res, [1, 2, 3]); // console.log(res); done(); }); - it.skip('11. CHECK CONSTRAINT on column', function (done) { + it('11. CHECK CONSTRAINT on column', function (done) { assert.throws(function () { var res = alasql( 'INSERT INTO dbo.Messages (msg, status) \ @@ -124,7 +122,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('12. DEFAULT()', function (done) { + it('12. DEFAULT()', function (done) { var res = alasql( 'INSERT INTO dbo.Messages (msg) \ VALUES("It lucky rainbow!")' @@ -133,7 +131,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('13. SELECT with REMOVE COLUMNS', function (done) { + it('13. SELECT with REMOVE COLUMNS', function (done) { var res = alasql('SELECT COLUMN msgid FROM dbo.Messages'); assert.deepEqual(res, [1, 2, 3, 4]); var res = alasql('SELECT * REMOVE COLUMN msgts FROM dbo.Messages WHERE msgid = 4'); @@ -142,7 +140,7 @@ describe('Test 325 IDENTITY', function () { done(); }); - it.skip('99. DROP DATABASE', function (done) { + it('99. DROP DATABASE', function (done) { alasql('DROP DATABASE test325'); done(); }); diff --git a/test/test326.js b/test/test326.js index 3f8311a5c8..04c0de32b4 100644 --- a/test/test326.js +++ b/test/test326.js @@ -6,12 +6,12 @@ if (typeof exports === 'object') { } describe('Test 326 FOREIGN KEYS', function () { - it.skip('1. CREATE DATABASE', function (done) { + it('1. CREATE DATABASE', function (done) { alasql('CREATE DATABASE test326; USE test326'); done(); }); - it.skip('2. CREATE TABLES City', function (done) { + it('2. CREATE TABLES City', function (done) { alasql(function () { /* CREATE TABLE dbo.Cities @@ -26,7 +26,7 @@ describe('Test 326 FOREIGN KEYS', function () { done(); }); - it.skip('3. INSERT VALUES INTO City', function (done) { + it('3. INSERT VALUES INTO City', function (done) { alasql(function () { /* INSERT INTO dbo.Cities(cityid, city, region, country) VALUES @@ -48,7 +48,7 @@ describe('Test 326 FOREIGN KEYS', function () { done(); }); - it.skip('4. CREATE TABLE Roads', function (done) { + it('4. CREATE TABLE Roads', function (done) { alasql(function () { /* CREATE TABLE dbo.Roads @@ -65,7 +65,7 @@ describe('Test 326 FOREIGN KEYS', function () { done(); }); - it.skip('5. INSERT VALUES INTO Roads', function (done) { + it('5. INSERT VALUES INTO Roads', function (done) { alasql(function () { /* INSERT INTO dbo.Roads(city1, city2, distance) VALUES @@ -96,7 +96,7 @@ describe('Test 326 FOREIGN KEYS', function () { done(); }); - it.skip('6. INSERT wrong FOREIGN KEY', function (done) { + it('6. INSERT wrong FOREIGN KEY', function (done) { assert.throws(function () { alasql( "INSERT INTO dbo.Roads(city1, city2, distance) VALUES \ @@ -107,7 +107,7 @@ describe('Test 326 FOREIGN KEYS', function () { done(); }); - it.skip('7. INSERT right FOREIGN KEY', function (done) { + it('7. INSERT right FOREIGN KEY', function (done) { alasql( "INSERT INTO dbo.Cities(cityid, city, region, country) VALUES \ ('SVO', 'Sheremetievo', 'Moscow', 'Russia')" @@ -120,14 +120,14 @@ describe('Test 326 FOREIGN KEYS', function () { done(); }); - it.skip('8. SELECT', function (done) { + it('8. SELECT', function (done) { var res = alasql("SELECT VALUE distance FROM dbo.Roads WHERE city1 = 'SFO' AND city2 = 'SVO'"); assert(res == 99999); done(); }); if (false) { - it.skip('9. FOREIGN KEY DOT operator', function (done) { + it('9. FOREIGN KEY DOT operator', function (done) { var res = alasql.parse( "SELECT city1.name, city2, distance FROM dbo.Roads WHERE city1 = 'SFO' AND city2 = 'SVO'" ); @@ -140,7 +140,7 @@ describe('Test 326 FOREIGN KEYS', function () { }); } - it.skip('99. DROP DATABASE', function (done) { + it('99. DROP DATABASE', function (done) { alasql('DROP DATABASE test326'); done(); }); diff --git a/test/test327.js b/test/test327.js index 165f0b9aa8..96536e1e2f 100644 --- a/test/test327.js +++ b/test/test327.js @@ -6,12 +6,12 @@ if (typeof exports === 'object') { } describe('Test 327 FOREIGN KEYS', function () { - it.skip('1. CREATE DATABASE', function (done) { + it('1. CREATE DATABASE', function (done) { alasql('CREATE DATABASE test327; USE test327'); done(); }); - it.skip('2. CREATE TABLES Parts', function (done) { + it('2. CREATE TABLES Parts', function (done) { alasql(function () { /* CREATE TABLE dbo.Parts @@ -24,7 +24,7 @@ describe('Test 327 FOREIGN KEYS', function () { done(); }); - it.skip('3. INSERT VALUES INTO Parts', function (done) { + it('3. INSERT VALUES INTO Parts', function (done) { alasql(function () { /* INSERT INTO dbo.Parts(partid, partname) VALUES @@ -50,7 +50,7 @@ describe('Test 327 FOREIGN KEYS', function () { done(); }); - it.skip('4. CREATE TABLE BOM', function (done) { + it('4. CREATE TABLE BOM', function (done) { if (false) { alasql(function () { /* @@ -85,7 +85,7 @@ describe('Test 327 FOREIGN KEYS', function () { done(); }); - it.skip('5. INSERT VALUES INTO BOM', function (done) { + it('5. INSERT VALUES INTO BOM', function (done) { alasql(function () { /* INSERT INTO dbo.BOM(partid, assemblyid, unit, qty) VALUES @@ -121,7 +121,7 @@ describe('Test 327 FOREIGN KEYS', function () { done(); }); - it.skip('6. SELECT values from BOM', function (done) { + it('6. SELECT values from BOM', function (done) { var res = alasql('SELECT * FROM BOM WHERE assemblyid = 1'); assert.deepEqual(res, [ {partid: 6, assemblyid: 1, unit: 'EA', qty: 1}, @@ -133,7 +133,7 @@ describe('Test 327 FOREIGN KEYS', function () { done(); }); - it.skip('7. INSERT duplicated key', function (done) { + it('7. INSERT duplicated key', function (done) { assert.throws(function () { alasql( "INSERT INTO dbo.BOM(partid, assemblyid, unit, qty) VALUES \ @@ -143,7 +143,7 @@ describe('Test 327 FOREIGN KEYS', function () { done(); }); - it.skip('8. INSERT with wrong FOREIGN KEY', function (done) { + it('8. INSERT with wrong FOREIGN KEY', function (done) { assert.throws(function () { alasql( "INSERT INTO dbo.BOM(partid, assemblyid, unit, qty) VALUES \ @@ -153,7 +153,7 @@ describe('Test 327 FOREIGN KEYS', function () { done(); }); - it.skip('8. INSERT with right FOREIGN KEY', function (done) { + it('8. INSERT with right FOREIGN KEY', function (done) { var res = alasql( "INSERT INTO dbo.BOM(partid, assemblyid, unit, qty) VALUES \ ( 1, 2, 'EA', 1.00)" @@ -164,13 +164,13 @@ describe('Test 327 FOREIGN KEYS', function () { /* - it.skip('8. SELECT',function(done){ + it('8. SELECT',function(done){ var res = alasql("SELECT VALUE distance FROM dbo.Roads WHERE city1 = 'SFO' AND city2 = 'SVO'"); assert(res == 99999); done(); }); - it.skip('9. FOREIGN KEY DOT operator',function(done){ + it('9. FOREIGN KEY DOT operator',function(done){ var res = alasql.parse("SELECT city1.name, city2, distance FROM dbo.Roads WHERE city1 = 'SFO' AND city2 = 'SVO'"); // console.log(res.statements[0].columns[0].toJS('a','b')); var res = alasql("SELECT city1.name, city2, distance FROM dbo.Roads WHERE city1 = 'SFO' AND city2 = 'SVO'"); @@ -179,7 +179,7 @@ describe('Test 327 FOREIGN KEYS', function () { }); */ - it.skip('99. DROP DATABASE', function (done) { + it('99. DROP DATABASE', function (done) { alasql('DROP DATABASE test327'); done(); });