Skip to content

Commit c285e8e

Browse files
KamilStefancoKamil
andauthored
Support for BigInt data type for aggregate functions (Issue #1977) (#1981)
Co-authored-by: Kamil <[email protected]>
1 parent 9ea01ac commit c285e8e

File tree

4 files changed

+382
-34
lines changed

4 files changed

+382
-34
lines changed

src/423groupby.js

Lines changed: 87 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,18 @@ yy.Select.prototype.compileGroup = function (query) {
117117
if ('funcid' in col.expression) {
118118
let colexp1 = colExpIfFunIdExists(col.expression);
119119

120-
return `'${colas}': (typeof ${colexp1} == 'number' ? ${colexp} : typeof ${colexp1} == 'object' ?
120+
return `'${colas}': (typeof ${colexp1} == 'number' || typeof ${colexp1} == 'bigint' ? ${colexp} : typeof ${colexp1} == 'object' ?
121121
typeof Number(${colexp1}) == 'number' && ${colexp1}!== null? ${colexp} : null : null),`;
122122
}
123-
return `'${colas}': (typeof ${colexp} == 'number' ? ${colexp} : typeof ${colexp} == 'object' ?
123+
return `'${colas}': (typeof ${colexp} == 'number' || typeof ${colexp} == 'bigint' ? ${colexp} : typeof ${colexp} == 'object' ?
124124
typeof Number(${colexp}) == 'number' && ${colexp}!== null? ${colexp} : null : null),`;
125125
} else if (col.aggregatorid === 'MAX') {
126126
if ('funcid' in col.expression) {
127127
let colexp1 = colExpIfFunIdExists(col.expression);
128-
return `'${colas}' : (typeof ${colexp1} == 'number' ? ${colexp} : typeof ${colexp1} == 'object' ?
128+
return `'${colas}' : (typeof ${colexp1} == 'number' || typeof ${colexp1} == 'bigint' ? ${colexp} : typeof ${colexp1} == 'object' ?
129129
typeof Number(${colexp1}) == 'number' ? ${colexp} : null : null),`;
130130
}
131-
return `'${colas}' : (typeof ${colexp} == 'number' ? ${colexp} : typeof ${colexp} == 'object' ?
131+
return `'${colas}' : (typeof ${colexp} == 'number' || typeof ${colexp} == 'bigint' ? ${colexp} : typeof ${colexp} == 'object' ?
132132
typeof Number(${colexp}) == 'number' ? ${colexp} : null : null),`;
133133
} else if (col.aggregatorid === 'ARRAY') {
134134
return `'${colas}':[${colexp}],`;
@@ -183,10 +183,13 @@ yy.Select.prototype.compileGroup = function (query) {
183183
{
184184
const __g_colas = g['${colas}'];
185185
const __typeof_colexp1 = typeof ${colexp1};
186+
const __colexp1 = ${colexp1};
186187
187188
if (__g_colas == null && ${colexp1} == null) {
188189
g['${colas}'] = null;
189-
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 !== 'object' && __typeof_colexp1 !== 'number') ||
190+
} else if (typeof __g_colas === 'bigint' || typeof __colexp1 === 'bigint') {
191+
g['${colas}'] = BigInt(__g_colas) + BigInt(__colexp);
192+
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 !== 'object' && __typeof_colexp1 !== 'number') ||
190193
(__g_colas == null || (typeof __g_colas !== 'number' && typeof __g_colas !== 'object')) && (${colexp1} == null || (__typeof_colexp1 !== 'number' && __typeof_colexp1 !== 'object'))) {
191194
g['${colas}'] = null;
192195
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 == 'number') ||
@@ -208,10 +211,13 @@ yy.Select.prototype.compileGroup = function (query) {
208211
{
209212
const __g_colas = g['${colas}'];
210213
const __typeof_colexp = typeof ${colexp};
214+
const __colexp = ${colexp};
211215
212216
if (__g_colas == null && ${colexp} == null) {
213217
g['${colas}'] = null;
214-
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp !== 'object' && __typeof_colexp !== 'number') ||
218+
} else if (typeof __g_colas === 'bigint' || typeof __colexp === 'bigint') {
219+
g['${colas}'] = BigInt(__g_colas) + BigInt(__colexp);
220+
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp !== 'object' && __typeof_colexp !== 'number') ||
215221
(__g_colas == null || (typeof __g_colas !== 'number' && typeof __g_colas !== 'object')) && (${colexp} == null || (__typeof_colexp !== 'number' && __typeof_colexp !== 'object'))) {
216222
g['${colas}'] = null;
217223
} else if (typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp == 'number') {
@@ -241,7 +247,9 @@ yy.Select.prototype.compileGroup = function (query) {
241247
if (__typeof_g_colas == 'string' && !isNaN(__g_colas) && typeof Number(__g_colas) == 'number' &&
242248
__typeof_colexp1 == 'string' && !isNaN(__colexp1) && typeof Number(__colexp1) == 'number') {
243249
g['${colas}'] = Number(__g_colas) + Number(__colexp1);
244-
} else if (__typeof_g_colas == 'string' && __typeof_colexp1 == 'string') {
250+
} else if (__typeof_g_colas === 'bigint' || __typeof_colexp1 === 'bigint') {
251+
g['${colas}'] = BigInt(__g_colas || 0) + BigInt(__colexp1 || 0);
252+
} else if (__typeof_g_colas == 'string' && __typeof_colexp1 == 'string') {
245253
g['${colas}'] = 0;
246254
} else if (__typeof_g_colas == 'string' && __typeof_colexp1 == 'number') {
247255
g['${colas}'] = __colexp1;
@@ -265,7 +273,9 @@ yy.Select.prototype.compileGroup = function (query) {
265273
if (__typeof_g_colas === 'string' && !isNaN(__g_colas) && typeof Number(__g_colas) === 'number' &&
266274
__typeof_colexp === 'string' && !isNaN(__colexp) && typeof Number(__colexp) === 'number') {
267275
g['${colas}'] = Number(__g_colas) + Number(__colexp);
268-
} else if (__typeof_g_colas === 'string' && __typeof_colexp === 'string') {
276+
} else if (__typeof_g_colas === 'bigint' || __typeof_colexp === 'bigint') {
277+
g['${colas}'] = BigInt(__g_colas || 0) + BigInt(__colexp || 0);
278+
} else if (__typeof_g_colas === 'string' && __typeof_colexp === 'string') {
269279
g['${colas}'] = 0;
270280
} else if (__typeof_g_colas === 'string' && __typeof_colexp === 'number') {
271281
g['${colas}'] = __colexp;
@@ -296,23 +306,41 @@ yy.Select.prototype.compileGroup = function (query) {
296306
let colexp1 = colExpIfFunIdExists(col.expression);
297307
return (
298308
pre +
299-
`if((g['${colas}'] == null && ${colexp1}!== null) ? y = ${colexp} : (g['${colas}']!== null &&
300-
${colexp1} == null) ? y = g['${colas}']:((y=${colexp}) < g['${colas}'])){ if(typeof y == 'number')
301-
{g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;}
302-
else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}}
303-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']}
304-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` +
309+
`if ((g['${colas}'] == null && ${colexp1} !== null) ? y = ${colexp} :
310+
(g['${colas}'] !== null && ${colexp1} == null) ? y = g['${colas}'] :
311+
((y = ${colexp}) < g['${colas}'])) {
312+
if (typeof y == 'number' || typeof y == 'bigint') {
313+
g['${colas}'] = y;
314+
} else if (typeof y == 'object' && y instanceof Date) {
315+
g['${colas}'] = y;
316+
} else if (typeof y == 'object' && typeof Number(y) == 'number') {
317+
g['${colas}'] = Number(y);
318+
}
319+
} else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object' && y instanceof Date) {
320+
g['${colas}'] = g['${colas}'];
321+
} else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object') {
322+
g['${colas}'] = Number(g['${colas}']);
323+
}` +
305324
post
306325
);
307326
}
308327
return (
309328
pre +
310-
`if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} : (g['${colas}']!== null &&
311-
${colexp} == null) ? y = g['${colas}']:((y=${colexp}) < g['${colas}'])){ if(typeof y == 'number')
312-
{g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;}
313-
else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}}
314-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']}
315-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` +
329+
`if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} :
330+
(g['${colas}']!== null && ${colexp} == null) ? y = g['${colas}'] :
331+
((y=${colexp}) < g['${colas}'])) {
332+
if(typeof y == 'number' || typeof y == 'bigint') {
333+
g['${colas}'] = y;
334+
} else if(typeof y == 'object' && y instanceof Date) {
335+
g['${colas}'] = y;
336+
} else if(typeof y == 'object' && typeof Number(y) == 'number') {
337+
g['${colas}'] = Number(y);
338+
}
339+
} else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date) {
340+
g['${colas}'] = g['${colas}'];
341+
} else if(g['${colas}']!== null && typeof g['${colas}'] == 'object') {
342+
g['${colas}'] = Number(g['${colas}']);
343+
}` +
316344
post
317345
);
318346
} else if (col.aggregatorid === 'MAX') {
@@ -321,23 +349,41 @@ yy.Select.prototype.compileGroup = function (query) {
321349
//console.log(pre + 'if ((y=' + colexp + ") > g['" + colas + "']) g['" + colas + "'])
322350
return (
323351
pre +
324-
`if((g['${colas}'] == null && ${colexp1}!== null) ? y = ${colexp} : (g['${colas}']!== null &&
325-
${colexp1} == null) ? y = g['${colas}']:((y=${colexp}) > g['${colas}'])){ if(typeof y == 'number')
326-
{g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;}
327-
else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}}
328-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']}
329-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` +
352+
`if ((g['${colas}'] == null && ${colexp1} !== null) ? y = ${colexp} :
353+
(g['${colas}'] !== null && ${colexp1} == null) ? y = g['${colas}'] :
354+
((y = ${colexp}) > g['${colas}'])) {
355+
if (typeof y == 'number' || typeof y == 'bigint') {
356+
g['${colas}'] = y;
357+
} else if (typeof y == 'object' && y instanceof Date) {
358+
g['${colas}'] = y;
359+
} else if (typeof y == 'object' && typeof Number(y) == 'number') {
360+
g['${colas}'] = Number(y);
361+
}
362+
} else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object' && y instanceof Date) {
363+
g['${colas}'] = g['${colas}'];
364+
} else if (g['${colas}'] !== null && typeof g['${colas}'] == 'object') {
365+
g['${colas}'] = Number(g['${colas}']);
366+
}` +
330367
post
331368
);
332369
}
333370
return (
334371
pre +
335-
`if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} : (g['${colas}']!== null &&
336-
${colexp} == null) ? y = g['${colas}']:((y=${colexp}) > g['${colas}'])){ if(typeof y == 'number')
337-
{g['${colas}'] = y;}else if(typeof y == 'object' && y instanceof Date){g['${colas}'] = y;}
338-
else if(typeof y == 'object' && typeof Number(y) == 'number'){g['${colas}'] = Number(y);}}
339-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date){g['${colas}'] = g['${colas}']}
340-
else if(g['${colas}']!== null && typeof g['${colas}'] == 'object'){g['${colas}'] = Number(g['${colas}'])}` +
372+
`if((g['${colas}'] == null && ${colexp}!== null) ? y = ${colexp} :
373+
(g['${colas}']!== null && ${colexp} == null) ? y = g['${colas}'] :
374+
((y=${colexp}) > g['${colas}'])) {
375+
if(typeof y == 'number' || typeof y == 'bigint') {
376+
g['${colas}'] = y;
377+
} else if(typeof y == 'object' && y instanceof Date) {
378+
g['${colas}'] = y;
379+
} else if(typeof y == 'object' && typeof Number(y) == 'number') {
380+
g['${colas}'] = Number(y);
381+
}
382+
} else if(g['${colas}']!== null && typeof g['${colas}'] == 'object' && y instanceof Date) {
383+
g['${colas}'] = g['${colas}'];
384+
} else if(g['${colas}']!== null && typeof g['${colas}'] == 'object') {
385+
g['${colas}'] = Number(g['${colas}']);
386+
}` +
341387
post
342388
);
343389
} else if (col.aggregatorid === 'FIRST') {
@@ -346,9 +392,16 @@ yy.Select.prototype.compileGroup = function (query) {
346392
return `${pre}g['${colas}']=${colexp};${post}`;
347393
} else if (col.aggregatorid === 'AVG') {
348394
return `${pre}
349-
g['_SUM_${colas}'] += (y=${colexp})||0;
395+
y= (${colexp});
350396
g['_COUNT_${colas}'] += (typeof y == "undefined" || y === null) ? 0 : 1;
351-
g['${colas}']=g['_SUM_${colas}'] / g['_COUNT_${colas}'];
397+
if (typeof g['_SUM_${colas}'] === 'bigint' || typeof y === 'bigint') {
398+
g['_SUM_${colas}'] = BigInt(g['_SUM_${colas}']);
399+
g['_SUM_${colas}'] += BigInt(y || 0);
400+
g['${colas}'] = BigInt(g['_SUM_${colas}']) / BigInt(g['_COUNT_${colas}']);
401+
} else {
402+
g['_SUM_${colas}'] += (y || 0);
403+
g['${colas}'] = g['_SUM_${colas}'] / g['_COUNT_${colas}'];
404+
}
352405
${post}`;
353406
} else if (col.aggregatorid === 'AGGR') {
354407
return `${pre}

src/58json.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ yy.Json.prototype.toString = function () {
1919
const JSONtoString = (alasql.utils.JSONtoString = function (obj) {
2020
if (typeof obj === 'string') return `"${obj}"`;
2121
if (typeof obj === 'number' || typeof obj === 'boolean') return String(obj);
22+
if (typeof obj === 'bigint') return `${obj.toString()}n`;
2223

2324
if (Array.isArray(obj)) {
2425
return `[${obj.map(b => JSONtoString(b)).join(',')}]`;
@@ -48,6 +49,7 @@ function JSONtoJS(obj, context, tableid, defcols) {
4849
if (typeof obj == 'string') s = '"' + obj + '"';
4950
else if (typeof obj == 'number') s = '(' + obj + ')';
5051
else if (typeof obj == 'boolean') s = obj;
52+
else if (typeof obj === 'bigint') s = obj.toString() + 'n';
5153
else if (typeof obj === 'object') {
5254
if (Array.isArray(obj)) {
5355
s += `[${obj.map(b => JSONtoJS(b, context, tableid, defcols)).join(',')}]`;

test/test1977.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
if (typeof exports === 'object') {
2+
var assert = require('assert');
3+
var alasql = require('..');
4+
}
5+
6+
describe('Test 1977 - BigInt support', function () {
7+
8+
it('A) Should sum, find max, min, average of BigInt values, and calculate TOTAL', function () {
9+
var data = [
10+
{a: 9045645645644442n}, {a: 9147483647334432n}, {a: 20n}, {a : 45875651254783254n}
11+
];
12+
13+
var res = alasql(
14+
`SELECT SUM(a) AS sum_a,
15+
MAX(a) AS max_a,
16+
MIN(a) AS min_a,
17+
AVG(a) AS avg_a,
18+
TOTAL(a) AS total_a
19+
FROM ?`,
20+
[data]
21+
);
22+
23+
assert.deepEqual(res, [{
24+
sum_a: 64068780547762148n,
25+
max_a: 45875651254783254n,
26+
min_a: 20n,
27+
avg_a: 16017195136940537n,
28+
total_a: 64068780547762148n,
29+
}]);
30+
});
31+
32+
it('B) Aggregate functions with mixed Number and BigInt types', function () {
33+
var data = [
34+
{a: 1}, {a: 2}, {a: 3}, {a: 4}, {a: 9147483647334432n}
35+
];
36+
37+
var res = alasql(
38+
`SELECT SUM(a) AS sum_a,
39+
MAX(a) AS max_a,
40+
MIN(a) AS min_a,
41+
AVG(a) AS avg_a,
42+
TOTAL(a) AS total_a
43+
FROM ?`,
44+
[data]
45+
);
46+
47+
assert.deepEqual(res, [{
48+
sum_a: 9147483647334442n,
49+
max_a: 9147483647334432n,
50+
min_a: 1n,
51+
avg_a: 1829496729466888n,
52+
total_a: 9147483647334442n,
53+
}]);
54+
});
55+
56+
it('C) Aggregate functions with negative BigInt values', function () {
57+
var data = [
58+
{a: -9045645645644442n},
59+
{a: -9147483647334432n}
60+
];
61+
62+
var res = alasql(
63+
`SELECT SUM(a) AS sum_a,
64+
MAX(a) AS max_a,
65+
MIN(a) AS min_a,
66+
AVG(a) AS avg_a,
67+
TOTAL(a) AS total_a
68+
FROM ?`,
69+
[data]
70+
);
71+
72+
assert.deepEqual(res, [{
73+
sum_a: -18193129292978874n,
74+
max_a: -9045645645644442n,
75+
min_a: -9147483647334432n,
76+
avg_a: -9096564646489437n,
77+
total_a: -18193129292978874n,
78+
}]);
79+
});
80+
81+
it('D) Aggregate functions with large BigInt values', function () {
82+
var data = [
83+
{a: BigInt('123456789012345678901234567890')},
84+
{a: BigInt('987654321098765432109876543210')}
85+
];
86+
87+
var res = alasql(
88+
`SELECT SUM(a) AS sum_a,
89+
MAX(a) AS max_a,
90+
MIN(a) AS min_a,
91+
AVG(a) AS avg_a,
92+
TOTAL(a) AS total_a
93+
FROM ?`,
94+
[data]
95+
);
96+
97+
assert.deepEqual(res, [{
98+
sum_a: BigInt('1111111110111111111011111111100'),
99+
max_a: BigInt('987654321098765432109876543210'),
100+
min_a: BigInt('123456789012345678901234567890'),
101+
avg_a: BigInt('555555555055555555505555555550'),
102+
total_a: BigInt('1111111110111111111011111111100'),
103+
}]);
104+
});
105+
106+
it('E) Aggregate functions with zero sum (positive and negative BigInt)', function () {
107+
var data = [
108+
{a: 12345678901234567890n},
109+
{a: -12345678901234567890n}
110+
];
111+
112+
var res = alasql(
113+
`SELECT SUM(a) AS sum_a,
114+
MAX(a) AS max_a,
115+
MIN(a) AS min_a,
116+
AVG(a) AS avg_a,
117+
TOTAL(a) AS total_a
118+
FROM ?`,
119+
[data]
120+
);
121+
122+
assert.deepEqual(res, [{
123+
sum_a: 0n,
124+
max_a: 12345678901234567890n,
125+
min_a: -12345678901234567890n,
126+
avg_a: 0n,
127+
total_a: 0n,
128+
}]);
129+
});
130+
});

0 commit comments

Comments
 (0)