diff --git a/src/423groupby.js b/src/423groupby.js index ee0de0a372..2773496d0b 100755 --- a/src/423groupby.js +++ b/src/423groupby.js @@ -117,18 +117,18 @@ yy.Select.prototype.compileGroup = function (query) { if ('funcid' in col.expression) { let colexp1 = colExpIfFunIdExists(col.expression); - return `'${colas}': (typeof ${colexp1} == 'number' ? ${colexp} : typeof ${colexp1} == 'object' ? + return `'${colas}': (typeof ${colexp1} == 'number' || typeof ${colexp1} == 'bigint' ? ${colexp} : typeof ${colexp1} == 'object' ? typeof Number(${colexp1}) == 'number' && ${colexp1}!== null? ${colexp} : null : null),`; } - return `'${colas}': (typeof ${colexp} == 'number' ? ${colexp} : typeof ${colexp} == 'object' ? + return `'${colas}': (typeof ${colexp} == 'number' || typeof ${colexp} == 'bigint' ? ${colexp} : typeof ${colexp} == 'object' ? typeof Number(${colexp}) == 'number' && ${colexp}!== null? ${colexp} : null : null),`; } else if (col.aggregatorid === 'MAX') { if ('funcid' in col.expression) { let colexp1 = colExpIfFunIdExists(col.expression); - return `'${colas}' : (typeof ${colexp1} == 'number' ? ${colexp} : typeof ${colexp1} == 'object' ? + return `'${colas}' : (typeof ${colexp1} == 'number' || typeof ${colexp1} == 'bigint' ? ${colexp} : typeof ${colexp1} == 'object' ? typeof Number(${colexp1}) == 'number' ? ${colexp} : null : null),`; } - return `'${colas}' : (typeof ${colexp} == 'number' ? ${colexp} : typeof ${colexp} == 'object' ? + return `'${colas}' : (typeof ${colexp} == 'number' || typeof ${colexp} == 'bigint' ? ${colexp} : typeof ${colexp} == 'object' ? typeof Number(${colexp}) == 'number' ? ${colexp} : null : null),`; } else if (col.aggregatorid === 'ARRAY') { return `'${colas}':[${colexp}],`; @@ -183,10 +183,13 @@ yy.Select.prototype.compileGroup = function (query) { { const __g_colas = g['${colas}']; const __typeof_colexp1 = typeof ${colexp1}; + const __colexp1 = ${colexp1}; if (__g_colas == null && ${colexp1} == null) { g['${colas}'] = null; - } else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 !== 'object' && __typeof_colexp1 !== 'number') || + } else if (typeof __g_colas === 'bigint' || typeof __colexp1 === 'bigint') { + g['${colas}'] = BigInt(__g_colas) + BigInt(__colexp); + } else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 !== 'object' && __typeof_colexp1 !== 'number') || (__g_colas == null || (typeof __g_colas !== 'number' && typeof __g_colas !== 'object')) && (${colexp1} == null || (__typeof_colexp1 !== 'number' && __typeof_colexp1 !== 'object'))) { g['${colas}'] = null; } else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 == 'number') || @@ -208,10 +211,13 @@ yy.Select.prototype.compileGroup = function (query) { { const __g_colas = g['${colas}']; const __typeof_colexp = typeof ${colexp}; + const __colexp = ${colexp}; if (__g_colas == null && ${colexp} == null) { g['${colas}'] = null; - } else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp !== 'object' && __typeof_colexp !== 'number') || + } else if (typeof __g_colas === 'bigint' || typeof __colexp === 'bigint') { + g['${colas}'] = BigInt(__g_colas) + BigInt(__colexp); + } else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp !== 'object' && __typeof_colexp !== 'number') || (__g_colas == null || (typeof __g_colas !== 'number' && typeof __g_colas !== 'object')) && (${colexp} == null || (__typeof_colexp !== 'number' && __typeof_colexp !== 'object'))) { g['${colas}'] = null; } else if (typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp == 'number') { @@ -241,7 +247,9 @@ yy.Select.prototype.compileGroup = function (query) { if (__typeof_g_colas == 'string' && !isNaN(__g_colas) && typeof Number(__g_colas) == 'number' && __typeof_colexp1 == 'string' && !isNaN(__colexp1) && typeof Number(__colexp1) == 'number') { g['${colas}'] = Number(__g_colas) + Number(__colexp1); - } else if (__typeof_g_colas == 'string' && __typeof_colexp1 == 'string') { + } else if (__typeof_g_colas === 'bigint' || __typeof_colexp1 === 'bigint') { + g['${colas}'] = BigInt(__g_colas || 0) + BigInt(__colexp1 || 0); + } else if (__typeof_g_colas == 'string' && __typeof_colexp1 == 'string') { g['${colas}'] = 0; } else if (__typeof_g_colas == 'string' && __typeof_colexp1 == 'number') { g['${colas}'] = __colexp1; @@ -265,7 +273,9 @@ yy.Select.prototype.compileGroup = function (query) { if (__typeof_g_colas === 'string' && !isNaN(__g_colas) && typeof Number(__g_colas) === 'number' && __typeof_colexp === 'string' && !isNaN(__colexp) && typeof Number(__colexp) === 'number') { g['${colas}'] = Number(__g_colas) + Number(__colexp); - } else if (__typeof_g_colas === 'string' && __typeof_colexp === 'string') { + } else if (__typeof_g_colas === 'bigint' || __typeof_colexp === 'bigint') { + g['${colas}'] = BigInt(__g_colas || 0) + BigInt(__colexp || 0); + } else if (__typeof_g_colas === 'string' && __typeof_colexp === 'string') { g['${colas}'] = 0; } else if (__typeof_g_colas === 'string' && __typeof_colexp === 'number') { g['${colas}'] = __colexp; @@ -296,23 +306,41 @@ yy.Select.prototype.compileGroup = function (query) { let colexp1 = colExpIfFunIdExists(col.expression); return ( pre + - `if((g['${colas}'] == null && ${colexp1}!== null) ? y = ${colexp} : (g['${colas}']!== null && - ${colexp1} == null) ? y = g['${colas}']:((y=${colexp}) < g['${colas}'])){ if(typeof y == 'number') - {g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;} - else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` + + `if ((g['${colas}'] == null && ${colexp1} !== null) ? y = ${colexp} : + (g['${colas}'] !== null && ${colexp1} == null) ? y = g['${colas}'] : + ((y = ${colexp}) < g['${colas}'])) { + if (typeof y == 'number' || typeof y == 'bigint') { + g['${colas}'] = y; + } else if (typeof y == 'object' && y instanceof Date) { + g['${colas}'] = y; + } else if (typeof y == 'object' && typeof Number(y) == 'number') { + g['${colas}'] = Number(y); + } + } else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object' && y instanceof Date) { + g['${colas}'] = g['${colas}']; + } else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object') { + g['${colas}'] = Number(g['${colas}']); + }` + post ); } return ( pre + - `if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} : (g['${colas}']!== null && - ${colexp} == null) ? y = g['${colas}']:((y=${colexp}) < g['${colas}'])){ if(typeof y == 'number') - {g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;} - else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` + + `if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} : + (g['${colas}']!== null && ${colexp} == null) ? y = g['${colas}'] : + ((y=${colexp}) < g['${colas}'])) { + if(typeof y == 'number' || typeof y == 'bigint') { + g['${colas}'] = y; + } else if(typeof y == 'object' && y instanceof Date) { + g['${colas}'] = y; + } else if(typeof y == 'object' && typeof Number(y) == 'number') { + g['${colas}'] = Number(y); + } + } else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date) { + g['${colas}'] = g['${colas}']; + } else if(g['${colas}']!== null && typeof g['${colas}'] == 'object') { + g['${colas}'] = Number(g['${colas}']); + }` + post ); } else if (col.aggregatorid === 'MAX') { @@ -321,23 +349,41 @@ yy.Select.prototype.compileGroup = function (query) { //console.log(pre + 'if ((y=' + colexp + ") > g['" + colas + "']) g['" + colas + "']) return ( pre + - `if((g['${colas}'] == null && ${colexp1}!== null) ? y = ${colexp} : (g['${colas}']!== null && - ${colexp1} == null) ? y = g['${colas}']:((y=${colexp}) > g['${colas}'])){ if(typeof y == 'number') - {g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;} - else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` + + `if ((g['${colas}'] == null && ${colexp1} !== null) ? y = ${colexp} : + (g['${colas}'] !== null && ${colexp1} == null) ? y = g['${colas}'] : + ((y = ${colexp}) > g['${colas}'])) { + if (typeof y == 'number' || typeof y == 'bigint') { + g['${colas}'] = y; + } else if (typeof y == 'object' && y instanceof Date) { + g['${colas}'] = y; + } else if (typeof y == 'object' && typeof Number(y) == 'number') { + g['${colas}'] = Number(y); + } + } else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object' && y instanceof Date) { + g['${colas}'] = g['${colas}']; + } else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object') { + g['${colas}'] = Number(g['${colas}']); + }` + post ); } return ( pre + - `if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} : (g['${colas}']!== null && - ${colexp} == null) ? y = g['${colas}']:((y=${colexp}) > g['${colas}'])){ if(typeof y == 'number') - {g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;} - else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']} - else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` + + `if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} : + (g['${colas}']!== null && ${colexp} == null) ? y = g['${colas}'] : + ((y=${colexp}) > g['${colas}'])) { + if(typeof y == 'number' || typeof y == 'bigint') { + g['${colas}'] = y; + } else if(typeof y == 'object' && y instanceof Date) { + g['${colas}'] = y; + } else if(typeof y == 'object' && typeof Number(y) == 'number') { + g['${colas}'] = Number(y); + } + } else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date) { + g['${colas}'] = g['${colas}']; + } else if(g['${colas}']!== null && typeof g['${colas}'] == 'object') { + g['${colas}'] = Number(g['${colas}']); + }` + post ); } else if (col.aggregatorid === 'FIRST') { @@ -346,9 +392,16 @@ yy.Select.prototype.compileGroup = function (query) { return `${pre}g['${colas}']=${colexp};${post}`; } else if (col.aggregatorid === 'AVG') { return `${pre} - g['_SUM_${colas}'] += (y=${colexp})||0; + y= (${colexp}); g['_COUNT_${colas}'] += (typeof y == "undefined" || y === null) ? 0 : 1; - g['${colas}']=g['_SUM_${colas}'] / g['_COUNT_${colas}']; + if (typeof g['_SUM_${colas}'] === 'bigint' || typeof y === 'bigint') { + g['_SUM_${colas}'] = BigInt(g['_SUM_${colas}']); + g['_SUM_${colas}'] += BigInt(y || 0); + g['${colas}'] = BigInt(g['_SUM_${colas}']) / BigInt(g['_COUNT_${colas}']); + } else { + g['_SUM_${colas}'] += (y || 0); + g['${colas}'] = g['_SUM_${colas}'] / g['_COUNT_${colas}']; + } ${post}`; } else if (col.aggregatorid === 'AGGR') { return `${pre} diff --git a/src/58json.js b/src/58json.js index 8143f1d99c..0308999ed3 100755 --- a/src/58json.js +++ b/src/58json.js @@ -19,6 +19,7 @@ yy.Json.prototype.toString = function () { const JSONtoString = (alasql.utils.JSONtoString = function (obj) { if (typeof obj === 'string') return `"${obj}"`; if (typeof obj === 'number' || typeof obj === 'boolean') return String(obj); + if (typeof obj === 'bigint') return `${obj.toString()}n`; if (Array.isArray(obj)) { return `[${obj.map(b => JSONtoString(b)).join(',')}]`; @@ -48,6 +49,7 @@ function JSONtoJS(obj, context, tableid, defcols) { if (typeof obj == 'string') s = '"' + obj + '"'; else if (typeof obj == 'number') s = '(' + obj + ')'; else if (typeof obj == 'boolean') s = obj; + else if (typeof obj === 'bigint') s = obj.toString() + 'n'; else if (typeof obj === 'object') { if (Array.isArray(obj)) { s += `[${obj.map(b => JSONtoJS(b, context, tableid, defcols)).join(',')}]`; diff --git a/test/test1977.js b/test/test1977.js new file mode 100644 index 0000000000..83f667fbb8 --- /dev/null +++ b/test/test1977.js @@ -0,0 +1,130 @@ +if (typeof exports === 'object') { + var assert = require('assert'); + var alasql = require('..'); +} + +describe('Test 1977 - BigInt support', function () { + + it('A) Should sum, find max, min, average of BigInt values, and calculate TOTAL', function () { + var data = [ + {a: 9045645645644442n}, {a: 9147483647334432n}, {a: 20n}, {a : 45875651254783254n} + ]; + + var res = alasql( + `SELECT SUM(a) AS sum_a, + MAX(a) AS max_a, + MIN(a) AS min_a, + AVG(a) AS avg_a, + TOTAL(a) AS total_a + FROM ?`, + [data] + ); + + assert.deepEqual(res, [{ + sum_a: 64068780547762148n, + max_a: 45875651254783254n, + min_a: 20n, + avg_a: 16017195136940537n, + total_a: 64068780547762148n, + }]); + }); + + it('B) Aggregate functions with mixed Number and BigInt types', function () { + var data = [ + {a: 1}, {a: 2}, {a: 3}, {a: 4}, {a: 9147483647334432n} + ]; + + var res = alasql( + `SELECT SUM(a) AS sum_a, + MAX(a) AS max_a, + MIN(a) AS min_a, + AVG(a) AS avg_a, + TOTAL(a) AS total_a + FROM ?`, + [data] + ); + + assert.deepEqual(res, [{ + sum_a: 9147483647334442n, + max_a: 9147483647334432n, + min_a: 1n, + avg_a: 1829496729466888n, + total_a: 9147483647334442n, + }]); + }); + + it('C) Aggregate functions with negative BigInt values', function () { + var data = [ + {a: -9045645645644442n}, + {a: -9147483647334432n} + ]; + + var res = alasql( + `SELECT SUM(a) AS sum_a, + MAX(a) AS max_a, + MIN(a) AS min_a, + AVG(a) AS avg_a, + TOTAL(a) AS total_a + FROM ?`, + [data] + ); + + assert.deepEqual(res, [{ + sum_a: -18193129292978874n, + max_a: -9045645645644442n, + min_a: -9147483647334432n, + avg_a: -9096564646489437n, + total_a: -18193129292978874n, + }]); + }); + + it('D) Aggregate functions with large BigInt values', function () { + var data = [ + {a: BigInt('123456789012345678901234567890')}, + {a: BigInt('987654321098765432109876543210')} + ]; + + var res = alasql( + `SELECT SUM(a) AS sum_a, + MAX(a) AS max_a, + MIN(a) AS min_a, + AVG(a) AS avg_a, + TOTAL(a) AS total_a + FROM ?`, + [data] + ); + + assert.deepEqual(res, [{ + sum_a: BigInt('1111111110111111111011111111100'), + max_a: BigInt('987654321098765432109876543210'), + min_a: BigInt('123456789012345678901234567890'), + avg_a: BigInt('555555555055555555505555555550'), + total_a: BigInt('1111111110111111111011111111100'), + }]); + }); + + it('E) Aggregate functions with zero sum (positive and negative BigInt)', function () { + var data = [ + {a: 12345678901234567890n}, + {a: -12345678901234567890n} + ]; + + var res = alasql( + `SELECT SUM(a) AS sum_a, + MAX(a) AS max_a, + MIN(a) AS min_a, + AVG(a) AS avg_a, + TOTAL(a) AS total_a + FROM ?`, + [data] + ); + + assert.deepEqual(res, [{ + sum_a: 0n, + max_a: 12345678901234567890n, + min_a: -12345678901234567890n, + avg_a: 0n, + total_a: 0n, + }]); + }); +}); diff --git a/test/test2000.js b/test/test2000.js new file mode 100644 index 0000000000..3731f55c28 --- /dev/null +++ b/test/test2000.js @@ -0,0 +1,163 @@ +const alasql = require('../dist/alasql.js'); + +if (typeof exports === 'object') { + var assert = require('assert'); +} + +describe('Test 2000', function () { + + before(function () { + alasql('create database test'); + alasql('use test'); + }); + + after(function () { + alasql('drop database test'); + }); + + it('A) Select from memory', () => { + alasql('CREATE TABLE osoby (id INT, meno STRING)'); + alasql('INSERT INTO osoby VALUES (1, "John"), (2, "Jane"), (3, "Jake")'); + const result = alasql('SELECT * FROM osoby'); + + assert.deepEqual(result, [ + { id: 1, meno: "John" }, + { id: 2, meno: "Jane" }, + { id: 3, meno: "Jake" } + ]); + }); + + it('B) Max from memory', () => { + alasql('CREATE TABLE produkty (id INT, cena INT)'); + alasql('INSERT INTO produkty VALUES (1, 100), (2, 150), (3, 200)'); + const result = alasql('SELECT MAX(cena) AS maxCena FROM produkty'); + + assert.strictEqual(result[0].maxCena, 200); + }); + + it('C) Min from memory', () => { + alasql('CREATE TABLE produkty3 (id INT, cena INT)'); + alasql('INSERT INTO produkty3 VALUES (1, 100), (2, 150), (3, 200)'); + const result = alasql('SELECT MIN(cena) AS minCena FROM produkty3'); + + assert.strictEqual(result[0].minCena, 100); + }); + + it('Total from memory', () => { + alasql('CREATE TABLE produkty4 (id INT, cena INT)'); + alasql('INSERT INTO produkty4 VALUES (1, 100), (2, 150), (3, 200)'); + + const result = alasql('SELECT TOTAL(cena) AS totalCena FROM produkty4'); + + assert.strictEqual(result[0].totalCena, 450); + }); + + it('E) Avg from memory', () => { + alasql('CREATE TABLE produkty2 (id INT, cena INT)'); + alasql('INSERT INTO produkty2 VALUES (1, 100), (2, 150), (3, 200)'); + const result = alasql('SELECT AVG(cena) AS avgCena FROM produkty2'); + + assert.strictEqual(result[0].avgCena, 150); + }); + + + + it('F) SUM with Round function from memory', function () { + var data = [ + { + a: null, + b: 9.45, + c: true, + c2: 1.39, + d: null, + e: 'XYZ1', + f: new Number(2), + }, + { + a: null, + b: 1.13, + c: false, + c2: false, + d: 5.15, + e: 'XYZ2', + f: new Number(11.25), + }, + ]; + res = alasql( + `SELECT SUM(ROUND(a)) AS a, + sum(ROUND(b)) as b, + sUm(c) as c, + sUm(ROUND(c2)) as c2, + SuM(ROUND(d)) as d, + SUM(ROUND(e)) as e, + SUM(ROUND(f)) as f + FROM ?`, + [data] + ); + assert.deepEqual(res, [ + { + a: null, + b: 10, + c: null, + c2: 1, + d: 5, + e: null, + f: 13, + }, + ]); + + + }); + + it('G) MAX/MIN/SUM with Round or Ceil function from memory', function () { + var data = [{a: 10.25}, {a: null}, {b: 10}, {a: 5.25}, {a: 33.45}]; + res = alasql( + `SELECT MIN(ROUND(a)) AS a, + MAX(ROUND(a)) AS b, + MIN(a) AS c, + MAX(a) AS d, + MIN(CEIL(a)) AS e, + MAX(CEIL(a)) AS f, + SUM(ROUND(a)) AS g, + SUM(CEIL(a)) AS h + FROM ?`, + [data] + ); + assert.deepEqual(res, [ + { + a: 5, + b: 33, + c: 5.25, + d: 33.45, + e: 6, + f: 34, + g: 48, + h: 51, + }, + ]); + }); + + it('H) MAX/MIN for Dates from memory', function () { + var data = [ + {a: new Date(2023, 6, 6, 0, 0, 0)}, + {a: new Date(2023, 6, 15, 0, 0, 0)}, + {a: null}, + {a: undefined}, + {a: new Date(2023, 7, 7, 0, 0, 0)}, + ]; + res = alasql( + `SELECT + MIN(a) AS c, + MAX(a) AS d + FROM ?`, + [data] + ); + assert.deepEqual(res, [ + { + c: new Date(2023, 6, 6, 0, 0, 0), + d: new Date(2023, 7, 7, 0, 0, 0), + }, + ]); + }); +}); +