Commit c405cec3 authored by Jeremy Mikola's avatar Jeremy Mikola

PHPLIB-205: Support providing collation per operation

This bumps the extension requirement to 1.2.0. Composer allows dev versions with the "^" operator, so this will accept 1.2.0alpha3, which introduces a "collation" option for Query and BulkWrite methods.
parent 55e2a31d
......@@ -13,17 +13,15 @@ env:
- MONGO_REPO_TYPE="precise/mongodb-enterprise/"
- SOURCES_LOC="/etc/apt/sources.list.d/mongodb.list"
matrix:
- DRIVER_VERSION=stable SERVER_VERSION=2.6
- DRIVER_VERSION=stable SERVER_VERSION=3.0
- DRIVER_VERSION=stable SERVER_VERSION=3.2
- DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=2.6
- DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=3.0
- DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=3.2
matrix:
fast_finish: true
include:
- php: 5.6
env: DRIVER_VERSION=1.1.0 SERVER_VERSION=3.2
- php: 7.0
env: DRIVER_VERSION=stable SERVER_VERSION=2.4
env: DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=2.4
- php: 7.0
env: DRIVER_VERSION=devel SERVER_VERSION=3.2
exclude:
......
......@@ -11,7 +11,7 @@
],
"require": {
"php": ">=5.4",
"ext-mongodb": "^1.1.0"
"ext-mongodb": "^1.2.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8"
......
<?php
namespace MongoDB\Exception;
class UnsupportedException extends RuntimeException implements Exception
{
/**
* Thrown when collations are not supported by a server.
*
* @return self
*/
public static function collationNotSupported()
{
return new static('Collations are not supported by the server executing this operation');
}
}
......@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use ArrayIterator;
use stdClass;
use Traversable;
......@@ -21,6 +22,7 @@ use Traversable;
*/
class Aggregate implements Executable
{
private static $wireVersionForCollation = 5;
private static $wireVersionForCursor = 2;
private static $wireVersionForDocumentLevelValidation = 4;
private static $wireVersionForReadConcern = 4;
......@@ -48,6 +50,11 @@ class Aggregate implements Executable
* For servers < 3.2, this option is ignored as document level validation
* is not available.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run.
*
......@@ -117,6 +124,10 @@ class Aggregate implements Executable
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}
......@@ -158,9 +169,14 @@ class Aggregate implements Executable
* @param Server $server
* @return Traversable
* @throws UnexpectedValueException if the command response was malformed
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$isCursorSupported = \MongoDB\server_supports_feature($server, self::$wireVersionForCursor);
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
......@@ -212,6 +228,10 @@ class Aggregate implements Executable
$cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation'];
}
if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}
if (isset($this->options['maxTimeMS'])) {
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
}
......
......@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for executing multiple write operations.
......@@ -23,12 +24,14 @@ class BulkWrite implements Executable
const UPDATE_MANY = 'updateMany';
const UPDATE_ONE = 'updateOne';
private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4;
private $databaseName;
private $collectionName;
private $operations;
private $options;
private $isCollationUsed = false;
/**
* Constructs a bulk write operation.
......@@ -36,8 +39,8 @@ class BulkWrite implements Executable
* Example array structure for all supported operation types:
*
* [
* [ 'deleteMany' => [ $filter ] ],
* [ 'deleteOne' => [ $filter ] ],
* [ 'deleteMany' => [ $filter, $options ] ],
* [ 'deleteOne' => [ $filter, $options ] ],
* [ 'insertOne' => [ $document ] ],
* [ 'replaceOne' => [ $filter, $replacement, $options ] ],
* [ 'updateMany' => [ $filter, $update, $options ] ],
......@@ -48,8 +51,20 @@ class BulkWrite implements Executable
* writeConcern option is specified for the top-level bulk write operation
* instead of each individual operation.
*
* Supported options for deleteMany and deleteOne operations:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* Supported options for replaceOne, updateMany, and updateOne operations:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * upsert (boolean): When true, a new document is created if no document
* matches the query. The default is false.
*
......@@ -108,7 +123,25 @@ class BulkWrite implements Executable
case self::DELETE_MANY:
case self::DELETE_ONE:
$operations[$i][$type][1] = ['limit' => ($type === self::DELETE_ONE ? 1 : 0)];
if ( ! isset($args[1])) {
$args[1] = [];
}
if ( ! is_array($args[1])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array');
}
$args[1]['limit'] = ($type === self::DELETE_ONE ? 1 : 0);
if (isset($args[1]['collation'])) {
$this->isCollationUsed = true;
if ( ! is_array($args[1]['collation']) && ! is_object($args[1]['collation'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation'], 'array or object');
}
}
$operations[$i][$type][1] = $args[1];
break;
......@@ -136,6 +169,14 @@ class BulkWrite implements Executable
$args[2]['multi'] = false;
$args[2] += ['upsert' => false];
if (isset($args[2]['collation'])) {
$this->isCollationUsed = true;
if ( ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object');
}
}
if ( ! is_bool($args[2]['upsert'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean');
}
......@@ -169,6 +210,14 @@ class BulkWrite implements Executable
$args[2]['multi'] = ($type === self::UPDATE_MANY);
$args[2] += ['upsert' => false];
if (isset($args[2]['collation'])) {
$this->isCollationUsed = true;
if ( ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object');
}
}
if ( ! is_bool($args[2]['upsert'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean');
}
......@@ -210,9 +259,14 @@ class BulkWrite implements Executable
* @see Executable::execute()
* @param Server $server
* @return BulkWriteResult
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
if ($this->isCollationUsed && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$options = ['ordered' => $this->options['ordered']];
if (isset($this->options['bypassDocumentValidation']) && \MongoDB\server_supports_feature($server, self::$wireVersionForDocumentLevelValidation)) {
......
......@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for the count command.
......@@ -18,6 +19,7 @@ use MongoDB\Exception\UnexpectedValueException;
*/
class Count implements Executable
{
private static $wireVersionForCollation = 5;
private static $wireVersionForReadConcern = 4;
private $databaseName;
......@@ -30,6 +32,11 @@ class Count implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * hint (string|document): The index to use. If a document, it will be
* interpretted as an index specification and a name will be generated.
*
......@@ -60,6 +67,10 @@ class Count implements Executable
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['hint'])) {
if (is_array($options['hint']) || is_object($options['hint'])) {
$options['hint'] = \MongoDB\generate_index_name($options['hint']);
......@@ -103,9 +114,14 @@ class Count implements Executable
* @param Server $server
* @return integer
* @throws UnexpectedValueException if the command response was malformed
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference);
......@@ -133,6 +149,10 @@ class Count implements Executable
$cmd['query'] = (object) $this->filter;
}
if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}
foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
......
......@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Command;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for the create command.
......@@ -18,6 +19,8 @@ class CreateCollection implements Executable
const USE_POWER_OF_2_SIZES = 1;
const NO_PADDING = 2;
private static $wireVersionForCollation = 5;
private $databaseName;
private $collectionName;
private $options = [];
......@@ -34,6 +37,11 @@ class CreateCollection implements Executable
* * capped (boolean): Specify true to create a capped collection. If set,
* the size option must also be specified. The default is false.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * flags (integer): Options for the MMAPv1 storage engine only. Must be a
* bitwise combination CreateCollection::USE_POWER_OF_2_SIZES and
* CreateCollection::NO_PADDING. The default is
......@@ -78,6 +86,10 @@ class CreateCollection implements Executable
throw InvalidArgumentException::invalidType('"capped" option', $options['capped'], 'boolean');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['flags']) && ! is_integer($options['flags'])) {
throw InvalidArgumentException::invalidType('"flags" option', $options['flags'], 'integer');
}
......@@ -129,9 +141,14 @@ class CreateCollection implements Executable
* @see Executable::execute()
* @param Server $server
* @return array|object Command result document
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$cursor = $server->executeCommand($this->databaseName, $this->createCommand());
if (isset($this->options['typeMap'])) {
......@@ -156,16 +173,10 @@ class CreateCollection implements Executable
}
}
if (isset($this->options['indexOptionDefaults'])) {
$cmd['indexOptionDefaults'] = (object) $this->options['indexOptionDefaults'];
}
if (isset($this->options['storageEngine'])) {
$cmd['storageEngine'] = (object) $this->options['storageEngine'];
foreach (['collation', 'indexOptionDefaults', 'storageEngine', 'validator'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = (object) $this->options[$option];
}
if (isset($this->options['validator'])) {
$cmd['validator'] = (object) $this->options['validator'];
}
return new Command($cmd);
......
......@@ -7,6 +7,7 @@ use MongoDB\Driver\Server;
use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\IndexInput;
/**
......@@ -19,11 +20,13 @@ use MongoDB\Model\IndexInput;
*/
class CreateIndexes implements Executable
{
private static $wireVersionForCollation = 5;
private static $wireVersionForCommand = 2;
private $databaseName;
private $collectionName;
private $indexes = [];
private $isCollationUsed = false;
/**
* Constructs a createIndexes command.
......@@ -54,6 +57,10 @@ class CreateIndexes implements Executable
$index['ns'] = $databaseName . '.' . $collectionName;
}
if (isset($index['collation'])) {
$this->isCollationUsed = true;
}
$this->indexes[] = new IndexInput($index);
$expectedIndex += 1;
......@@ -72,9 +79,14 @@ class CreateIndexes implements Executable
* @see Executable::execute()
* @param Server $server
* @return string[] The names of the created indexes
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
if ($this->isCollationUsed && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
if (\MongoDB\server_supports_feature($server, self::$wireVersionForCommand)) {
$this->executeCommand($server);
} else {
......
......@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for the delete command.
......@@ -19,6 +20,8 @@ use MongoDB\Exception\InvalidArgumentException;
*/
class Delete implements Executable
{
private static $wireVersionForCollation = 5;
private $databaseName;
private $collectionName;
private $filter;
......@@ -30,6 +33,11 @@ class Delete implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
*
* @param string $databaseName Database name
......@@ -51,6 +59,10 @@ class Delete implements Executable
throw new InvalidArgumentException('$limit must be 0 or 1');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern');
}
......@@ -71,8 +83,18 @@ class Delete implements Executable
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$deleteOptions = ['limit' => $this->limit];
if (isset($this->options['collation'])) {
$deleteOptions['collation'] = (object) $this->options['collation'];
}
$bulk = new Bulk();
$bulk->delete($this->filter, ['limit' => $this->limit]);
$bulk->delete($this->filter, $deleteOptions);
$writeConcern = isset($this->options['writeConcern']) ? $this->options['writeConcern'] : null;
$writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $writeConcern);
......
......@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\DeleteResult;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for deleting multiple document with the delete command.
......@@ -22,6 +23,11 @@ class DeleteMany implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
*
* @param string $databaseName Database name
......@@ -41,6 +47,7 @@ class DeleteMany implements Executable
* @see Executable::execute()
* @param Server $server
* @return DeleteResult
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\DeleteResult;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for deleting a single document with the delete command.
......@@ -22,6 +23,11 @@ class DeleteOne implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
*
* @param string $databaseName Database name
......@@ -41,6 +47,7 @@ class DeleteOne implements Executable
* @see Executable::execute()
* @param Server $server
* @return DeleteResult
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for the distinct command.
......@@ -18,6 +19,7 @@ use MongoDB\Exception\UnexpectedValueException;
*/
class Distinct implements Executable
{
private static $wireVersionForCollation = 5;
private static $wireVersionForReadConcern = 4;
private $databaseName;
......@@ -31,6 +33,11 @@ class Distinct implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run.
*
......@@ -54,6 +61,10 @@ class Distinct implements Executable
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}
......@@ -83,6 +94,10 @@ class Distinct implements Executable
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference);
......@@ -112,6 +127,10 @@ class Distinct implements Executable
$cmd['query'] = (object) $this->filter;
}
if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}
if (isset($this->options['maxTimeMS'])) {
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
}
......
......@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for the find command.
......@@ -23,6 +24,8 @@ class Find implements Executable
const TAILABLE = 2;
const TAILABLE_AWAIT = 3;
private static $wireVersionForCollation = 5;
private $databaseName;
private $collectionName;
private $filter;
......@@ -38,6 +41,11 @@ class Find implements Executable
*
* * batchSize (integer): The number of documents to return per batch.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * comment (string): Attaches a comment to the query. If "$comment" also
* exists in the modifiers document, this option will take precedence.
*
......@@ -100,6 +108,10 @@ class Find implements Executable
throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['comment']) && ! is_string($options['comment'])) {
throw InvalidArgumentException::invalidType('"comment" option', $options['comment'], 'comment');
}
......@@ -175,6 +187,10 @@ class Find implements Executable
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, $this->createQuery(), $readPreference);
......@@ -215,6 +231,10 @@ class Find implements Executable
}
}
if (isset($this->options['collation'])) {
$options['collation'] = (object) $this->options['collation'];
}
$modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers'];
if (isset($this->options['comment'])) {
......
......@@ -7,6 +7,7 @@ use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for the findAndModify command.
......@@ -19,6 +20,7 @@ use MongoDB\Exception\UnexpectedValueException;
*/
class FindAndModify implements Executable
{
private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4;
private static $wireVersionForWriteConcern = 4;
......@@ -31,6 +33,11 @@ class FindAndModify implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation.
*
......@@ -79,6 +86,10 @@ class FindAndModify implements Executable
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['fields']) && ! is_array($options['fields']) && ! is_object($options['fields'])) {
throw InvalidArgumentException::invalidType('"fields" option', $options['fields'], 'array or object');
}
......@@ -134,6 +145,10 @@ class FindAndModify implements Executable
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$cursor = $server->executeCommand($this->databaseName, $this->createCommand($server));
$result = current($cursor->toArray());
......@@ -176,7 +191,7 @@ class FindAndModify implements Executable
$cmd['upsert'] = $this->options['upsert'];
}
foreach (['fields', 'query', 'sort', 'update'] as $option) {
foreach (['collation', 'fields', 'query', 'sort', 'update'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = (object) $this->options[$option];
}
......
......@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for finding a single document with the find command.
......@@ -23,6 +24,11 @@ class FindOne implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * comment (string): Attaches a comment to the query. If "$comment" also
* exists in the modifiers document, this option will take precedence.
*
......@@ -75,6 +81,7 @@ class FindOne implements Executable
* @see Executable::execute()
* @param Server $server
* @return array|object|null
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for deleting a document with the findAndModify command.
......@@ -21,6 +22,11 @@ class FindOneAndDelete implements Executable
*
* Supported options:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run.
*
......@@ -68,6 +74,7 @@ class FindOneAndDelete implements Executable
* @see Executable::execute()
* @param Server $server
* @return object|null
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for replacing a document with the findAndModify command.
......@@ -27,6 +28,11 @@ class FindOneAndReplace implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run.
*
......@@ -108,6 +114,7 @@ class FindOneAndReplace implements Executable
* @see Executable::execute()
* @param Server $server
* @return object|null
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for updating a document with the findAndModify command.
......@@ -27,6 +28,11 @@ class FindOneAndUpdate implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run.
*
......@@ -108,6 +114,7 @@ class FindOneAndUpdate implements Executable
* @see Executable::execute()
* @param Server $server
* @return object|null
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\UpdateResult;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for replacing a single document with the update command.
......@@ -25,6 +26,11 @@ class ReplaceOne implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * upsert (boolean): When true, a new document is created if no document
* matches the query. The default is false.
*
......@@ -62,6 +68,7 @@ class ReplaceOne implements Executable
* @see Executable::execute()
* @param Server $server
* @return UpdateResult
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for the update command.
......@@ -19,6 +20,7 @@ use MongoDB\Exception\InvalidArgumentException;
*/
class Update implements Executable
{
private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4;
private $databaseName;
......@@ -35,6 +37,11 @@ class Update implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * multi (boolean): When true, updates all documents matching the query.
* This option cannot be true if the $update argument is a replacement
* document (i.e. contains no update operators). The default is false.
......@@ -71,6 +78,10 @@ class Update implements Executable
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
}
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if ( ! is_bool($options['multi'])) {
throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean');
}
......@@ -100,14 +111,23 @@ class Update implements Executable
* @see Executable::execute()
* @param Server $server
* @return UpdateResult
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$updateOptions = [
'multi' => $this->options['multi'],
'upsert' => $this->options['upsert'],
];
if (isset($this->options['collation'])) {
$updateOptions['collation'] = (object) $this->options['collation'];
}
$bulkOptions = [];
if (isset($this->options['bypassDocumentValidation']) && \MongoDB\server_supports_feature($server, self::$wireVersionForDocumentLevelValidation)) {
......
......@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\UpdateResult;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for updating multiple documents with the update command.
......@@ -25,6 +26,11 @@ class UpdateMany implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * upsert (boolean): When true, a new document is created if no document
* matches the query. The default is false.
*
......@@ -62,6 +68,7 @@ class UpdateMany implements Executable
* @see Executable::execute()
* @param Server $server
* @return UpdateResult
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\UpdateResult;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/**
* Operation for updating a single document with the update command.
......@@ -25,6 +26,11 @@ class UpdateOne implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation.
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * upsert (boolean): When true, a new document is created if no document
* matches the query. The default is false.
*
......@@ -62,6 +68,7 @@ class UpdateOne implements Executable
* @see Executable::execute()
* @param Server $server
* @return UpdateResult
* @throws UnsupportedException if collation is used and unsupported
*/
public function execute(Server $server)
{
......
......@@ -49,6 +49,10 @@ class AggregateTest extends TestCase
$options[][] = ['bypassDocumentValidation' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['maxTimeMS' => $value];
}
......
......@@ -97,6 +97,23 @@ class BulkWriteTest extends TestCase
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["deleteMany"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testDeleteManyCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::DELETE_MANY => [['x' => 1], ['collation' => $collation]]],
]);
}
public function provideInvalidDocumentValues()
{
return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues());
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage Missing first argument for $operations[0]["deleteOne"]
......@@ -120,6 +137,18 @@ class BulkWriteTest extends TestCase
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["deleteOne"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testDeleteOneCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::DELETE_ONE => [['x' => 1], ['collation' => $collation]]],
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage Missing first argument for $operations[0]["replaceOne"]
......@@ -177,6 +206,18 @@ class BulkWriteTest extends TestCase
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["replaceOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testReplaceOneCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::REPLACE_ONE => [['x' => 1], ['y' => 1], ['collation' => $collation]]],
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["replaceOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/
......@@ -189,6 +230,11 @@ class BulkWriteTest extends TestCase
]);
}
public function provideInvalidBooleanValues()
{
return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues());
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage Missing first argument for $operations[0]["updateMany"]
......@@ -246,6 +292,18 @@ class BulkWriteTest extends TestCase
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testUpdateManyCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::UPDATE_MANY => [['x' => 1], ['$set' => ['x' => 1]], ['collation' => $collation]]],
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/
......@@ -315,6 +373,18 @@ class BulkWriteTest extends TestCase
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testUpdateOneCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::UPDATE_ONE => [['x' => 1], ['$set' => ['x' => 1]], ['collation' => $collation]]],
]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/
......@@ -341,11 +411,6 @@ class BulkWriteTest extends TestCase
);
}
public function provideInvalidBooleanValues()
{
return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues());
}
public function provideInvalidConstructorOptions()
{
$options = [];
......
......@@ -28,6 +28,10 @@ class CountTest extends TestCase
{
$options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidHintValues() as $value) {
$options[][] = ['hint' => $value];
}
......
......@@ -27,6 +27,10 @@ class CreateCollectionTest extends TestCase
$options[][] = ['capped' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['flags' => $value];
}
......
......@@ -43,6 +43,10 @@ class DeleteTest extends TestCase
{
$options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidWriteConcernValues() as $value) {
$options[][] = ['writeConcern' => $value];
}
......
......@@ -28,6 +28,10 @@ class DistinctTest extends TestCase
{
$options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['maxTimeMS' => $value];
}
......
......@@ -23,6 +23,10 @@ class FindAndModifyTest extends TestCase
$options[][] = ['bypassDocumentValidation' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['fields' => $value];
}
......
......@@ -36,6 +36,10 @@ class FindTest extends TestCase
$options[][] = ['batchSize' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidStringValues() as $value) {
$options[][] = ['comment' => $value];
}
......
......@@ -43,6 +43,10 @@ class UpdateTest extends TestCase
$options[][] = ['bypassDocumentValidation' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['multi' => $value];
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment