PG: Fixes issues comparing to zero or false (#4667)
* initial test * more test * clean up * added correct errors * test fix * $eq null test * type check
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
const pkg = require('./package.json');
|
const pkg = require('./package.json');
|
||||||
|
|
||||||
const version = parseFloat( process.version.substr(1) );
|
const version = parseFloat(process.version.substr(1));
|
||||||
const minimum = parseFloat( pkg.engines.node.match(/\d+/g).join('.') );
|
const minimum = parseFloat(pkg.engines.node.match(/\d+/g).join('.'));
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
const openCollective = `
|
const openCollective = `
|
||||||
@@ -47,4 +47,4 @@ module.exports = function () {
|
|||||||
|
|
||||||
process.stdout.write(errorMessage);
|
process.stdout.write(errorMessage);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,18 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Parse = require('parse/node');
|
const Parse = require('parse/node');
|
||||||
|
const rp = require('request-promise');
|
||||||
|
|
||||||
|
const masterKeyHeaders = {
|
||||||
|
'X-Parse-Application-Id': 'test',
|
||||||
|
'X-Parse-Rest-API-Key': 'test',
|
||||||
|
'X-Parse-Master-Key': 'test'
|
||||||
|
}
|
||||||
|
|
||||||
|
const masterKeyOptions = {
|
||||||
|
headers: masterKeyHeaders,
|
||||||
|
json: true
|
||||||
|
}
|
||||||
|
|
||||||
describe('Parse.Query testing', () => {
|
describe('Parse.Query testing', () => {
|
||||||
it("basic query", function(done) {
|
it("basic query", function(done) {
|
||||||
@@ -570,6 +582,38 @@ describe('Parse.Query testing', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("lessThan zero queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.lessThan('number', 0);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lessThanOrEqualTo zero queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.lessThanOrEqualTo('number', 0);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 4);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("greaterThan queries", function(done) {
|
it("greaterThan queries", function(done) {
|
||||||
const makeBoxedNumber = function(i) {
|
const makeBoxedNumber = function(i) {
|
||||||
return new BoxedNumber({ number: i });
|
return new BoxedNumber({ number: i });
|
||||||
@@ -606,6 +650,38 @@ describe('Parse.Query testing', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("greaterThan zero queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.greaterThan('number', 0);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("greaterThanOrEqualTo zero queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.greaterThanOrEqualTo('number', 0);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("lessThanOrEqualTo greaterThanOrEqualTo queries", function(done) {
|
it("lessThanOrEqualTo greaterThanOrEqualTo queries", function(done) {
|
||||||
const makeBoxedNumber = function(i) {
|
const makeBoxedNumber = function(i) {
|
||||||
return new BoxedNumber({ number: i });
|
return new BoxedNumber({ number: i });
|
||||||
@@ -662,6 +738,101 @@ describe('Parse.Query testing', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("notEqualTo zero queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.notEqualTo('number', 0);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 4);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("equalTo zero queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.equalTo('number', 0);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("number equalTo boolean queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.equalTo('number', false);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("equalTo false queries", (done) => {
|
||||||
|
const obj1 = new TestObject({ field: false });
|
||||||
|
const obj2 = new TestObject({ field: true });
|
||||||
|
Parse.Object.saveAll([obj1, obj2]).then(() => {
|
||||||
|
const query = new Parse.Query(TestObject);
|
||||||
|
query.equalTo('field', false);
|
||||||
|
return query.find();
|
||||||
|
}).then((results) => {
|
||||||
|
equal(results.length, 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("where $eq false queries (rest)", (done) => {
|
||||||
|
const options = Object.assign({}, masterKeyOptions, {
|
||||||
|
body: {
|
||||||
|
where: { field: { $eq: false } },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const obj1 = new TestObject({ field: false });
|
||||||
|
const obj2 = new TestObject({ field: true });
|
||||||
|
Parse.Object.saveAll([obj1, obj2]).then(() => {
|
||||||
|
rp.get(Parse.serverURL + '/classes/TestObject', options)
|
||||||
|
.then((resp) => {
|
||||||
|
equal(resp.results.length, 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it("where $eq null queries (rest)", (done) => {
|
||||||
|
const options = Object.assign({}, masterKeyOptions, {
|
||||||
|
body: {
|
||||||
|
where: { field: { $eq: null } },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const obj1 = new TestObject({ field: false });
|
||||||
|
const obj2 = new TestObject({ field: null });
|
||||||
|
Parse.Object.saveAll([obj1, obj2]).then(() => {
|
||||||
|
rp.get(Parse.serverURL + '/classes/TestObject', options)
|
||||||
|
.then((resp) => {
|
||||||
|
equal(resp.results.length, 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it("containedIn queries", function(done) {
|
it("containedIn queries", function(done) {
|
||||||
const makeBoxedNumber = function(i) {
|
const makeBoxedNumber = function(i) {
|
||||||
return new BoxedNumber({ number: i });
|
return new BoxedNumber({ number: i });
|
||||||
@@ -680,6 +851,40 @@ describe('Parse.Query testing', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("containedIn false queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.containedIn('number', false);
|
||||||
|
return query.find();
|
||||||
|
}).then(done.fail).catch((error) => {
|
||||||
|
equal(error.code, Parse.Error.INVALID_JSON);
|
||||||
|
equal(error.message, 'bad $in value');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("notContainedIn false queries", (done) => {
|
||||||
|
const makeBoxedNumber = (i) => {
|
||||||
|
return new BoxedNumber({ number: i });
|
||||||
|
};
|
||||||
|
const numbers = [-3, -2, -1, 0, 1];
|
||||||
|
const boxedNumbers = numbers.map(makeBoxedNumber);
|
||||||
|
Parse.Object.saveAll(boxedNumbers).then(() => {
|
||||||
|
const query = new Parse.Query(BoxedNumber);
|
||||||
|
query.notContainedIn('number', false);
|
||||||
|
return query.find();
|
||||||
|
}).then(done.fail).catch((error) => {
|
||||||
|
equal(error.code, Parse.Error.INVALID_JSON);
|
||||||
|
equal(error.message, 'bad $nin value');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("notContainedIn queries", function(done) {
|
it("notContainedIn queries", function(done) {
|
||||||
const makeBoxedNumber = function(i) {
|
const makeBoxedNumber = function(i) {
|
||||||
return new BoxedNumber({ number: i });
|
return new BoxedNumber({ number: i });
|
||||||
|
|||||||
@@ -287,7 +287,14 @@ const buildWhereClause = ({ schema, query, index }) => {
|
|||||||
index += 2;
|
index += 2;
|
||||||
} else if (typeof fieldValue === 'boolean') {
|
} else if (typeof fieldValue === 'boolean') {
|
||||||
patterns.push(`$${index}:name = $${index + 1}`);
|
patterns.push(`$${index}:name = $${index + 1}`);
|
||||||
values.push(fieldName, fieldValue);
|
// Can't cast boolean to double precision
|
||||||
|
if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Number') {
|
||||||
|
// Should always return zero results
|
||||||
|
const MAX_INT_PLUS_ONE = 9223372036854775808;
|
||||||
|
values.push(fieldName, MAX_INT_PLUS_ONE);
|
||||||
|
} else {
|
||||||
|
values.push(fieldName, fieldValue);
|
||||||
|
}
|
||||||
index += 2;
|
index += 2;
|
||||||
} else if (typeof fieldValue === 'number') {
|
} else if (typeof fieldValue === 'number') {
|
||||||
patterns.push(`$${index}:name = $${index + 1}`);
|
patterns.push(`$${index}:name = $${index + 1}`);
|
||||||
@@ -329,11 +336,16 @@ const buildWhereClause = ({ schema, query, index }) => {
|
|||||||
values.push(fieldName, fieldValue.$ne);
|
values.push(fieldName, fieldValue.$ne);
|
||||||
index += 2;
|
index += 2;
|
||||||
}
|
}
|
||||||
|
if (fieldValue.$eq !== undefined) {
|
||||||
if (fieldValue.$eq) {
|
if (fieldValue.$eq === null) {
|
||||||
patterns.push(`$${index}:name = $${index + 1}`);
|
patterns.push(`$${index}:name IS NULL`);
|
||||||
values.push(fieldName, fieldValue.$eq);
|
values.push(fieldName);
|
||||||
index += 2;
|
index += 1;
|
||||||
|
} else {
|
||||||
|
patterns.push(`$${index}:name = $${index + 1}`);
|
||||||
|
values.push(fieldName, fieldValue.$eq);
|
||||||
|
index += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const isInOrNin = Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
|
const isInOrNin = Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
|
||||||
if (Array.isArray(fieldValue.$in) &&
|
if (Array.isArray(fieldValue.$in) &&
|
||||||
@@ -393,6 +405,10 @@ const buildWhereClause = ({ schema, query, index }) => {
|
|||||||
if (fieldValue.$nin) {
|
if (fieldValue.$nin) {
|
||||||
createConstraint(_.flatMap(fieldValue.$nin, elt => elt), true);
|
createConstraint(_.flatMap(fieldValue.$nin, elt => elt), true);
|
||||||
}
|
}
|
||||||
|
} else if(typeof fieldValue.$in !== 'undefined') {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $in value');
|
||||||
|
} else if (typeof fieldValue.$nin !== 'undefined') {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $nin value');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(fieldValue.$all) && isArrayField) {
|
if (Array.isArray(fieldValue.$all) && isArrayField) {
|
||||||
@@ -578,7 +594,7 @@ const buildWhereClause = ({ schema, query, index }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
||||||
if (fieldValue[cmp]) {
|
if (fieldValue[cmp] || fieldValue[cmp] === 0) {
|
||||||
const pgComparator = ParseToPosgresComparator[cmp];
|
const pgComparator = ParseToPosgresComparator[cmp];
|
||||||
patterns.push(`$${index}:name ${pgComparator} $${index + 1}`);
|
patterns.push(`$${index}:name ${pgComparator} $${index + 1}`);
|
||||||
values.push(fieldName, toPostgresValue(fieldValue[cmp]));
|
values.push(fieldName, toPostgresValue(fieldValue[cmp]));
|
||||||
|
|||||||
Reference in New Issue
Block a user