Skip to content

Commit 916ff64

Browse files
authored
Let ROUND of NULL be undefined to fix #2155
1 parent 2cf0875 commit 916ff64

File tree

5 files changed

+68
-60
lines changed

5 files changed

+68
-60
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
- _AlaSQL is an unfunded open source project installed 500k+ times each month. [Please donate your time](https://github.com/AlaSQL/alasql/issues?q=is%3Aopen+label%3A%22Help+wanted%22+sort%3Aupdated-desc). We appreciate any and all contributions we can get._
1+
- _AlaSQL is an unfunded open source project installed 650k+ times each month. [Please donate your time](https://github.com/AlaSQL/alasql/issues?q=is%3Aopen+label%3A%22Help+wanted%22+sort%3Aupdated-desc). We appreciate any and all contributions we can get._
22

3-
- _Have a question? [Ask the AlaSQL bot](https://chat.openai.com/g/g-XcBL24WTe-alasql-bot) or post on [Stack Overflow](http://stackoverflow.com/questions/ask?tags=AlaSQL)._
3+
- _Have a question? [Ask The AlaSQL Bot](https://chatgpt.com/g/g-XcBL24WTe-alasql-bot) or post on [Stack Overflow](http://stackoverflow.com/questions/ask?tags=AlaSQL)._
44

55
[![CI-test](https://github.com/alasql/alasql/workflows/CI%20build%20&%20test/badge.svg)](https://github.com/alasql/alasql/actions)
66
[![NPM downloads](http://img.shields.io/npm/dm/alasql.svg?style=flat&label=npm%20downloads)](https://npm-stat.com/charts.html?package=alasql)

src/423groupby.js

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ yy.Select.prototype.compileGroup = function (query) {
9898
if (col.aggregatorid === 'SUM') {
9999
if ('funcid' in col.expression) {
100100
let colexp1 = colExpIfFunIdExists(col.expression);
101-
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? null : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : null)),`;
101+
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? undefined : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : undefined)),`;
102102
}
103-
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? null : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : null)),`;
103+
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? undefined : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : undefined)),`;
104104
} else if (col.aggregatorid === 'TOTAL') {
105105
if ('funcid' in col.expression) {
106106
let colexp1 = colExpIfFunIdExists(col.expression);
@@ -117,16 +117,16 @@ yy.Select.prototype.compileGroup = function (query) {
117117
if ('funcid' in col.expression) {
118118
let colexp1 = colExpIfFunIdExists(col.expression);
119119

120-
return `'${colas}': (__alasql_tmp = ${colexp}, typeof __alasql_tmp == 'number' || typeof __alasql_tmp == 'bigint' || (typeof __alasql_tmp == 'object' && (typeof Number(__alasql_tmp) == 'number' || __alasql_tmp instanceof Date)) ? __alasql_tmp : null),`;
120+
return `'${colas}': (__alasql_tmp = ${colexp}, typeof __alasql_tmp == 'number' || typeof __alasql_tmp == 'bigint' || (typeof __alasql_tmp == 'object' && (typeof Number(__alasql_tmp) == 'number' || __alasql_tmp instanceof Date)) ? __alasql_tmp : undefined),`;
121121
}
122-
return `'${colas}': (__alasql_tmp = ${colexp}, typeof __alasql_tmp == 'number' || typeof __alasql_tmp == 'bigint' || (typeof __alasql_tmp == 'object' && (typeof Number(__alasql_tmp) == 'number' || __alasql_tmp instanceof Date)) ? __alasql_tmp : null),`;
122+
return `'${colas}': (__alasql_tmp = ${colexp}, typeof __alasql_tmp == 'number' || typeof __alasql_tmp == 'bigint' || (typeof __alasql_tmp == 'object' && (typeof Number(__alasql_tmp) == 'number' || __alasql_tmp instanceof Date)) ? __alasql_tmp : undefined),`;
123123
} else if (col.aggregatorid === 'MAX') {
124124
if ('funcid' in col.expression) {
125125
let colexp1 = colExpIfFunIdExists(col.expression);
126-
return `'${colas}': (__alasql_tmp = ${colexp}, typeof __alasql_tmp == 'number' || typeof __alasql_tmp == 'bigint' || (typeof __alasql_tmp == 'object' && (typeof Number(__alasql_tmp) == 'number' || __alasql_tmp instanceof Date)) ? __alasql_tmp : null),`;
126+
return `'${colas}': (__alasql_tmp = ${colexp}, typeof __alasql_tmp == 'number' || typeof __alasql_tmp == 'bigint' || (typeof __alasql_tmp == 'object' && (typeof Number(__alasql_tmp) == 'number' || __alasql_tmp instanceof Date)) ? __alasql_tmp : undefined),`;
127127
}
128128
return `'${colas}' : (typeof ${colexp} == 'number' || typeof ${colexp} == 'bigint' ? ${colexp} : typeof ${colexp} == 'object' ?
129-
typeof Number(${colexp}) == 'number' ? ${colexp} : null : null),`;
129+
typeof Number(${colexp}) == 'number' ? ${colexp} : undefined : undefined),`;
130130
} else if (col.aggregatorid === 'ARRAY') {
131131
return `'${colas}':[${colexp}],`;
132132
} else if (col.aggregatorid === 'COUNT') {
@@ -139,7 +139,7 @@ yy.Select.prototype.compileGroup = function (query) {
139139
query.removeKeys.push(`_SUM_${colas}`);
140140
query.removeKeys.push(`_COUNT_${colas}`);
141141

142-
return `'${colas}':(function() { var t = ${colexp}; return (t instanceof Date) ? null : t; })(),'_SUM_${colas}':(function() { var t = ${colexp}; return (t instanceof Date) ? null : (t || 0); })(),'_COUNT_${colas}':(typeof ${colexp} == "undefined" || ${colexp} === null) ? 0 : 1,`;
142+
return `'${colas}':(function() { var t = ${colexp}; return (t instanceof Date) ? undefined : t; })(),'_SUM_${colas}':(function() { var t = ${colexp}; return (t instanceof Date) ? undefined : (t || 0); })(),'_COUNT_${colas}':(typeof ${colexp} == "undefined" || ${colexp} === null) ? 0 : 1,`;
143143
} else if (col.aggregatorid === 'AGGR') {
144144
aft += `,g['${colas}']=${col.expression.toJS('g', -1)}`;
145145
return '';
@@ -183,20 +183,20 @@ yy.Select.prototype.compileGroup = function (query) {
183183
const __colexp1 = ${colexp1};
184184
185185
if (__g_colas == null && ${colexp1} == null) {
186-
g['${colas}'] = null;
186+
g['${colas}'] = undefined;
187187
} else if (typeof __g_colas === 'bigint' || typeof __colexp1 === 'bigint') {
188188
g['${colas}'] = BigInt(__g_colas) + BigInt(__colexp);
189189
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 !== 'object' && __typeof_colexp1 !== 'number') ||
190190
(__g_colas == null || (typeof __g_colas !== 'number' && typeof __g_colas !== 'object')) && (${colexp1} == null || (__typeof_colexp1 !== 'number' && __typeof_colexp1 !== 'object'))) {
191-
g['${colas}'] = null;
191+
g['${colas}'] = undefined;
192192
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 == 'number') ||
193193
(__g_colas == null && __typeof_colexp1 == 'number')) {
194194
g['${colas}'] = ${colexp};
195195
} else if (typeof __g_colas == 'number' && ${colexp1} == null) {
196196
g['${colas}'] = __g_colas;
197197
} else if (__g_colas instanceof Date || __colexp1 instanceof Date) {
198-
// Date objects cause string concatenation with +=, return null instead
199-
g['${colas}'] = null;
198+
// Date objects cause string concatenation with +=, return undefined instead
199+
g['${colas}'] = undefined;
200200
} else {
201201
g['${colas}'] += ${colexp} || 0;
202202
}
@@ -214,26 +214,26 @@ yy.Select.prototype.compileGroup = function (query) {
214214
const __colexp = ${colexp};
215215
216216
if (__g_colas == null && ${colexp} == null) {
217-
g['${colas}'] = null;
217+
g['${colas}'] = undefined;
218218
} else if (typeof __g_colas === 'bigint' || typeof __colexp === 'bigint') {
219219
g['${colas}'] = BigInt(__g_colas) + BigInt(__colexp);
220220
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp !== 'object' && __typeof_colexp !== 'number') ||
221221
(__g_colas == null || (typeof __g_colas !== 'number' && typeof __g_colas !== 'object')) && (${colexp} == null || (__typeof_colexp !== 'number' && __typeof_colexp !== 'object'))) {
222-
g['${colas}'] = null;
223-
} else if (typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp == 'number') {
224-
g['${colas}'] = ${colexp};
225-
} else if (typeof __g_colas == 'number' && ${colexp} == null) {
226-
g['${colas}'] = __g_colas;
227-
} else if (__g_colas == null && __typeof_colexp == 'number') {
228-
g['${colas}'] = ${colexp};
229-
} else if (__g_colas instanceof Date || __colexp instanceof Date) {
230-
// Date objects cause string concatenation with +=, return null instead
231-
g['${colas}'] = null;
232-
} else {
233-
g['${colas}'] += ${colexp} || 0;
234-
}
222+
g['${colas}'] = undefined;
223+
} else if (typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp == 'number') {
224+
g['${colas}'] = ${colexp};
225+
} else if (typeof __g_colas == 'number' && ${colexp} == null) {
226+
g['${colas}'] = __g_colas;
227+
} else if (__g_colas == null && __typeof_colexp == 'number') {
228+
g['${colas}'] = ${colexp};
229+
} else if (__g_colas instanceof Date || __colexp instanceof Date) {
230+
// Date objects cause string concatenation with +=, return undefined instead
231+
g['${colas}'] = undefined;
232+
} else {
233+
g['${colas}'] += ${colexp} || 0;
235234
}
236-
` +
235+
}
236+
` +
237237
post
238238
);
239239
} else if (col.aggregatorid === 'TOTAL') {
@@ -398,9 +398,9 @@ yy.Select.prototype.compileGroup = function (query) {
398398
y= (${colexp});
399399
g['_COUNT_${colas}'] += (typeof y == "undefined" || y === null) ? 0 : 1;
400400
if (y instanceof Date || (g['_SUM_${colas}'] && g['_SUM_${colas}'] instanceof Date)) {
401-
// AVG on Date objects doesn't make semantic sense - return null
402-
g['_SUM_${colas}'] = null;
403-
g['${colas}'] = null;
401+
// AVG on Date objects doesn't make semantic sense - return undefined
402+
g['_SUM_${colas}'] = undefined;
403+
g['${colas}'] = undefined;
404404
} else if (typeof g['_SUM_${colas}'] === 'bigint' || typeof y === 'bigint') {
405405
g['_SUM_${colas}'] = BigInt(g['_SUM_${colas}']);
406406
g['_SUM_${colas}'] += BigInt(y || 0);

src/55functions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ stdlib.ROUND = function (s, d) {
210210
return (
211211
'(__alasql_tmp = (' +
212212
s +
213-
'), (__alasql_tmp == null || (typeof __alasql_tmp === "string" && __alasql_tmp.trim() === "")) ? null : ((__alasql_tmp = Number(__alasql_tmp)), isNaN(__alasql_tmp) ? null : Math.round(__alasql_tmp*Math.pow(10,(' +
213+
'), (__alasql_tmp == null || (typeof __alasql_tmp === "string" && __alasql_tmp.trim() === "")) ? undefined : ((__alasql_tmp = Number(__alasql_tmp)), isNaN(__alasql_tmp) ? undefined : Math.round(__alasql_tmp*Math.pow(10,(' +
214214
d +
215215
')))/Math.pow(10,(' +
216216
d +
@@ -220,7 +220,7 @@ stdlib.ROUND = function (s, d) {
220220
return (
221221
'(__alasql_tmp = (' +
222222
s +
223-
'), (__alasql_tmp == null || (typeof __alasql_tmp === "string" && __alasql_tmp.trim() === "")) ? null : ((__alasql_tmp = Number(__alasql_tmp)), isNaN(__alasql_tmp) ? null : Math.round(__alasql_tmp)))'
223+
'), (__alasql_tmp == null || (typeof __alasql_tmp === "string" && __alasql_tmp.trim() === "")) ? undefined : ((__alasql_tmp = Number(__alasql_tmp)), isNaN(__alasql_tmp) ? undefined : Math.round(__alasql_tmp)))'
224224
);
225225
}
226226
};

test/test2147.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,28 +93,28 @@ describe('Test 2147 - Aggregate functions on DATETIME', function () {
9393
done();
9494
});
9595

96-
it('SUM on DATETIME - returns null for semantic correctness', function (done) {
97-
// SUM on Date objects doesn't make semantic sense, so it returns null
96+
it('SUM on DATETIME - returns undefined for semantic correctness', function (done) {
97+
// SUM on Date objects doesn't make semantic sense, so it returns undefined
9898
var res = alasql('SELECT id, SUM(DATETIME(date)) as sumTimestamps FROM ? GROUP BY id;', [data]);
9999

100100
var expected = [
101-
{id: 1, sumTimestamps: null},
102-
{id: 2, sumTimestamps: null},
103-
{id: 3, sumTimestamps: null},
101+
{id: 1, sumTimestamps: undefined},
102+
{id: 2, sumTimestamps: undefined},
103+
{id: 3, sumTimestamps: undefined},
104104
];
105105

106106
assert.deepEqual(res, expected);
107107
done();
108108
});
109109

110-
it('AVG on DATETIME - returns null for semantic correctness', function (done) {
111-
// AVG on Date objects doesn't make semantic sense, so it returns null
110+
it('AVG on DATETIME - returns undefined for semantic correctness', function (done) {
111+
// AVG on Date objects doesn't make semantic sense, so it returns undefined
112112
var res = alasql('SELECT id, AVG(DATETIME(date)) as avgTimestamp FROM ? GROUP BY id;', [data]);
113113

114114
var expected = [
115-
{id: 1, avgTimestamp: null},
116-
{id: 2, avgTimestamp: null},
117-
{id: 3, avgTimestamp: null},
115+
{id: 1, avgTimestamp: undefined},
116+
{id: 2, avgTimestamp: undefined},
117+
{id: 3, avgTimestamp: undefined},
118118
];
119119

120120
assert.deepEqual(res, expected);

test/test2155.js

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
describe.skip('Test 2155 - ROUND should return null for null input', function () {
2-
it('ROUND(null) should return null, not undefined or 0', function (done) {
1+
var alasql = require('../dist/alasql.js');
2+
alasql.options.errorlog = true;
3+
var assert = require('assert');
4+
5+
describe('Test 2155 - ROUND should return undefined for null input', function () {
6+
it('ROUND(null) should return undefined per AlaSQL NULL definition', function (done) {
37
var res = alasql('SELECT ROUND(null) as r FROM ?', [[{id: 1}]]);
48

5-
assert.strictEqual(res[0].r, null, 'ROUND(null) should return null');
9+
assert.strictEqual(res[0].r, undefined, 'ROUND(null) should return undefined');
610
done();
711
});
812

@@ -13,17 +17,17 @@ describe.skip('Test 2155 - ROUND should return null for null input', function ()
1317
done();
1418
});
1519

16-
it('ROUND("abc") should return null', function (done) {
20+
it('ROUND("abc") should return undefined', function (done) {
1721
var res = alasql('SELECT ROUND(?) as r', ['abc']);
1822

19-
assert.strictEqual(res[0].r, null, 'ROUND("abc") should return null for non-numeric');
23+
assert.strictEqual(res[0].r, undefined, 'ROUND("abc") should return undefined for non-numeric');
2024
done();
2125
});
2226

23-
it('ROUND("") should return null', function (done) {
27+
it('ROUND("") should return undefined', function (done) {
2428
var res = alasql('SELECT ROUND(?) as r', ['']);
2529

26-
assert.strictEqual(res[0].r, null, 'ROUND("") should return null for empty string');
30+
assert.strictEqual(res[0].r, undefined, 'ROUND("") should return undefined for empty string');
2731
done();
2832
});
2933

@@ -34,17 +38,21 @@ describe.skip('Test 2155 - ROUND should return null for null input', function ()
3438
done();
3539
});
3640

37-
it('ROUND("null") should return null', function (done) {
41+
it('ROUND("null") should return undefined', function (done) {
3842
var res = alasql('SELECT ROUND(?) as r', ['null']);
3943

40-
assert.strictEqual(res[0].r, null, 'ROUND("null") should return null for string "null"');
44+
assert.strictEqual(
45+
res[0].r,
46+
undefined,
47+
'ROUND("null") should return undefined for string "null"'
48+
);
4149
done();
4250
});
4351

44-
it('ROUND(" ") should return null', function (done) {
52+
it('ROUND(" ") should return undefined', function (done) {
4553
var res = alasql('SELECT ROUND(?) as r', [' ']);
4654

47-
assert.strictEqual(res[0].r, null, 'ROUND(" ") should return null for whitespace');
55+
assert.strictEqual(res[0].r, undefined, 'ROUND(" ") should return undefined for whitespace');
4856
done();
4957
});
5058

@@ -69,12 +77,12 @@ describe.skip('Test 2155 - ROUND should return null for null input', function ()
6977
done();
7078
});
7179

72-
it('SUM(ROUND(null)) should return null when all values are null', function (done) {
80+
it('SUM(ROUND(null)) should return undefined when all values are null', function (done) {
7381
var data = [{a: null}, {a: null}];
7482

7583
var res = alasql('SELECT SUM(ROUND(a)) as sum_a FROM ?', [data]);
7684

77-
assert.strictEqual(res[0].sum_a, null, 'SUM of all ROUND(null) should be null');
85+
assert.strictEqual(res[0].sum_a, undefined, 'SUM of all ROUND(null) should be undefined');
7886
done();
7987
});
8088

@@ -87,19 +95,19 @@ describe.skip('Test 2155 - ROUND should return null for null input', function ()
8795
done();
8896
});
8997

90-
it('ROUND(string) should return null', function (done) {
98+
it('ROUND(string) should return undefined', function (done) {
9199
var res = alasql('SELECT ROUND(?) as r', ['XYZ']);
92100

93-
assert.strictEqual(res[0].r, null, 'ROUND of non-numeric string should return null');
101+
assert.strictEqual(res[0].r, undefined, 'ROUND of non-numeric string should return undefined');
94102
done();
95103
});
96104

97-
it('SUM(ROUND(string)) should return null when all values are strings', function (done) {
105+
it('SUM(ROUND(string)) should return undefined when all values are strings', function (done) {
98106
var data = [{e: 'XYZ1'}, {e: 'XYZ2'}];
99107

100108
var res = alasql('SELECT SUM(ROUND(e)) as sum_e FROM ?', [data]);
101109

102-
assert.strictEqual(res[0].sum_e, null, 'SUM of all ROUND(string) should be null');
110+
assert.strictEqual(res[0].sum_e, undefined, 'SUM of all ROUND(string) should be undefined');
103111
done();
104112
});
105113
});

0 commit comments

Comments
 (0)