Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
- _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._
- _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._

- _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)._
- _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)._

[![CI-test](https://github.com/alasql/alasql/workflows/CI%20build%20&%20test/badge.svg)](https://github.com/alasql/alasql/actions)
[![NPM downloads](http://img.shields.io/npm/dm/alasql.svg?style=flat&label=npm%20downloads)](https://npm-stat.com/charts.html?package=alasql)
Expand Down
58 changes: 29 additions & 29 deletions src/423groupby.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ yy.Select.prototype.compileGroup = function (query) {
if (col.aggregatorid === 'SUM') {
if ('funcid' in col.expression) {
let colexp1 = colExpIfFunIdExists(col.expression);
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? null : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : null)),`;
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? undefined : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : undefined)),`;
}
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? null : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : null)),`;
return `'${colas}':(__alasql_tmp = ${colexp}, (__alasql_tmp instanceof Date) ? undefined : ((__alasql_tmp || typeof __alasql_tmp == 'number') ? __alasql_tmp : undefined)),`;
} else if (col.aggregatorid === 'TOTAL') {
if ('funcid' in col.expression) {
let colexp1 = colExpIfFunIdExists(col.expression);
Expand All @@ -117,16 +117,16 @@ yy.Select.prototype.compileGroup = function (query) {
if ('funcid' in col.expression) {
let colexp1 = colExpIfFunIdExists(col.expression);

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),`;
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),`;
}
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),`;
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),`;
} else if (col.aggregatorid === 'MAX') {
if ('funcid' in col.expression) {
let colexp1 = colExpIfFunIdExists(col.expression);
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),`;
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),`;
}
return `'${colas}' : (typeof ${colexp} == 'number' || typeof ${colexp} == 'bigint' ? ${colexp} : typeof ${colexp} == 'object' ?
typeof Number(${colexp}) == 'number' ? ${colexp} : null : null),`;
typeof Number(${colexp}) == 'number' ? ${colexp} : undefined : undefined),`;
} else if (col.aggregatorid === 'ARRAY') {
return `'${colas}':[${colexp}],`;
} else if (col.aggregatorid === 'COUNT') {
Expand All @@ -139,7 +139,7 @@ yy.Select.prototype.compileGroup = function (query) {
query.removeKeys.push(`_SUM_${colas}`);
query.removeKeys.push(`_COUNT_${colas}`);

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,`;
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,`;
} else if (col.aggregatorid === 'AGGR') {
aft += `,g['${colas}']=${col.expression.toJS('g', -1)}`;
return '';
Expand Down Expand Up @@ -183,20 +183,20 @@ yy.Select.prototype.compileGroup = function (query) {
const __colexp1 = ${colexp1};

if (__g_colas == null && ${colexp1} == null) {
g['${colas}'] = null;
g['${colas}'] = undefined;
} 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;
g['${colas}'] = undefined;
} else if ((typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp1 == 'number') ||
(__g_colas == null && __typeof_colexp1 == 'number')) {
g['${colas}'] = ${colexp};
} else if (typeof __g_colas == 'number' && ${colexp1} == null) {
g['${colas}'] = __g_colas;
} else if (__g_colas instanceof Date || __colexp1 instanceof Date) {
// Date objects cause string concatenation with +=, return null instead
g['${colas}'] = null;
// Date objects cause string concatenation with +=, return undefined instead
g['${colas}'] = undefined;
} else {
g['${colas}'] += ${colexp} || 0;
}
Expand All @@ -214,26 +214,26 @@ yy.Select.prototype.compileGroup = function (query) {
const __colexp = ${colexp};

if (__g_colas == null && ${colexp} == null) {
g['${colas}'] = null;
g['${colas}'] = undefined;
} 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') {
g['${colas}'] = ${colexp};
} else if (typeof __g_colas == 'number' && ${colexp} == null) {
g['${colas}'] = __g_colas;
} else if (__g_colas == null && __typeof_colexp == 'number') {
g['${colas}'] = ${colexp};
} else if (__g_colas instanceof Date || __colexp instanceof Date) {
// Date objects cause string concatenation with +=, return null instead
g['${colas}'] = null;
} else {
g['${colas}'] += ${colexp} || 0;
}
g['${colas}'] = undefined;
} else if (typeof __g_colas !== 'object' && typeof __g_colas !== 'number' && __typeof_colexp == 'number') {
g['${colas}'] = ${colexp};
} else if (typeof __g_colas == 'number' && ${colexp} == null) {
g['${colas}'] = __g_colas;
} else if (__g_colas == null && __typeof_colexp == 'number') {
g['${colas}'] = ${colexp};
} else if (__g_colas instanceof Date || __colexp instanceof Date) {
// Date objects cause string concatenation with +=, return undefined instead
g['${colas}'] = undefined;
} else {
g['${colas}'] += ${colexp} || 0;
}
` +
}
` +
post
);
} else if (col.aggregatorid === 'TOTAL') {
Expand Down Expand Up @@ -398,9 +398,9 @@ yy.Select.prototype.compileGroup = function (query) {
y= (${colexp});
g['_COUNT_${colas}'] += (typeof y == "undefined" || y === null) ? 0 : 1;
if (y instanceof Date || (g['_SUM_${colas}'] && g['_SUM_${colas}'] instanceof Date)) {
// AVG on Date objects doesn't make semantic sense - return null
g['_SUM_${colas}'] = null;
g['${colas}'] = null;
// AVG on Date objects doesn't make semantic sense - return undefined
g['_SUM_${colas}'] = undefined;
g['${colas}'] = undefined;
} else if (typeof g['_SUM_${colas}'] === 'bigint' || typeof y === 'bigint') {
g['_SUM_${colas}'] = BigInt(g['_SUM_${colas}']);
g['_SUM_${colas}'] += BigInt(y || 0);
Expand Down
4 changes: 2 additions & 2 deletions src/55functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ stdlib.ROUND = function (s, d) {
return (
'(__alasql_tmp = (' +
s +
'), (__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,(' +
'), (__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,(' +
d +
')))/Math.pow(10,(' +
d +
Expand All @@ -220,7 +220,7 @@ stdlib.ROUND = function (s, d) {
return (
'(__alasql_tmp = (' +
s +
'), (__alasql_tmp == null || (typeof __alasql_tmp === "string" && __alasql_tmp.trim() === "")) ? null : ((__alasql_tmp = Number(__alasql_tmp)), isNaN(__alasql_tmp) ? null : Math.round(__alasql_tmp)))'
'), (__alasql_tmp == null || (typeof __alasql_tmp === "string" && __alasql_tmp.trim() === "")) ? undefined : ((__alasql_tmp = Number(__alasql_tmp)), isNaN(__alasql_tmp) ? undefined : Math.round(__alasql_tmp)))'
);
}
};
Expand Down
20 changes: 10 additions & 10 deletions test/test2147.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,28 +93,28 @@ describe('Test 2147 - Aggregate functions on DATETIME', function () {
done();
});

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

var expected = [
{id: 1, sumTimestamps: null},
{id: 2, sumTimestamps: null},
{id: 3, sumTimestamps: null},
{id: 1, sumTimestamps: undefined},
{id: 2, sumTimestamps: undefined},
{id: 3, sumTimestamps: undefined},
];

assert.deepEqual(res, expected);
done();
});

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

var expected = [
{id: 1, avgTimestamp: null},
{id: 2, avgTimestamp: null},
{id: 3, avgTimestamp: null},
{id: 1, avgTimestamp: undefined},
{id: 2, avgTimestamp: undefined},
{id: 3, avgTimestamp: undefined},
];

assert.deepEqual(res, expected);
Expand Down
42 changes: 25 additions & 17 deletions test/test2155.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
describe.skip('Test 2155 - ROUND should return null for null input', function () {
it('ROUND(null) should return null, not undefined or 0', function (done) {
var alasql = require('../dist/alasql.js');
alasql.options.errorlog = true;
var assert = require('assert');

describe('Test 2155 - ROUND should return undefined for null input', function () {
it('ROUND(null) should return undefined per AlaSQL NULL definition', function (done) {
var res = alasql('SELECT ROUND(null) as r FROM ?', [[{id: 1}]]);

assert.strictEqual(res[0].r, null, 'ROUND(null) should return null');
assert.strictEqual(res[0].r, undefined, 'ROUND(null) should return undefined');
done();
});

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

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

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

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

assert.strictEqual(res[0].r, null, 'ROUND("") should return null for empty string');
assert.strictEqual(res[0].r, undefined, 'ROUND("") should return undefined for empty string');
done();
});

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

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

assert.strictEqual(res[0].r, null, 'ROUND("null") should return null for string "null"');
assert.strictEqual(
res[0].r,
undefined,
'ROUND("null") should return undefined for string "null"'
);
done();
});

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

assert.strictEqual(res[0].r, null, 'ROUND(" ") should return null for whitespace');
assert.strictEqual(res[0].r, undefined, 'ROUND(" ") should return undefined for whitespace');
done();
});

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

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

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

assert.strictEqual(res[0].sum_a, null, 'SUM of all ROUND(null) should be null');
assert.strictEqual(res[0].sum_a, undefined, 'SUM of all ROUND(null) should be undefined');
done();
});

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

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

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

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

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

assert.strictEqual(res[0].sum_e, null, 'SUM of all ROUND(string) should be null');
assert.strictEqual(res[0].sum_e, undefined, 'SUM of all ROUND(string) should be undefined');
done();
});
});