From e8f94d2da2a7848aa2199d6fefac9292b339bca6 Mon Sep 17 00:00:00 2001 From: shen2 Date: Sat, 11 Jul 2015 16:45:25 +0800 Subject: [PATCH] throw exception when use NAMES_FOR_VALUES flag in batch request issue https://github.com/duoshuo/php-cassandra/issues/40 --- src/Request/Batch.php | 191 +++++++++++-------- src/Request/Register.php | 66 +++---- src/Request/Request.php | 385 +++++++++++++++++++-------------------- 3 files changed, 336 insertions(+), 306 deletions(-) diff --git a/src/Request/Batch.php b/src/Request/Batch.php index e575040..3a6d33d 100644 --- a/src/Request/Batch.php +++ b/src/Request/Batch.php @@ -3,89 +3,120 @@ use Cassandra\Protocol\Frame; use Cassandra\Protocol; use Cassandra\Connection; +use Cassandra\Type; class Batch extends Request{ - const TYPE_LOGGED = 0; - const TYPE_UNLOGGED = 1; - const TYPE_COUNTER = 2; - - protected $opcode = Frame::OPCODE_BATCH; - - /** - * @var array - */ - protected $_queryArray = []; + const TYPE_LOGGED = 0; + const TYPE_UNLOGGED = 1; + const TYPE_COUNTER = 2; + + protected $opcode = Frame::OPCODE_BATCH; + + /** + * @var array + */ + protected $_queryArray = []; - /** - * @var int - */ - protected $_batchType; - - /** - * - * @var int - */ - protected $_consistency; - - /** - * - * @var array - */ - protected $_options; - - /** - * - * @param string $type - * @param string $consistency - * @param array $options - */ - public function __construct($type = null, $consistency = null, $options = []) { - $this->_batchType = $type === null ? Batch::TYPE_LOGGED : $type; - $this->_consistency = $consistency === null ? Request::CONSISTENCY_ONE : $consistency; - $this->_options = $options; - } + /** + * @var int + */ + protected $_batchType; + + /** + * + * @var int + */ + protected $_consistency; + + /** + * + * @var array + */ + protected $_options; + + /** + * + * @param string $type + * @param string $consistency + * @param array $options + */ + public function __construct($type = null, $consistency = null, $options = []) { + $this->_batchType = $type === null ? Batch::TYPE_LOGGED : $type; + $this->_consistency = $consistency === null ? Request::CONSISTENCY_ONE : $consistency; + $this->_options = $options; + } - /** - * Exec transaction - */ - public function getBody() { - $body = pack('C', $this->_batchType); - $body .= pack('n', count($this->_queryArray)) . implode('', $this->_queryArray); - - $body .= Request::queryParameters($this->_consistency, [], $this->_options); - return $body; - } + /** + * Exec transaction + */ + public function getBody() { + return pack('C', $this->_batchType) + . pack('n', count($this->_queryArray)) . implode('', $this->_queryArray) + . self::queryParameters($this->_consistency, $this->_options); + } - /** - * @param string $cql - * @param array $values - * @return self - */ - public function appendQuery($cql, array $values = []) { - $binary = pack('C', 0); - - $binary .= pack('N', strlen($cql)) . $cql; - $binary .= Request::valuesBinary($values, !empty($this->_options['names_for_values'])); - - $this->_queryArray[] = $binary; - - return $this; - } - - /** - * - * @param string $queryId - * @param array $values - * @return self - */ - public function appendQueryId($queryId, array $values = []) { - $binary = pack('C', 1); - - $binary .= pack('n', strlen($queryId)) . $queryId; - $binary .= Request::valuesBinary($values, !empty($this->_options['names_for_values'])); - - $this->_queryArray[] = $binary; - - return $this; - } + /** + * @param string $cql + * @param array $values + * @return self + */ + public function appendQuery($cql, array $values = []) { + $binary = pack('C', 0); + + $binary .= pack('N', strlen($cql)) . $cql; + $binary .= Request::valuesBinary($values, !empty($this->_options['names_for_values'])); + + $this->_queryArray[] = $binary; + + return $this; + } + + /** + * + * @param string $queryId + * @param array $values + * @return self + */ + public function appendQueryId($queryId, array $values = []) { + $binary = pack('C', 1); + + $binary .= pack('n', strlen($queryId)) . $queryId; + $binary .= Request::valuesBinary($values, !empty($this->_options['names_for_values'])); + + $this->_queryArray[] = $binary; + + return $this; + } + + /** + * + * @param int $consistency + * @param array $options + * @return string + */ + public static function queryParameters($consistency, array $options = []){ + $flags = 0; + $optional = ''; + + if (isset($options['serial_consistency'])) { + $flags |= Query::FLAG_WITH_SERIAL_CONSISTENCY; + $optional .= pack('n', $options['serial_consistency']); + } + + if (isset($options['default_timestamp'])) { + $flags |= Query::FLAG_WITH_DEFAULT_TIMESTAMP; + $optional .= Type\Bigint::binary($options['default_timestamp']); + } + + if (!empty($options['names_for_values'])){ + /** + * @link https://github.com/duoshuo/php-cassandra/issues/40 + */ + throw new \Cassandra\Exception('NAMES_FOR_VALUES in batch request seems never work in Cassandra 2.1.x. Keep NAMES_FOR_VALUES flag false to avoid this bug.'); + + $flags |= Query::FLAG_WITH_NAMES_FOR_VALUES; + } + + return pack('n', $consistency) . pack('C', $flags) . $optional; + } } diff --git a/src/Request/Register.php b/src/Request/Register.php index 4039392..3a6afdc 100644 --- a/src/Request/Register.php +++ b/src/Request/Register.php @@ -4,37 +4,37 @@ use Cassandra\Type; class Register extends Request{ - - protected $opcode = Frame::OPCODE_REGISTER; - - /** - * - * @var array - */ - protected $_events; - - /** - * REGISTER - * - * Register this connection to receive some type of events. The body of the - * message is a [string list] representing the event types to register to. See - * section 4.2.6 for the list of valid event types. - * - * The response to a REGISTER message will be a READY message. - * - * Please note that if a client driver maintains multiple connections to a - * Cassandra node and/or connections to multiple nodes, it is advised to - * dedicate a handful of connections to receive events, but to *not* register - * for events on all connections, as this would only result in receiving - * multiple times the same event messages, wasting bandwidth. - * - * @param array $events - */ - public function __construct(array $events) { - $this->_events = $events; - } - - public function getBody(){ - return (new Type\CollectionList($this->_events, Type\Base::TEXT))->getBinary(); - } + + protected $opcode = Frame::OPCODE_REGISTER; + + /** + * + * @var array + */ + protected $_events; + + /** + * REGISTER + * + * Register this connection to receive some type of events. The body of the + * message is a [string list] representing the event types to register to. See + * section 4.2.6 for the list of valid event types. + * + * The response to a REGISTER message will be a READY message. + * + * Please note that if a client driver maintains multiple connections to a + * Cassandra node and/or connections to multiple nodes, it is advised to + * dedicate a handful of connections to receive events, but to *not* register + * for events on all connections, as this would only result in receiving + * multiple times the same event messages, wasting bandwidth. + * + * @param array $events + */ + public function __construct(array $events) { + $this->_events = $events; + } + + public function getBody(){ + return Type\CollectionList::binary($this->_events, [Type\Base::TEXT]); + } } diff --git a/src/Request/Request.php b/src/Request/Request.php index c958edd..6ea42ad 100644 --- a/src/Request/Request.php +++ b/src/Request/Request.php @@ -5,206 +5,205 @@ class Request implements Frame{ - const CONSISTENCY_ANY = 0x0000; - const CONSISTENCY_ONE = 0x0001; - const CONSISTENCY_TWO = 0x0002; - const CONSISTENCY_THREE = 0x0003; - const CONSISTENCY_QUORUM = 0x0004; - const CONSISTENCY_ALL = 0x0005; - const CONSISTENCY_LOCAL_QUORUM = 0x0006; - const CONSISTENCY_EACH_QUORUM = 0x0007; - const CONSISTENCY_SERIAL = 0x0008; - const CONSISTENCY_LOCAL_SERIAL = 0x0009; - const CONSISTENCY_LOCAL_ONE = 0x000A; - - /** - * @var int - */ - protected $version = 0x03; - - /** - * @var int - */ - protected $opcode; - - /** - * @var int - */ - protected $stream = 0; - - /** - * @var int - */ - protected $flags = 0; - - /** - * @param int $opcode - * @param int $stream - * @param int $flags - */ - public function __construct($opcode, $stream = 0, $flags = 0) { - $this->opcode = $opcode; - $this->stream = $stream; - $this->flags = $flags; - } - - public function getVersion(){ - return $this->version; - } - - public function getFlags(){ - return $this->flags; - } - - public function getStream(){ - return $this->stream; - } - - public function getOpcode(){ - return $this->opcode; - } - - public function getBody(){ - return ''; - } - - public function setStream($stream){ - $this->stream = $stream; - } - - /** - * @return string - */ - public function __toString(){ - $body = $this->getBody(); - return pack( - 'CCnCN', - $this->version, - $this->flags, - $this->stream, - $this->opcode, - strlen($body) - ) . $body; - } - - /** - * - * @param array $values - * @throws Type\Exception - * @return string - */ - public static function valuesBinary(array $values, $namesForValues = false) { - $valuesBinary = pack('n', count($values)); - - $index = 0; - foreach($values as $name => $value) { - switch (true) { - case $value instanceof Type\Base: - $binary = $value->getBinary(); - break; - case $value === null: - $binary = null; - break; - case is_int($value): - $binary = pack('N', $value); - break; - case is_string($value): - $binary = $value; - break; - case is_bool($value): - $binary = $value ? chr(1) : chr(0); - break; - default: - throw new Type\Exception('Unknown type.'); - } + const CONSISTENCY_ANY = 0x0000; + const CONSISTENCY_ONE = 0x0001; + const CONSISTENCY_TWO = 0x0002; + const CONSISTENCY_THREE = 0x0003; + const CONSISTENCY_QUORUM = 0x0004; + const CONSISTENCY_ALL = 0x0005; + const CONSISTENCY_LOCAL_QUORUM = 0x0006; + const CONSISTENCY_EACH_QUORUM = 0x0007; + const CONSISTENCY_SERIAL = 0x0008; + const CONSISTENCY_LOCAL_SERIAL = 0x0009; + const CONSISTENCY_LOCAL_ONE = 0x000A; + + /** + * @var int + */ + protected $version = 0x03; + + /** + * @var int + */ + protected $opcode; + + /** + * @var int + */ + protected $stream = 0; + + /** + * @var int + */ + protected $flags = 0; + + /** + * @param int $opcode + * @param int $stream + * @param int $flags + */ + public function __construct($opcode, $stream = 0, $flags = 0) { + $this->opcode = $opcode; + $this->stream = $stream; + $this->flags = $flags; + } + + public function getVersion(){ + return $this->version; + } + + public function getFlags(){ + return $this->flags; + } + + public function getStream(){ + return $this->stream; + } + + public function getOpcode(){ + return $this->opcode; + } + + public function getBody(){ + return ''; + } + + public function setStream($stream){ + $this->stream = $stream; + } + + /** + * @return string + */ + public function __toString(){ + $body = $this->getBody(); + return pack( + 'CCnCN', + $this->version, + $this->flags, + $this->stream, + $this->opcode, + strlen($body) + ) . $body; + } + + /** + * + * @param array $values + * @throws Type\Exception + * @return string + */ + public static function valuesBinary(array $values, $namesForValues = false) { + $valuesBinary = pack('n', count($values)); + + $index = 0; + foreach($values as $name => $value) { + switch (true) { + case $value instanceof Type\Base: + $binary = $value->getBinary(); + break; + case $value === null: + $binary = null; + break; + case is_int($value): + $binary = pack('N', $value); + break; + case is_string($value): + $binary = $value; + break; + case is_bool($value): + $binary = $value ? chr(1) : chr(0); + break; + default: + throw new Type\Exception('Unknown type.'); + } - if ($namesForValues){ - $valuesBinary .= pack('n', strlen($name)) . strtolower($name); - } - else{ - /** - * @see https://github.com/duoshuo/php-cassandra/issues/29 - */ - if ($index++ !== $name) - throw new Type\Exception('$values should be an sequential array, associative array given. Or you can set "names_for_values" option to true.'); - } + if ($namesForValues){ + $valuesBinary .= pack('n', strlen($name)) . strtolower($name); + } + else{ + /** + * @see https://github.com/duoshuo/php-cassandra/issues/29 + */ + if ($index++ !== $name) + throw new Type\Exception('$values should be an sequential array, associative array given. Or you can set "names_for_values" option to true.'); + } - $valuesBinary .= $binary === null - ? "\xff\xff\xff\xff" - : pack('N', strlen($binary)) . $binary; - } - - return $valuesBinary; - } - - /** - * - * @param array $values - * @param array $columns - * @return array - */ - public static function strictTypeValues(array $values, array $columns) { - $strictTypeValues = []; - foreach($columns as $index => $column) { - $key = array_key_exists($column['name'], $values) ? $column['name'] : $index; - - if (!isset($values[$key])){ - $strictTypeValues[$key] = null; - } - elseif($values[$key] instanceof Type\Base){ - $strictTypeValues[$key] = $values[$key]; - } - else{ - $strictTypeValues[$key] = Type\Base::getTypeObject($column['type'], $values[$key]); - } - } - - return $strictTypeValues; - } - - /** - * - * @param int $consistency - * @param array $values - * @param array $options - * @return string - */ - public static function queryParameters($consistency, array $values = [], array $options = []){ - $flags = 0; - $optional = ''; - - if (!empty($values)) { - $flags |= Query::FLAG_VALUES; - $optional .= Request::valuesBinary($values, !empty($options['names_for_values'])); - } + $valuesBinary .= $binary === null + ? "\xff\xff\xff\xff" + : pack('N', strlen($binary)) . $binary; + } + + return $valuesBinary; + } + + /** + * + * @param array $values + * @param array $columns + * @return array + */ + public static function strictTypeValues(array $values, array $columns) { + $strictTypeValues = []; + foreach($columns as $index => $column) { + $key = array_key_exists($column['name'], $values) ? $column['name'] : $index; + + if (!isset($values[$key])){ + $strictTypeValues[$key] = null; + } + elseif($values[$key] instanceof Type\Base){ + $strictTypeValues[$key] = $values[$key]; + } + else{ + $strictTypeValues[$key] = Type\Base::getTypeObject($column['type'], $values[$key]); + } + } + + return $strictTypeValues; + } + + /** + * + * @param int $consistency + * @param array $values + * @param array $options + * @return string + */ + public static function queryParameters($consistency, array $values = [], array $options = []){ + $flags = 0; + $optional = ''; + + if (!empty($values)) { + $flags |= Query::FLAG_VALUES; + $optional .= Request::valuesBinary($values, !empty($options['names_for_values'])); + } - if (!empty($options['skip_metadata'])) - $flags |= Query::FLAG_SKIP_METADATA; + if (!empty($options['skip_metadata'])) + $flags |= Query::FLAG_SKIP_METADATA; - if (isset($options['page_size'])) { - $flags |= Query::FLAG_PAGE_SIZE; - $optional .= pack('N', $options['page_size']); - } + if (isset($options['page_size'])) { + $flags |= Query::FLAG_PAGE_SIZE; + $optional .= pack('N', $options['page_size']); + } - if (isset($options['paging_state'])) { - $flags |= Query::FLAG_WITH_PAGING_STATE; - $optional .= pack('N', strlen($options['paging_state'])) . $options['paging_state']; - } + if (isset($options['paging_state'])) { + $flags |= Query::FLAG_WITH_PAGING_STATE; + $optional .= pack('N', strlen($options['paging_state'])) . $options['paging_state']; + } - if (isset($options['serial_consistency'])) { - $flags |= Query::FLAG_WITH_SERIAL_CONSISTENCY; - $optional .= pack('n', $options['serial_consistency']); - } + if (isset($options['serial_consistency'])) { + $flags |= Query::FLAG_WITH_SERIAL_CONSISTENCY; + $optional .= pack('n', $options['serial_consistency']); + } - if (isset($options['default_timestamp'])) { - $flags |= Query::FLAG_WITH_DEFAULT_TIMESTAMP; - $bigint = new Type\Bigint($options['default_timestamp']); - $optional .= $bigint->getBinary(); - } + if (isset($options['default_timestamp'])) { + $flags |= Query::FLAG_WITH_DEFAULT_TIMESTAMP; + $optional .= Type\Bigint::binary($options['default_timestamp']); + } - if (!empty($options['names_for_values'])) - $flags |= Query::FLAG_WITH_NAMES_FOR_VALUES; + if (!empty($options['names_for_values'])) + $flags |= Query::FLAG_WITH_NAMES_FOR_VALUES; - return pack('n', $consistency) . pack('C', $flags) . $optional; - } + return pack('n', $consistency) . pack('C', $flags) . $optional; + } }