From 40ae6f825bad928042ec6999ca08f793aaef9efa Mon Sep 17 00:00:00 2001 From: Valentin Kovalenko Date: Thu, 10 Aug 2023 08:33:04 -0600 Subject: [PATCH] JAVA-4845 --- ...ateCollectionOperationSpecification.groovy | 28 +++++++++++++----- .../legacy/bypassedCommand.json | 9 +++--- .../src/main/com/mongodb/DBCollection.java | 12 +++++++- .../mongodb/DBCollectionSpecification.groovy | 3 ++ .../test/functional/com/mongodb/DBTest.java | 29 +++++++++++++++++-- .../unit/com/mongodb/DBSpecification.groovy | 5 ++++ .../documentation/DocumentationSamples.java | 7 ----- 7 files changed, 70 insertions(+), 23 deletions(-) diff --git a/driver-core/src/test/functional/com/mongodb/internal/operation/CreateCollectionOperationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/operation/CreateCollectionOperationSpecification.groovy index 2d3bdf962f..c327721bbd 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/operation/CreateCollectionOperationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/internal/operation/CreateCollectionOperationSpecification.groovy @@ -26,13 +26,13 @@ import org.bson.BsonDocument import org.bson.BsonInt32 import org.bson.BsonString import org.bson.codecs.BsonDocumentCodec -import org.bson.codecs.DocumentCodec import spock.lang.IgnoreIf import static com.mongodb.ClusterFixture.getBinding import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet import static com.mongodb.ClusterFixture.serverVersionAtLeast import static com.mongodb.ClusterFixture.serverVersionLessThan +import static java.util.Collections.singletonList class CreateCollectionOperationSpecification extends OperationFunctionalSpecification { @@ -160,9 +160,7 @@ class CreateCollectionOperationSpecification extends OperationFunctionalSpecific collectionNameExists(getCollectionName()) when: - def stats = new CommandReadOperation<>(getDatabaseName(), - new BsonDocument('collStats', new BsonString(getCollectionName())), - new BsonDocumentCodec()).execute(getBinding()) + def stats = storageStats() then: stats.getBoolean('capped').getValue() @@ -186,10 +184,7 @@ class CreateCollectionOperationSpecification extends OperationFunctionalSpecific execute(operation, async) then: - new CommandReadOperation<>(getDatabaseName(), - new BsonDocument('collStats', new BsonString(getCollectionName())), - new DocumentCodec()).execute(getBinding()) - .getInteger('nindexes') == expectedNumberOfIndexes + storageStats().getInt32('nindexes').intValue() == expectedNumberOfIndexes where: autoIndex | expectedNumberOfIndexes | async @@ -291,4 +286,21 @@ class CreateCollectionOperationSpecification extends OperationFunctionalSpecific def collectionNameExists(String collectionName) { getCollectionInfo(collectionName) != null } + + BsonDocument storageStats() { + if (serverVersionLessThan(6, 2)) { + return new CommandReadOperation<>(getDatabaseName(), + new BsonDocument('collStats', new BsonString(getCollectionName())), + new BsonDocumentCodec()).execute(getBinding()) + } + BatchCursor cursor = new AggregateOperation( + getNamespace(), + singletonList(new BsonDocument('$collStats', new BsonDocument('storageStats', new BsonDocument()))), + new BsonDocumentCodec()).execute(getBinding()) + try { + return cursor.next().first().getDocument('storageStats') + } finally { + cursor.close() + } + } } diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/bypassedCommand.json b/driver-core/src/test/resources/client-side-encryption/legacy/bypassedCommand.json index bd0b1c565d..18054a70cb 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/bypassedCommand.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/bypassedCommand.json @@ -78,7 +78,7 @@ ] }, { - "description": "current op is not bypassed", + "description": "kill op is not bypassed", "clientOptions": { "autoEncryptOpts": { "kmsProviders": { @@ -90,14 +90,15 @@ { "name": "runCommand", "object": "database", - "command_name": "currentOp", + "command_name": "killOp", "arguments": { "command": { - "currentOp": 1 + "killOp": 1, + "op": 1234 } }, "result": { - "errorContains": "command not supported for auto encryption: currentOp" + "errorContains": "command not supported for auto encryption: killOp" } } ] diff --git a/driver-legacy/src/main/com/mongodb/DBCollection.java b/driver-legacy/src/main/com/mongodb/DBCollection.java index 7489fa21fa..28ab7444dd 100644 --- a/driver-legacy/src/main/com/mongodb/DBCollection.java +++ b/driver-legacy/src/main/com/mongodb/DBCollection.java @@ -1914,7 +1914,12 @@ public void dropIndexes(final String indexName) { * * @return a CommandResult containing the statistics about this collection * @mongodb.driver.manual reference/command/collStats/ collStats Command + * @mongodb.driver.manual reference/operator/aggregation/collStats/ $collStats + * @deprecated If you are using server release 3.4 or newer, use the {@code $collStats} aggregation pipeline stage via + * {@link #aggregate(List, AggregationOptions)} instead. + * This method uses the {@code collStats} command, which is deprecated since server release 6.2. */ + @Deprecated public CommandResult getStats() { return getDB().executeCommand(new BsonDocument("collStats", new BsonString(getName())), getReadPreference()); } @@ -1923,8 +1928,13 @@ public CommandResult getStats() { * Checks whether this collection is capped * * @return true if this is a capped collection - * @mongodb.driver.manual /core/capped-collections/#check-if-a-collection-is-capped Capped Collections + * @mongodb.driver.manual core/capped-collections/#check-if-a-collection-is-capped Capped Collections + * @mongodb.driver.manual reference/operator/aggregation/collStats/ $collStats + * @deprecated If you are using server release 3.4 or newer, use the {@code $collStats} aggregation pipeline stage via + * {@link #aggregate(List, AggregationOptions)} instead, and inspect the {@code storageStats.capped} field. + * This method uses the {@code collStats} command, which is deprecated since server release 6.2. */ + @Deprecated public boolean isCapped() { CommandResult commandResult = getStats(); Object cappedField = commandResult.get("capped"); diff --git a/driver-legacy/src/test/functional/com/mongodb/DBCollectionSpecification.groovy b/driver-legacy/src/test/functional/com/mongodb/DBCollectionSpecification.groovy index 3073b7968a..c24368de96 100644 --- a/driver-legacy/src/test/functional/com/mongodb/DBCollectionSpecification.groovy +++ b/driver-legacy/src/test/functional/com/mongodb/DBCollectionSpecification.groovy @@ -58,11 +58,13 @@ import org.bson.UuidRepresentation import org.bson.codecs.BsonDocumentCodec import org.bson.codecs.BsonValueCodec import org.bson.codecs.UuidCodec +import spock.lang.IgnoreIf import spock.lang.Specification import java.util.concurrent.TimeUnit import static Fixture.getMongoClient +import static com.mongodb.ClusterFixture.serverVersionAtLeast import static com.mongodb.CustomMatchers.isTheSameAs import static com.mongodb.LegacyMixedBulkWriteOperation.createBulkWriteOperationForDelete import static com.mongodb.LegacyMixedBulkWriteOperation.createBulkWriteOperationForUpdate @@ -260,6 +262,7 @@ class DBCollectionSpecification extends Specification { thrown(IllegalArgumentException) } + @IgnoreIf({ serverVersionAtLeast(6, 2) }) def 'getStats should execute the expected command with the collection default read preference'() { given: def executor = new TestOperationExecutor([new BsonDocument('ok', new BsonInt32(1))]) diff --git a/driver-legacy/src/test/functional/com/mongodb/DBTest.java b/driver-legacy/src/test/functional/com/mongodb/DBTest.java index 6f5c4df1c4..0dc89b21f2 100644 --- a/driver-legacy/src/test/functional/com/mongodb/DBTest.java +++ b/driver-legacy/src/test/functional/com/mongodb/DBTest.java @@ -37,12 +37,14 @@ import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet; import static com.mongodb.ClusterFixture.isSharded; import static com.mongodb.ClusterFixture.serverVersionAtLeast; +import static com.mongodb.ClusterFixture.serverVersionLessThan; import static com.mongodb.DBObjectMatchers.hasFields; import static com.mongodb.DBObjectMatchers.hasSubdocument; import static com.mongodb.Fixture.getDefaultDatabaseName; import static com.mongodb.Fixture.getMongoClient; import static com.mongodb.ReadPreference.secondary; import static com.mongodb.client.Fixture.getMongoClientSettingsBuilder; +import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.hasItems; @@ -124,7 +126,7 @@ public void shouldCreateCappedCollection() { collection.drop(); database.createCollection(collectionName, new BasicDBObject("capped", true) .append("size", 242880)); - assertTrue(database.getCollection(collectionName).isCapped()); + assertTrue(isCapped(database.getCollection(collectionName))); } @Test @@ -134,7 +136,7 @@ public void shouldCreateCappedCollectionWithMaxNumberOfDocuments() { .append("size", 242880) .append("max", 10)); - assertThat(cappedCollectionWithMax.getStats(), hasSubdocument(new BasicDBObject("capped", true).append("max", 10))); + assertThat(storageStats(cappedCollectionWithMax), hasSubdocument(new BasicDBObject("capped", true).append("max", 10))); for (int i = 0; i < 11; i++) { cappedCollectionWithMax.insert(new BasicDBObject("x", i)); @@ -148,7 +150,7 @@ public void shouldCreateUncappedCollection() { BasicDBObject creationOptions = new BasicDBObject("capped", false); database.createCollection(collectionName, creationOptions); - assertFalse(database.getCollection(collectionName).isCapped()); + assertFalse(isCapped(database.getCollection(collectionName))); } @Test(expected = MongoCommandException.class) @@ -347,4 +349,25 @@ BsonDocument getCollectionInfo(final String collectionName) { return new ListCollectionsOperation<>(getDefaultDatabaseName(), new BsonDocumentCodec()) .filter(new BsonDocument("name", new BsonString(collectionName))).execute(getBinding()).next().get(0); } + + private boolean isCapped(final DBCollection collection) { + if (serverVersionLessThan(6, 2)) { + return collection.isCapped(); + } else { + Object capped = storageStats(collection).get("capped"); + return Boolean.TRUE.equals(capped) || Integer.valueOf(1).equals(capped); + } + } + + private DBObject storageStats(final DBCollection collection) { + if (serverVersionLessThan(6, 2)) { + return collection.getStats(); + } else { + try (Cursor cursor = collection.aggregate(singletonList( + new BasicDBObject("$collStats", new BasicDBObject("storageStats", new BasicDBObject()))), + AggregationOptions.builder().build())) { + return (DBObject) cursor.next().get("storageStats"); + } + } + } } diff --git a/driver-legacy/src/test/unit/com/mongodb/DBSpecification.groovy b/driver-legacy/src/test/unit/com/mongodb/DBSpecification.groovy index b54daabc07..fe61ba00a3 100644 --- a/driver-legacy/src/test/unit/com/mongodb/DBSpecification.groovy +++ b/driver-legacy/src/test/unit/com/mongodb/DBSpecification.groovy @@ -35,8 +35,10 @@ import org.bson.BsonDouble import spock.lang.Specification import static Fixture.getMongoClient +import static com.mongodb.ClusterFixture.serverVersionLessThan import static com.mongodb.CustomMatchers.isTheSameAs import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry +import static org.junit.Assume.assumeTrue import static spock.util.matcher.HamcrestSupport.expect class DBSpecification extends Specification { @@ -202,6 +204,9 @@ class DBSpecification extends Specification { } def 'should use provided read preference for obedient commands'() { + if (cmd.get('collStats') != null) { + assumeTrue(serverVersionLessThan(6, 2)) + } given: def mongo = Stub(MongoClient) mongo.mongoClientOptions >> MongoClientOptions.builder().build() diff --git a/driver-sync/src/examples/documentation/DocumentationSamples.java b/driver-sync/src/examples/documentation/DocumentationSamples.java index 2091456cb2..9251031def 100644 --- a/driver-sync/src/examples/documentation/DocumentationSamples.java +++ b/driver-sync/src/examples/documentation/DocumentationSamples.java @@ -721,13 +721,6 @@ public void testRunCommand() { // Start runCommand Example 1 database.runCommand(new Document("buildInfo", 1)); // End runCommand Example 1 - - database.getCollection("restaurants").drop(); - database.createCollection("restaurants"); - - // Start runCommand Example 2 - database.runCommand(new Document("collStats", "restaurants")); - // End runCommand Example 2 } @Test