Skip to content

Commit

Permalink
Support for BigInt data type for aggregate functions (Issue #1977) (#…
Browse files Browse the repository at this point in the history
…1981)

Co-authored-by: Kamil <kamil.stefanco@student.tuke.sk>
  • Loading branch information
KamilStefanco and Kamil authored Dec 8, 2024
1 parent 9ea01ac commit c285e8e
Show file tree
Hide file tree
Showing 4 changed files with 382 additions and 34 deletions.
121 changes: 87 additions & 34 deletions src/423groupby.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}],`;
Expand Down Expand Up @@ -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') ||
Expand All @@ -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') {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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') {
Expand All @@ -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') {
Expand All @@ -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}
Expand Down
2 changes: 2 additions & 0 deletions src/58json.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(',')}]`;
Expand Down Expand Up @@ -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(',')}]`;
Expand Down
130 changes: 130 additions & 0 deletions test/test1977.js
Original file line number Diff line number Diff line change
@@ -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,
}]);
});
});
Loading

0 comments on commit c285e8e

Please sign in to comment.