Skip to content

Commit 894775c

Browse files
aherlihybehackett
authored andcommitted
Prohibit bypass_document_validation with w=0
1 parent ab6b3a3 commit 894775c

File tree

3 files changed

+46
-8
lines changed

3 files changed

+46
-8
lines changed

pymongo/bulk.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ def execute_command(self, sock_info, generator, write_concern):
314314
def execute_no_results(self, sock_info, generator):
315315
"""Execute all operations, returning no results (w=0).
316316
"""
317+
# Cannot have both unacknowledged write and bypass document validation.
318+
if self.bypass_doc_val and sock_info.max_wire_version >= 4:
319+
raise OperationFailure("Cannot set bypass_document_validation with"
320+
" unacknowledged write concern")
317321
coll = self.collection
318322
# If ordered is True we have to send GLE or use write
319323
# commands so we can abort on the first error.

pymongo/collection.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -433,9 +433,14 @@ def bulk_write(self, requests, ordered=True,
433433
return BulkWriteResult(bulk_api_result, True)
434434
return BulkWriteResult({}, False)
435435

436-
def _legacy_write(
437-
self, sock_info, name, cmd, acknowledged, op_id, func, *args):
436+
def _legacy_write(self, sock_info, name, cmd, acknowledged, op_id,
437+
bypass_doc_val, func, *args):
438438
"""Internal legacy write helper."""
439+
# Cannot have both unacknowledged write and bypass document validation.
440+
if (bypass_doc_val and not acknowledged and
441+
sock_info.max_wire_version >= 4):
442+
raise OperationFailure("Cannot set bypass_document_validation with"
443+
" unacknowledged write concern")
439444
listeners = self.database.client._event_listeners
440445
publish = listeners.enabled_for_commands
441446

@@ -509,8 +514,8 @@ def _insert_one(
509514
# Legacy OP_INSERT.
510515
self._legacy_write(
511516
sock_info, 'insert', command, acknowledged, op_id,
512-
message.insert, self.__full_name, [doc], check_keys,
513-
acknowledged, concern, False, self.codec_options)
517+
bypass_doc_val, message.insert, self.__full_name, [doc],
518+
check_keys, acknowledged, concern, False, self.codec_options)
514519
return doc.get('_id')
515520

516521
def _insert(self, sock_info, docs, ordered=True, check_keys=True,
@@ -713,8 +718,9 @@ def _update(self, sock_info, criteria, document, upsert=False,
713718
# Legacy OP_UPDATE.
714719
return self._legacy_write(
715720
sock_info, 'update', command, acknowledged, op_id,
716-
message.update, self.__full_name, upsert, multi, criteria,
717-
document, acknowledged, concern, check_keys, self.codec_options)
721+
bypass_doc_val, message.update, self.__full_name, upsert,
722+
multi, criteria, document, acknowledged, concern, check_keys,
723+
self.codec_options)
718724

719725
def replace_one(self, filter, replacement, upsert=False,
720726
bypass_document_validation=False):
@@ -909,8 +915,8 @@ def _delete(
909915
# Legacy OP_DELETE.
910916
return self._legacy_write(
911917
sock_info, 'delete', command, acknowledged, op_id,
912-
message.delete, self.__full_name, criteria, acknowledged,
913-
concern, self.codec_options, int(not multi))
918+
False, message.delete, self.__full_name, criteria,
919+
acknowledged, concern, self.codec_options, int(not multi))
914920

915921
def delete_one(self, filter):
916922
"""Delete a single document matching the filter.

test/test_collection.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,8 @@ def test_insert_bypass_document_validation(self):
757757
db = self.db
758758
db.test.drop()
759759
db.create_collection("test", validator={"a": {"$exists": True}})
760+
db_w0 = self.db.client.get_database(
761+
self.db.name, write_concern=WriteConcern(w=0))
760762

761763
# Test insert_one
762764
self.assertRaises(OperationFailure, db.test.insert_one,
@@ -769,6 +771,9 @@ def test_insert_bypass_document_validation(self):
769771
self.assertTrue(isinstance(result, InsertOneResult))
770772
self.assertEqual(2, result.inserted_id)
771773

774+
self.assertRaises(OperationFailure, db_w0.test.insert_one,
775+
{"x": 1}, bypass_document_validation=True)
776+
772777
# Test insert_many
773778
docs = [{"_id": i, "x": 100 - i} for i in range(3, 100)]
774779
self.assertRaises(OperationFailure, db.test.insert_many, docs)
@@ -792,12 +797,18 @@ def test_insert_bypass_document_validation(self):
792797
self.assertEqual(1, db.test.count({"a": doc["a"]}))
793798
self.assertTrue(result.acknowledged)
794799

800+
self.assertRaises(OperationFailure, db_w0.test.insert_many,
801+
[{"x": 1}, {"x": 2}],
802+
bypass_document_validation=True)
803+
795804
@client_context.require_version_min(3, 1, 9, -1)
796805
@client_context.require_no_auth
797806
def test_replace_bypass_document_validation(self):
798807
db = self.db
799808
db.test.drop()
800809
db.create_collection("test", validator={"a": {"$exists": True}})
810+
db_w0 = self.db.client.get_database(
811+
self.db.name, write_concern=WriteConcern(w=0))
801812

802813
# Test replace_one
803814
db.test.insert_one({"a": 101})
@@ -828,6 +839,9 @@ def test_replace_bypass_document_validation(self):
828839
self.assertEqual(0, db.test.count({"x": 101}))
829840
self.assertEqual(1, db.test.count({"a": 103}))
830841

842+
self.assertRaises(OperationFailure, db_w0.test.replace_one, {"y": 1},
843+
{"x": 1}, bypass_document_validation=True)
844+
831845
@client_context.require_version_min(3, 1, 9, -1)
832846
@client_context.require_no_auth
833847
def test_update_bypass_document_validation(self):
@@ -836,6 +850,8 @@ def test_update_bypass_document_validation(self):
836850
db.test.insert_one({"z": 5})
837851
db.command(SON([("collMod", "test"),
838852
("validator", {"z": {"$gte": 0}})]))
853+
db_w0 = self.db.client.get_database(
854+
self.db.name, write_concern=WriteConcern(w=0))
839855

840856
# Test update_one
841857
self.assertRaises(OperationFailure, db.test.update_one,
@@ -866,6 +882,9 @@ def test_update_bypass_document_validation(self):
866882
self.assertEqual(0, db.test.count({"z": -9}))
867883
self.assertEqual(1, db.test.count({"z": 0}))
868884

885+
self.assertRaises(OperationFailure, db_w0.test.update_one, {"y": 1},
886+
{"$inc": {"x": 1}}, bypass_document_validation=True)
887+
869888
# Test update_many
870889
db.test.insert_many([{"z": i} for i in range(3, 101)])
871890
db.test.insert_one({"y": 0},
@@ -899,12 +918,18 @@ def test_update_bypass_document_validation(self):
899918
self.assertEqual(150, db.test.count({"z": {"$gte": 0}}))
900919
self.assertEqual(0, db.test.count({"z": {"$lt": 0}}))
901920

921+
self.assertRaises(OperationFailure, db_w0.test.update_many, {"y": 1},
922+
{"$inc": {"x": 1}}, bypass_document_validation=True)
923+
902924
@client_context.require_version_min(3, 1, 9, -1)
903925
@client_context.require_no_auth
904926
def test_bypass_document_validation_bulk_write(self):
905927
db = self.db
906928
db.test.drop()
907929
db.create_collection("test", validator={"a": {"$gte": 0}})
930+
db_w0 = self.db.client.get_database(
931+
self.db.name, write_concern=WriteConcern(w=0))
932+
908933
ops = [InsertOne({"a": -10}),
909934
InsertOne({"a": -11}),
910935
InsertOne({"a": -12}),
@@ -922,6 +947,9 @@ def test_bypass_document_validation_bulk_write(self):
922947
for op in ops:
923948
self.assertRaises(BulkWriteError, db.test.bulk_write, [op])
924949

950+
self.assertRaises(OperationFailure, db_w0.test.bulk_write, ops,
951+
bypass_document_validation=True)
952+
925953
def test_find_by_default_dct(self):
926954
db = self.db
927955
db.test.insert_one({'foo': 'bar'})

0 commit comments

Comments
 (0)