Skip to content

Commit f37ed6e

Browse files
authored
PG: Fixes issues comparing to zero or false (parse-community#4667)
* initial test * more test * clean up * added correct errors * test fix * $eq null test * type check
1 parent a8e2a69 commit f37ed6e

File tree

3 files changed

+231
-10
lines changed

3 files changed

+231
-10
lines changed

postinstall.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const pkg = require('./package.json');
22

3-
const version = parseFloat( process.version.substr(1) );
4-
const minimum = parseFloat( pkg.engines.node.match(/\d+/g).join('.') );
3+
const version = parseFloat(process.version.substr(1));
4+
const minimum = parseFloat(pkg.engines.node.match(/\d+/g).join('.'));
55

66
module.exports = function () {
77
const openCollective = `
@@ -47,4 +47,4 @@ module.exports = function () {
4747

4848
process.stdout.write(errorMessage);
4949
process.exit(1);
50-
};
50+
};

spec/ParseQuery.spec.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
'use strict';
66

77
const Parse = require('parse/node');
8+
const rp = require('request-promise');
9+
10+
const masterKeyHeaders = {
11+
'X-Parse-Application-Id': 'test',
12+
'X-Parse-Rest-API-Key': 'test',
13+
'X-Parse-Master-Key': 'test'
14+
}
15+
16+
const masterKeyOptions = {
17+
headers: masterKeyHeaders,
18+
json: true
19+
}
820

921
describe('Parse.Query testing', () => {
1022
it("basic query", function(done) {
@@ -570,6 +582,38 @@ describe('Parse.Query testing', () => {
570582
});
571583
});
572584

585+
it("lessThan zero queries", (done) => {
586+
const makeBoxedNumber = (i) => {
587+
return new BoxedNumber({ number: i });
588+
};
589+
const numbers = [-3, -2, -1, 0, 1];
590+
const boxedNumbers = numbers.map(makeBoxedNumber);
591+
Parse.Object.saveAll(boxedNumbers).then(() => {
592+
const query = new Parse.Query(BoxedNumber);
593+
query.lessThan('number', 0);
594+
return query.find();
595+
}).then((results) => {
596+
equal(results.length, 3);
597+
done();
598+
});
599+
});
600+
601+
it("lessThanOrEqualTo zero queries", (done) => {
602+
const makeBoxedNumber = (i) => {
603+
return new BoxedNumber({ number: i });
604+
};
605+
const numbers = [-3, -2, -1, 0, 1];
606+
const boxedNumbers = numbers.map(makeBoxedNumber);
607+
Parse.Object.saveAll(boxedNumbers).then(() => {
608+
const query = new Parse.Query(BoxedNumber);
609+
query.lessThanOrEqualTo('number', 0);
610+
return query.find();
611+
}).then((results) => {
612+
equal(results.length, 4);
613+
done();
614+
});
615+
});
616+
573617
it("greaterThan queries", function(done) {
574618
const makeBoxedNumber = function(i) {
575619
return new BoxedNumber({ number: i });
@@ -606,6 +650,38 @@ describe('Parse.Query testing', () => {
606650
});
607651
});
608652

653+
it("greaterThan zero queries", (done) => {
654+
const makeBoxedNumber = (i) => {
655+
return new BoxedNumber({ number: i });
656+
};
657+
const numbers = [-3, -2, -1, 0, 1];
658+
const boxedNumbers = numbers.map(makeBoxedNumber);
659+
Parse.Object.saveAll(boxedNumbers).then(() => {
660+
const query = new Parse.Query(BoxedNumber);
661+
query.greaterThan('number', 0);
662+
return query.find();
663+
}).then((results) => {
664+
equal(results.length, 1);
665+
done();
666+
});
667+
});
668+
669+
it("greaterThanOrEqualTo zero queries", (done) => {
670+
const makeBoxedNumber = (i) => {
671+
return new BoxedNumber({ number: i });
672+
};
673+
const numbers = [-3, -2, -1, 0, 1];
674+
const boxedNumbers = numbers.map(makeBoxedNumber);
675+
Parse.Object.saveAll(boxedNumbers).then(() => {
676+
const query = new Parse.Query(BoxedNumber);
677+
query.greaterThanOrEqualTo('number', 0);
678+
return query.find();
679+
}).then((results) => {
680+
equal(results.length, 2);
681+
done();
682+
});
683+
});
684+
609685
it("lessThanOrEqualTo greaterThanOrEqualTo queries", function(done) {
610686
const makeBoxedNumber = function(i) {
611687
return new BoxedNumber({ number: i });
@@ -662,6 +738,101 @@ describe('Parse.Query testing', () => {
662738
});
663739
});
664740

741+
it("notEqualTo zero queries", (done) => {
742+
const makeBoxedNumber = (i) => {
743+
return new BoxedNumber({ number: i });
744+
};
745+
const numbers = [-3, -2, -1, 0, 1];
746+
const boxedNumbers = numbers.map(makeBoxedNumber);
747+
Parse.Object.saveAll(boxedNumbers).then(() => {
748+
const query = new Parse.Query(BoxedNumber);
749+
query.notEqualTo('number', 0);
750+
return query.find();
751+
}).then((results) => {
752+
equal(results.length, 4);
753+
done();
754+
});
755+
});
756+
757+
it("equalTo zero queries", (done) => {
758+
const makeBoxedNumber = (i) => {
759+
return new BoxedNumber({ number: i });
760+
};
761+
const numbers = [-3, -2, -1, 0, 1];
762+
const boxedNumbers = numbers.map(makeBoxedNumber);
763+
Parse.Object.saveAll(boxedNumbers).then(() => {
764+
const query = new Parse.Query(BoxedNumber);
765+
query.equalTo('number', 0);
766+
return query.find();
767+
}).then((results) => {
768+
equal(results.length, 1);
769+
done();
770+
});
771+
});
772+
773+
it("number equalTo boolean queries", (done) => {
774+
const makeBoxedNumber = (i) => {
775+
return new BoxedNumber({ number: i });
776+
};
777+
const numbers = [-3, -2, -1, 0, 1];
778+
const boxedNumbers = numbers.map(makeBoxedNumber);
779+
Parse.Object.saveAll(boxedNumbers).then(() => {
780+
const query = new Parse.Query(BoxedNumber);
781+
query.equalTo('number', false);
782+
return query.find();
783+
}).then((results) => {
784+
equal(results.length, 0);
785+
done();
786+
});
787+
});
788+
789+
it("equalTo false queries", (done) => {
790+
const obj1 = new TestObject({ field: false });
791+
const obj2 = new TestObject({ field: true });
792+
Parse.Object.saveAll([obj1, obj2]).then(() => {
793+
const query = new Parse.Query(TestObject);
794+
query.equalTo('field', false);
795+
return query.find();
796+
}).then((results) => {
797+
equal(results.length, 1);
798+
done();
799+
});
800+
});
801+
802+
it("where $eq false queries (rest)", (done) => {
803+
const options = Object.assign({}, masterKeyOptions, {
804+
body: {
805+
where: { field: { $eq: false } },
806+
}
807+
});
808+
const obj1 = new TestObject({ field: false });
809+
const obj2 = new TestObject({ field: true });
810+
Parse.Object.saveAll([obj1, obj2]).then(() => {
811+
rp.get(Parse.serverURL + '/classes/TestObject', options)
812+
.then((resp) => {
813+
equal(resp.results.length, 1);
814+
done();
815+
});
816+
})
817+
});
818+
819+
it("where $eq null queries (rest)", (done) => {
820+
const options = Object.assign({}, masterKeyOptions, {
821+
body: {
822+
where: { field: { $eq: null } },
823+
}
824+
});
825+
const obj1 = new TestObject({ field: false });
826+
const obj2 = new TestObject({ field: null });
827+
Parse.Object.saveAll([obj1, obj2]).then(() => {
828+
rp.get(Parse.serverURL + '/classes/TestObject', options)
829+
.then((resp) => {
830+
equal(resp.results.length, 1);
831+
done();
832+
});
833+
})
834+
});
835+
665836
it("containedIn queries", function(done) {
666837
const makeBoxedNumber = function(i) {
667838
return new BoxedNumber({ number: i });
@@ -680,6 +851,40 @@ describe('Parse.Query testing', () => {
680851
});
681852
});
682853

854+
it("containedIn false queries", (done) => {
855+
const makeBoxedNumber = (i) => {
856+
return new BoxedNumber({ number: i });
857+
};
858+
const numbers = [-3, -2, -1, 0, 1];
859+
const boxedNumbers = numbers.map(makeBoxedNumber);
860+
Parse.Object.saveAll(boxedNumbers).then(() => {
861+
const query = new Parse.Query(BoxedNumber);
862+
query.containedIn('number', false);
863+
return query.find();
864+
}).then(done.fail).catch((error) => {
865+
equal(error.code, Parse.Error.INVALID_JSON);
866+
equal(error.message, 'bad $in value');
867+
done();
868+
});
869+
});
870+
871+
it("notContainedIn false queries", (done) => {
872+
const makeBoxedNumber = (i) => {
873+
return new BoxedNumber({ number: i });
874+
};
875+
const numbers = [-3, -2, -1, 0, 1];
876+
const boxedNumbers = numbers.map(makeBoxedNumber);
877+
Parse.Object.saveAll(boxedNumbers).then(() => {
878+
const query = new Parse.Query(BoxedNumber);
879+
query.notContainedIn('number', false);
880+
return query.find();
881+
}).then(done.fail).catch((error) => {
882+
equal(error.code, Parse.Error.INVALID_JSON);
883+
equal(error.message, 'bad $nin value');
884+
done();
885+
});
886+
});
887+
683888
it("notContainedIn queries", function(done) {
684889
const makeBoxedNumber = function(i) {
685890
return new BoxedNumber({ number: i });

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,14 @@ const buildWhereClause = ({ schema, query, index }) => {
287287
index += 2;
288288
} else if (typeof fieldValue === 'boolean') {
289289
patterns.push(`$${index}:name = $${index + 1}`);
290-
values.push(fieldName, fieldValue);
290+
// Can't cast boolean to double precision
291+
if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Number') {
292+
// Should always return zero results
293+
const MAX_INT_PLUS_ONE = 9223372036854775808;
294+
values.push(fieldName, MAX_INT_PLUS_ONE);
295+
} else {
296+
values.push(fieldName, fieldValue);
297+
}
291298
index += 2;
292299
} else if (typeof fieldValue === 'number') {
293300
patterns.push(`$${index}:name = $${index + 1}`);
@@ -329,11 +336,16 @@ const buildWhereClause = ({ schema, query, index }) => {
329336
values.push(fieldName, fieldValue.$ne);
330337
index += 2;
331338
}
332-
333-
if (fieldValue.$eq) {
334-
patterns.push(`$${index}:name = $${index + 1}`);
335-
values.push(fieldName, fieldValue.$eq);
336-
index += 2;
339+
if (fieldValue.$eq !== undefined) {
340+
if (fieldValue.$eq === null) {
341+
patterns.push(`$${index}:name IS NULL`);
342+
values.push(fieldName);
343+
index += 1;
344+
} else {
345+
patterns.push(`$${index}:name = $${index + 1}`);
346+
values.push(fieldName, fieldValue.$eq);
347+
index += 2;
348+
}
337349
}
338350
const isInOrNin = Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
339351
if (Array.isArray(fieldValue.$in) &&
@@ -393,6 +405,10 @@ const buildWhereClause = ({ schema, query, index }) => {
393405
if (fieldValue.$nin) {
394406
createConstraint(_.flatMap(fieldValue.$nin, elt => elt), true);
395407
}
408+
} else if(typeof fieldValue.$in !== 'undefined') {
409+
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $in value');
410+
} else if (typeof fieldValue.$nin !== 'undefined') {
411+
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $nin value');
396412
}
397413

398414
if (Array.isArray(fieldValue.$all) && isArrayField) {
@@ -578,7 +594,7 @@ const buildWhereClause = ({ schema, query, index }) => {
578594
}
579595

580596
Object.keys(ParseToPosgresComparator).forEach(cmp => {
581-
if (fieldValue[cmp]) {
597+
if (fieldValue[cmp] || fieldValue[cmp] === 0) {
582598
const pgComparator = ParseToPosgresComparator[cmp];
583599
patterns.push(`$${index}:name ${pgComparator} $${index + 1}`);
584600
values.push(fieldName, toPostgresValue(fieldValue[cmp]));

0 commit comments

Comments
 (0)