From c0ea6acbd57d3ad0e13085b03d134ef28e57efff Mon Sep 17 00:00:00 2001 From: Julien Jomier Date: Wed, 23 Mar 2011 10:11:07 +0100 Subject: [PATCH] ENH: Added phpcassa library --- library/phpcassa/.gitignore | 34 + library/phpcassa/AUTHORS | 10 + library/phpcassa/CHANGES | 42 + library/phpcassa/INSTALLING | 16 + library/phpcassa/LICENSE | 19 + library/phpcassa/README.mkd | 78 + library/phpcassa/columnfamily.php | 1194 +++++ library/phpcassa/connection.php | 283 ++ library/phpcassa/doc/Makefile | 89 + library/phpcassa/doc/__init__.py | 1 + library/phpcassa/doc/changelog.rst | 42 + library/phpcassa/doc/conf.py | 154 + library/phpcassa/doc/index.rst | 72 + library/phpcassa/doc/installation.rst | 27 + library/phpcassa/doc/sphinxtogithub.py | 377 ++ library/phpcassa/doc/troubleshooting.rst | 8 + library/phpcassa/doc/tutorial.rst | 353 ++ library/phpcassa/test/all_tests.php | 13 + library/phpcassa/test/cassandra.yaml | 626 +++ library/phpcassa/test/test_autopacking.php | 620 +++ library/phpcassa/test/test_columnfamily.php | 548 +++ library/phpcassa/test/test_large_ops.php | 41 + library/phpcassa/test/test_pooling.php | 112 + library/phpcassa/thrift/Thrift.php | 785 ++++ library/phpcassa/thrift/autoload.php | 51 + .../thrift/ext/thrift_protocol/config.m4 | 15 + .../thrift_protocol/php_thrift_protocol.cpp | 1030 +++++ .../ext/thrift_protocol/php_thrift_protocol.h | 27 + .../thrift/packages/cassandra/Cassandra.php | 3894 +++++++++++++++++ .../cassandra/cassandra_constants.php | 13 + .../packages/cassandra/cassandra_types.php | 1172 +++++ .../thrift/protocol/TBinaryProtocol.php | 429 ++ .../thrift/protocol/TBinarySerializer.php | 69 + .../phpcassa/thrift/protocol/TProtocol.php | 374 ++ .../thrift/transport/TBufferedTransport.php | 161 + .../thrift/transport/TFramedTransport.php | 179 + .../phpcassa/thrift/transport/THttpClient.php | 200 + .../thrift/transport/TMemoryBuffer.php | 82 + .../thrift/transport/TNullTransport.php | 46 + .../phpcassa/thrift/transport/TPhpStream.php | 109 + .../thrift/transport/TServerSocket.php | 96 + .../thrift/transport/TServerTransport.php | 50 + library/phpcassa/thrift/transport/TSocket.php | 320 ++ .../phpcassa/thrift/transport/TSocketPool.php | 294 ++ .../phpcassa/thrift/transport/TTransport.php | 106 + .../thrift/transport/TTransportFactory.php | 12 + library/phpcassa/uuid.php | 323 ++ 47 files changed, 14596 insertions(+) create mode 100644 library/phpcassa/.gitignore create mode 100644 library/phpcassa/AUTHORS create mode 100644 library/phpcassa/CHANGES create mode 100644 library/phpcassa/INSTALLING create mode 100644 library/phpcassa/LICENSE create mode 100644 library/phpcassa/README.mkd create mode 100644 library/phpcassa/columnfamily.php create mode 100644 library/phpcassa/connection.php create mode 100644 library/phpcassa/doc/Makefile create mode 100644 library/phpcassa/doc/__init__.py create mode 100644 library/phpcassa/doc/changelog.rst create mode 100644 library/phpcassa/doc/conf.py create mode 100644 library/phpcassa/doc/index.rst create mode 100644 library/phpcassa/doc/installation.rst create mode 100644 library/phpcassa/doc/sphinxtogithub.py create mode 100644 library/phpcassa/doc/troubleshooting.rst create mode 100644 library/phpcassa/doc/tutorial.rst create mode 100644 library/phpcassa/test/all_tests.php create mode 100644 library/phpcassa/test/cassandra.yaml create mode 100644 library/phpcassa/test/test_autopacking.php create mode 100644 library/phpcassa/test/test_columnfamily.php create mode 100644 library/phpcassa/test/test_large_ops.php create mode 100644 library/phpcassa/test/test_pooling.php create mode 100644 library/phpcassa/thrift/Thrift.php create mode 100644 library/phpcassa/thrift/autoload.php create mode 100644 library/phpcassa/thrift/ext/thrift_protocol/config.m4 create mode 100644 library/phpcassa/thrift/ext/thrift_protocol/php_thrift_protocol.cpp create mode 100644 library/phpcassa/thrift/ext/thrift_protocol/php_thrift_protocol.h create mode 100644 library/phpcassa/thrift/packages/cassandra/Cassandra.php create mode 100644 library/phpcassa/thrift/packages/cassandra/cassandra_constants.php create mode 100644 library/phpcassa/thrift/packages/cassandra/cassandra_types.php create mode 100644 library/phpcassa/thrift/protocol/TBinaryProtocol.php create mode 100644 library/phpcassa/thrift/protocol/TBinarySerializer.php create mode 100644 library/phpcassa/thrift/protocol/TProtocol.php create mode 100644 library/phpcassa/thrift/transport/TBufferedTransport.php create mode 100644 library/phpcassa/thrift/transport/TFramedTransport.php create mode 100644 library/phpcassa/thrift/transport/THttpClient.php create mode 100644 library/phpcassa/thrift/transport/TMemoryBuffer.php create mode 100644 library/phpcassa/thrift/transport/TNullTransport.php create mode 100644 library/phpcassa/thrift/transport/TPhpStream.php create mode 100644 library/phpcassa/thrift/transport/TServerSocket.php create mode 100644 library/phpcassa/thrift/transport/TServerTransport.php create mode 100644 library/phpcassa/thrift/transport/TSocket.php create mode 100644 library/phpcassa/thrift/transport/TSocketPool.php create mode 100644 library/phpcassa/thrift/transport/TTransport.php create mode 100644 library/phpcassa/thrift/transport/TTransportFactory.php create mode 100644 library/phpcassa/uuid.php diff --git a/library/phpcassa/.gitignore b/library/phpcassa/.gitignore new file mode 100644 index 000000000..5023bbc87 --- /dev/null +++ b/library/phpcassa/.gitignore @@ -0,0 +1,34 @@ +*.swp +*.swo +*.diff +*.pyc +doc/_build/* +thrift/ext/thrift_protocol/.deps +thrift/ext/thrift_protocol/.libs/ +thrift/ext/thrift_protocol/Makefile +thrift/ext/thrift_protocol/Makefile.fragments +thrift/ext/thrift_protocol/Makefile.global +thrift/ext/thrift_protocol/Makefile.objects +thrift/ext/thrift_protocol/acinclude.m4 +thrift/ext/thrift_protocol/aclocal.m4 +thrift/ext/thrift_protocol/autom4te.cache/ +thrift/ext/thrift_protocol/build/ +thrift/ext/thrift_protocol/config.guess +thrift/ext/thrift_protocol/config.h +thrift/ext/thrift_protocol/config.h.in +thrift/ext/thrift_protocol/config.log +thrift/ext/thrift_protocol/config.nice +thrift/ext/thrift_protocol/config.status +thrift/ext/thrift_protocol/config.sub +thrift/ext/thrift_protocol/configure +thrift/ext/thrift_protocol/configure.in +thrift/ext/thrift_protocol/install-sh +thrift/ext/thrift_protocol/libtool +thrift/ext/thrift_protocol/ltmain.sh +thrift/ext/thrift_protocol/missing +thrift/ext/thrift_protocol/mkinstalldirs +thrift/ext/thrift_protocol/modules/ +thrift/ext/thrift_protocol/php_thrift_protocol.lo +thrift/ext/thrift_protocol/run-tests.php +thrift/ext/thrift_protocol/thrift_protocol.la +thrift/ext/thrift_protocol/tmp-php.ini diff --git a/library/phpcassa/AUTHORS b/library/phpcassa/AUTHORS new file mode 100644 index 000000000..8d4fd75bc --- /dev/null +++ b/library/phpcassa/AUTHORS @@ -0,0 +1,10 @@ +Hoan Ton-That (hoan.tonthat@gmail.com) +Benjamin Sussman (ben@fwix.com) +Anthony ROUX (anthony.rx43@gmail.com) +Vadim Derkach +Zach Buller (zachbuller@gmail.com) +Timandes +Todd Zusman +Yancho Georgiev (yancho@inspirestudio.net) +Pieter Maes (maescool@gmail.com) +Tyler Hobbs diff --git a/library/phpcassa/CHANGES b/library/phpcassa/CHANGES new file mode 100644 index 000000000..061aa8a5e --- /dev/null +++ b/library/phpcassa/CHANGES @@ -0,0 +1,42 @@ +Changelog +========= + +Changes in 0.7.a.3 +------------------ + +Bugfixes +^^^^^^^^ +- Typo in throwing IncompatibleAPIException +- remove() on super column families did not pack names correctly +- CassandraUtil::uuid3() param name should be $node not $null + +Features +^^^^^^^^ +- Use remove() Thrift API call instead of batch_mutate() when possible +- Allow a microsecond timestamp to be passed in for v1 UUID creation +- Log connection errors with error_log() + +Deprecated +^^^^^^^^^^ +None + +Changes in 0.7.a.2 +------------------ + +Bugfixes +^^^^^^^^ +- Fix server revival bug +- Remove print statement from Connection on connection failure + +Features +^^^^^^^^ +- Add an import() method for UUIDs to CassandraUtil to convert binary UUID + representations back into UUID objects + +Deprecated +^^^^^^^^^^^^ +None + +Changes in 0.7.a1 +----------------- +Initial release diff --git a/library/phpcassa/INSTALLING b/library/phpcassa/INSTALLING new file mode 100644 index 000000000..d78328664 --- /dev/null +++ b/library/phpcassa/INSTALLING @@ -0,0 +1,16 @@ +Using the C Extension +--------------------- + +The C extension is crucial for phpcassa's performance. + +You need to configure and make to be able to use the C extension. + + cd thrift/ext/thrift_protocol + phpize + ./configure + make + sudo make install + +Add the following line to your php.ini file: + + extension=thrift_protocol.so diff --git a/library/phpcassa/LICENSE b/library/phpcassa/LICENSE new file mode 100644 index 000000000..83a7363f5 --- /dev/null +++ b/library/phpcassa/LICENSE @@ -0,0 +1,19 @@ +http://www.opensource.org/licenses/mit-license.php + +Copyright (c) 2010, 2011 Tyler Hobbs + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/library/phpcassa/README.mkd b/library/phpcassa/README.mkd new file mode 100644 index 000000000..c9bd1a92b --- /dev/null +++ b/library/phpcassa/README.mkd @@ -0,0 +1,78 @@ +phpcassa +======== +phpcassa is a PHP client library for [Apache Cassandra](http://cassandra.apache.org). + +* Compatible with Cassandra 0.7 +* Port of [pycassa](http://github.com/pycassa/pycassa) +* Support for TBinaryProtocolAccelerated (uses a C extension) + +Documentation +------------- + +While this README includes some useful information, the official and more +thorough documentation can be found here: + +[http://thobbs.github.com/phpcassa](http://thobbs.github.com/phpcassa) + +Opening Connections +------------------- + + $conn = new Connection('Keyspace1'); + +or + + $servers[0]['host'] = '127.0.0.1'; + $servers[0]['port'] = '9160'; + $conn = new Connection('Keyspace1', $servers); + +Create a column family object +----------------------------- + + $users = new ColumnFamily($conn, 'Standard1'); // ColumnFamily + $super = new ColumnFamily($conn, 'Super1'); // SuperColumnFamily + +Inserting +--------- + + $users->insert('key', array('column1' => 'value1', 'column2' => 'value2')); + +Querying +-------- + + $users->get('key'); + $users->multiget(array('key1', 'key2')); + +Removing +-------- + + $users->remove('key1'); // removes whole row + $users->remove('key1', 'column1'); // removes 'column1' + +Other +----- + + $users->get_count('key1'); // counts the number of columns in row 'key1' + $users->get_range('key1', 'key9'); // gets all rows with keys between '1' and '9' + +Using the C Extension +--------------------- + +The C extension is crucial for phpcassa's performance. + +You need to configure and make to be able to use the C extension. + + cd thrift/ext/thrift_protocol + phpize + ./configure + make + sudo make install + +Add the following line to your php.ini file: + + extension=thrift_protocol.so + +Getting Help +------------ + +* Mailing list: [phpcassa on google groups](http://groups.google.com/group/phpcassa) +* IRC: Channel #cassandra on irc.freenode.net diff --git a/library/phpcassa/columnfamily.php b/library/phpcassa/columnfamily.php new file mode 100644 index 000000000..cae65f33a --- /dev/null +++ b/library/phpcassa/columnfamily.php @@ -0,0 +1,1194 @@ +bytes; + } + + /** + * Generate a v3 UUID + * @return string a byte[] representation of a UUID + */ + static public function uuid3($node=null, $namespace=null) { + $uuid = UUID::mint(3, $node, $namespace); + return $uuid->bytes; + } + + /** + * Generate a v4 UUID + * @return string a byte[] representation of a UUID + */ + static public function uuid4() { + $uuid = UUID::mint(4); + return $uuid->bytes; + } + + /** + * Generate a v5 UUID + * @return string a byte[] representation of a UUID + */ + static public function uuid5($node, $namespace=null) { + $uuid = UUID::mint(5, $node, $namespace); + return $uuid->bytes; + } + + /** + * Get a timestamp with microsecond precision + */ + static public function get_time() { + // By Zach Buller (zachbuller@gmail.com) + $time1 = microtime(); + settype($time1, 'string'); //convert to string to keep trailing zeroes + $time2 = explode(" ", $time1); + $sub_secs = preg_replace('/0./', '', $time2[0], 1); + $time3 = ($time2[1].$sub_secs)/100; + return $time3; + } + + /** + * Constructs an IndexExpression to be used in an IndexClause, which can + * be used with get_indexed_slices(). + * @param mixed $column_name the name of the column this expression will apply to; + * this column may or may not be indexed + * @param mixed $value the value that will be compared to column values using op + * @param classandra_IndexOperator $op the binary operator to apply to column values + * and the 'value' parameter. Defaults to testing for equality. + * @return cassandra_IndexExpression + */ + static public function create_index_expression($column_name, $value, + $op=cassandra_IndexOperator::EQ) { + $ie = new cassandra_IndexExpression(); + $ie->column_name = $column_name; + $ie->value = $value; + $ie->op = $op; + return $ie; + } + + /** + * Constructs a cassandra_IndexClause for use with get_indexed_slices(). + * @param cassandra_IndexExpression[] $expr_list the list of expressions to match; at + * least one of these must be on an indexed column + * @param string $start_key the key to begin searching from + * @param int $count the number of results to return + * @return cassandra_IndexClause + */ + static public function create_index_clause($expr_list, $start_key='', + $count=ColumnFamily::DEFAULT_COLUMN_COUNT) { + $ic = new cassandra_IndexClause(); + $ic->expressions = $expr_list; + $ic->start_key = $start_key; + $ic->count = $count; + return $ic; + } +} + +/** + * Representation of a ColumnFamily in Cassandra. This may be used for + * standard column families or super column families. All data insertions, + * deletions, or retrievals will go through a ColumnFamily. + * + * @package phpcassa + * @subpackage columnfamily + */ +class ColumnFamily { + + /** The default limit to the number of rows retrieved in queries. */ + const DEFAULT_ROW_COUNT = 100; // default max # of rows for get_range() + /** The default limit to the number of columns retrieved in queries. */ + const DEFAULT_COLUMN_COUNT = 100; // default max # of columns for get() + /** The maximum number that can be returned by get_count(). */ + const MAX_COUNT = 2147483647; # 2^31 - 1 + + const DEFAULT_BUFFER_SIZE = 8096; + + public $client; + private $column_family; + private $is_super; + private $cf_data_type; + private $col_name_type; + private $supercol_name_type; + private $col_type_dict; + + /** @var bool whether or not column names are automatically packed/unpacked */ + public $autopack_names; + /** @var bool whether or not column values are automatically packed/unpacked */ + public $autopack_values; + /** @var cassandra_ConsistencyLevel the default read consistency level */ + public $read_consistency_level; + /** @var cassandra_ConsistencyLevel the default write consistency level */ + public $write_consistency_level; + + /** + * Constructs a ColumnFamily. + * + * @param Connection $connection the connection to use for this ColumnFamily + * @param string $column_family the name of the column family in Cassandra + * @param bool $autopack_names whether or not to automatically convert column names + * to and from their binary representation in Cassandra + * based on their comparator type + * @param bool $autopack_values whether or not to automatically convert column values + * to and from their binary representation in Cassandra + * based on their validator type + * @param cassandra_ConsistencyLevel $read_consistency_level the default consistency + * level for read operations on this column family + * @param cassandra_ConsistencyLevel $write_consistency_level the default consistency + * level for write operations on this column family + * @param int $buffer_size When calling `get_range`, the intermediate results need + * to be buffered if we are fetching many rows, otherwise the Cassandra + * server will overallocate memory and fail. This is the size of + * that buffer in number of rows. + */ + public function __construct($pool, + $column_family, + $autopack_names=true, + $autopack_values=true, + $read_consistency_level=cassandra_ConsistencyLevel::ONE, + $write_consistency_level=cassandra_ConsistencyLevel::ONE, + $buffer_size=self::DEFAULT_BUFFER_SIZE) { + + $this->pool = $pool; + $this->column_family = $column_family; + $this->autopack_names = $autopack_names; + $this->autopack_values = $autopack_values; + $this->read_consistency_level = $read_consistency_level; + $this->write_consistency_level = $write_consistency_level; + $this->buffer_size = $buffer_size; + + $this->cf_data_type = 'BytesType'; + $this->col_name_type = 'BytesType'; + $this->supercol_name_type = 'BytesType'; + $this->col_type_dict = array(); + + $ks = $this->pool->describe_keyspace(); + + $cf_def = null; + foreach($ks->cf_defs as $cfdef) { + if ($cfdef->name == $this->column_family) { + $cf_def = $cfdef; + break; + } + } + if ($cf_def == null) + throw new cassandra_NotFoundException(); + + $this->is_super = $cf_def->column_type == 'Super'; + if ($this->autopack_names) { + if (!$this->is_super) { + $this->col_name_type = self::extract_type_name($cfdef->comparator_type); + } else { + $this->col_name_type = self::extract_type_name($cfdef->subcomparator_type); + $this->supercol_name_type = self::extract_type_name($cfdef->comparator_type); + } + } + if ($this->autopack_values) { + $this->cf_data_type = self::extract_type_name($cfdef->default_validation_class); + foreach($cfdef->column_metadata as $coldef) { + $this->col_type_dict[$coldef->name] = + self::extract_type_name($coldef->validation_class); + } + } + } + + /** + * Fetch a row from this column family. + * + * @param string $key row key to fetch + * @param mixed[] $columns limit the columns or super columns fetched to this list + * @param mixed $column_start only fetch columns with name >= this + * @param mixed $column_finish only fetch columns with name <= this + * @param bool $column_reversed fetch the columns in reverse order + * @param int $column_count limit the number of columns returned to this amount + * @param mixed $super_column return only columns in this super column + * @param cassandra_ConsistencyLevel $read_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * + * @return mixed array(column_name => column_value) + */ + public function get($key, + $columns=null, + $column_start="", + $column_finish="", + $column_reversed=False, + $column_count=self::DEFAULT_COLUMN_COUNT, + $super_column=null, + $read_consistency_level=null) { + + $column_parent = $this->create_column_parent($super_column); + $predicate = $this->create_slice_predicate($columns, $column_start, $column_finish, + $column_reversed, $column_count); + + $resp = $this->pool->call("get_slice", + $key, $column_parent, $predicate, + $this->rcl($read_consistency_level)); + + if (count($resp) == 0) + throw new cassandra_NotFoundException(); + + return $this->supercolumns_or_columns_to_array($resp); + } + + /** + * Fetch a set of rows from this column family. + * + * @param string[] $keys row keys to fetch + * @param mixed[] $columns limit the columns or super columns fetched to this list + * @param mixed $column_start only fetch columns with name >= this + * @param mixed $column_finish only fetch columns with name <= this + * @param bool $column_reversed fetch the columns in reverse order + * @param int $column_count limit the number of columns returned to this amount + * @param mixed $super_column return only columns in this super column + * @param cassandra_ConsistencyLevel $read_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * @param int $buffer_size the number of keys to multiget at a single time. If your + * rows are large, having a high buffer size gives poor performance; if your + * rows are small, consider increasing this value. + * + * @return mixed array(key => array(column_name => column_value)) + */ + public function multiget($keys, + $columns=null, + $column_start="", + $column_finish="", + $column_reversed=False, + $column_count=self::DEFAULT_COLUMN_COUNT, + $super_column=null, + $read_consistency_level=null, + $buffer_size=16) { + + $column_parent = $this->create_column_parent($super_column); + $predicate = $this->create_slice_predicate($columns, $column_start, $column_finish, + $column_reversed, $column_count); + + $ret = array(); + foreach($keys as $key) { + $ret[$key] = null; + } + + $resp = array(); + if(count($keys) <= $buffer_size) { + $resp = $this->pool->call("multiget_slice", + $keys, $column_parent, $predicate, + $this->rcl($read_consistency_level)); + } else { + $subset_keys = array(); + $i = 0; + foreach($keys as $key) { + $i += 1; + $subset_keys[] = $key; + if ($i == $buffer_size) { + $sub_resp = $this->pool->call("multiget_slice", + $subset_keys, $column_parent, $predicate, + $this->rcl($read_consistency_level)); + $subset_keys = array(); + $i = 0; + $resp = array_merge($resp, $sub_resp); + } + } + if (count($subset_keys) != 0) { + $sub_resp = $this->pool->call("multiget_slice", + $subset_keys, $column_parent, $predicate, + $this->rcl($read_consistency_level)); + $resp = array_merge($resp, $sub_resp); + } + } + + $non_empty_keys = array(); + foreach($resp as $key => $val) { + if (count($val) > 0) { + $non_empty_keys[] = $key; + $ret[$key] = $this->supercolumns_or_columns_to_array($val); + } + } + + foreach($keys as $key) { + if (!in_array($key, $non_empty_keys)) + unset($ret[$key]); + } + return $ret; + } + + /** + * Count the number of columns in a row. + * + * @param string $key row to be counted + * @param mixed[] $columns limit the possible columns or super columns counted to this list + * @param mixed $column_start only count columns with name >= this + * @param mixed $column_finish only count columns with name <= this + * @param mixed $super_column count only columns in this super column + * @param cassandra_ConsistencyLevel $read_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * + * @return int + */ + public function get_count($key, + $columns=null, + $column_start='', + $column_finish='', + $super_column=null, + $read_consistency_level=null) { + + $column_parent = $this->create_column_parent($super_column); + $predicate = $this->create_slice_predicate($columns, $column_start, $column_finish, + false, self::MAX_COUNT); + + return $this->pool->call("get_count", $key, $column_parent, $predicate, + $this->rcl($read_consistency_level)); + } + + /** + * Count the number of columns in a set of rows. + * + * @param string[] $keys rows to be counted + * @param mixed[] $columns limit the possible columns or super columns counted to this list + * @param mixed $column_start only count columns with name >= this + * @param mixed $column_finish only count columns with name <= this + * @param mixed $super_column count only columns in this super column + * @param cassandra_ConsistencyLevel $read_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * + * @return mixed array(row_key => row_count) + */ + public function multiget_count($keys, + $columns=null, + $column_start='', + $column_finish='', + $super_column=null, + $read_consistency_level=null) { + + $column_parent = $this->create_column_parent($super_column); + $predicate = $this->create_slice_predicate($columns, $column_start, $column_finish, + false, self::MAX_COUNT); + + return $this->pool->call("multiget_count", $keys, $column_parent, $predicate, + $this->rcl($read_consistency_level)); + } + + /** + * Get an iterator over a range of rows. + * + * @param string $key_start fetch rows with a key >= this + * @param string $key_finish fetch rows with a key <= this + * @param int $row_count limit the number of rows returned to this amount + * @param mixed[] $columns limit the columns or super columns fetched to this list + * @param mixed $column_start only fetch columns with name >= this + * @param mixed $column_finish only fetch columns with name <= this + * @param bool $column_reversed fetch the columns in reverse order + * @param int $column_count limit the number of columns returned to this amount + * @param mixed $super_column return only columns in this super column + * @param cassandra_ConsistencyLevel $read_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * @param int $buffer_size When calling `get_range`, the intermediate results need + * to be buffered if we are fetching many rows, otherwise the Cassandra + * server will overallocate memory and fail. This is the size of + * that buffer in number of rows. + * + * @return RangeColumnFamilyIterator + */ + public function get_range($key_start="", + $key_finish="", + $row_count=self::DEFAULT_ROW_COUNT, + $columns=null, + $column_start="", + $column_finish="", + $column_reversed=false, + $column_count=self::DEFAULT_COLUMN_COUNT, + $super_column=null, + $read_consistency_level=null, + $buffer_size=null) { + + if ($buffer_size == null) + $buffer_size = $this->buffer_size; + if ($buffer_size < 2) { + $ire = new cassandra_InvalidRequestException(); + $ire->message = 'buffer_size cannot be less than 2'; + throw $ire; + } + + $column_parent = $this->create_column_parent($super_column); + $predicate = $this->create_slice_predicate($columns, $column_start, + $column_finish, $column_reversed, + $column_count); + + return new RangeColumnFamilyIterator($this, $buffer_size, + $key_start, $key_finish, $row_count, + $column_parent, $predicate, + $this->rcl($read_consistency_level)); + } + + /** + * Fetch a set of rows from this column family based on an index clause. + * + * @param cassandra_IndexClause $index_clause limits the keys that are returned based + * on expressions that compare the value of a column to a given value. At least + * one of the expressions in the IndexClause must be on an indexed column. You + * can use the CassandraUtil::create_index_expression() and + * CassandraUtil::create_index_clause() methods to help build this. + * @param mixed[] $columns limit the columns or super columns fetched to this list + * @param mixed $column_start only fetch columns with name >= this + * @param mixed $column_finish only fetch columns with name <= this + * @param bool $column_reversed fetch the columns in reverse order + * @param int $column_count limit the number of columns returned to this amount + * @param mixed $super_column return only columns in this super column + * @param cassandra_ConsistencyLevel $read_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * + * @return mixed array(row_key => array(column_name => column_value)) + */ + public function get_indexed_slices($index_clause, + $columns=null, + $column_start='', + $column_finish='', + $column_reversed=false, + $column_count=self::DEFAULT_COLUMN_COUNT, + $super_column=null, + $read_consistency_level=null, + $buffer_size=null) { + + if ($buffer_size == null) + $buffer_size = $this->buffer_size; + if ($buffer_size < 2) { + $ire = new cassandra_InvalidRequestException(); + $ire->message = 'buffer_size cannot be less than 2'; + throw $ire; + } + + $new_clause = new cassandra_IndexClause(); + foreach($index_clause->expressions as $expr) { + $new_expr = new cassandra_IndexExpression(); + $new_expr->value = $this->pack_value($expr->value, $expr->column_name); + $new_expr->column_name = $this->pack_name($expr->column_name); + $new_expr->op = $expr->op; + $new_clause->expressions[] = $new_expr; + } + $new_clause->start_key = $index_clause->start_key; + $new_clause->count = $index_clause->count; + + $column_parent = $this->create_column_parent($super_column); + $predicate = $this->create_slice_predicate($columns, $column_start, + $column_finish, $column_reversed, + $column_count); + + return new IndexedColumnFamilyIterator($this, $new_clause, $buffer_size, + $column_parent, $predicate, + $this->rcl($read_consistency_level)); + } + + /** + * Insert or update columns in a row. + * + * @param string $key the row to insert or update the columns in + * @param mixed $columns array(column_name => column_value) the columns to insert or update + * @param int $timestamp the timestamp to use for this insertion. Leaving this as null will + * result in a timestamp being generated for you + * @param int $ttl time to live for the columns; after ttl seconds they will be deleted + * @param cassandra_ConsistencyLevel $write_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * + * @return int the timestamp for the operation + */ + public function insert($key, + $columns, + $timestamp=null, + $ttl=null, + $write_consistency_level=null) { + + if ($timestamp == null) + $timestamp = CassandraUtil::get_time(); + + $cfmap = array(); + $cfmap[$key][$this->column_family] = $this->array_to_mutation($columns, $timestamp, $ttl); + + return $this->pool->call("batch_mutate", $cfmap, $this->wcl($write_consistency_level)); + } + + /** + * Insert or update columns in multiple rows. Note that this operation is only atomic + * per row. + * + * @param array $rows an array of keys, each of which maps to an array of columns. This + * looks like array(key => array(column_name => column_value)) + * @param int $timestamp the timestamp to use for these insertions. Leaving this as null will + * result in a timestamp being generated for you + * @param int $ttl time to live for the columns; after ttl seconds they will be deleted + * @param cassandra_ConsistencyLevel $write_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * + * @return int the timestamp for the operation + */ + public function batch_insert($rows, $timestamp=null, $ttl=null, $write_consistency_level=null) { + if ($timestamp == null) + $timestamp = CassandraUtil::get_time(); + + $cfmap = array(); + foreach($rows as $key => $columns) + $cfmap[$key][$this->column_family] = $this->array_to_mutation($columns, $timestamp, $ttl); + + return $this->pool->call("batch_mutate", $cfmap, $this->wcl($write_consistency_level)); + } + + /** + * Remove columns from a row. + * + * @param string $key the row to remove columns from + * @param mixed[] $columns the columns to remove. If null, the entire row will be removed. + * @param mixed $super_column only remove this super column + * @param cassandra_ConsistencyLevel $write_consistency_level affects the guaranteed + * number of nodes that must respond before the operation returns + * + * @return int the timestamp for the operation + */ + public function remove($key, $columns=null, $super_column=null, $write_consistency_level=null) { + + $timestamp = CassandraUtil::get_time(); + + if ($columns == null || count($columns) == 1) + { + $cp = new cassandra_ColumnPath(); + $cp->column_family = $this->column_family; + $cp->super_column = $this->pack_name($super_column, true); + if ($columns != null) { + if ($this->is_super && $super_column == null) + $cp->super_column = $this->pack_name($columns[0], true); + else + $cp->column = $this->pack_name($columns[0], false); + } + return $this->pool->call("remove", $key, $cp, $timestamp, + $this->wcl($write_consistency_level)); + } + + $deletion = new cassandra_Deletion(); + $deletion->timestamp = $timestamp; + $deletion->super_column = $this->pack_name($super_column, true); + + if ($columns != null) { + $predicate = $this->create_slice_predicate($columns, '', '', false, + self::DEFAULT_COLUMN_COUNT); + $deletion->predicate = $predicate; + } + + $mutation = new cassandra_Mutation(); + $mutation->deletion = $deletion; + + $mut_map = array($key => array($this->column_family => array($mutation))); + + return $this->pool->call("batch_mutate", $mut_map, $this->wcl($write_consistency_level)); + } + + /* + * Mark the entire column family as deleted. + * + * From the user's perspective a successful call to truncate will result + * complete data deletion from cfname. Internally, however, disk space + * will not be immediatily released, as with all deletes in cassandra, + * this one only marks the data as deleted. + * + * The operation succeeds only if all hosts in the cluster at available + * and will throw an UnavailableException if some hosts are down. + */ + public function truncate() { + return $this->pool->call("truncate", $this->column_family); + } + + + /********************* Helper functions *************************/ + + private static $TYPES = array('BytesType', 'LongType', 'IntegerType', + 'UTF8Type', 'AsciiType', 'LexicalUUIDType', + 'TimeUUIDType'); + + private static function extract_type_name($type_string) { + if ($type_string == null or $type_string == '') + return 'BytesType'; + + $index = strrpos($type_string, '.'); + if ($index == false) + return 'BytesType'; + + $type = substr($type_string, $index + 1); + if (!in_array($type, self::$TYPES)) + return 'BytesType'; + + return $type; + } + + private function rcl($read_consistency_level) { + if ($read_consistency_level == null) + return $this->read_consistency_level; + else + return $read_consistency_level; + } + + private function wcl($write_consistency_level) { + if ($write_consistency_level == null) + return $this->write_consistency_level; + else + return $write_consistency_level; + } + + private function create_slice_predicate($columns, $column_start, $column_finish, + $column_reversed, $column_count) { + + $predicate = new cassandra_SlicePredicate(); + if ($columns !== null) { + $packed_cols = array(); + foreach($columns as $col) + $packed_cols[] = $this->pack_name($col, $this->is_super); + $predicate->column_names = $packed_cols; + } else { + if ($column_start != null and $column_start != '') + $column_start = $this->pack_name($column_start, + $this->is_super, + self::SLICE_START); + if ($column_finish != null and $column_finish != '') + $column_finish = $this->pack_name($column_finish, + $this->is_super, + self::SLICE_FINISH); + + $slice_range = new cassandra_SliceRange(); + $slice_range->count = $column_count; + $slice_range->reversed = $column_reversed; + $slice_range->start = $column_start; + $slice_range->finish = $column_finish; + $predicate->slice_range = $slice_range; + } + return $predicate; + } + + private function create_column_parent($super_column=null) { + $column_parent = new cassandra_ColumnParent(); + $column_parent->column_family = $this->column_family; + $column_parent->super_column = $this->pack_name($super_column, true); + return $column_parent; + } + + const NON_SLICE = 0; + const SLICE_START = 1; + const SLICE_FINISH = 2; + + private function pack_name($value, $is_supercol_name=false, $slice_end=self::NON_SLICE) { + if (!$this->autopack_names) + return $value; + if ($value == null) + return; + if ($is_supercol_name) + $d_type = $this->supercol_name_type; + else + $d_type = $this->col_name_type; + + if ($d_type == 'TimeUUIDType') { + if ($slice_end) { + + } else { + + } + } + + return $this->pack($value, $d_type); + } + + private function unpack_name($b, $is_supercol_name=false) { + if (!$this->autopack_names) + return $b; + if ($b == null) + return; + + if ($is_supercol_name) + $d_type = $this->supercol_name_type; + else + $d_type = $this->col_name_type; + + return $this->unpack($b, $d_type); + } + + private function get_data_type_for_col($col_name) { + if (!in_array($col_name, array_keys($this->col_type_dict))) + return $this->cf_data_type; + else + return $this->col_type_dict[$col_name]; + } + + private function pack_value($value, $col_name) { + if (!$this->autopack_values) + return $value; + return $this->pack($value, $this->get_data_type_for_col($col_name)); + } + + private function unpack_value($value, $col_name) { + if (!$this->autopack_values) + return $value; + return $this->unpack($value, $this->get_data_type_for_col($col_name)); + } + + private static function unpack_str($str, $len) { + $tmp_arr = unpack("c".$len."chars", $str); + $out_str = ""; + foreach($tmp_arr as $v) + if($v > 0) { $out_str .= chr($v); } + return $out_str; + } + + private static function pack_str($str, $len) { + $out_str = ""; + for($i=0; $i<$len; $i++) + $out_str .= pack("c", ord(substr($str, $i, 1))); + return $out_str; + } + + private static function pack_long($value) { + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + $neg = $value < 0; + + if ($neg) { + $value *= -1; + } + + $hi = (int)($value / 4294967296); + $lo = (int)$value; + + if ($neg) { + $hi = ~$hi; + $lo = ~$lo; + if (($lo & (int)0xffffffff) == (int)0xffffffff) { + $lo = 0; + $hi++; + } else { + $lo++; + } + } + $data = pack('N2', $hi, $lo); + } else { + $hi = $value >> 32; + $lo = $value & 0xFFFFFFFF; + $data = pack('N2', $hi, $lo); + } + return $data; + } + + private static function unpack_long($data) { + $arr = unpack('N2', $data); + + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + + $hi = $arr[1]; + $lo = $arr[2]; + $isNeg = $hi < 0; + + // Check for a negative + if ($isNeg) { + $hi = ~$hi & (int)0xffffffff; + $lo = ~$lo & (int)0xffffffff; + + if ($lo == (int)0xffffffff) { + $hi++; + $lo = 0; + } else { + $lo++; + } + } + + // Force 32bit words in excess of 2G to pe positive - we deal wigh sign + // explicitly below + + if ($hi & (int)0x80000000) { + $hi &= (int)0x7fffffff; + $hi += 0x80000000; + } + + if ($lo & (int)0x80000000) { + $lo &= (int)0x7fffffff; + $lo += 0x80000000; + } + + $value = $hi * 4294967296 + $lo; + + if ($isNeg) + $value = 0 - $value; + + } else { + // Upcast negatives in LSB bit + if ($arr[2] & 0x80000000) + $arr[2] = $arr[2] & 0xffffffff; + + // Check for a negative + if ($arr[1] & 0x80000000) { + $arr[1] = $arr[1] & 0xffffffff; + $arr[1] = $arr[1] ^ 0xffffffff; + $arr[2] = $arr[2] ^ 0xffffffff; + $value = 0 - $arr[1]*4294967296 - $arr[2] - 1; + } else { + $value = $arr[1]*4294967296 + $arr[2]; + } + } + return $value; + } + + private function pack($value, $data_type) { + if ($data_type == 'LongType') + return self::pack_long($value); + else if ($data_type == 'IntegerType') + return pack('N', $value); // Unsigned 32bit big-endian + else if ($data_type == 'AsciiType') + return self::pack_str($value, strlen($value)); + else if ($data_type == 'UTF8Type') { + if (mb_detect_encoding($value, "UTF-8") != "UTF-8") + $value = utf8_encode($value); + return self::pack_str($value, strlen($value)); + } + else if ($data_type == 'TimeUUIDType' or $data_type == 'LexicalUUIDType') + return self::pack_str($value, 16); + else + return $value; + } + + private function unpack($value, $data_type) { + if ($data_type == 'LongType') + return self::unpack_long($value); + else if ($data_type == 'IntegerType') { + $res = unpack('N', $value); + return $res[1]; + } + else if ($data_type == 'AsciiType') + return self::unpack_str($value, strlen($value)); + else if ($data_type == 'UTF8Type') + return utf8_decode(self::unpack_str($value, strlen($value))); + else if ($data_type == 'TimeUUIDType' or $data_type == 'LexicalUUIDType') + return $value; + else + return $value; + } + + public function keyslices_to_array($keyslices) { + $ret = null; + foreach($keyslices as $keyslice) { + $key = $keyslice->key; + $columns = $keyslice->columns; + $ret[$key] = $this->supercolumns_or_columns_to_array($columns); + } + return $ret; + } + + private function supercolumns_or_columns_to_array($array_of_c_or_sc) { + $ret = null; + foreach($array_of_c_or_sc as $c_or_sc) { + if($c_or_sc->column) { // normal columns + $name = $this->unpack_name($c_or_sc->column->name, false); + $value = $this->unpack_value($c_or_sc->column->value, $c_or_sc->column->name); + $ret[$name] = $value; + } else if($c_or_sc->super_column) { // super columns + $name = $this->unpack_name($c_or_sc->super_column->name, true); + $columns = $c_or_sc->super_column->columns; + $ret[$name] = $this->columns_to_array($columns); + } + } + return $ret; + } + + private function columns_to_array($array_of_c) { + $ret = null; + foreach($array_of_c as $c) { + $name = $this->unpack_name($c->name, false); + $value = $this->unpack_value($c->value, $c->name); + $ret[$name] = $value; + } + return $ret; + } + + private function array_to_mutation($array, $timestamp=null, $ttl=null) { + if(empty($timestamp)) $timestamp = CassandraUtil::get_time(); + + $c_or_sc = $this->array_to_supercolumns_or_columns($array, $timestamp, $ttl); + $ret = null; + foreach($c_or_sc as $row) { + $mutation = new cassandra_Mutation(); + $mutation->column_or_supercolumn = $row; + $ret[] = $mutation; + } + return $ret; + } + + private function array_to_supercolumns_or_columns($array, $timestamp=null, $ttl=null) { + if(empty($timestamp)) $timestamp = CassandraUtil::get_time(); + + $ret = null; + foreach($array as $name => $value) { + $c_or_sc = new cassandra_ColumnOrSuperColumn(); + if(is_array($value)) { + $c_or_sc->super_column = new cassandra_SuperColumn(); + $c_or_sc->super_column->name = $this->pack_name($name, true); + $c_or_sc->super_column->columns = $this->array_to_columns($value, $timestamp, $ttl); + $c_or_sc->super_column->timestamp = $timestamp; + } else { + $c_or_sc = new cassandra_ColumnOrSuperColumn(); + $c_or_sc->column = new cassandra_Column(); + $c_or_sc->column->name = $this->pack_name($name, false); + $c_or_sc->column->value = $this->pack_value($value, $name); + $c_or_sc->column->timestamp = $timestamp; + $c_or_sc->column->ttl = $ttl; + } + $ret[] = $c_or_sc; + } + + return $ret; + } + + private function array_to_columns($array, $timestamp=null, $ttl=null) { + if(empty($timestamp)) $timestamp = CassandraUtil::get_time(); + + $ret = null; + foreach($array as $name => $value) { + $column = new cassandra_Column(); + $column->name = $this->pack_name($name, false); + $column->value = $this->pack_value($value, $name); + $column->timestamp = $timestamp; + $column->ttl = $ttl; + $ret[] = $column; + } + return $ret; + } +} + +class ColumnFamilyIterator implements Iterator { + + protected $column_family; + protected $buffer_size; + protected $row_count; + protected $read_consistency_level; + protected $column_parent, $predicate; + + protected $current_buffer; + protected $next_start_key, $orig_start_key; + protected $is_valid; + protected $rows_seen; + + protected function __construct($column_family, + $buffer_size, + $row_count, + $orig_start_key, + $column_parent, + $predicate, + $read_consistency_level) { + + $this->column_family = $column_family; + $this->buffer_size = $buffer_size; + $this->row_count = $row_count; + $this->orig_start_key = $orig_start_key; + $this->next_start_key = $orig_start_key; + $this->column_parent = $column_parent; + $this->predicate = $predicate; + $this->read_consistency_level = $read_consistency_level; + + if ($this->row_count != null) + $this->buffer_size = min($this->row_count, $buffer_size); + } + + public function rewind() { + // Setup first buffer + $this->rows_seen = 0; + $this->is_valid = true; + $this->next_start_key = $this->orig_start_key; + $this->get_buffer(); + + # If nothing was inserted, this may happen + if (count($this->current_buffer) == 0) { + $this->is_valid = false; + return; + } + + # If the very first row is a deleted row + if (count(current($this->current_buffer)) == 0) + $this->next(); + else + $this->rows_seen++; + } + + public function current() { + return current($this->current_buffer); + } + + public function key() { + return key($this->current_buffer); + } + + public function valid() { + return $this->is_valid; + } + + public function next() { + $beyond_last_row = false; + + # If we haven't run off the end + if ($this->current_buffer != null) + { + # Save this key incase we run off the end + $this->next_start_key = key($this->current_buffer); + next($this->current_buffer); + + if (count(current($this->current_buffer)) == 0) + { + # this is an empty row, skip it + $key = key($this->current_buffer); + $this->next(); + } + else # count > 0 + { + $key = key($this->current_buffer); + $beyond_last_row = !isset($key); + + if (!$beyond_last_row) + { + $this->rows_seen++; + if ($this->rows_seen > $this->row_count) { + $this->is_valid = false; + return; + } + } + } + } + else + { + $beyond_last_row = true; + } + + if($beyond_last_row && $this->current_page_size < $this->expected_page_size) + { + # The page was shorter than we expected, so we know that this + # was the last page in the column family + $this->is_valid = false; + } + else if($beyond_last_row) + { + # We've reached the end of this page, but there should be more + # in the CF + + # Get the next buffer (next_start_key has already been set) + $this->get_buffer(); + + # If the result set is 1, we can stop because the first item + # should always be skipped + if(count($this->current_buffer) == 1) + $this->is_valid = false; + else + $this->next(); + } + } +} + +/** + * Iterates over a column family row-by-row, typically with only a subset + * of each row's columns. + * + * @package phpcassa + * @subpackage columnfamily + */ +class RangeColumnFamilyIterator extends ColumnFamilyIterator { + + private $key_start, $key_finish; + + public function __construct($column_family, $buffer_size, + $key_start, $key_finish, $row_count, + $column_parent, $predicate, + $read_consistency_level) { + + $this->key_start = $key_start; + $this->key_finish = $key_finish; + + parent::__construct($column_family, $buffer_size, $row_count, + $key_start, $column_parent, $predicate, + $read_consistency_level); + } + + protected function get_buffer() { + if($this->row_count != null) + $buff_sz = min($this->row_count - $this->rows_seen + 1, $this->buffer_size); + else + $buff_sz = $this->buffer_size; + $this->expected_page_size = $buff_sz; + + $key_range = new cassandra_KeyRange(); + $key_range->start_key = $this->next_start_key; + $key_range->end_key = $this->key_finish; + $key_range->count = $buff_sz; + + $resp = $this->column_family->pool->call("get_range_slices", $this->column_parent, $this->predicate, + $key_range, $this->read_consistency_level); + + $this->current_buffer = $this->column_family->keyslices_to_array($resp); + $this->current_page_size = count($this->current_buffer); + } +} + +/** + * Iterates over a column family row-by-row, typically with only a subset + * of each row's columns. + * + * @package phpcassa + * @subpackage columnfamily + */ +class IndexedColumnFamilyIterator extends ColumnFamilyIterator { + + private $index_clause; + + public function __construct($column_family, $index_clause, $buffer_size, + $column_parent, $predicate, + $read_consistency_level) { + + $this->index_clause = $index_clause; + $row_count = $index_clause->count; + $orig_start_key = $index_clause->start_key; + + parent::__construct($column_family, $buffer_size, $row_count, + $orig_start_key, $column_parent, $predicate, + $read_consistency_level); + } + + protected function get_buffer() { + # Figure out how many rows we need to get and record that + if($this->row_count != null) + $this->index_clause->count = min($this->row_count - $this->rows_seen + 1, $this->buffer_size); + else + $this->index_clause->count = $this->buffer_size; + $this->expected_page_size = $this->index_clause->count; + + $this->index_clause->start_key = $this->next_start_key; + $resp = $this->column_family->pool->call("get_indexed_slices", + $this->column_parent, $this->index_clause, $this->predicate, + $this->read_consistency_level); + + $this->current_buffer = $this->column_family->keyslices_to_array($resp); + $this->current_page_size = count($this->current_buffer); + } +} + +?> diff --git a/library/phpcassa/connection.php b/library/phpcassa/connection.php new file mode 100644 index 000000000..81f5d936d --- /dev/null +++ b/library/phpcassa/connection.php @@ -0,0 +1,283 @@ +server = $server; + $server = explode(':', $server); + $host = $server[0]; + if(count($server) == 2) + $port = (int)$server[1]; + else + $port = self::DEFAULT_PORT; + $socket = new TSocket($host, $port); + + if($send_timeout) $socket->setSendTimeout($send_timeout); + if($recv_timeout) $socket->setRecvTimeout($recv_timeout); + + if($framed_transport) { + $transport = new TFramedTransport($socket, true, true); + } else { + $transport = new TBufferedTransport($socket, 1024, 1024); + } + + $client = new CassandraClient(new TBinaryProtocolAccelerated($transport)); + $transport->open(); + + $server_version = explode(".", $client->describe_version()); + $server_version = $server_version[0]; + if ($server_version < self::LOWEST_COMPATIBLE_VERSION) { + $ver = self::LOWEST_COMPATIBLE_VERSION; + throw new IncompatibleAPIException("The server's API version is too ". + "low to be comptible with phpcassa (server: $server_version, ". + "lowest compatible version: $ver)"); + } + + $client->set_keyspace($keyspace); + + if ($credentials) { + $request = new cassandra_AuthenticationRequest($credentials); + $client->login($request); + } + + $this->keyspace = $keyspace; + $this->client = $client; + $this->transport = $transport; + $this->op_count = 0; + } + + public function close() { + $this->transport->close(); + } + +} + +class ConnectionPool { + + const BASE_BACKOFF = 0.1; + const MICROS = 1000000; + const MAX_RETRIES = 2147483647; // 2^31 - 1 + private static $default_servers = array('localhost:9160'); + + public $keyspace; + private $servers; + private $pool_size; + private $timeout; + private $recycle; + private $max_retries; + private $credentials; + private $framed_transport; + private $queue; + private $keyspace_description = NULL; + + public function __construct($keyspace, + $servers=NULL, + $max_retries=5, + $send_timeout=5000, + $recv_timeout=5000, + $recycle=10000, + $credentials=NULL, + $framed_transport=true) + { + $this->keyspace = $keyspace; + $this->send_timeout = $send_timeout; + $this->recv_timeout = $recv_timeout; + $this->recycle = $recycle; + $this->max_retries = $max_retries; + $this->credentials = $credentials; + $this->framed_transport = $framed_transport; + + $this->stats = array( + 'created' => 0, + 'failed' => 0, + 'recycled' => 0); + + if ($servers == NULL) + $servers = self::$default_servers; + $this->servers = $servers; + $this->pool_size = max(count($this->servers) * 2, 5); + + $this->queue = array(); + + // Randomly permute the server list + $n = count($servers); + if ($n > 1) { + foreach (range(0, $n - 1) as $i) { + $j = rand($i, $n - 1); + $temp = $servers[$j]; + $servers[$j] = $servers[$i]; + $servers[$i] = $temp; + } + } + $this->list_position = 0; + + foreach(range(0, $this->pool_size - 1) as $i) + $this->make_conn(); + } + + private function make_conn() { + // Keep trying to make a new connection, stopping after we've + // tried every server twice + $err = ""; + foreach (range(1, count($this->servers) * 2) as $i) + { + try { + $this->list_position = ($this->list_position + 1) % count($this->servers); + $new_conn = new ConnectionWrapper($this->keyspace, $this->servers[$this->list_position], + $this->credentials, $this->framed_transport, $this->send_timeout, $this->recv_timeout); + array_push($this->queue, $new_conn); + $this->stats['created'] += 1; + return; + } catch (TException $e) { + $h = $this->servers[$this->list_position]; + $err = (string)$e; + error_log("Error connecting to $h: $err", 0); + $this->stats['failed'] += 1; + } + } + throw new NoServerAvailable("An attempt was made to connect to every server twice, but " . + "all attempts failed. The last error was: $err"); + } + + public function get() { + return array_shift($this->queue); + } + + public function return_connection($connection) { + if ($connection->op_count >= $this->recycle) { + $this->stats['recycled'] += 1; + $connection->close(); + $this->make_conn(); + $connection = $this->get(); + } + array_push($this->queue, $connection); + } + + public function describe_keyspace() { + if (NULL === $this->keyspace_description) { + $this->keyspace_description = $this->call("describe_keyspace", $this->keyspace); + } + + return $this->keyspace_description; + } + + public function dispose() { + foreach($this->queue as $conn) + $conn->close(); + } + + public function close() { + $this->dispose(); + } + + public function stats() { + return $this->stats; + } + + public function call() { + $args = func_get_args(); // Get all of the args passed to this function + $f = array_shift($args); // pull the function from the beginning + + $retry_count = 0; + if ($this->max_retries == -1) + $tries = self::MAX_RETRIES; + elseif ($this->max_retries == 0) + $tries = 1; + else + $tries = $this->max_retries + 1; + + foreach (range(1, $tries) as $retry_count) { + $conn = $this->get(); + + $conn->op_count += 1; + try { + $resp = call_user_func_array(array($conn->client, $f), $args); + $this->return_connection($conn); + return $resp; + } catch (cassandra_TimedOutException $toe) { + $last_err = $toe; + $this->handle_conn_failure($conn, $f, $toe, $retry_count); + } catch (cassandra_UnavailableException $ue) { + $last_err = $ue; + $this->handle_conn_failure($conn, $f, $ue, $retry_count); + } catch (TTransportException $tte) { + $last_err = $tte; + $this->handle_conn_failure($conn, $f, $tte, $retry_count); + } + } + throw new MaxRetriesException("An attempt to execute $f failed $tries times.". + " The last error was " . (string)$last_err); + } + + private function handle_conn_failure($conn, $f, $exc, $retry_count) { + $err = (string)$exc; + error_log("Error performing $f on $conn->server: $err", 0); + $conn->close(); + $this->stats['failed'] += 1; + usleep(self::BASE_BACKOFF * pow(2, $retry_count) * self::MICROS); + $this->make_conn(); + } + +} + +class Connection extends ConnectionPool { + // Here for backwards compatibility reasons only + public function __construct($keyspace, + $servers=NULL, + $max_retries=5, + $send_timeout=5000, + $recv_timeout=5000, + $recycle=10000, + $credentials=NULL, + $framed_transport=true) + { + if ($servers != NULL) { + $new_servers = array(); + foreach ($servers as $server) { + $new_servers[] = $server['host'] . ':' . (string)$server['port']; + } + } else { + $new_servers = NULL; + } + + parent::__construct($keyspace, $new_servers, $max_retries, $send_timeout, + $recv_timeout, $recycle, $credentials, $framed_transport); + } +} +?> diff --git a/library/phpcassa/doc/Makefile b/library/phpcassa/doc/Makefile new file mode 100644 index 000000000..ff895bc5b --- /dev/null +++ b/library/phpcassa/doc/Makefile @@ -0,0 +1,89 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pycassa.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pycassa.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/library/phpcassa/doc/__init__.py b/library/phpcassa/doc/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/library/phpcassa/doc/__init__.py @@ -0,0 +1 @@ + diff --git a/library/phpcassa/doc/changelog.rst b/library/phpcassa/doc/changelog.rst new file mode 100644 index 000000000..061aa8a5e --- /dev/null +++ b/library/phpcassa/doc/changelog.rst @@ -0,0 +1,42 @@ +Changelog +========= + +Changes in 0.7.a.3 +------------------ + +Bugfixes +^^^^^^^^ +- Typo in throwing IncompatibleAPIException +- remove() on super column families did not pack names correctly +- CassandraUtil::uuid3() param name should be $node not $null + +Features +^^^^^^^^ +- Use remove() Thrift API call instead of batch_mutate() when possible +- Allow a microsecond timestamp to be passed in for v1 UUID creation +- Log connection errors with error_log() + +Deprecated +^^^^^^^^^^ +None + +Changes in 0.7.a.2 +------------------ + +Bugfixes +^^^^^^^^ +- Fix server revival bug +- Remove print statement from Connection on connection failure + +Features +^^^^^^^^ +- Add an import() method for UUIDs to CassandraUtil to convert binary UUID + representations back into UUID objects + +Deprecated +^^^^^^^^^^^^ +None + +Changes in 0.7.a1 +----------------- +Initial release diff --git a/library/phpcassa/doc/conf.py b/library/phpcassa/doc/conf.py new file mode 100644 index 000000000..560ea14b2 --- /dev/null +++ b/library/phpcassa/doc/conf.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- +# +# PyMongo documentation build configuration file +# +# This file is execfile()d with the current directory set to its containing dir. + +import sys, os +sys.path.append(os.path.abspath('..')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', + 'sphinx.ext.todo', 'doc.sphinxtogithub'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'phpcassa' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.7.a.3' +# The full version, including alpha/beta/rc tags. +release = '0.7.a.3' + +# List of documents that shouldn't be included in the build. +unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +add_module_names = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer +lexers['php'] = PhpLexer(startinline=True) + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# -- Options for extensions ---------------------------------------------------- +autoclass_content = 'init' + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'sphinxdoc' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = False + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'phpcassa' + release.replace('.', '_') + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'phpcassa.tex', 'phpcassa Documentation', + 'Tyler Hobbs', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/library/phpcassa/doc/index.rst b/library/phpcassa/doc/index.rst new file mode 100644 index 000000000..88de765f1 --- /dev/null +++ b/library/phpcassa/doc/index.rst @@ -0,0 +1,72 @@ +phpcassa |release| Documentation +================================ + +Contents +-------- +**phpcassa** is a PHP client for +`Apache Cassandra `_. + + +:doc:`installation` + How to install **phpcassa**. + +:doc:`tutorial` + A short overview of **phpcassa** usage. + +`API Documentation `_ + The **phpcassa** API documentation. + +:doc:`troubleshooting` + Troubleshooting connection and other problems. + + +Help +------------ +Mailing List + * Mail to `phpcassa@googlegroups.com `_ or `view online `_. + +IRC + * Use #cassandra on `irc.freenode.net `_. If you don't have an IRC client, you can use `freenode's web based client `_. + +Issues +------ +Bugs and feature requests for **phpcassa** are currently tracked through the `github issue tracker `_. + +Contributing +------------ +**phpcassa** encourages you to offer any contributions or ideas you have. +Contributing to the documentation or examples, reporting bugs, requesting +features, and (of course) improving the code are all equally welcome. +To contribute, fork the project on +`github `_ and make a pull request. + +Changes +------- +The :doc:`changelog` lists the changes between versions of **phpcassa**. + +About This Documentation +------------------------ +This documentation is generated using the `Sphinx +`_ documentation generator. The source files +for the documentation are located in the `doc/` directory of +**phpcassa**. To generate the documentation, run the +following command from the `doc` directory: + +.. code-block:: bash + + $ make html + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +.. toctree:: + :hidden: + + installation + tutorial + troubleshooting + changelog diff --git a/library/phpcassa/doc/installation.rst b/library/phpcassa/doc/installation.rst new file mode 100644 index 000000000..5ea049ce2 --- /dev/null +++ b/library/phpcassa/doc/installation.rst @@ -0,0 +1,27 @@ +.. _installing: + +Installing +========== +Copying the `phpcassa` directory into your path should be enough to +begin using **phpcassa**. This will not automatically allow the +C extension to be used, though. + +C Extension +----------- +The C extension is crucial for phpcassa's performance. + +You need to configure and make to be able to use the C extension: + +.. code-block:: bash + + cd thrift/ext/thrift_protocol + ./configure + make + sudo make install + +Add the following line to your php.ini file: + +:: + + extension=thrift_protocol.so + diff --git a/library/phpcassa/doc/sphinxtogithub.py b/library/phpcassa/doc/sphinxtogithub.py new file mode 100644 index 000000000..1bf14a284 --- /dev/null +++ b/library/phpcassa/doc/sphinxtogithub.py @@ -0,0 +1,377 @@ +#! /usr/bin/env python + +from optparse import OptionParser +import os +import sys +import shutil + + +class NoDirectoriesError(Exception): + "Error thrown when no directories starting with an underscore are found" + +class DirHelper(object): + + def __init__(self, is_dir, list_dir, walk, rmtree): + + self.is_dir = is_dir + self.list_dir = list_dir + self.walk = walk + self.rmtree = rmtree + +class FileSystemHelper(object): + + def __init__(self, open_, path_join, move, exists): + + self.open_ = open_ + self.path_join = path_join + self.move = move + self.exists = exists + +class Replacer(object): + "Encapsulates a simple text replace" + + def __init__(self, from_, to): + + self.from_ = from_ + self.to = to + + def process(self, text): + + return text.replace( self.from_, self.to ) + +class FileHandler(object): + "Applies a series of replacements the contents of a file inplace" + + def __init__(self, name, replacers, opener): + + self.name = name + self.replacers = replacers + self.opener = opener + + def process(self): + + text = self.opener(self.name).read() + + for replacer in self.replacers: + text = replacer.process( text ) + + self.opener(self.name, "w").write(text) + +class Remover(object): + + def __init__(self, exists, remove): + self.exists = exists + self.remove = remove + + def __call__(self, name): + + if self.exists(name): + self.remove(name) + +class ForceRename(object): + + def __init__(self, renamer, remove): + + self.renamer = renamer + self.remove = remove + + def __call__(self, from_, to): + + self.remove(to) + self.renamer(from_, to) + +class VerboseRename(object): + + def __init__(self, renamer, stream): + + self.renamer = renamer + self.stream = stream + + def __call__(self, from_, to): + + self.stream.write( + "Renaming directory '%s' -> '%s'\n" + % (os.path.basename(from_), os.path.basename(to)) + ) + + self.renamer(from_, to) + + +class DirectoryHandler(object): + "Encapsulates renaming a directory by removing its first character" + + def __init__(self, name, root, renamer): + + self.name = name + self.new_name = name[1:] + self.root = root + os.sep + self.renamer = renamer + + def path(self): + + return os.path.join(self.root, self.name) + + def relative_path(self, directory, filename): + + path = directory.replace(self.root, "", 1) + return os.path.join(path, filename) + + def new_relative_path(self, directory, filename): + + path = self.relative_path(directory, filename) + return path.replace(self.name, self.new_name, 1) + + def process(self): + + from_ = os.path.join(self.root, self.name) + to = os.path.join(self.root, self.new_name) + self.renamer(from_, to) + + +class HandlerFactory(object): + + def create_file_handler(self, name, replacers, opener): + + return FileHandler(name, replacers, opener) + + def create_dir_handler(self, name, root, renamer): + + return DirectoryHandler(name, root, renamer) + + +class OperationsFactory(object): + + def create_force_rename(self, renamer, remover): + + return ForceRename(renamer, remover) + + def create_verbose_rename(self, renamer, stream): + + return VerboseRename(renamer, stream) + + def create_replacer(self, from_, to): + + return Replacer(from_, to) + + def create_remover(self, exists, remove): + + return Remover(exists, remove) + + +class Layout(object): + """ + Applies a set of operations which result in the layout + of a directory changing + """ + + def __init__(self, directory_handlers, file_handlers): + + self.directory_handlers = directory_handlers + self.file_handlers = file_handlers + + def process(self): + + for handler in self.file_handlers: + handler.process() + + for handler in self.directory_handlers: + handler.process() + + +class LayoutFactory(object): + "Creates a layout object" + + def __init__(self, operations_factory, handler_factory, file_helper, dir_helper, verbose, stream, force): + + self.operations_factory = operations_factory + self.handler_factory = handler_factory + + self.file_helper = file_helper + self.dir_helper = dir_helper + + self.verbose = verbose + self.output_stream = stream + self.force = force + + def create_layout(self, path): + + contents = self.dir_helper.list_dir(path) + + renamer = self.file_helper.move + + if self.force: + remove = self.operations_factory.create_remover(self.file_helper.exists, self.dir_helper.rmtree) + renamer = self.operations_factory.create_force_rename(renamer, remove) + + if self.verbose: + renamer = self.operations_factory.create_verbose_rename(renamer, self.output_stream) + + # Build list of directories to process + directories = [d for d in contents if self.is_underscore_dir(path, d)] + underscore_directories = [ + self.handler_factory.create_dir_handler(d, path, renamer) + for d in directories + ] + + if not underscore_directories: + raise NoDirectoriesError() + + # Build list of files that are in those directories + replacers = [] + for handler in underscore_directories: + for directory, dirs, files in self.dir_helper.walk(handler.path()): + for f in files: + replacers.append( + self.operations_factory.create_replacer( + handler.relative_path(directory, f), + handler.new_relative_path(directory, f) + ) + ) + + # Build list of handlers to process all files + filelist = [] + for root, dirs, files in self.dir_helper.walk(path): + for f in files: + if f.endswith(".html"): + filelist.append( + self.handler_factory.create_file_handler( + self.file_helper.path_join(root, f), + replacers, + self.file_helper.open_) + ) + if f.endswith(".js"): + filelist.append( + self.handler_factory.create_file_handler( + self.file_helper.path_join(root, f), + [self.operations_factory.create_replacer("'_sources/'", "'sources/'")], + self.file_helper.open_ + ) + ) + + return Layout(underscore_directories, filelist) + + def is_underscore_dir(self, path, directory): + + return (self.dir_helper.is_dir(self.file_helper.path_join(path, directory)) + and directory.startswith("_")) + + + +def sphinx_extension(app, exception): + "Wrapped up as a Sphinx Extension" + + if not app.builder.name in ("html", "dirhtml"): + return + + if not app.config.sphinx_to_github: + if app.config.sphinx_to_github_verbose: + print "Sphinx-to-github: Disabled, doing nothing." + return + + if exception: + if app.config.sphinx_to_github_verbose: + print "Sphinx-to-github: Exception raised in main build, doing nothing." + return + + dir_helper = DirHelper( + os.path.isdir, + os.listdir, + os.walk, + shutil.rmtree + ) + + file_helper = FileSystemHelper( + open, + os.path.join, + shutil.move, + os.path.exists + ) + + operations_factory = OperationsFactory() + handler_factory = HandlerFactory() + + layout_factory = LayoutFactory( + operations_factory, + handler_factory, + file_helper, + dir_helper, + app.config.sphinx_to_github_verbose, + sys.stdout, + force=True + ) + + layout = layout_factory.create_layout(app.outdir) + layout.process() + + +def setup(app): + "Setup function for Sphinx Extension" + + app.add_config_value("sphinx_to_github", True, '') + app.add_config_value("sphinx_to_github_verbose", True, '') + + app.connect("build-finished", sphinx_extension) + + +def main(args): + + usage = "usage: %prog [options] " + parser = OptionParser(usage=usage) + parser.add_option("-v","--verbose", action="store_true", + dest="verbose", default=False, help="Provides verbose output") + opts, args = parser.parse_args(args) + + try: + path = args[0] + except IndexError: + sys.stderr.write( + "Error - Expecting path to html directory:" + "sphinx-to-github \n" + ) + return + + dir_helper = DirHelper( + os.path.isdir, + os.listdir, + os.walk, + shutil.rmtree + ) + + file_helper = FileSystemHelper( + open, + os.path.join, + shutil.move, + os.path.exists + ) + + operations_factory = OperationsFactory() + handler_factory = HandlerFactory() + + layout_factory = LayoutFactory( + operations_factory, + handler_factory, + file_helper, + dir_helper, + opts.verbose, + sys.stdout, + force=False + ) + + try: + layout = layout_factory.create_layout(path) + except NoDirectoriesError: + sys.stderr.write( + "Error - No top level directories starting with an underscore " + "were found in '%s'\n" % path + ) + return + + layout.process() + + + +if __name__ == "__main__": + main(sys.argv[1:]) + + + diff --git a/library/phpcassa/doc/troubleshooting.rst b/library/phpcassa/doc/troubleshooting.rst new file mode 100644 index 000000000..71b39c36b --- /dev/null +++ b/library/phpcassa/doc/troubleshooting.rst @@ -0,0 +1,8 @@ +Troubleshooting +=============== + +"A connection attempt failed because the connected party did not properly respond" +---------------------------------------------------------------------------------- +This may be an issue on Windows systems: see +`here `_ +for the solution. diff --git a/library/phpcassa/doc/tutorial.rst b/library/phpcassa/doc/tutorial.rst new file mode 100644 index 000000000..4d3fa91c1 --- /dev/null +++ b/library/phpcassa/doc/tutorial.rst @@ -0,0 +1,353 @@ +Tutorial +======== + +This tutorial is intended as an introduction to working with +Cassandra and **phpcassa**. + +Prerequisites +------------- +Before we start, make sure that you have **phpcassa** +:doc:`installed `. The following +should execute without raising an exception: + +.. code-block:: php + + require_once('phpcassa/connection.php'); + require_once('phpcassa/columnfamily.php'); + +This tutorial also assumes that a Cassandra instance is running on the +default host and port. Read the `instructions for getting started +with Cassandra `_. +You can start Cassandra like so: + +.. code-block:: bash + + $ pwd + ~/cassandra + $ bin/cassandra -f + +and import the included schema to start out: + +.. code-block:: bash + + $ bin/schematool localhost 8080 import + +Making a Connection +------------------- +The first step when working with **phpcassa** is to create a +`Connection `_ to the running cassandra instance: + +.. code-block:: php + + $conn = new Connection('Keyspace'); + +The above code will connect on the default host and port. We can also +specify the host and port explicitly, as follows: + +.. code-block:: php + + $conn = new Connection('Keyspace1', array(array('host' => localhost, 'port' => 9160))); + +Getting a ColumnFamily +---------------------- +A column family is a collection of rows and columns in Cassandra, +and can be thought of as roughly the equivalent of a table in a +relational database. We'll use one of the column families that +were already included in the schema file: + +.. code-block:: php + + $column_family = new ColumnFamily($conn, 'Standard1'); + +Inserting Data +-------------- +To insert a row into a column family we can use the +`ColumnFamily::insert() `_ method: + +.. code-block:: php + + $column_family->insert('row_key', array('col_name' => 'col_val')); + +We can also insert more than one column at a time: + +.. code-block:: php + + $column_family->insert('row_key', array('name1' => 'val1', 'name2' => 'val2')); + +.. todo: batch_insert + +.. And we can insert more than one row at a time: + +.. .. code-block:: php + +.. $column_family.batch_insert({'row1': {'name1':'val1', 'name2':'val2'}, +.. ... 'row2': {'foo':'bar'}) +.. 1354491238721387 + +Getting Data +------------ +There are many more ways to get data out of Cassandra than there are +to insert data. + +The simplest way to get data is to use +`ColumnFamily::get() `_ + +.. code-block:: php + + $column_family->get('row_key'); + // returns: array('colname' => 'col_val') + +Without any other arguments, :meth:`ColumnFamily::get()` +returns every column in the row (up to `$column_count`, which defaults to 100). +If you only want a few of the columns and you know them by name, you can +specify them using a `$columns` argument: + +.. code-block:: php + + $column_family->get('row_key', $columns=array('name1', 'name2')); + // returns: array('name1' => 'foo', 'name2' => 'bar') + +We may also get a slice (or subrange) or the columns in a row. To do this, +use the `$column_start` and `$column_finish` parameters. One or both of these may +be left empty to allow the slice to extend to one or both ends the. +Note that `$column_finish` is inclusive. Assuming we've inserted several +columns with names '1' through '9', we can do the following: + +.. code-block:: php + + $column_family->get('row_key', $columns=null, $column_start='5', $column_finish='7'); + // returns: array('5' => 'foo', '6' => 'bar', '7' => 'baz') + +There are also two ways to get multiple rows at the same time. +The first is to specify them by name using +`ColumnFamily::multiget() `_ + +.. code-block:: php + + $column_family->multiget(['row_key1', 'row_key2']); + // returns: array('row_key1' => array('name' => 'val'), 'row_key2' => array('name' => 'val')) + +The other way is to get a range of keys at once by using +`ColumnFamily::get_range() `_. +The parameter `$key_finish` is also inclusive here, too. Assuming we've inserted +some rows with keys 'row_key1' through 'row_key9', we can do this: + +.. code-block:: php + + $rows = $column_family->get_range($key_start='row_key5', $key_finish='row_key7'); + // returns an Iterator over: + // array('row_key5' => array('name' => 'val'), + // 'row_key6' => array('name' => 'val'), + // 'row_key7' => array('name' => 'val')) + + foreach($rows as $key => $columns) { + // Do stuff with $key or $columns + Print_r($columns); + } + +It's also possible to specify a set of columns or a slice for +`ColumnFamily::multiget() `_ +and +`ColumnFamily::get_range() `_, +just like we did for +`ColumnFamily::get() `_ + +Counting +-------- +If you just want to know how many columns are in a row, you can use +`ColumnFamily::get_count() `_: + +.. code-block:: php + + $column_family->get_count('row_key'); + // returns: 3 + +If you only want to get a count of the number of columns that are inside +of a slice or have particular names, you can do that as well: + +.. code-block:: php + + $column_family->get_count('row_key', $columns=array('foo', 'bar')); + // returns: 2 + $column_family->get_count('row_key', $column_start='foo'); + // returns: 3 + +You can also do this in parallel for multiple rows using +`ColumnFamily::multiget_count() `_: + +.. code-block:: php + + $column_family->multiget_count(array('fib0', 'fib1', 'fib2', 'fib3', 'fib4')); + // returns: array('fib0' => 1, 'fib1' => 1, 'fib2' => 2, 'fib3' => 3, 'fib4' => 5) + +.. code-block:: php + + $column_family->multiget_count(array('fib0', 'fib1', 'fib2', 'fib3', 'fib4'), + $columns=array('col1', 'col2', 'col3')); + // returns: array('fib0' => 1, 'fib1' => 1, 'fib2' => 2, 'fib3' => 3, 'fib4' => 3) + +.. code-block:: php + + $column_family->multiget_count(array('fib0', 'fib1', 'fib2', 'fib3', 'fib4'), + $columns=null, $column_start='col1', $column_finish='col3') + // returns: array('fib0' => 1, 'fib1' => 1, 'fib2' => 2, 'fib3' => 3, 'fib4' => 3) + +Super Columns +------------- +Cassandra allows you to group columns in "super columns". In a +``cassandra.yaml`` file, this looks like this: + +:: + + - name: Super1 + column_type: Super + +To use a super column in **phpcassa**, you only need to +add an extra level to the array: + +.. code-block:: php + + $column_family = new ColumnFamily($conn, 'Super1'); + $column_family->insert('row_key', array('supercol_name' => array('col_name' => 'col_val'))); + $column_family->get('row_key'); + // returns: array('supercol_name' => ('col_name' => 'col_val')) + +Typed Column Names and Values +----------------------------- +In Cassandra 0.7, you can specify a comparator type for column names +and a validator type for column values. + +The types available are: + +* BytesType - no type +* IntegerType - 32 bit integer +* LongType - 64 bit integer +* AsciiType - ASCII string +* UTF8Type - UTF8 encoded string +* TimeUUIDType - version 1 UUID (timestamp based) +* LexicalUUID - non-version 1 UUID + +The column name comparator types affect how columns are sorted within +a row. You can use these with standard column families as well as with +super column families; with super column families, the subcolumns may +even have a different comparator type. Here's an example ``cassandra.yaml``: + +:: + + - name: StandardInt + column_type: Standard + compare_with: IntegerType + + - name: SuperLongSubAscii + column_type: Super + compare_with: LongType + compare_subcolumns_with: AsciiType + +Cassandra still requires you to pack these types into a binary format it +can understand. Fortunately, when **phpcassa** sees that a column family +uses these types, it knows to pack and unpack these data types automatically +for you. So, if we want to write to the StandardInt column family, we can do +the following: + +.. code-block:: php + + $column_family = new ColumnFamily($conn, 'StandardInt'); + $column_family->insert('row_key', array(42 => 'some_val')); + $column_family->get('row_key') + // returns: array(42 => 'some_val') + +Notice that 42 is an integer here, not a string. + +As mentioned above, Cassandra also offers validators on column values with +the same set of types. Validators can be set for an entire column family, +for individual columns, or both. Here's another example ``cassandra.yaml``: + +:: + + - name: AllLongs + column_type: Standard + default_validation_class: LongType + + - name: OneUUID + column_type: Standard + column_metadata: + - name: uuid + validator_class: TimeUUIDType + + - name: LongsExceptUUID + column_type: Standard + default_validation_class: LongType + column_metadata: + - name: uuid + validator_class: TimeUUIDType + +**phpcassa** knows to pack these column values automatically too: + +.. code-block:: php + + $column_family = new ColumnFamily($connection, 'LongsExceptUUID') + $column_family->insert('row_key', array('foo' 123456789, 'uuid' => CassandraUtil::uuid1())); + $column_family->get('row_key'); + // returns: array('foo' => 123456789, 'uuid' => UUID('5880c4b8-bd1a-11df-bbe1-00234d21610a')) + +Of course, if **phpcassa**'s automatic behavior isn't working for you, you +can turn it off when you create the +`ColumnFamily `_: + +.. code-block:: php + + $column_family = new ColumnFamily($conn, 'Standard1', + $autopack_names=False, + $autopack_values=False); + + +Indexes +------- +Cassandra 0.7.0 adds support for secondary indexes, which allow you to +efficiently get only rows which match a certain expression. + +To use secondary indexes with Cassandra, you need to specify what columns +will be indexed. In a ``cassandra.yaml`` file, this might look like: + +:: + + - name: Indexed1 + column_type: Standard + column_metadata: + - name: birthdate + validator_class: LongType + index_type: KEYS + +In order to use +`ColumnFamily::get_indexed_slices() `_ +to get data from Indexed1 using the indexed column, we need to create an +`IndexClause `_ +which contains a list of +`IndexExpression `_ +objects. The functions +`CassandraUtil::create_index_expression() `_ +and +`CassandraUtil::create_index_clause() `_ +are designed to make this easier. + +Suppose we are only interested in rows where 'birthdate' is 1984. We might do +the following: + +.. code-block:: php + + $column_family = new ColumnFamily($conn, 'Indexed1'); + $index_exp = CassandraUtil::create_index_expression('birthdate', 1984); + $index_clause = CassandraUtil::create_index_clause(array($index_exp)); + $rows = $column_family->get_indexed_slices($index_clause); + // returns an Iterator over: + // array('winston smith' => array('birthdate' => 1984)) + + foreach($rows as $key => $columns) { + // Do stuff with $key and $columns + Print_r($columns) + } + +Although at least one +`IndexExpression `_ +in every clause must be on an indexed column, you may also have other expressions +which are on non-indexed columns. diff --git a/library/phpcassa/test/all_tests.php b/library/phpcassa/test/all_tests.php new file mode 100644 index 000000000..68588a733 --- /dev/null +++ b/library/phpcassa/test/all_tests.php @@ -0,0 +1,13 @@ +TestSuite('All tests'); + $this->addFile('test_columnfamily.php'); + $this->addFile('test_autopacking.php'); + $this->addFile('test_large_ops.php'); + $this->addFile('test_pooling.php'); + } +} +?> diff --git a/library/phpcassa/test/cassandra.yaml b/library/phpcassa/test/cassandra.yaml new file mode 100644 index 000000000..be6e29191 --- /dev/null +++ b/library/phpcassa/test/cassandra.yaml @@ -0,0 +1,626 @@ +# Cassandra storage config YAML + +#NOTE !!!!!!!! NOTE +# See http://wiki.apache.org/cassandra/StorageConfiguration for +# full explanations of configuration directives +#NOTE !!!!!!!! NOTE + +# The name of the cluster. This is mainly used to prevent machines in +# one logical cluster from joining another. +cluster_name: 'Test Cluster' + +# You should always specify InitialToken when setting up a production +# cluster for the first time, and often when adding capacity later. +# The principle is that each node should be given an equal slice of +# the token ring; see http://wiki.apache.org/cassandra/Operations +# for more details. +# +# If blank, Cassandra will request a token bisecting the range of +# the heaviest-loaded existing node. If there is no load information +# available, such as is the case with a new cluster, it will pick +# a random token, which will lead to hot spots. +initial_token: + +# Set to true to make new [non-seed] nodes automatically migrate data +# to themselves from the pre-existing nodes in the cluster. Defaults +# to false because you can only bootstrap N machines at a time from +# an existing cluster of N, so if you are bringing up a cluster of +# 10 machines with 3 seeds you would have to do it in stages. Leaving +# this off for the initial start simplifies that. +auto_bootstrap: false + +# See http://wiki.apache.org/cassandra/HintedHandoff +hinted_handoff_enabled: true + +# authentication backend, implementing IAuthenticator; used to identify users +authenticator: org.apache.cassandra.auth.SimpleAuthenticator + +# authorization backend, implementing IAuthority; used to limit access/provide permissions +authority: org.apache.cassandra.auth.AllowAllAuthority + +# any IPartitioner may be used, including your own as long as it is on +# the classpath. Out of the box, Cassandra provides +# org.apache.cassandra.dht.RandomPartitioner +# org.apache.cassandra.dht.ByteOrderedPartitioner, +# org.apache.cassandra.dht.OrderPreservingPartitioner, and +# org.apache.cassandra.dht.CollatingOrderPreservingPartitioner. +# (CollatingOPP colates according to EN,US rules, not naive byte +# ordering. Use this as an example if you need locale-aware collation.) +partitioner: org.apache.cassandra.dht.OrderPreservingPartitioner + +# directories where Cassandra should store data on disk. +data_file_directories: + - /var/lib/cassandra/data + +# commit log +commitlog_directory: /var/lib/cassandra/commitlog + +# saved caches +saved_caches_directory: /var/lib/cassandra/saved_caches + +# Size to allow commitlog to grow to before creating a new segment +commitlog_rotation_threshold_in_mb: 128 + +# commitlog_sync may be either "periodic" or "batch." +# When in batch mode, Cassandra won't ack writes until the commit log +# has been fsynced to disk. It will wait up to +# CommitLogSyncBatchWindowInMS milliseconds for other writes, before +# performing the sync. +commitlog_sync: periodic + +# the other option is "timed," where writes may be acked immediately +# and the CommitLog is simply synced every commitlog_sync_period_in_ms +# milliseconds. +commitlog_sync_period_in_ms: 10000 + +# Addresses of hosts that are deemed contact points. +# Cassandra nodes use this list of hosts to find each other and learn +# the topology of the ring. You must change this if you are running +# multiple nodes! +seeds: + - 127.0.0.1 + +# Access mode. mmapped i/o is substantially faster, but only practical on +# a 64bit machine (which notably does not include EC2 "small" instances) +# or relatively small datasets. "auto", the safe choice, will enable +# mmapping on a 64bit JVM. Other values are "mmap", "mmap_index_only" +# (which may allow you to get part of the benefits of mmap on a 32bit +# machine by mmapping only index files) and "standard". +# (The buffer size settings that follow only apply to standard, +# non-mmapped i/o.) +disk_access_mode: auto + +# Unlike most systems, in Cassandra writes are faster than reads, so +# you can afford more of those in parallel. A good rule of thumb is 2 +# concurrent reads per processor core. Increase ConcurrentWrites to +# the number of clients writing at once if you enable CommitLogSync + +# CommitLogSyncDelay. --> +concurrent_reads: 8 +concurrent_writes: 32 + +# This sets the amount of memtable flush writer threads. These will +# be blocked by disk io, and each one will hold a memtable in memory +# while blocked. If you have a large heap and many data directories, +# you can increase this value for better flush performance. +# By default this will be set to the amount of data directories defined. +#memtable_flush_writers: 1 + +# Buffer size to use when performing contiguous column slices. +# Increase this to the size of the column slices you typically perform +sliced_buffer_size_in_kb: 64 + +# TCP port, for commands and data +storage_port: 7000 + +# Address to bind to and tell other Cassandra nodes to connect to. You +# _must_ change this if you want multiple nodes to be able to +# communicate! +# +# Leaving it blank leaves it up to InetAddress.getLocalHost(). This +# will always do the Right Thing *if* the node is properly configured +# (hostname, name resolution, etc), and the Right Thing is to use the +# address associated with the hostname (it might not be). +# +# Setting this to 0.0.0.0 is always wrong. +listen_address: localhost + +# The address to bind the Thrift RPC service to -- clients connect +# here. Unlike ListenAddress above, you *can* specify 0.0.0.0 here if +# you want Thrift to listen on all interfaces. +# +# Leaving this blank has the same effect it does for ListenAddress, +# (i.e. it will be based on the configured hostname of the node). +rpc_address: localhost +# port for Thrift to listen for clients on +rpc_port: 9160 + +# enable or disable keepalive on rpc connections +rpc_keepalive: true + +# uncomment to set socket buffer sizes on rpc connections +# rpc_send_buff_size_in_bytes: +# rpc_recv_buff_size_in_bytes: + +# Frame size for thrift (maximum field length). +# 0 disables TFramedTransport in favor of TSocket. This option +# is deprecated; we strongly recommend using Framed mode. +thrift_framed_transport_size_in_mb: 15 + +# The max length of a thrift message, including all fields and +# internal thrift overhead. +thrift_max_message_length_in_mb: 16 + +# Whether or not to take a snapshot before each compaction. Be +# careful using this option, since Cassandra won't clean up the +# snapshots for you. Mostly useful if you're paranoid when there +# is a data format change. +snapshot_before_compaction: false + +# change this to increase the compaction thread's priority. In java, 1 is the +# lowest priority and that is our default. +# compaction_thread_priority: 1 + +# The threshold size in megabytes the binary memtable must grow to, +# before it's submitted for flushing to disk. +binary_memtable_throughput_in_mb: 256 +# The maximum time to leave a dirty memtable unflushed. +# (While any affected columnfamilies have unflushed data from a +# commit log segment, that segment cannot be deleted.) +# This needs to be large enough that it won't cause a flush storm +# of all your memtables flushing at once because none has hit +# the size or count thresholds yet. +# defaults to 60 +#memtable_flush_after_mins: 60 +# Size of the memtable in memory before it is flushed +# if left undefined, 1/8 of the heap will be used +#memtable_throughput_in_mb: 256 +# Number of objects in millions in the memtable before it is flushed +# if left undefined, the memtable_throughput_in_mb / 64 * 0.3 will be used +#memtable_operations_in_millions: 1.2 + +# Add column indexes to a row after its contents reach this size. +# Increase if your column values are large, or if you have a very large +# number of columns. The competing causes are, Cassandra has to +# deserialize this much of the row to read a single column, so you want +# it to be small - at least if you do many partial-row reads - but all +# the index data is read for each access, so you don't want to generate +# that wastefully either. +column_index_size_in_kb: 64 + +# Size limit for rows being compacted in memory. Larger rows will spill +# over to disk and use a slower two-pass compaction process. A message +# will be logged specifying the row key. +in_memory_compaction_limit_in_mb: 64 + +# Time to wait for a reply from other nodes before failing the command +rpc_timeout_in_ms: 10000 + +# phi value that must be reached for a host to be marked down. +# most users should never need to adjust this. +# phi_convict_threshold: 8 + +# endpoint_snitch -- Set this to a class that implements +# IEndpointSnitch, which will let Cassandra know enough +# about your network topology to route requests efficiently. +# Out of the box, Cassandra provides +# - org.apache.cassandra.locator.SimpleSnitch: +# Treats Strategy order as proximity. This improves cache locality +# when disabling read repair, which can further improve throughput. +# - org.apache.cassandra.locator.RackInferringSnitch: +# Proximity is determined by rack and data center, which are +# assumed to correspond to the 3rd and 2nd octet of each node's +# IP address, respectively +# org.apache.cassandra.locator.PropertyFileSnitch: +# - Proximity is determined by rack and data center, which are +# explicitly configured in cassandra-rack.properties. +endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch + +# dynamic_snitch -- This boolean controls whether the above snitch is +# wrapped with a dynamic snitch, which will monitor read latencies +# and avoid reading from hosts that have slowed (due to compaction, +# for instance) +dynamic_snitch: true +# controls how often to perform the more expensive part of host score +# calculation +dynamic_snitch_update_interval_in_ms: 100 +# controls how often to reset all host scores, allowing a bad host to +# possibly recover +dynamic_snitch_reset_interval_in_ms: 600000 +# if set greater than zero and read_repair_chance is < 1.0, this will allow +# 'pinning' of replicas to hosts in order to increase cache capacity. +# The badness threshold will control how much worse the pinned host has to be +# before the dynamic snitch will prefer other replicas over it. This is +# expressed as a double which represents a percentage. +dynamic_snitch_badness_threshold: 0.0 + +# request_scheduler -- Set this to a class that implements +# RequestScheduler, which will schedule incoming client requests +# according to the specific policy. This is useful for multi-tenancy +# with a single Cassandra cluster. +# NOTE: This is specifically for requests from the client and does +# not affect inter node communication. +# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place +# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of +# client requests to a node with a separate queue for each +# request_scheduler_id. The scheduler is further customized by +# request_scheduler_options as described below. +request_scheduler: org.apache.cassandra.scheduler.NoScheduler + +# Scheduler Options vary based on the type of scheduler +# NoScheduler - Has no options +# RoundRobin +# - throttle_limit -- The throttle_limit is the number of in-flight +# requests per client. Requests beyond +# that limit are queued up until +# running requests can complete. +# The value of 80 here is twice the number of +# concurrent_reads + concurrent_writes. +# - default_weight -- default_weight is optional and allows for +# overriding the default which is 1. +# - weights -- Weights are optional and will default to 1 or the +# overridden default_weight. The weight translates into how +# many requests are handled during each turn of the +# RoundRobin, based on the scheduler id. +# +# request_scheduler_options: +# throttle_limit: 80 +# default_weight: 5 +# weights: +# Keyspace1: 1 +# Keyspace2: 5 + +# request_scheduler_id -- An identifer based on which to perform +# the request scheduling. Currently the only valid option is keyspace. +# request_scheduler_id: keyspace + +# The Index Interval determines how large the sampling of row keys +# is for a given SSTable. The larger the sampling, the more effective +# the index is at the cost of space. +index_interval: 128 + +# A ColumnFamily is the Cassandra concept closest to a relational table. +# +# Keyspaces are separate groups of ColumnFamilies. Except in very +# unusual circumstances you will have one Keyspace per application. +# +# Keyspace required parameters: +# - name: name of the keyspace; "system" and "definitions" are +# reserved for Cassandra Internals. +# - replica_placement_strategy: the class that determines how replicas +# are distributed among nodes. Contains both the class as well as +# configuration information. Must extend AbstractReplicationStrategy. +# Out of the box, Cassandra provides +# * org.apache.cassandra.locator.SimpleStrategy +# * org.apache.cassandra.locator.NetworkTopologyStrategy +# * org.apache.cassandra.locator.OldNetworkTopologyStrategy +# +# SimpleStrategy merely places the first +# replica at the node whose token is closest to the key (as determined +# by the Partitioner), and additional replicas on subsequent nodes +# along the ring in increasing Token order. +# +# With NetworkTopologyStrategy, +# for each datacenter, you can specify how many replicas you want +# on a per-keyspace basis. Replicas are placed on different racks +# within each DC, if possible. This strategy also requires rack aware +# snitch, such as RackInferringSnitch or PropertyFileSnitch. +# An example: +# - name: Keyspace1 +# replica_placement_strategy: org.apache.cassandra.locator.NetworkTopologyStrategy +# strategy_options: +# DC1 : 3 +# DC2 : 2 +# DC3 : 1 +# +# OldNetworkToplogyStrategy [formerly RackAwareStrategy] +# places one replica in each of two datacenters, and the third on a +# different rack in in the first. Additional datacenters are not +# guaranteed to get a replica. Additional replicas after three are placed +# in ring order after the third without regard to rack or datacenter. +# +# - replication_factor: Number of replicas of each row +# - column_families: column families associated with this keyspace +# +# ColumnFamily required parameters: +# - name: name of the ColumnFamily. Must not contain the character "-". +# - compare_with: tells Cassandra how to sort the columns for slicing +# operations. The default is BytesType, which is a straightforward +# lexical comparison of the bytes in each column. Other options are +# AsciiType, UTF8Type, LexicalUUIDType, TimeUUIDType, LongType, +# and IntegerType (a generic variable-length integer type). +# You can also specify the fully-qualified class name to a class of +# your choice extending org.apache.cassandra.db.marshal.AbstractType. +# +# ColumnFamily optional parameters: +# - keys_cached: specifies the number of keys per sstable whose +# locations we keep in memory in "mostly LRU" order. (JUST the key +# locations, NOT any column values.) Specify a fraction (value less +# than 1) or an absolute number of keys to cache. Defaults to 200000 +# keys. +# - rows_cached: specifies the number of rows whose entire contents we +# cache in memory. Do not use this on ColumnFamilies with large rows, +# or ColumnFamilies with high write:read ratios. Specify a fraction +# (value less than 1) or an absolute number of rows to cache. +# Defaults to 0. (i.e. row caching is off by default) +# - comment: used to attach additional human-readable information about +# the column family to its definition. +# - read_repair_chance: specifies the probability with which read +# repairs should be invoked on non-quorum reads. must be between 0 +# and 1. defaults to 1.0 (always read repair). +# - preload_row_cache: If true, will populate row cache on startup. +# Defaults to false. +# - gc_grace_seconds: specifies the time to wait before garbage +# collecting tombstones (deletion markers). defaults to 864000 (10 +# days). See http://wiki.apache.org/cassandra/DistributedDeletes +# - default_validation_class: specifies a validator class to use for +# validating all the column values in the CF. +# - min_compaction_threshold: the minimum number of SSTables needed +# to start a minor compaction. increasing this will cause minor +# compactions to start less frequently and be more intensive. setting +# this to 0 disables minor compactions. defaults to 4. +# - max_compaction_threshold: the maximum number of SSTables allowed +# before a minor compaction is forced. decreasing this will cause +# minor compactions to start more frequently and be less intensive. +# setting this to 0 disables minor compactions. defaults to 32. +# - row_cache_save_period_in_seconds: number of seconds between saving row caches. +# The row caches can be saved periodically and if one exists on startup it will be loaded. +# - key_cache_save_period_in_seconds: number of seconds between saving key caches. +# The key caches can be saved periodically and if one exists on startup it will be loaded. +# +# NOTE: this keyspace definition is for demonstration purposes only. +# Cassandra will not load these definitions during startup. See +# http://wiki.apache.org/cassandra/FAQ#no_keyspaces for an explanation. +keyspaces: + - name: Keyspace1 + replica_placement_strategy: org.apache.cassandra.locator.RackUnawareStrategy + replication_factor: 1 + column_families: + - name: Standard1 + compare_with: BytesType + + - name: Standard2 + compare_with: UTF8Type + read_repair_chance: 0.1 + keys_cached: 100 + + - name: StandardByUUID1 + compare_with: TimeUUIDType + + - name: Super1 + column_type: Super + compare_with: BytesType + compare_subcolumns_with: BytesType + + - name: Super2 + column_type: Super + compare_subcolumns_with: UTF8Type + rows_cached: 10000 + keys_cached: 50 + comment: 'A column family with supercolumns, whose column and subcolumn names are UTF8 strings' + + - name: Super3 + column_type: Super + compare_with: LongType + comment: 'A column family with supercolumns, whose column names are Longs (8 bytes)' + +# CFs for autopacking tests + + - name: StdBytes + compare_with: BytesType + comment: 'A standard column family with column names that are Longs (8 bytes)' + gc_grace_seconds: 0 + + - name: StdLong + compare_with: LongType + comment: 'A standard column family with column names that are Longs (8 bytes)' + gc_grace_seconds: 0 + + - name: StdInteger + compare_with: IntegerType + comment: 'A standard column family with column names that are Integers (4 bytes)' + gc_grace_seconds: 0 + + - name: StdAscii + compare_with: AsciiType + comment: 'A standard column family with column names that are ASCII strings' + gc_grace_seconds: 0 + + - name: StdUTF8 + compare_with: UTF8Type + comment: 'A standard column family with column names that are UTF8 strings' + gc_grace_seconds: 0 + + - name: StdLexicalUUID + compare_with: LexicalUUIDType + comment: 'A standard column family with column names that are lexical UUIDs (16 bytes)' + gc_grace_seconds: 0 + + - name: StdTimeUUID + compare_with: TimeUUIDType + comment: 'A standard column family with column names that are time UUIDs (16 bytes)' + gc_grace_seconds: 0 + + - name: SuperBytes + column_type: Super + compare_with: BytesType + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + + - name: SuperLong + column_type: Super + compare_with: LongType + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + + - name: SuperInt + column_type: Super + compare_with: IntegerType + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + + - name: SuperAscii + column_type: Super + compare_with: AsciiType + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + + - name: SuperUTF8 + column_type: Super + compare_with: UTF8Type + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + + - name: SuperLex + column_type: Super + compare_with: LexicalUUIDType + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + + - name: SuperTime + column_type: Super + compare_with: TimeUUIDType + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + +# Supercols with different subcol types + - name: SuperLongSubLong + column_type: Super + compare_with: LongType + compare_subcolumns_with: LongType + gc_grace_seconds: 0 + + - name: SuperLongSubInt + column_type: Super + compare_with: LongType + compare_subcolumns_with: IntegerType + gc_grace_seconds: 0 + + - name: SuperLongSubTime + column_type: Super + compare_with: LongType + compare_subcolumns_with: TimeUUIDType + gc_grace_seconds: 0 + + - name: SuperLongSubLex + column_type: Super + compare_with: LongType + compare_subcolumns_with: LexicalUUIDType + gc_grace_seconds: 0 + + - name: SuperLongSubAscii + column_type: Super + compare_with: LongType + compare_subcolumns_with: AsciiType + gc_grace_seconds: 0 + + - name: SuperLongSubUTF8 + column_type: Super + compare_with: LongType + compare_subcolumns_with: UTF8Type + gc_grace_seconds: 0 + + - name: SuperLongSubBytes + column_type: Super + compare_with: LongType + compare_subcolumns_with: BytesType + gc_grace_seconds: 0 + +# Indexed column family + - name: Indexed1 + column_metadata: + - name: birthdate + validator_class: LongType + index_type: KEYS + +# Validator tests + - name: ValidatorLong + column_type: Standard + column_metadata: + - name: subcol + validator_class: LongType + + - name: ValidatorInt + column_type: Standard + column_metadata: + - name: subcol + validator_class: IntegerType + + - name: ValidatorTime + column_type: Standard + column_metadata: + - name: subcol + validator_class: TimeUUIDType + + - name: ValidatorLex + column_type: Standard + column_metadata: + - name: subcol + validator_class: LexicalUUIDType + + - name: ValidatorAscii + column_type: Standard + column_metadata: + - name: subcol + validator_class: AsciiType + + - name: ValidatorUTF8 + column_type: Standard + column_metadata: + - name: subcol + validator_class: UTF8Type + + - name: ValidatorBytes + column_type: Standard + column_metadata: + - name: subcol + validator_class: BytesType + +# Duplicate CFS for simplified pool testing + + - name: StandardQueuePool + column_type: Standard + compare_with: UTF8Type + + - name: StandardSingletonThreadPool + column_type: Standard + compare_with: UTF8Type + + - name: StandardStaticPool + column_type: Standard + compare_with: UTF8Type + + - name: StandardNullPool + column_type: Standard + compare_with: UTF8Type + + - name: StandardAssertionPool + column_type: Standard + compare_with: UTF8Type + + - name: SuperQueuePool + column_type: Super + compare_subcolumns_with: UTF8Type + + - name: SuperSingletonThreadPool + column_type: Super + compare_subcolumns_with: UTF8Type + + - name: SuperStaticPool + column_type: Super + compare_subcolumns_with: UTF8Type + + - name: SuperNullPool + column_type: Super + compare_subcolumns_with: UTF8Type + + - name: SuperAssertionPool + column_type: Super + compare_subcolumns_with: UTF8Type + +# Default validator test + - name: DefaultValidator + column_type: Standard + default_validation_class: LongType + column_metadata: + - name: subcol + validator_class: TimeUUIDType diff --git a/library/phpcassa/test/test_autopacking.php b/library/phpcassa/test/test_autopacking.php new file mode 100644 index 000000000..a11bdd2d1 --- /dev/null +++ b/library/phpcassa/test/test_autopacking.php @@ -0,0 +1,620 @@ +client = new ConnectionPool('Keyspace1'); + + $this->cf_long = new ColumnFamily($this->client, 'StdLong'); + $this->cf_int = new ColumnFamily($this->client, 'StdInteger'); + $this->cf_time = new ColumnFamily($this->client, 'StdTimeUUID'); + $this->cf_lex = new ColumnFamily($this->client, 'StdLexicalUUID'); + $this->cf_ascii = new ColumnFamily($this->client, 'StdAscii'); + $this->cf_utf8 = new ColumnFamily($this->client, 'StdUTF8'); + + $this->cf_suplong = new ColumnFamily($this->client, 'SuperLong'); + $this->cf_supint = new ColumnFamily($this->client, 'SuperInt'); + $this->cf_suptime = new ColumnFamily($this->client, 'SuperTime'); + $this->cf_suplex = new ColumnFamily($this->client, 'SuperLex'); + $this->cf_supascii = new ColumnFamily($this->client, 'SuperAscii'); + $this->cf_suputf8 = new ColumnFamily($this->client, 'SuperUTF8'); + + $this->cf_suplong_sublong = new ColumnFamily($this->client, 'SuperLongSubLong'); + $this->cf_suplong_subint = new ColumnFamily($this->client, 'SuperLongSubInt'); + $this->cf_suplong_subtime = new ColumnFamily($this->client, 'SuperLongSubTime'); + $this->cf_suplong_sublex = new ColumnFamily($this->client, 'SuperLongSubLex'); + $this->cf_suplong_subascii = new ColumnFamily($this->client, 'SuperLongSubAscii'); + $this->cf_suplong_subutf8 = new ColumnFamily($this->client, 'SuperLongSubUTF8'); + + $this->cf_valid_long = new ColumnFamily($this->client, 'ValidatorLong'); + $this->cf_valid_int = new ColumnFamily($this->client, 'ValidatorInt'); + $this->cf_valid_time = new ColumnFamily($this->client, 'ValidatorTime'); + $this->cf_valid_lex = new ColumnFamily($this->client, 'ValidatorLex'); + $this->cf_valid_ascii = new ColumnFamily($this->client, 'ValidatorAscii'); + $this->cf_valid_utf8 = new ColumnFamily($this->client, 'ValidatorUTF8'); + $this->cf_valid_bytes = new ColumnFamily($this->client, 'ValidatorBytes'); + + $this->cf_def_valid = new ColumnFamily($this->client, 'DefaultValidator'); + + $this->cfs = array($this->cf_long, $this->cf_int, $this->cf_ascii, + $this->cf_time, $this->cf_lex, $this->cf_utf8, + + $this->cf_suplong, $this->cf_supint, $this->cf_suptime, + $this->cf_suplex, $this->cf_supascii, $this->cf_suputf8, + + $this->cf_suplong_sublong, $this->cf_suplong_subint, + $this->cf_suplong_subtime, $this->cf_suplong_sublex, + $this->cf_suplong_subascii, $this->cf_suplong_subutf8, + + $this->cf_valid_long, $this->cf_valid_int, + $this->cf_valid_time, $this->cf_valid_lex, + $this->cf_valid_ascii, $this->cf_valid_utf8, + $this->cf_valid_bytes, + + $this->cf_def_valid); + + $this->TIME1 = CassandraUtil::uuid1(); + $this->TIME2 = CassandraUtil::uuid1(); + $this->TIME3 = CassandraUtil::uuid1(); + + $this->LEX1 = UUID::import('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')->bytes; + $this->LEX2 = UUID::import('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')->bytes; + $this->LEX3 = UUID::import('cccccccccccccccccccccccccccccccc')->bytes; + } + + public function tearDown() { + foreach($this->cfs as $cf) { + foreach(self::$KEYS as $key) + $cf->remove($key); + } + if ($this->client) + $this->client->close(); + } + + public function test_basic_ints() { + $int_col = array(3 => self::$VALS[0]); + $this->cf_int->insert(self::$KEYS[0], $int_col); + self::assertEqual($this->cf_int->get(self::$KEYS[0]), $int_col); + + $this->cf_supint->insert(self::$KEYS[0], array(111123 => $int_col)); + self::assertEqual($this->cf_supint->get(self::$KEYS[0]), array(111123 => $int_col)); + + $this->cf_suplong_subint->insert(self::$KEYS[0], array(222222222222 => $int_col)); + self::assertEqual($this->cf_suplong_subint->get(self::$KEYS[0]), + array(222222222222 => $int_col)); + } + + public function test_basic_longs() { + $long_col = array(1111111111111111 => self::$VALS[0]); + $this->cf_long->insert(self::$KEYS[0], $long_col); + self::assertEqual($this->cf_long->get(self::$KEYS[0]), $long_col); + + $this->cf_suplong->insert(self::$KEYS[0], array(222222222222 => $long_col)); + self::assertEqual($this->cf_suplong->get(self::$KEYS[0]), array(222222222222 => $long_col)); + + $this->cf_suplong_sublong->insert(self::$KEYS[0], array(222222222222 => $long_col)); + self::assertEqual($this->cf_suplong_sublong->get(self::$KEYS[0]), + array(222222222222 => $long_col)); + } + + public function test_basic_ascii() { + $ascii_col = array('foo' => self::$VALS[0]); + $this->cf_ascii->insert(self::$KEYS[0], $ascii_col); + self::assertEqual($this->cf_ascii->get(self::$KEYS[0]), $ascii_col); + + $this->cf_supascii->insert(self::$KEYS[0], array('aaaa' => $ascii_col)); + self::assertEqual($this->cf_supascii->get(self::$KEYS[0]), array('aaaa' => $ascii_col)); + + $this->cf_suplong_subascii->insert(self::$KEYS[0], array(222222222222 => $ascii_col)); + self::assertEqual($this->cf_suplong_subascii->get(self::$KEYS[0]), + array(222222222222 => $ascii_col)); + } + + public function test_basic_time() { + $time_col = array($this->TIME1 => self::$VALS[0]); + $this->cf_time->insert(self::$KEYS[0], $time_col); + $result = $this->cf_time->get(self::$KEYS[0]); + self::assertEqual($result, $time_col); + + $this->cf_suptime->insert(self::$KEYS[0], array($this->TIME2 => $time_col)); + self::assertEqual($this->cf_suptime->get(self::$KEYS[0]), array($this->TIME2 => $time_col)); + + $this->cf_suplong_subtime->insert(self::$KEYS[0], array(222222222222 => $time_col)); + self::assertEqual($this->cf_suplong_subtime->get(self::$KEYS[0]), + array(222222222222 => $time_col)); + } + + public function test_basic_lexical() { + $lex_col = array($this->LEX1 => self::$VALS[0]); + $this->cf_lex->insert(self::$KEYS[0], $lex_col); + $result = $this->cf_lex->get(self::$KEYS[0]); + self::assertEqual($result, $lex_col); + + $this->cf_suplex->insert(self::$KEYS[0], array($this->LEX2 => $lex_col)); + self::assertEqual($this->cf_suplex->get(self::$KEYS[0]), array($this->LEX2 => $lex_col)); + + $this->cf_suplong_sublex->insert(self::$KEYS[0], array(222222222222 => $lex_col)); + self::assertEqual($this->cf_suplong_sublex->get(self::$KEYS[0]), + array(222222222222 => $lex_col)); + } + + public function test_basic_utf8() { + # Fun fact - "hello" in Russian: + $uni = "Здравс". + "ствуй". + "те"; + + $utf8_col = array($uni => self::$VALS[0]); + $this->cf_utf8->insert(self::$KEYS[0], $utf8_col); + $result = $this->cf_utf8->get(self::$KEYS[0]); + self::assertEqual($result, $utf8_col); + + $this->cf_suputf8->insert(self::$KEYS[0], array($uni => $utf8_col)); + self::assertEqual($this->cf_suputf8->get(self::$KEYS[0]), array($uni => $utf8_col)); + + $this->cf_suplong_subutf8->insert(self::$KEYS[0], array(222222222222 => $utf8_col)); + self::assertEqual($this->cf_suplong_subutf8->get(self::$KEYS[0]), + array(222222222222 => $utf8_col)); + } + + static function make_group($cf, $cols) { + $dict = array($cols[0] => self::$VALS[0], + $cols[1] => self::$VALS[1], + $cols[2] => self::$VALS[2]); + return array('cf' => $cf, 'cols' => $cols, 'dict' => $dict); + } + + public function test_standard_column_family() { + $type_groups = array(); + + $long_cols = array(111111111111, + 222222222222, + 333333333333); + $type_groups[] = self::make_group($this->cf_long, $long_cols); + + $int_cols = array(1, 2, 3); + $type_groups[] = self::make_group($this->cf_int, $int_cols); + + $time_cols = array($this->TIME1, $this->TIME2, $this->TIME3); + $type_groups[] = self::make_group($this->cf_time, $time_cols); + + $lex_cols = array($this->LEX1, $this->LEX2, $this->LEX3); + $type_groups[] = self::make_group($this->cf_lex, $lex_cols); + + $ascii_cols = array('aaaa', 'bbbb', 'cccc'); + $type_groups[] = self::make_group($this->cf_ascii, $ascii_cols); + + $utf8_cols = array("aЗ", "bИ", "cЙ"); + $type_groups[] = self::make_group($this->cf_utf8, $utf8_cols); + + + foreach($type_groups as $group) { + + $group['cf']->insert(self::$KEYS[0], $group['dict']); + self::assertEqual($group['cf']->get(self::$KEYS[0]), $group['dict']); + + # Check each column individually + foreach(range(0,2) as $i) + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=array($group['cols'][$i])), + array($group['cols'][$i] => self::$VALS[$i])); + + # Check with list of all columns + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=$group['cols']), + $group['dict']); + + # Same thing but with start and end + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][2]), + $group['dict']); + + # Start and end are the same + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][0]), + + array($group['cols'][0] => self::$VALS[0])); + + + ### remove() tests ### + + $group['cf']->remove(self::$KEYS[0], $columns=array($group['cols'][0])); + self::assertEqual($group['cf']->get_count(self::$KEYS[0]), 2); + + $group['cf']->remove(self::$KEYS[0], $columns=array($group['cols'][1], $group['cols'][2])); + self::assertEqual($group['cf']->get_count(self::$KEYS[0]), 0); + + # Insert more than one row + $group['cf']->insert(self::$KEYS[0], $group['dict']); + $group['cf']->insert(self::$KEYS[1], $group['dict']); + $group['cf']->insert(self::$KEYS[2], $group['dict']); + + + ### multiget() tests ### + + $result = $group['cf']->multiget(self::$KEYS); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[0]], $group['dict']); + + $result = $group['cf']->multiget(array(self::$KEYS[2])); + self::assertEqual($result[self::$KEYS[2]], $group['dict']); + + # Check each column individually + foreach(range(0,2) as $i) { + $result = $group['cf']->multiget(self::$KEYS, $columns=array($group['cols'][$i])); + foreach(range(0,2) as $j) + self::assertEqual($result[self::$KEYS[$j]], + array($group['cols'][$i] => self::$VALS[$i])); + + } + + # Check that if we list all columns, we get the full dict + $result = $group['cf']->multiget(self::$KEYS, $columns=$group['cols']); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$j]], $group['dict']); + + # The same thing with a start and end instead + $result = $group['cf']->multiget(self::$KEYS, $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][2]); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$j]], $group['dict']); + + # A start and end that are the same + $result = $group['cf']->multiget(self::$KEYS, $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][0]); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$j]], + array($group['cols'][0] => self::$VALS[0])); + + + ### get_range() tests ### + + $result = $group['cf']->get_range($key_start=self::$KEYS[0]); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + + $result = $group['cf']->get_range($key_start=self::$KEYS[0], $key_finish='', + $key_count=ColumnFamily::DEFAULT_ROW_COUNT, + $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][2]); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + + $result = $group['cf']->get_range($key_start=self::$KEYS[0], $key_finish='', + $key_count=ColumnFamily::DEFAULT_ROW_COUNT, + $columns=$group['cols']); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + } + } + + private function make_super_group($cf, $cols) { + $diction = array($cols[0] => array('bytes' => self::$VALS[0]), + $cols[1] => array('bytes' => self::$VALS[1]), + $cols[2] => array('bytes' => self::$VALS[2])); + return array('cf' => $cf, 'cols' => $cols, 'dict' => $diction); + } + + public function test_super_column_families() { + $type_groups = array(); + + $long_cols = array(111111111111, + 222222222222, + 333333333333); + $type_groups[] = self::make_super_group($this->cf_suplong, $long_cols); + + $int_cols = array(1, 2, 3); + $type_groups[] = self::make_super_group($this->cf_supint, $int_cols); + + $time_cols = array($this->TIME1, $this->TIME2, $this->TIME3); + $type_groups[] = self::make_super_group($this->cf_suptime, $time_cols); + + $lex_cols = array($this->LEX1, $this->LEX2, $this->LEX3); + $type_groups[] = self::make_super_group($this->cf_suplex, $lex_cols); + + $ascii_cols = array('aaaa', 'bbbb', 'cccc'); + $type_groups[] = self::make_super_group($this->cf_supascii, $ascii_cols); + + $utf8_cols = array("aЗ", "bИ", "cЙ"); + $type_groups[] = self::make_super_group($this->cf_suputf8, $utf8_cols); + + foreach($type_groups as $group) { + + $group['cf']->insert(self::$KEYS[0], $group['dict']); + self::assertEqual($group['cf']->get(self::$KEYS[0]), $group['dict']); + + # Check each column individually + foreach(range(0,2) as $i) + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=array($group['cols'][$i])), + array($group['cols'][$i] => array('bytes' => self::$VALS[$i]))); + + # Check with list of all columns + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=$group['cols']), + $group['dict']); + + # Same thing but with start and end + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][2]), + $group['dict']); + + # Start and end are the same + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][0]), + + array($group['cols'][0] => array('bytes' => self::$VALS[0]))); + + + ### remove() tests ### + + $group['cf']->remove(self::$KEYS[0], $columns=array($group['cols'][0])); + self::assertEqual($group['cf']->get_count(self::$KEYS[0]), 2); + + $group['cf']->remove(self::$KEYS[0], $columns=array($group['cols'][1], $group['cols'][2])); + self::assertEqual($group['cf']->get_count(self::$KEYS[0]), 0); + + # Insert more than one row + $group['cf']->insert(self::$KEYS[0], $group['dict']); + $group['cf']->insert(self::$KEYS[1], $group['dict']); + $group['cf']->insert(self::$KEYS[2], $group['dict']); + + + ### multiget() tests ### + + $result = $group['cf']->multiget(self::$KEYS); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[0]], $group['dict']); + + $result = $group['cf']->multiget(array(self::$KEYS[2])); + self::assertEqual($result[self::$KEYS[2]], $group['dict']); + + # Check each column individually + foreach(range(0,2) as $i) { + $result = $group['cf']->multiget(self::$KEYS, $columns=array($group['cols'][$i])); + foreach(range(0,2) as $j) + self::assertEqual($result[self::$KEYS[$j]], + array($group['cols'][$i] => array('bytes' => self::$VALS[$i]))); + + } + + # Check that if we list all columns, we get the full dict + $result = $group['cf']->multiget(self::$KEYS, $columns=$group['cols']); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$j]], $group['dict']); + + # The same thing with a start and end instead + $result = $group['cf']->multiget(self::$KEYS, $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][2]); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$j]], $group['dict']); + + # A start and end that are the same + $result = $group['cf']->multiget(self::$KEYS, $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][0]); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$j]], + array($group['cols'][0] => array('bytes' => self::$VALS[0]))); + + + ### get_range() tests ### + + $result = $group['cf']->get_range($key_start=self::$KEYS[0]); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + + $result = $group['cf']->get_range($key_start=self::$KEYS[0], $key_finish='', + $key_count=ColumnFamily::DEFAULT_ROW_COUNT, + $columns=null, + $column_start=$group['cols'][0], + $column_finish=$group['cols'][2]); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + + $result = $group['cf']->get_range($key_start=self::$KEYS[0], $key_finish='', + $key_count=ColumnFamily::DEFAULT_ROW_COUNT, + $columns=$group['cols']); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + } + } + + private function make_sub_group($cf, $cols) { + $diction = array(222222222222 => array($cols[0] => self::$VALS[0], + $cols[1] => self::$VALS[1], + $cols[2] => self::$VALS[2])); + return array('cf' => $cf, 'cols' => $cols, 'dict' => $diction); + } + + public function test_super_column_family_subs() { + $LONG = 222222222222; + + $type_groups = array(); + + $long_cols = array(111111111111, + 222222222222, + 333333333333); + $type_groups[] = self::make_sub_group($this->cf_suplong_sublong, $long_cols); + + $int_cols = array(1, 2, 3); + $type_groups[] = self::make_sub_group($this->cf_suplong_subint, $int_cols); + + $time_cols = array($this->TIME1, $this->TIME2, $this->TIME3); + $type_groups[] = self::make_sub_group($this->cf_suplong_subtime, $time_cols); + + $lex_cols = array($this->LEX1, $this->LEX2, $this->LEX3); + $type_groups[] = self::make_sub_group($this->cf_suplong_sublex, $lex_cols); + + $ascii_cols = array('aaaa', 'bbbb', 'cccc'); + $type_groups[] = self::make_sub_group($this->cf_suplong_subascii, $ascii_cols); + + $utf8_cols = array("aЗ", "bИ", "cЙ"); + $type_groups[] = self::make_sub_group($this->cf_suplong_subutf8, $utf8_cols); + + + foreach($type_groups as $group) { + + $group['cf']->insert(self::$KEYS[0], $group['dict']); + self::assertEqual($group['cf']->get(self::$KEYS[0]), + $group['dict']); + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=array($LONG)), + $group['dict']); + + # A start and end that are the same + self::assertEqual($group['cf']->get(self::$KEYS[0], $columns=null, + $column_start=$LONG, + $column_finish=$LONG), + $group['dict']); + + self::assertEqual($group['cf']->get_count(self::$KEYS[0]), 1); + + ### remove() tests ### + + $group['cf']->remove(self::$KEYS[0], $columns=null, $super_column=$LONG); + self::assertEqual($group['cf']->get_count(self::$KEYS[0]), 0); + + # Insert more than one row + $group['cf']->insert(self::$KEYS[0], $group['dict']); + $group['cf']->insert(self::$KEYS[1], $group['dict']); + $group['cf']->insert(self::$KEYS[2], $group['dict']); + + + ### multiget() tests ### + + $result = $group['cf']->multiget(self::$KEYS); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[0]], $group['dict']); + + $result = $group['cf']->multiget(array(self::$KEYS[2])); + self::assertEqual($result[self::$KEYS[2]], $group['dict']); + + $result = $group['cf']->multiget(self::$KEYS, $columns=array($LONG)); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$i]], $group['dict']); + + $result = $group['cf']->multiget(self::$KEYS, + $columns=null, + $column_start='', + $column_finish='', + $column_reverse=False, + $count=ColumnFamily::DEFAULT_COLUMN_COUNT, + $supercolumn=$LONG); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$i]], $group['dict'][$LONG]); + + $result = $group['cf']->multiget(self::$KEYS, + $columns=null, + $column_start=$LONG, + $column_finish=$LONG); + foreach(range(0,2) as $i) + self::assertEqual($result[self::$KEYS[$i]], $group['dict']); + + ### get_range() tests ### + + $result = $group['cf']->get_range($key_start=self::$KEYS[0]); + foreach($result as $subres) { + self::assertEqual($subres, $group['dict']); + } + + $result = $group['cf']->get_range($key_start=self::$KEYS[0], $key_finish='', + $row_count=ColumnFamily::DEFAULT_ROW_COUNT, + $columns=null, + $column_start=$LONG, + $column_finish=$LONG); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + + $result = $group['cf']->get_range($key_start=self::$KEYS[0], + $key_finish='', + $row_count=ColumnFamily::DEFAULT_ROW_COUNT, + $columns=array($LONG)); + foreach($result as $subres) + self::assertEqual($subres, $group['dict']); + + $result = $group['cf']->get_range($key_start=self::$KEYS[0], + $key_finish='', + $row_count=ColumnFamily::DEFAULT_ROW_COUNT, + $columns=null, + $column_start='', + $column_finish='', + $column_revered=False, + $column_count=ColumnFamily::DEFAULT_COLUMN_COUNT, + $super_column=$LONG); + foreach($result as $subres) + self::assertEqual($subres, $group['dict'][$LONG]); + } + } + + public function test_validated_columns(){ + + # Longs + $col = array('subcol' => 222222222222); + $this->cf_valid_long->insert(self::$KEYS[0], $col); + self::assertEqual($this->cf_valid_long->get(self::$KEYS[0]), $col); + + # Integers + $col = array('subcol' => 2); + $this->cf_valid_int->insert(self::$KEYS[0], $col); + self::assertEqual($this->cf_valid_int->get(self::$KEYS[0]), $col); + + # TimeUUIDs + $col = array('subcol' => $this->TIME1); + $this->cf_valid_time->insert(self::$KEYS[0], $col); + self::assertEqual($this->cf_valid_time->get(self::$KEYS[0]), $col); + + # LexicalUUIDs + $col = array('subcol' => $this->LEX1); + $this->cf_valid_lex->insert(self::$KEYS[0], $col); + self::assertEqual($this->cf_valid_lex->get(self::$KEYS[0]), $col); + + # ASCII + $col = array('subcol' => 'aaa'); + $this->cf_valid_ascii->insert(self::$KEYS[0], $col); + self::assertEqual($this->cf_valid_ascii->get(self::$KEYS[0]), $col); + + # UTF8 + $col = array('subcol' => "aЗ"); + $this->cf_valid_utf8->insert(self::$KEYS[0], $col); + self::assertEqual($this->cf_valid_utf8->get(self::$KEYS[0]), $col); + + # BytesType + $col = array('subcol' => 'aaa123'); + $this->cf_valid_bytes->insert(self::$KEYS[0], $col); + self::assertEqual($this->cf_valid_bytes->get(self::$KEYS[0]), $col); + } + + public function test_default_validated_columns() { + $col_cf = array('aaaaaa' => 222222222222); + $col_cm = array('subcol' => $this->TIME1); + + # Both of these inserts work, as cf allows + # longs and cm for 'subcol' allows TimeUUIDs + $this->cf_def_valid->insert(self::$KEYS[0], $col_cf); + $this->cf_def_valid->insert(self::$KEYS[0], $col_cm); + self::assertEqual($this->cf_def_valid->get(self::$KEYS[0]), + array('aaaaaa' => 222222222222, 'subcol' => $this->TIME1)); + } + + public function test_uuid1_generation() { + $micros = 1293769171436849; + $uuid = CassandraUtil::import(CassandraUtil::uuid1(null, $micros)); + $t = (int)($uuid->time * 1000000); + self::assertWithinMargin($micros, $t, 100); + } +} +?> diff --git a/library/phpcassa/test/test_columnfamily.php b/library/phpcassa/test/test_columnfamily.php new file mode 100644 index 000000000..856e58d50 --- /dev/null +++ b/library/phpcassa/test/test_columnfamily.php @@ -0,0 +1,548 @@ +pool = new Connection('Keyspace1'); + $this->cf = new ColumnFamily($this->pool, 'Standard1'); + } + + public function tearDown() { + if ($this->cf) + foreach(self::$KEYS as $key) + $this->cf->remove($key); + } + + public function test_empty() { + try { + $this->cf->get(self::$KEYS[0]); + self::assertTrue(false); + } catch (cassandra_NotFoundException $e) { + } + } + + public function test_insert_get() { + $this->cf->insert(self::$KEYS[0], array('col' => 'val')); + self::assertEqual($this->cf->get(self::$KEYS[0]), array('col' => 'val')); + } + + public function test_insert_multiget() { + $columns1 = array('1' => 'val1', '2' => 'val2'); + $columns2 = array('3' => 'val1', '4' => 'val2'); + + $this->cf->insert(self::$KEYS[0], $columns1); + $this->cf->insert(self::$KEYS[1], $columns2); + $rows = $this->cf->multiget(self::$KEYS); + self::assertEqual(count($rows), 2); + self::assertEqual($rows[self::$KEYS[0]], $columns1); + self::assertEqual($rows[self::$KEYS[1]], $columns2); + self::assertFalse(in_array(self::$KEYS[2], $rows)); + } + + public function test_batch_insert() { + $columns1 = array('1' => 'val1', '2' => 'val2'); + $columns2 = array('3' => 'val1', '4' => 'val2'); + $rows = array(self::$KEYS[0] => $columns1, + self::$KEYS[1] => $columns2); + $this->cf->batch_insert($rows); + $rows = $this->cf->multiget(self::$KEYS); + self::assertEqual(count($rows), 2); + self::assertEqual($rows[self::$KEYS[0]], $columns1); + self::assertEqual($rows[self::$KEYS[1]], $columns2); + self::assertFalse(in_array(self::$KEYS[2], $rows)); + } + + public function test_insert_get_count() { + $cols = array('1' => 'val1', '2' => 'val2'); + $this->cf->insert(self::$KEYS[0], $cols); + self::assertEqual($this->cf->get_count(self::$KEYS[0]), 2); + + self::assertEqual($this->cf->get_count(self::$KEYS[0], $columns=null, $column_start='1'), 2); + self::assertEqual($this->cf->get_count(self::$KEYS[0], $columns=null, $column_start='', + $column_finish='2'), 2); + self::assertEqual($this->cf->get_count(self::$KEYS[0], $columns=null, $column_start='1', $column_finish='2'), 2); + self::assertEqual($this->cf->get_count(self::$KEYS[0], $columns=null, $column_start='1', $column_finish='1'), 1); + self::assertEqual($this->cf->get_count(self::$KEYS[0], $columns=array('1', '2')), 2); + self::assertEqual($this->cf->get_count(self::$KEYS[0], $columns=array('1')), 1); + } + + public function test_insert_multiget_count() { + $columns = array('1' => 'val1', '2' => 'val2'); + foreach(self::$KEYS as $key) + $this->cf->insert($key, $columns); + + $result = $this->cf->multiget_count(self::$KEYS); + foreach(self::$KEYS as $key) + self::assertEqual($result[$key], 2); + + $result = $this->cf->multiget_count(self::$KEYS, $columns=null, $column_start='1'); + self::assertEqual(count($result), 3); + self::assertEqual($result[self::$KEYS[0]], 2); + + $result = $this->cf->multiget_count(self::$KEYS, $columns=null, $column_start='', $column_finish='2'); + self::assertEqual(count($result), 3); + self::assertEqual($result[self::$KEYS[0]], 2); + + $result = $this->cf->multiget_count(self::$KEYS, $columns=null, $column_start='1', $column_finish='2'); + self::assertEqual(count($result), 3); + self::assertEqual($result[self::$KEYS[0]], 2); + + $result = $this->cf->multiget_count(self::$KEYS, $columns=null, $column_start='1', $column_finish='1'); + self::assertEqual(count($result), 3); + self::assertEqual($result[self::$KEYS[0]], 1); + + $result = $this->cf->multiget_count(self::$KEYS, $columns=array('1', '2')); + self::assertEqual(count($result), 3); + self::assertEqual($result[self::$KEYS[0]], 2); + + $result = $this->cf->multiget_count(self::$KEYS, $columns=array('1')); + self::assertEqual(count($result), 3); + self::assertEqual($result[self::$KEYS[0]], 1); + } + + public function test_insert_get_range() { + $cl = cassandra_ConsistencyLevel::ONE; + $cf = new ColumnFamily($this->pool, + 'Standard1', true, true, + $read_consistency_level=$cl, + $write_consistency_level=$cl, + $buffer_size=10); + $keys = array(); + $columns = array('c' => 'v'); + foreach (range(100, 200) as $i) { + $keys[] = 'key'.$i; + $cf->insert('key'.$i, $columns); + } + + # Keys at the end that we don't want + foreach (range(201, 300) as $i) + $cf->insert('key'.$i, $columns); + + + # Buffer size = 10; rowcount is divisible by buffer size + $count = 0; + foreach ($cf->get_range() as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Buffer size larger than row count + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=1000); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=100) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Buffer size larger than row count, less than total number of rows + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=150); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=100) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Odd number for batch size + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=7); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=100) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Smallest buffer size available + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=2); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=100) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Put the remaining keys in our list + foreach (range(201, 300) as $i) + $keys[] = 'key'.$i; + + + # Row count above total number of rows + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=2); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=10000) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + + # Row count above total number of rows + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=7); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=10000) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + + + # Row count above total number of rows, buffer_size = total number of rows + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=200); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=10000) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + + # Row count above total number of rows, buffer_size = total number of rows + $cf = new ColumnFamily($this->pool, 'Standard1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=10000); + $count = 0; + foreach ($cf->get_range($key_start='', $key_finish='', $row_count=10000) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + + $cf->truncate(); + } + + public function test_batched_get_indexed_slices() { + + $cl = cassandra_ConsistencyLevel::ONE; + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=10); + $cf->truncate(); + + $keys = array(); + $columns = array('birthdate' => 1); + foreach (range(100, 200) as $i) { + $keys[] = 'key'.$i; + $cf->insert('key'.$i, $columns); + } + + # Keys at the end that we don't want + foreach (range(201, 300) as $i) + $cf->insert('key'.$i, $columns); + + + $expr = CassandraUtil::create_index_expression($column_name='birthdate', $value=1); + $clause = CassandraUtil::create_index_clause(array($expr), 100); + + # Buffer size = 10; rowcount is divisible by buffer size + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + # Buffer size larger than row count + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=1000); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Buffer size larger than row count, less than total number of rows + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=150); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Odd number for batch size + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=7); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Smallest buffer size available + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=2); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 100); + + + # Put the remaining keys in our list + foreach (range(201, 300) as $i) + $keys[] = 'key'.$i; + + + # Row count above total number of rows + $clause->count = 10000; + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=2); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + + # Row count above total number of rows + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=7); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + + + # Row count above total number of rows, buffer_size = total number of rows + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=200); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + + # Row count above total number of rows, buffer_size = total number of rows + $cf = new ColumnFamily($this->pool, 'Indexed1', true, true, + $read_consistency_level=$cl, $write_consistency_level=$cl, + $buffer_size=10000); + $count = 0; + foreach ($cf->get_indexed_slices($clause) as $key => $cols) { + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + $count++; + } + self::assertEqual($count, 201); + + $cf->truncate(); + } + + public function test_get_indexed_slices() { + $indexed_cf = new ColumnFamily($this->pool, 'Indexed1'); + $indexed_cf->truncate(); + + $columns = array('birthdate' => 1); + + foreach(range(1,3) as $i) + $indexed_cf->insert('key'.$i, $columns); + + $expr = CassandraUtil::create_index_expression($column_name='birthdate', $value=1); + $clause = CassandraUtil::create_index_clause(array($expr), 10000); + $result = $indexed_cf->get_indexed_slices($clause); + + $count = 0; + foreach($result as $key => $cols) { + $count++; + self::assertEqual($columns, $cols); + self::assertEqual($key, "key$count"); + } + self::assertEqual($count, 3); + + # Insert and remove a matching row at the beginning + $indexed_cf->insert('key0', $columns); + $indexed_cf->remove('key0'); + # Insert and remove a matching row at the end + $indexed_cf->insert('key4', $columns); + $indexed_cf->remove('key4'); + # Remove a matching row from the middle + $indexed_cf->remove('key2'); + + $result = $indexed_cf->get_indexed_slices($clause); + + $count = 0; + foreach($result as $key => $cols) { + $count++; + self::assertTrue($key == "key1" || $key == "key3"); + } + self::assertEqual($count, 2); + + $indexed_cf->truncate(); + + $keys = array(); + foreach(range(1,1000) as $i) { + $indexed_cf->insert("key$i", $columns); + if ($i % 50 != 0) + $indexed_cf->remove("key$i"); + else + $keys[] = "key$i"; + } + + $count = 0; + foreach($result as $key => $cols) { + $count++; + self::assertTrue(in_array($key, $keys)); + unset($keys[$key]); + } + self::assertEqual($count, 20); + + $indexed_cf->truncate(); + } + + public function test_remove() { + $columns = array('1' => 'val1', '2' => 'val2'); + $this->cf->insert(self::$KEYS[0], $columns); + + self::assertEqual($this->cf->get(self::$KEYS[0]), $columns); + + $this->cf->remove(self::$KEYS[0], array('2')); + unset($columns['2']); + self::assertEqual($this->cf->get(self::$KEYS[0]), $columns); + + $this->cf->remove(self::$KEYS[0]); + try { + $this->cf->get(self::$KEYS[0]); + self::assertTrue(false); + } catch (cassandra_NotFoundException $e) { + } + } +} + +class TestSuperColumnFamily extends UnitTestCase { + + private $pool; + private $cf; + + private static $KEYS = array('key1', 'key2', 'key3'); + + public function setUp() { + $this->pool = new Connection('Keyspace1'); + $this->cf = new ColumnFamily($this->pool, 'Super1'); + } + + public function tearDown() { + foreach(self::$KEYS as $key) + $this->cf->remove($key); + } + + public function test_super() { + $columns = array('1' => array('sub1' => 'val1', 'sub2' => 'val2'), + '2' => array('sub3' => 'val3', 'sub3' => 'val3')); + try { + $this->cf->get(self::$KEYS[0]); + assert(false); + } catch (cassandra_NotFoundException $e) { + } + + $this->cf->insert(self::$KEYS[0], $columns); + self::assertEqual($this->cf->get(self::$KEYS[0]), $columns); + self::assertEqual($this->cf->multiget(array(self::$KEYS[0])), array(self::$KEYS[0] => $columns)); + $response = $this->cf->get_range($start_key=self::$KEYS[0], + $finish_key=self::$KEYS[0]); + foreach($response as $key => $cols) { + #should only be one row + self::assertEqual($key, self::$KEYS[0]); + self::assertEqual($cols, $columns); + } + } + + public function test_super_column_argument() { + $key = self::$KEYS[0]; + $sub12 = array('sub1' => 'val1', 'sub2' => 'val2'); + $sub34 = array('sub3' => 'val3', 'sub4' => 'val4'); + $cols = array('1' => $sub12, '2' => $sub34); + $this->cf->insert($key, $cols); + self::assertEqual($this->cf->get($key, null, '', '', false, 100, $super_column='1'), $sub12); + try { + $this->cf->get($key, null, '', '', false, 100, $super_column='3'); + assert(false); + } catch (cassandra_NotFoundException $e) { + } + + self::assertEqual($this->cf->multiget(array($key), null, '', '', false, 100, $super_column='1'), + array($key => $sub12)); + + $response = $this->cf->get_range($start_key=$key, $end_key=$key, 100, null, '', + '', false, 100, $super_column='1'); + foreach($response as $res_key => $cols) { + #should only be one row + self::assertEqual($res_key, $key); + self::assertEqual($cols, $sub12); + } + + self::assertEqual($this->cf->get_count($key), 2); + $this->cf->remove($key, null, '1'); + self::assertEqual($this->cf->get_count($key), 1); + $this->cf->remove($key, array('sub3'), '2'); + self::assertEqual($this->cf->get_count($key), 1); + self::assertEqual($this->cf->get($key), array('2' => array('sub4' => 'val4'))); + } +} +?> diff --git a/library/phpcassa/test/test_large_ops.php b/library/phpcassa/test/test_large_ops.php new file mode 100644 index 000000000..20ae7f563 --- /dev/null +++ b/library/phpcassa/test/test_large_ops.php @@ -0,0 +1,41 @@ +client = new ConnectionPool('Keyspace1'); + + $this->cf = new ColumnFamily($this->client, 'Standard1'); + } + + public function tearDown() { + $this->cf->truncate(); + } + + public function test_large_ops() { + $str = ''; + foreach(range(0,255) as $i) { + # each addition is 64 bytes + $str .= 'aaaaaaa aaaaaaa aaaaaaa aaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa '; + } + + foreach(range(0, 99) as $i) + $this->cf->insert("key$i", array($str => $str)); + + foreach(range(0, 99) as $i) { + $res = $this->cf->get("key$i"); + self::assertEqual($res[$str], $str); + } + } +} +?> diff --git a/library/phpcassa/test/test_pooling.php b/library/phpcassa/test/test_pooling.php new file mode 100644 index 000000000..aa8a84c19 --- /dev/null +++ b/library/phpcassa/test/test_pooling.php @@ -0,0 +1,112 @@ + 'localhost', 'port' => 9160)); + $pool = new Connection('Keyspace1', $tservers); + $stats = $pool->stats(); + self::assertEqual($stats['created'], 5); + foreach (range(1, 4) as $i) { + $conn = $pool->get(); + $conn->client = new MockClient($conn->transport); + $pool->return_connection($conn); + } + $cf = new ColumnFamily($pool, 'Standard1'); + $cf->insert('key', array('col' => 'val')); + $stats = $pool->stats(); + self::assertEqual($stats['created'], 9); + self::assertEqual($stats['failed'], 4); + self::assertEqual($stats['recycled'], 0); + } + + public function test_failover_over_limit() { + $pool = new ConnectionPool('Keyspace1', NULL, 4); + $stats = $pool->stats(); + self::assertEqual($stats['created'], 5); + foreach (range(1, 5) as $i) { + $conn = $pool->get(); + $conn->client = new MockClient($conn->transport); + $pool->return_connection($conn); + } + $cf = new ColumnFamily($pool, 'Standard1'); + try { + $cf->insert('key', array('col' => 'val')); + self::assertTrue(false); + } catch (MaxRetriesException $ex) { + } + $stats = $pool->stats(); + self::assertEqual($stats['created'], 10); + self::assertEqual($stats['failed'], 5); + self::assertEqual($stats['recycled'], 0); + } + + public function test_recycle() { + $pool = new ConnectionPool('Keyspace1', NULL, 5, 5000, 5000, 10); + $cf = new ColumnFamily($pool, 'Standard1'); + foreach (range(1, 50) as $i) { + $cf->insert('key', array('c' => 'v')); + } + $stats = $pool->stats(); + self::assertEqual($stats['created'], 10); + self::assertEqual($stats['failed'], 0); + self::assertEqual($stats['recycled'], 5); + + foreach (range(1, 50) as $i) { + $cf->insert('key', array('c' => 'v')); + } + $stats = $pool->stats(); + self::assertEqual($stats['created'], 15); + self::assertEqual($stats['failed'], 0); + self::assertEqual($stats['recycled'], 10); + } + + public function test_multiple_servers() { + $servers = array('localhost:9160', '127.0.0.1:9160', '127.0.0.1'); + $pool = new ConnectionPool('Keyspace1', $servers); + $cf = new ColumnFamily($pool, 'Standard1'); + foreach (range(1, 50) as $i) { + $cf->insert('key', array('c' => 'v')); + } + $stats = $pool->stats(); + self::assertEqual($stats['created'], 6); + self::assertEqual($stats['failed'], 0); + } + + public function test_initial_connection_failure() { + $servers = array('localhost', 'foobar'); + $pool = new ConnectionPool('Keyspace1', $servers); + $stats = $pool->stats(); + self::assertEqual($stats['created'], 5); + self::assertTrue($stats['failed'] == 5 || $stats['failed'] == 4); + $cf = new ColumnFamily($pool, 'Standard1'); + foreach (range(1, 50) as $i) { + $cf->insert('key', array('c' => 'v')); + } + $pool->dispose(); + + $servers = array('barfoo', 'foobar'); + try { + $pool = new ConnectionPool('Keyspace1', $servers); + self::assertTrue(false); + } catch (NoServerAvailable $ex) { + } + } +} + +class MockClient extends CassandraClient { + + public function __construct($transport) { + parent::__construct(new TBinaryProtocolAccelerated($transport)); + } + + public function batch_mutate($mutation_map, $consistency_level) { + throw new cassandra_TimedOutException(); + } + +} +?> diff --git a/library/phpcassa/thrift/Thrift.php b/library/phpcassa/thrift/Thrift.php new file mode 100644 index 000000000..a14a3f300 --- /dev/null +++ b/library/phpcassa/thrift/Thrift.php @@ -0,0 +1,785 @@ + $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } else { + parent::__construct($p1, $p2); + } + } + + static $tmethod = array(TType::BOOL => 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + private function _readMap(&$var, $spec, $input) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + return $xfer; + } + + protected function _read($class, $spec, $input) { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + return $xfer; + } + + private function _writeMap($var, $spec, $output) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + return $xfer; + } + + protected function _write($class, $spec, $output) { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + return $xfer; + } + +} + +/** + * Base class from which other Thrift structs extend. This is so that we can + * cut back on the size of the generated code which is turning out to have a + * nontrivial cost just to load thanks to the wondrously abysmal implementation + * of PHP. Note that code is intentionally duplicated in here to avoid making + * function calls for every field or member of a container.. + */ +abstract class TBase { + + static $tmethod = array(TType::BOOL => 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + abstract function read($input); + + abstract function write($output); + + public function __construct($spec=null, $vals=null) { + if (is_array($spec) && is_array($vals)) { + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } + } + + private function _readMap(&$var, $spec, $input) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + return $xfer; + } + + protected function _read($class, $spec, $input) { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + return $xfer; + } + + private function _writeMap($var, $spec, $output) { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + return $xfer; + } + + protected function _write($class, $spec, $output) { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + return $xfer; + } +} + +class TApplicationException extends TException { + static $_TSPEC = + array(1 => array('var' => 'message', + 'type' => TType::STRING), + 2 => array('var' => 'code', + 'type' => TType::I32)); + + const UNKNOWN = 0; + const UNKNOWN_METHOD = 1; + const INVALID_MESSAGE_TYPE = 2; + const WRONG_METHOD_NAME = 3; + const BAD_SEQUENCE_ID = 4; + const MISSING_RESULT = 5; + + function __construct($message=null, $code=0) { + parent::__construct($message, $code); + } + + public function read($output) { + return $this->_read('TApplicationException', self::$_TSPEC, $output); + } + + public function write($output) { + $xfer = 0; + $xfer += $output->writeStructBegin('TApplicationException'); + if ($message = $this->getMessage()) { + $xfer += $output->writeFieldBegin('message', TType::STRING, 1); + $xfer += $output->writeString($message); + $xfer += $output->writeFieldEnd(); + } + if ($code = $this->getCode()) { + $xfer += $output->writeFieldBegin('type', TType::I32, 2); + $xfer += $output->writeI32($code); + $xfer += $output->writeFieldEnd(); + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + return $xfer; + } +} + +/** + * Set global THRIFT ROOT automatically via inclusion here + */ +if (!isset($GLOBALS['THRIFT_ROOT'])) { + $GLOBALS['THRIFT_ROOT'] = dirname(__FILE__); +} +include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TProtocol.php'; +include_once $GLOBALS['THRIFT_ROOT'].'/transport/TTransport.php'; diff --git a/library/phpcassa/thrift/autoload.php b/library/phpcassa/thrift/autoload.php new file mode 100644 index 000000000..3a35545d1 --- /dev/null +++ b/library/phpcassa/thrift/autoload.php @@ -0,0 +1,51 @@ + +#include +#include +#include +#include + +#ifndef bswap_64 +#define bswap_64(x) (((uint64_t)(x) << 56) | \ + (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \ + (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \ + (((uint64_t)(x) << 8) & 0xff00000000ULL) | \ + (((uint64_t)(x) >> 8) & 0xff000000ULL) | \ + (((uint64_t)(x) >> 24) & 0xff0000ULL) | \ + (((uint64_t)(x) >> 40) & 0xff00ULL) | \ + ((uint64_t)(x) >> 56)) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htonll(x) bswap_64(x) +#define ntohll(x) bswap_64(x) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define htonll(x) x +#define ntohll(x) x +#else +#error Unknown __BYTE_ORDER +#endif + +enum TType { + T_STOP = 0, + T_VOID = 1, + T_BOOL = 2, + T_BYTE = 3, + T_I08 = 3, + T_I16 = 6, + T_I32 = 8, + T_U64 = 9, + T_I64 = 10, + T_DOUBLE = 4, + T_STRING = 11, + T_UTF7 = 11, + T_STRUCT = 12, + T_MAP = 13, + T_SET = 14, + T_LIST = 15, + T_UTF8 = 16, + T_UTF16 = 17 +}; + +const int32_t VERSION_MASK = 0xffff0000; +const int32_t VERSION_1 = 0x80010000; +const int8_t T_CALL = 1; +const int8_t T_REPLY = 2; +const int8_t T_EXCEPTION = 3; +// tprotocolexception +const int INVALID_DATA = 1; +const int BAD_VERSION = 4; + +#include "php.h" +#include "zend_interfaces.h" +#include "zend_exceptions.h" +#include "php_thrift_protocol.h" + +static function_entry thrift_protocol_functions[] = { + PHP_FE(thrift_protocol_write_binary, NULL) + PHP_FE(thrift_protocol_read_binary, NULL) + {NULL, NULL, NULL} +} ; + +zend_module_entry thrift_protocol_module_entry = { + STANDARD_MODULE_HEADER, + "thrift_protocol", + thrift_protocol_functions, + NULL, + NULL, + NULL, + NULL, + NULL, + "1.0", + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_THRIFT_PROTOCOL +ZEND_GET_MODULE(thrift_protocol) +#endif + +class PHPExceptionWrapper : public std::exception { +public: + PHPExceptionWrapper(zval* _ex) throw() : ex(_ex) { + snprintf(_what, 40, "PHP exception zval=%p", ex); + } + const char* what() const throw() { return _what; } + ~PHPExceptionWrapper() throw() {} + operator zval*() const throw() { return const_cast(ex); } // Zend API doesn't do 'const'... +protected: + zval* ex; + char _what[40]; +} ; + +class PHPTransport { +public: + zval* protocol() { return p; } + zval* transport() { return t; } +protected: + PHPTransport() {} + + void construct_with_zval(zval* _p, size_t _buffer_size) { + buffer = reinterpret_cast(emalloc(_buffer_size)); + buffer_ptr = buffer; + buffer_used = 0; + buffer_size = _buffer_size; + p = _p; + + // Get the transport for the passed protocol + zval gettransport; + ZVAL_STRING(&gettransport, "getTransport", 0); + MAKE_STD_ZVAL(t); + ZVAL_NULL(t); + TSRMLS_FETCH(); + call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC); + } + ~PHPTransport() { + efree(buffer); + zval_ptr_dtor(&t); + } + + char* buffer; + char* buffer_ptr; + size_t buffer_used; + size_t buffer_size; + + zval* p; + zval* t; +}; + + +class PHPOutputTransport : public PHPTransport { +public: + PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) { + construct_with_zval(_p, _buffer_size); + } + + ~PHPOutputTransport() { + //flush(); + } + + void write(const char* data, size_t len) { + if ((len + buffer_used) > buffer_size) { + flush(); + } + if (len > buffer_size) { + directWrite(data, len); + } else { + memcpy(buffer_ptr, data, len); + buffer_used += len; + buffer_ptr += len; + } + } + + void writeI64(int64_t i) { + i = htonll(i); + write((const char*)&i, 8); + } + + void writeU32(uint32_t i) { + i = htonl(i); + write((const char*)&i, 4); + } + + void writeI32(int32_t i) { + i = htonl(i); + write((const char*)&i, 4); + } + + void writeI16(int16_t i) { + i = htons(i); + write((const char*)&i, 2); + } + + void writeI8(int8_t i) { + write((const char*)&i, 1); + } + + void writeString(const char* str, size_t len) { + writeU32(len); + write(str, len); + } + + void flush() { + if (buffer_used) { + directWrite(buffer, buffer_used); + buffer_ptr = buffer; + buffer_used = 0; + } + directFlush(); + } + +protected: + void directFlush() { + zval ret; + ZVAL_NULL(&ret); + zval flushfn; + ZVAL_STRING(&flushfn, "flush", 0); + TSRMLS_FETCH(); + call_user_function(EG(function_table), &t, &flushfn, &ret, 0, NULL TSRMLS_CC); + zval_dtor(&ret); + } + void directWrite(const char* data, size_t len) { + zval writefn; + ZVAL_STRING(&writefn, "write", 0); + char* newbuf = (char*)emalloc(len + 1); + memcpy(newbuf, data, len); + newbuf[len] = '\0'; + zval *args[1]; + MAKE_STD_ZVAL(args[0]); + ZVAL_STRINGL(args[0], newbuf, len, 0); + TSRMLS_FETCH(); + zval ret; + ZVAL_NULL(&ret); + call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC); + zval_ptr_dtor(args); + zval_dtor(&ret); + if (EG(exception)) { + zval* ex = EG(exception); + EG(exception) = NULL; + throw PHPExceptionWrapper(ex); + } + } +}; + +class PHPInputTransport : public PHPTransport { +public: + PHPInputTransport(zval* _p, size_t _buffer_size = 8192) { + construct_with_zval(_p, _buffer_size); + } + + ~PHPInputTransport() { + put_back(); + } + + void put_back() { + if (buffer_used) { + zval putbackfn; + ZVAL_STRING(&putbackfn, "putBack", 0); + + char* newbuf = (char*)emalloc(buffer_used + 1); + memcpy(newbuf, buffer_ptr, buffer_used); + newbuf[buffer_used] = '\0'; + + zval *args[1]; + MAKE_STD_ZVAL(args[0]); + ZVAL_STRINGL(args[0], newbuf, buffer_used, 0); + + TSRMLS_FETCH(); + + zval ret; + ZVAL_NULL(&ret); + call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC); + zval_ptr_dtor(args); + zval_dtor(&ret); + } + buffer_used = 0; + buffer_ptr = buffer; + } + + void skip(size_t len) { + while (len) { + size_t chunk_size = MIN(len, buffer_used); + if (chunk_size) { + buffer_ptr = reinterpret_cast(buffer_ptr) + chunk_size; + buffer_used -= chunk_size; + len -= chunk_size; + } + if (! len) break; + refill(); + } + } + + void readBytes(void* buf, size_t len) { + while (len) { + size_t chunk_size = MIN(len, buffer_used); + if (chunk_size) { + memcpy(buf, buffer_ptr, chunk_size); + buffer_ptr = reinterpret_cast(buffer_ptr) + chunk_size; + buffer_used -= chunk_size; + buf = reinterpret_cast(buf) + chunk_size; + len -= chunk_size; + } + if (! len) break; + refill(); + } + } + + int8_t readI8() { + int8_t c; + readBytes(&c, 1); + return c; + } + + int16_t readI16() { + int16_t c; + readBytes(&c, 2); + return (int16_t)ntohs(c); + } + + uint32_t readU32() { + uint32_t c; + readBytes(&c, 4); + return (uint32_t)ntohl(c); + } + + int32_t readI32() { + int32_t c; + readBytes(&c, 4); + return (int32_t)ntohl(c); + } + +protected: + void refill() { + assert(buffer_used == 0); + zval retval; + ZVAL_NULL(&retval); + + zval *args[1]; + MAKE_STD_ZVAL(args[0]); + ZVAL_LONG(args[0], buffer_size); + + TSRMLS_FETCH(); + + zval funcname; + ZVAL_STRING(&funcname, "read", 0); + + call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC); + zval_ptr_dtor(args); + + if (EG(exception)) { + zval_dtor(&retval); + zval* ex = EG(exception); + EG(exception) = NULL; + throw PHPExceptionWrapper(ex); + } + + buffer_used = Z_STRLEN(retval); + memcpy(buffer, Z_STRVAL(retval), buffer_used); + zval_dtor(&retval); + + buffer_ptr = buffer; + } + +}; + +void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec); +void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec); +void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec); +void skip_element(long thrift_typeID, PHPInputTransport& transport); + +// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments +void createObject(char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = NULL, zval* arg2 = NULL) { + TSRMLS_FETCH(); + size_t obj_typename_len = strlen(obj_typename); + zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC); + if (! ce) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename); + RETURN_NULL(); + } + + object_and_properties_init(return_value, ce, NULL); + zend_function* constructor = zend_std_get_constructor(return_value TSRMLS_CC); + zval* ctor_rv = NULL; + zend_call_method(&return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2 TSRMLS_CC); + zval_ptr_dtor(&ctor_rv); +} + +void throw_tprotocolexception(char* what, long errorcode) { + TSRMLS_FETCH(); + + zval *zwhat, *zerrorcode; + MAKE_STD_ZVAL(zwhat); + MAKE_STD_ZVAL(zerrorcode); + + ZVAL_STRING(zwhat, what, 1); + ZVAL_LONG(zerrorcode, errorcode); + + zval* ex; + MAKE_STD_ZVAL(ex); + createObject("TProtocolException", ex, 2, zwhat, zerrorcode); + zval_ptr_dtor(&zwhat); + zval_ptr_dtor(&zerrorcode); + throw PHPExceptionWrapper(ex); +} + +// Sets EG(exception), call this and then RETURN_NULL(); +void throw_zend_exception_from_std_exception(const std::exception& ex) { + zend_throw_exception(zend_exception_get_default(TSRMLS_CC), const_cast(ex.what()), 0 TSRMLS_CC); +} + + +void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) { + zval** val_ptr; + Z_TYPE_P(return_value) = IS_NULL; // just in case + + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + RETURN_NULL(); + return; + case T_STRUCT: { + if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) { + throw_tprotocolexception("no class type in spec", INVALID_DATA); + skip_element(T_STRUCT, transport); + RETURN_NULL(); + } + char* structType = Z_STRVAL_PP(val_ptr); + createObject(structType, return_value); + if (Z_TYPE_P(return_value) == IS_NULL) { + // unable to create class entry + skip_element(T_STRUCT, transport); + RETURN_NULL(); + } + TSRMLS_FETCH(); + zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + if (Z_TYPE_P(spec) != IS_ARRAY) { + char errbuf[128]; + snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec)); + throw_tprotocolexception(errbuf, INVALID_DATA); + RETURN_NULL(); + } + binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); + return; + } break; + case T_BOOL: { + uint8_t c; + transport.readBytes(&c, 1); + RETURN_BOOL(c != 0); + } + //case T_I08: // same numeric value as T_BYTE + case T_BYTE: { + uint8_t c; + transport.readBytes(&c, 1); + RETURN_LONG((int8_t)c); + } + case T_I16: { + uint16_t c; + transport.readBytes(&c, 2); + RETURN_LONG((int16_t)ntohs(c)); + } + case T_I32: { + uint32_t c; + transport.readBytes(&c, 4); + RETURN_LONG((int32_t)ntohl(c)); + } + case T_U64: + case T_I64: { + uint64_t c; + transport.readBytes(&c, 8); + RETURN_LONG((int64_t)ntohll(c)); + } + case T_DOUBLE: { + union { + uint64_t c; + double d; + } a; + transport.readBytes(&(a.c), 8); + a.c = ntohll(a.c); + RETURN_DOUBLE(a.d); + } + //case T_UTF7: // aliases T_STRING + case T_UTF8: + case T_UTF16: + case T_STRING: { + uint32_t size = transport.readU32(); + if (size) { + char* strbuf = (char*) emalloc(size + 1); + transport.readBytes(strbuf, size); + strbuf[size] = '\0'; + ZVAL_STRINGL(return_value, strbuf, size, 0); + } else { + ZVAL_EMPTY_STRING(return_value); + } + return; + } + case T_MAP: { // array of key -> value + uint8_t types[2]; + transport.readBytes(types, 2); + uint32_t size = transport.readU32(); + array_init(return_value); + + zend_hash_find(fieldspec, "key", 4, (void**)&val_ptr); + HashTable* keyspec = Z_ARRVAL_PP(val_ptr); + zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr); + HashTable* valspec = Z_ARRVAL_PP(val_ptr); + + for (uint32_t s = 0; s < size; ++s) { + zval *value; + MAKE_STD_ZVAL(value); + + zval* key; + MAKE_STD_ZVAL(key); + + binary_deserialize(types[0], transport, key, keyspec); + binary_deserialize(types[1], transport, value, valspec); + if (Z_TYPE_P(key) == IS_LONG) { + zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL); + } + else { + if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key); + zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL); + } + zval_ptr_dtor(&key); + } + return; // return_value already populated + } + case T_LIST: { // array with autogenerated numeric keys + int8_t type = transport.readI8(); + uint32_t size = transport.readU32(); + zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); + HashTable* elemspec = Z_ARRVAL_PP(val_ptr); + + array_init(return_value); + for (uint32_t s = 0; s < size; ++s) { + zval *value; + MAKE_STD_ZVAL(value); + binary_deserialize(type, transport, value, elemspec); + zend_hash_next_index_insert(return_value->value.ht, &value, sizeof(zval *), NULL); + } + return; + } + case T_SET: { // array of key -> TRUE + uint8_t type; + uint32_t size; + transport.readBytes(&type, 1); + transport.readBytes(&size, 4); + size = ntohl(size); + zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); + HashTable* elemspec = Z_ARRVAL_PP(val_ptr); + + array_init(return_value); + + for (uint32_t s = 0; s < size; ++s) { + zval* key; + zval* value; + MAKE_STD_ZVAL(key); + MAKE_STD_ZVAL(value); + ZVAL_TRUE(value); + + binary_deserialize(type, transport, key, elemspec); + + if (Z_TYPE_P(key) == IS_LONG) { + zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL); + } + else { + if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key); + zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL); + } + zval_ptr_dtor(&key); + } + return; + } + }; + + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + +void skip_element(long thrift_typeID, PHPInputTransport& transport) { + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + return; + case T_STRUCT: + while (true) { + int8_t ttype = transport.readI8(); // get field type + if (ttype == T_STOP) break; + transport.skip(2); // skip field number, I16 + skip_element(ttype, transport); // skip field payload + } + return; + case T_BOOL: + case T_BYTE: + transport.skip(1); + return; + case T_I16: + transport.skip(2); + return; + case T_I32: + transport.skip(4); + return; + case T_U64: + case T_I64: + case T_DOUBLE: + transport.skip(8); + return; + //case T_UTF7: // aliases T_STRING + case T_UTF8: + case T_UTF16: + case T_STRING: { + uint32_t len = transport.readU32(); + transport.skip(len); + } return; + case T_MAP: { + int8_t keytype = transport.readI8(); + int8_t valtype = transport.readI8(); + uint32_t size = transport.readU32(); + for (uint32_t i = 0; i < size; ++i) { + skip_element(keytype, transport); + skip_element(valtype, transport); + } + } return; + case T_LIST: + case T_SET: { + int8_t valtype = transport.readI8(); + uint32_t size = transport.readU32(); + for (uint32_t i = 0; i < size; ++i) { + skip_element(valtype, transport); + } + } return; + }; + + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + +void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) { + bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16))); + + char* key; + uint key_len; + long index = 0; + + zval* z; + MAKE_STD_ZVAL(z); + + int res = zend_hash_get_current_key_ex(ht, &key, &key_len, (ulong*)&index, 0, &ht_pos); + if (keytype_is_numeric) { + if (res == HASH_KEY_IS_STRING) { + index = strtol(key, NULL, 10); + } + ZVAL_LONG(z, index); + } else { + char buf[64]; + if (res == HASH_KEY_IS_STRING) { + key_len -= 1; // skip the null terminator + } else { + sprintf(buf, "%ld", index); + key = buf; key_len = strlen(buf); + } + ZVAL_STRINGL(z, key, key_len, 1); + } + binary_serialize(keytype, transport, &z, NULL); + zval_ptr_dtor(&z); +} + +inline bool ttype_is_int(int8_t t) { + return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64))); +} + +inline bool ttypes_are_compatible(int8_t t1, int8_t t2) { + // Integer types of different widths are considered compatible; + // otherwise the typeID must match. + return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2))); +} + +void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) { + // SET and LIST have 'elem' => array('type', [optional] 'class') + // MAP has 'val' => array('type', [optiona] 'class') + TSRMLS_FETCH(); + zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC); + while (true) { + zval** val_ptr = NULL; + + int8_t ttype = transport.readI8(); + if (ttype == T_STOP) return; + int16_t fieldno = transport.readI16(); + if (zend_hash_index_find(spec, fieldno, (void**)&val_ptr) == SUCCESS) { + HashTable* fieldspec = Z_ARRVAL_PP(val_ptr); + // pull the field name + // zend hash tables use the null at the end in the length... so strlen(hash key) + 1. + zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr); + char* varname = Z_STRVAL_PP(val_ptr); + + // and the type + zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + int8_t expected_ttype = Z_LVAL_PP(val_ptr); + + if (ttypes_are_compatible(ttype, expected_ttype)) { + zval* rv = NULL; + MAKE_STD_ZVAL(rv); + binary_deserialize(ttype, transport, rv, fieldspec); + zend_update_property(ce, zthis, varname, strlen(varname), rv TSRMLS_CC); + zval_ptr_dtor(&rv); + } else { + skip_element(ttype, transport); + } + } else { + skip_element(ttype, transport); + } + } +} + +void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec) { + // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload. + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + return; + case T_STRUCT: { + TSRMLS_FETCH(); + if (Z_TYPE_PP(value) != IS_OBJECT) { + throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA); + } + zval* spec = zend_read_static_property(zend_get_class_entry(*value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + if (Z_TYPE_P(spec) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA); + } + binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec)); + } return; + case T_BOOL: + if (Z_TYPE_PP(value) != IS_BOOL) convert_to_boolean(*value); + transport.writeI8(Z_BVAL_PP(value) ? 1 : 0); + return; + case T_BYTE: + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + transport.writeI8(Z_LVAL_PP(value)); + return; + case T_I16: + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + transport.writeI16(Z_LVAL_PP(value)); + return; + case T_I32: + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + transport.writeI32(Z_LVAL_PP(value)); + return; + case T_I64: + case T_U64: + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + transport.writeI64(Z_LVAL_PP(value)); + return; + case T_DOUBLE: { + union { + int64_t c; + double d; + } a; + if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value); + a.d = Z_DVAL_PP(value); + transport.writeI64(a.c); + } return; + //case T_UTF7: + case T_UTF8: + case T_UTF16: + case T_STRING: + if (Z_TYPE_PP(value) != IS_STRING) convert_to_string(*value); + transport.writeString(Z_STRVAL_PP(value), Z_STRLEN_PP(value)); + return; + case T_MAP: { + if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); + if (Z_TYPE_PP(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_PP(value); + zval** val_ptr; + + zend_hash_find(fieldspec, "ktype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t keytype = Z_LVAL_PP(val_ptr); + transport.writeI8(keytype); + zend_hash_find(fieldspec, "vtype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t valtype = Z_LVAL_PP(val_ptr); + transport.writeI8(valtype); + + zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr); + HashTable* valspec = Z_ARRVAL_PP(val_ptr); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); + binary_serialize(valtype, transport, val_ptr, valspec); + } + } return; + case T_LIST: { + if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); + if (Z_TYPE_PP(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_PP(value); + zval** val_ptr; + + zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t valtype = Z_LVAL_PP(val_ptr); + transport.writeI8(valtype); + + zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); + HashTable* valspec = Z_ARRVAL_PP(val_ptr); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize(valtype, transport, val_ptr, valspec); + } + } return; + case T_SET: { + if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); + if (Z_TYPE_PP(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_PP(value); + zval** val_ptr; + + zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t keytype = Z_LVAL_PP(val_ptr); + transport.writeI8(keytype); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); + } + } return; + }; + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + + +void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) { + HashPosition key_ptr; + zval** val_ptr; + + TSRMLS_FETCH(); + zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC); + + for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) { + ulong fieldno; + if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) { + throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA); + return; + } + HashTable* fieldspec = Z_ARRVAL_PP(val_ptr); + + // field name + zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr); + char* varname = Z_STRVAL_PP(val_ptr); + + // thrift type + zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + int8_t ttype = Z_LVAL_PP(val_ptr); + + zval* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC); + if (Z_TYPE_P(prop) != IS_NULL) { + transport.writeI8(ttype); + transport.writeI16(fieldno); + binary_serialize(ttype, transport, &prop, fieldspec); + } + } + transport.writeI8(T_STOP); // struct end +} + +// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write +PHP_FUNCTION(thrift_protocol_write_binary) { + int argc = ZEND_NUM_ARGS(); + if (argc < 6) { + WRONG_PARAM_COUNT; + } + + zval ***args = (zval***) emalloc(argc * sizeof(zval**)); + zend_get_parameters_array_ex(argc, args); + + if (Z_TYPE_PP(args[0]) != IS_OBJECT) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)"); + efree(args); + RETURN_NULL(); + } + + if (Z_TYPE_PP(args[1]) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (method name)"); + efree(args); + RETURN_NULL(); + } + + if (Z_TYPE_PP(args[3]) != IS_OBJECT) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "4th parameter is not an object (request struct)"); + efree(args); + RETURN_NULL(); + } + + try { + PHPOutputTransport transport(*args[0]); + const char* method_name = Z_STRVAL_PP(args[1]); + convert_to_long(*args[2]); + int32_t msgtype = Z_LVAL_PP(args[2]); + zval* request_struct = *args[3]; + convert_to_long(*args[4]); + int32_t seqID = Z_LVAL_PP(args[4]); + convert_to_boolean(*args[5]); + bool strictWrite = Z_BVAL_PP(args[5]); + efree(args); + args = NULL; + + if (strictWrite) { + int32_t version = VERSION_1 | msgtype; + transport.writeI32(version); + transport.writeString(method_name, strlen(method_name)); + transport.writeI32(seqID); + } else { + transport.writeString(method_name, strlen(method_name)); + transport.writeI8(msgtype); + transport.writeI32(seqID); + } + + zval* spec = zend_read_static_property(zend_get_class_entry(request_struct TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + if (Z_TYPE_P(spec) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send non-Thrift object", INVALID_DATA); + } + binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec)); + transport.flush(); + } catch (const PHPExceptionWrapper& ex) { + zend_throw_exception_object(ex TSRMLS_CC); + RETURN_NULL(); + } catch (const std::exception& ex) { + throw_zend_exception_from_std_exception(ex); + RETURN_NULL(); + } +} + +// 3 params: $transport $response_Typename $strict_read +PHP_FUNCTION(thrift_protocol_read_binary) { + int argc = ZEND_NUM_ARGS(); + + if (argc < 3) { + WRONG_PARAM_COUNT; + } + + zval ***args = (zval***) emalloc(argc * sizeof(zval**)); + zend_get_parameters_array_ex(argc, args); + + if (Z_TYPE_PP(args[0]) != IS_OBJECT) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)"); + efree(args); + RETURN_NULL(); + } + + if (Z_TYPE_PP(args[1]) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (typename of expected response struct)"); + efree(args); + RETURN_NULL(); + } + + try { + PHPInputTransport transport(*args[0]); + char* obj_typename = Z_STRVAL_PP(args[1]); + convert_to_boolean(*args[2]); + bool strict_read = Z_BVAL_PP(args[2]); + efree(args); + args = NULL; + + int8_t messageType = 0; + int32_t sz = transport.readI32(); + + if (sz < 0) { + // Check for correct version number + int32_t version = sz & VERSION_MASK; + if (version != VERSION_1) { + throw_tprotocolexception("Bad version identifier", BAD_VERSION); + } + messageType = (sz & 0x000000ff); + int32_t namelen = transport.readI32(); + // skip the name string and the sequence ID, we don't care about those + transport.skip(namelen + 4); + } else { + if (strict_read) { + throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION); + } else { + // Handle pre-versioned input + transport.skip(sz); // skip string body + messageType = transport.readI8(); + transport.skip(4); // skip sequence number + } + } + + if (messageType == T_EXCEPTION) { + zval* ex; + MAKE_STD_ZVAL(ex); + createObject("TApplicationException", ex); + zval* spec = zend_read_static_property(zend_get_class_entry(ex TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + binary_deserialize_spec(ex, transport, Z_ARRVAL_P(spec)); + throw PHPExceptionWrapper(ex); + } + + createObject(obj_typename, return_value); + zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); + } catch (const PHPExceptionWrapper& ex) { + zend_throw_exception_object(ex TSRMLS_CC); + RETURN_NULL(); + } catch (const std::exception& ex) { + throw_zend_exception_from_std_exception(ex); + RETURN_NULL(); + } +} + diff --git a/library/phpcassa/thrift/ext/thrift_protocol/php_thrift_protocol.h b/library/phpcassa/thrift/ext/thrift_protocol/php_thrift_protocol.h new file mode 100644 index 000000000..44d03ccde --- /dev/null +++ b/library/phpcassa/thrift/ext/thrift_protocol/php_thrift_protocol.h @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#pragma once + +PHP_FUNCTION(thrift_protocol_write_binary); +PHP_FUNCTION(thrift_protocol_read_binary); + +extern zend_module_entry thrift_protocol_module_entry; +#define phpext_thrift_protocol_ptr &thrift_protocol_module_entry + diff --git a/library/phpcassa/thrift/packages/cassandra/Cassandra.php b/library/phpcassa/thrift/packages/cassandra/Cassandra.php new file mode 100644 index 000000000..232f47c67 --- /dev/null +++ b/library/phpcassa/thrift/packages/cassandra/Cassandra.php @@ -0,0 +1,3894 @@ +input_ = $input; + $this->output_ = $output ? $output : $input; + } + + public function login($auth_request) + { + $this->send_login($auth_request); + $this->recv_login(); + } + + public function send_login($auth_request) + { + $args = new cassandra_Cassandra_login_args(); + $args->auth_request = $auth_request; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'login', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('login', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_login() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_login_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_login_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->authnx !== null) { + throw $result->authnx; + } + if ($result->authzx !== null) { + throw $result->authzx; + } + return; + } + + public function set_keyspace($keyspace) + { + $this->send_set_keyspace($keyspace); + $this->recv_set_keyspace(); + } + + public function send_set_keyspace($keyspace) + { + $args = new cassandra_Cassandra_set_keyspace_args(); + $args->keyspace = $keyspace; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'set_keyspace', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('set_keyspace', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_set_keyspace() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_set_keyspace_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_set_keyspace_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->ire !== null) { + throw $result->ire; + } + return; + } + + public function get($key, $column_path, $consistency_level) + { + $this->send_get($key, $column_path, $consistency_level); + return $this->recv_get(); + } + + public function send_get($key, $column_path, $consistency_level) + { + $args = new cassandra_Cassandra_get_args(); + $args->key = $key; + $args->column_path = $column_path; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'get', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('get', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_get() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_get_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_get_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->nfe !== null) { + throw $result->nfe; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + throw new Exception("get failed: unknown result"); + } + + public function get_slice($key, $column_parent, $predicate, $consistency_level) + { + $this->send_get_slice($key, $column_parent, $predicate, $consistency_level); + return $this->recv_get_slice(); + } + + public function send_get_slice($key, $column_parent, $predicate, $consistency_level) + { + $args = new cassandra_Cassandra_get_slice_args(); + $args->key = $key; + $args->column_parent = $column_parent; + $args->predicate = $predicate; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'get_slice', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('get_slice', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_get_slice() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_get_slice_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_get_slice_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + throw new Exception("get_slice failed: unknown result"); + } + + public function get_count($key, $column_parent, $predicate, $consistency_level) + { + $this->send_get_count($key, $column_parent, $predicate, $consistency_level); + return $this->recv_get_count(); + } + + public function send_get_count($key, $column_parent, $predicate, $consistency_level) + { + $args = new cassandra_Cassandra_get_count_args(); + $args->key = $key; + $args->column_parent = $column_parent; + $args->predicate = $predicate; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'get_count', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('get_count', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_get_count() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_get_count_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_get_count_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + throw new Exception("get_count failed: unknown result"); + } + + public function multiget_slice($keys, $column_parent, $predicate, $consistency_level) + { + $this->send_multiget_slice($keys, $column_parent, $predicate, $consistency_level); + return $this->recv_multiget_slice(); + } + + public function send_multiget_slice($keys, $column_parent, $predicate, $consistency_level) + { + $args = new cassandra_Cassandra_multiget_slice_args(); + $args->keys = $keys; + $args->column_parent = $column_parent; + $args->predicate = $predicate; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'multiget_slice', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('multiget_slice', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_multiget_slice() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_multiget_slice_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_multiget_slice_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + throw new Exception("multiget_slice failed: unknown result"); + } + + public function multiget_count($keys, $column_parent, $predicate, $consistency_level) + { + $this->send_multiget_count($keys, $column_parent, $predicate, $consistency_level); + return $this->recv_multiget_count(); + } + + public function send_multiget_count($keys, $column_parent, $predicate, $consistency_level) + { + $args = new cassandra_Cassandra_multiget_count_args(); + $args->keys = $keys; + $args->column_parent = $column_parent; + $args->predicate = $predicate; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'multiget_count', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('multiget_count', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_multiget_count() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_multiget_count_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_multiget_count_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + throw new Exception("multiget_count failed: unknown result"); + } + + public function get_range_slices($column_parent, $predicate, $range, $consistency_level) + { + $this->send_get_range_slices($column_parent, $predicate, $range, $consistency_level); + return $this->recv_get_range_slices(); + } + + public function send_get_range_slices($column_parent, $predicate, $range, $consistency_level) + { + $args = new cassandra_Cassandra_get_range_slices_args(); + $args->column_parent = $column_parent; + $args->predicate = $predicate; + $args->range = $range; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'get_range_slices', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('get_range_slices', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_get_range_slices() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_get_range_slices_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_get_range_slices_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + throw new Exception("get_range_slices failed: unknown result"); + } + + public function get_indexed_slices($column_parent, $index_clause, $column_predicate, $consistency_level) + { + $this->send_get_indexed_slices($column_parent, $index_clause, $column_predicate, $consistency_level); + return $this->recv_get_indexed_slices(); + } + + public function send_get_indexed_slices($column_parent, $index_clause, $column_predicate, $consistency_level) + { + $args = new cassandra_Cassandra_get_indexed_slices_args(); + $args->column_parent = $column_parent; + $args->index_clause = $index_clause; + $args->column_predicate = $column_predicate; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'get_indexed_slices', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('get_indexed_slices', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_get_indexed_slices() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_get_indexed_slices_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_get_indexed_slices_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + throw new Exception("get_indexed_slices failed: unknown result"); + } + + public function insert($key, $column_parent, $column, $consistency_level) + { + $this->send_insert($key, $column_parent, $column, $consistency_level); + $this->recv_insert(); + } + + public function send_insert($key, $column_parent, $column, $consistency_level) + { + $args = new cassandra_Cassandra_insert_args(); + $args->key = $key; + $args->column_parent = $column_parent; + $args->column = $column; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'insert', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('insert', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_insert() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_insert_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_insert_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + return; + } + + public function remove($key, $column_path, $timestamp, $consistency_level) + { + $this->send_remove($key, $column_path, $timestamp, $consistency_level); + $this->recv_remove(); + } + + public function send_remove($key, $column_path, $timestamp, $consistency_level) + { + $args = new cassandra_Cassandra_remove_args(); + $args->key = $key; + $args->column_path = $column_path; + $args->timestamp = $timestamp; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'remove', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('remove', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_remove() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_remove_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_remove_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + return; + } + + public function batch_mutate($mutation_map, $consistency_level) + { + $this->send_batch_mutate($mutation_map, $consistency_level); + $this->recv_batch_mutate(); + } + + public function send_batch_mutate($mutation_map, $consistency_level) + { + $args = new cassandra_Cassandra_batch_mutate_args(); + $args->mutation_map = $mutation_map; + $args->consistency_level = $consistency_level; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'batch_mutate', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('batch_mutate', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_batch_mutate() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_batch_mutate_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_batch_mutate_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + if ($result->te !== null) { + throw $result->te; + } + return; + } + + public function truncate($cfname) + { + $this->send_truncate($cfname); + $this->recv_truncate(); + } + + public function send_truncate($cfname) + { + $args = new cassandra_Cassandra_truncate_args(); + $args->cfname = $cfname; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'truncate', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('truncate', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_truncate() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_truncate_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_truncate_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->ire !== null) { + throw $result->ire; + } + if ($result->ue !== null) { + throw $result->ue; + } + return; + } + + public function describe_schema_versions() + { + $this->send_describe_schema_versions(); + return $this->recv_describe_schema_versions(); + } + + public function send_describe_schema_versions() + { + $args = new cassandra_Cassandra_describe_schema_versions_args(); + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_schema_versions', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_schema_versions', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_schema_versions() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_schema_versions_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_schema_versions_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("describe_schema_versions failed: unknown result"); + } + + public function describe_keyspaces() + { + $this->send_describe_keyspaces(); + return $this->recv_describe_keyspaces(); + } + + public function send_describe_keyspaces() + { + $args = new cassandra_Cassandra_describe_keyspaces_args(); + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_keyspaces', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_keyspaces', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_keyspaces() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_keyspaces_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_keyspaces_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("describe_keyspaces failed: unknown result"); + } + + public function describe_cluster_name() + { + $this->send_describe_cluster_name(); + return $this->recv_describe_cluster_name(); + } + + public function send_describe_cluster_name() + { + $args = new cassandra_Cassandra_describe_cluster_name_args(); + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_cluster_name', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_cluster_name', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_cluster_name() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_cluster_name_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_cluster_name_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + throw new Exception("describe_cluster_name failed: unknown result"); + } + + public function describe_version() + { + $this->send_describe_version(); + return $this->recv_describe_version(); + } + + public function send_describe_version() + { + $args = new cassandra_Cassandra_describe_version_args(); + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_version', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_version', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_version() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_version_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_version_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + throw new Exception("describe_version failed: unknown result"); + } + + public function describe_ring($keyspace) + { + $this->send_describe_ring($keyspace); + return $this->recv_describe_ring(); + } + + public function send_describe_ring($keyspace) + { + $args = new cassandra_Cassandra_describe_ring_args(); + $args->keyspace = $keyspace; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_ring', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_ring', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_ring() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_ring_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_ring_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("describe_ring failed: unknown result"); + } + + public function describe_partitioner() + { + $this->send_describe_partitioner(); + return $this->recv_describe_partitioner(); + } + + public function send_describe_partitioner() + { + $args = new cassandra_Cassandra_describe_partitioner_args(); + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_partitioner', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_partitioner', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_partitioner() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_partitioner_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_partitioner_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + throw new Exception("describe_partitioner failed: unknown result"); + } + + public function describe_snitch() + { + $this->send_describe_snitch(); + return $this->recv_describe_snitch(); + } + + public function send_describe_snitch() + { + $args = new cassandra_Cassandra_describe_snitch_args(); + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_snitch', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_snitch', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_snitch() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_snitch_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_snitch_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + throw new Exception("describe_snitch failed: unknown result"); + } + + public function describe_keyspace($keyspace) + { + $this->send_describe_keyspace($keyspace); + return $this->recv_describe_keyspace(); + } + + public function send_describe_keyspace($keyspace) + { + $args = new cassandra_Cassandra_describe_keyspace_args(); + $args->keyspace = $keyspace; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_keyspace', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_keyspace', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_keyspace() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_keyspace_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_keyspace_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->nfe !== null) { + throw $result->nfe; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("describe_keyspace failed: unknown result"); + } + + public function describe_splits($cfName, $start_token, $end_token, $keys_per_split) + { + $this->send_describe_splits($cfName, $start_token, $end_token, $keys_per_split); + return $this->recv_describe_splits(); + } + + public function send_describe_splits($cfName, $start_token, $end_token, $keys_per_split) + { + $args = new cassandra_Cassandra_describe_splits_args(); + $args->cfName = $cfName; + $args->start_token = $start_token; + $args->end_token = $end_token; + $args->keys_per_split = $keys_per_split; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'describe_splits', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('describe_splits', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_describe_splits() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_describe_splits_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_describe_splits_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + throw new Exception("describe_splits failed: unknown result"); + } + + public function system_add_column_family($cf_def) + { + $this->send_system_add_column_family($cf_def); + return $this->recv_system_add_column_family(); + } + + public function send_system_add_column_family($cf_def) + { + $args = new cassandra_Cassandra_system_add_column_family_args(); + $args->cf_def = $cf_def; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'system_add_column_family', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('system_add_column_family', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_system_add_column_family() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_system_add_column_family_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_system_add_column_family_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("system_add_column_family failed: unknown result"); + } + + public function system_drop_column_family($column_family) + { + $this->send_system_drop_column_family($column_family); + return $this->recv_system_drop_column_family(); + } + + public function send_system_drop_column_family($column_family) + { + $args = new cassandra_Cassandra_system_drop_column_family_args(); + $args->column_family = $column_family; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'system_drop_column_family', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('system_drop_column_family', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_system_drop_column_family() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_system_drop_column_family_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_system_drop_column_family_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("system_drop_column_family failed: unknown result"); + } + + public function system_add_keyspace($ks_def) + { + $this->send_system_add_keyspace($ks_def); + return $this->recv_system_add_keyspace(); + } + + public function send_system_add_keyspace($ks_def) + { + $args = new cassandra_Cassandra_system_add_keyspace_args(); + $args->ks_def = $ks_def; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'system_add_keyspace', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('system_add_keyspace', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_system_add_keyspace() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_system_add_keyspace_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_system_add_keyspace_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("system_add_keyspace failed: unknown result"); + } + + public function system_drop_keyspace($keyspace) + { + $this->send_system_drop_keyspace($keyspace); + return $this->recv_system_drop_keyspace(); + } + + public function send_system_drop_keyspace($keyspace) + { + $args = new cassandra_Cassandra_system_drop_keyspace_args(); + $args->keyspace = $keyspace; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'system_drop_keyspace', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('system_drop_keyspace', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_system_drop_keyspace() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_system_drop_keyspace_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_system_drop_keyspace_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("system_drop_keyspace failed: unknown result"); + } + + public function system_update_keyspace($ks_def) + { + $this->send_system_update_keyspace($ks_def); + return $this->recv_system_update_keyspace(); + } + + public function send_system_update_keyspace($ks_def) + { + $args = new cassandra_Cassandra_system_update_keyspace_args(); + $args->ks_def = $ks_def; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'system_update_keyspace', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('system_update_keyspace', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_system_update_keyspace() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_system_update_keyspace_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_system_update_keyspace_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("system_update_keyspace failed: unknown result"); + } + + public function system_update_column_family($cf_def) + { + $this->send_system_update_column_family($cf_def); + return $this->recv_system_update_column_family(); + } + + public function send_system_update_column_family($cf_def) + { + $args = new cassandra_Cassandra_system_update_column_family_args(); + $args->cf_def = $cf_def; + $bin_accel = ($this->output_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_write_binary'); + if ($bin_accel) + { + thrift_protocol_write_binary($this->output_, 'system_update_column_family', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); + } + else + { + $this->output_->writeMessageBegin('system_update_column_family', TMessageType::CALL, $this->seqid_); + $args->write($this->output_); + $this->output_->writeMessageEnd(); + $this->output_->getTransport()->flush(); + } + } + + public function recv_system_update_column_family() + { + $bin_accel = ($this->input_ instanceof TProtocol::$TBINARYPROTOCOLACCELERATED) && function_exists('thrift_protocol_read_binary'); + if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, 'cassandra_Cassandra_system_update_column_family_result', $this->input_->isStrictRead()); + else + { + $rseqid = 0; + $fname = null; + $mtype = 0; + + $this->input_->readMessageBegin($fname, $mtype, $rseqid); + if ($mtype == TMessageType::EXCEPTION) { + $x = new TApplicationException(); + $x->read($this->input_); + $this->input_->readMessageEnd(); + throw $x; + } + $result = new cassandra_Cassandra_system_update_column_family_result(); + $result->read($this->input_); + $this->input_->readMessageEnd(); + } + if ($result->success !== null) { + return $result->success; + } + if ($result->ire !== null) { + throw $result->ire; + } + throw new Exception("system_update_column_family failed: unknown result"); + } + +} + +// HELPER FUNCTIONS AND STRUCTURES + +class cassandra_Cassandra_login_args extends TBase { + static $_TSPEC; + + public $auth_request = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'auth_request', + 'type' => TType::STRUCT, + 'class' => 'cassandra_AuthenticationRequest', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_login_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_login_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_login_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_login_result extends TBase { + static $_TSPEC; + + public $authnx = null; + public $authzx = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'authnx', + 'type' => TType::STRUCT, + 'class' => 'cassandra_AuthenticationException', + ), + 2 => array( + 'var' => 'authzx', + 'type' => TType::STRUCT, + 'class' => 'cassandra_AuthorizationException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_login_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_login_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_login_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_set_keyspace_args extends TBase { + static $_TSPEC; + + public $keyspace = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'keyspace', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_set_keyspace_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_set_keyspace_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_set_keyspace_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_set_keyspace_result extends TBase { + static $_TSPEC; + + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_set_keyspace_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_set_keyspace_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_set_keyspace_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_args extends TBase { + static $_TSPEC; + + public $key = null; + public $column_path = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'column_path', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnPath', + ), + 3 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + public $nfe = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnOrSuperColumn', + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'nfe', + 'type' => TType::STRUCT, + 'class' => 'cassandra_NotFoundException', + ), + 3 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 4 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_slice_args extends TBase { + static $_TSPEC; + + public $key = null; + public $column_parent = null; + public $predicate = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'column_parent', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnParent', + ), + 3 => array( + 'var' => 'predicate', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SlicePredicate', + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_slice_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_slice_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_slice_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_slice_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnOrSuperColumn', + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_slice_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_slice_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_slice_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_count_args extends TBase { + static $_TSPEC; + + public $key = null; + public $column_parent = null; + public $predicate = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'column_parent', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnParent', + ), + 3 => array( + 'var' => 'predicate', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SlicePredicate', + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_count_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_count_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_count_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_count_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::I32, + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_count_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_count_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_count_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_multiget_slice_args extends TBase { + static $_TSPEC; + + public $keys = null; + public $column_parent = null; + public $predicate = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'keys', + 'type' => TType::LST, + 'etype' => TType::STRING, + 'elem' => array( + 'type' => TType::STRING, + ), + ), + 2 => array( + 'var' => 'column_parent', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnParent', + ), + 3 => array( + 'var' => 'predicate', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SlicePredicate', + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_multiget_slice_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_multiget_slice_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_multiget_slice_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_multiget_slice_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::MAP, + 'ktype' => TType::STRING, + 'vtype' => TType::LST, + 'key' => array( + 'type' => TType::STRING, + ), + 'val' => array( + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnOrSuperColumn', + ), + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_multiget_slice_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_multiget_slice_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_multiget_slice_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_multiget_count_args extends TBase { + static $_TSPEC; + + public $keys = null; + public $column_parent = null; + public $predicate = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'keys', + 'type' => TType::LST, + 'etype' => TType::STRING, + 'elem' => array( + 'type' => TType::STRING, + ), + ), + 2 => array( + 'var' => 'column_parent', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnParent', + ), + 3 => array( + 'var' => 'predicate', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SlicePredicate', + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_multiget_count_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_multiget_count_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_multiget_count_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_multiget_count_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::MAP, + 'ktype' => TType::STRING, + 'vtype' => TType::I32, + 'key' => array( + 'type' => TType::STRING, + ), + 'val' => array( + 'type' => TType::I32, + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_multiget_count_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_multiget_count_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_multiget_count_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_range_slices_args extends TBase { + static $_TSPEC; + + public $column_parent = null; + public $predicate = null; + public $range = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'column_parent', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnParent', + ), + 2 => array( + 'var' => 'predicate', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SlicePredicate', + ), + 3 => array( + 'var' => 'range', + 'type' => TType::STRUCT, + 'class' => 'cassandra_KeyRange', + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_range_slices_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_range_slices_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_range_slices_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_range_slices_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_KeySlice', + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_range_slices_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_range_slices_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_range_slices_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_indexed_slices_args extends TBase { + static $_TSPEC; + + public $column_parent = null; + public $index_clause = null; + public $column_predicate = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'column_parent', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnParent', + ), + 2 => array( + 'var' => 'index_clause', + 'type' => TType::STRUCT, + 'class' => 'cassandra_IndexClause', + ), + 3 => array( + 'var' => 'column_predicate', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SlicePredicate', + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_indexed_slices_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_indexed_slices_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_indexed_slices_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_get_indexed_slices_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_KeySlice', + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_get_indexed_slices_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_get_indexed_slices_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_get_indexed_slices_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_insert_args extends TBase { + static $_TSPEC; + + public $key = null; + public $column_parent = null; + public $column = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'column_parent', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnParent', + ), + 3 => array( + 'var' => 'column', + 'type' => TType::STRUCT, + 'class' => 'cassandra_Column', + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_insert_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_insert_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_insert_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_insert_result extends TBase { + static $_TSPEC; + + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_insert_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_insert_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_insert_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_remove_args extends TBase { + static $_TSPEC; + + public $key = null; + public $column_path = null; + public $timestamp = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'column_path', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnPath', + ), + 3 => array( + 'var' => 'timestamp', + 'type' => TType::I64, + ), + 4 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_remove_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_remove_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_remove_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_remove_result extends TBase { + static $_TSPEC; + + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_remove_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_remove_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_remove_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_batch_mutate_args extends TBase { + static $_TSPEC; + + public $mutation_map = null; + public $consistency_level = 1; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'mutation_map', + 'type' => TType::MAP, + 'ktype' => TType::STRING, + 'vtype' => TType::MAP, + 'key' => array( + 'type' => TType::STRING, + ), + 'val' => array( + 'type' => TType::MAP, + 'ktype' => TType::STRING, + 'vtype' => TType::LST, + 'key' => array( + 'type' => TType::STRING, + ), + 'val' => array( + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_Mutation', + ), + ), + ), + ), + 2 => array( + 'var' => 'consistency_level', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_batch_mutate_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_batch_mutate_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_batch_mutate_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_batch_mutate_result extends TBase { + static $_TSPEC; + + public $ire = null; + public $ue = null; + public $te = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + 3 => array( + 'var' => 'te', + 'type' => TType::STRUCT, + 'class' => 'cassandra_TimedOutException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_batch_mutate_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_batch_mutate_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_batch_mutate_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_truncate_args extends TBase { + static $_TSPEC; + + public $cfname = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'cfname', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_truncate_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_truncate_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_truncate_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_truncate_result extends TBase { + static $_TSPEC; + + public $ire = null; + public $ue = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + 2 => array( + 'var' => 'ue', + 'type' => TType::STRUCT, + 'class' => 'cassandra_UnavailableException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_truncate_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_truncate_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_truncate_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_schema_versions_args extends TBase { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'Cassandra_describe_schema_versions_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_schema_versions_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_schema_versions_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_schema_versions_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::MAP, + 'ktype' => TType::STRING, + 'vtype' => TType::LST, + 'key' => array( + 'type' => TType::STRING, + ), + 'val' => array( + 'type' => TType::LST, + 'etype' => TType::STRING, + 'elem' => array( + 'type' => TType::STRING, + ), + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_schema_versions_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_schema_versions_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_schema_versions_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_keyspaces_args extends TBase { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'Cassandra_describe_keyspaces_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_keyspaces_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_keyspaces_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_keyspaces_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_KsDef', + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_keyspaces_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_keyspaces_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_keyspaces_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_cluster_name_args extends TBase { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'Cassandra_describe_cluster_name_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_cluster_name_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_cluster_name_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_cluster_name_result extends TBase { + static $_TSPEC; + + public $success = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_cluster_name_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_cluster_name_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_cluster_name_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_version_args extends TBase { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'Cassandra_describe_version_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_version_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_version_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_version_result extends TBase { + static $_TSPEC; + + public $success = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_version_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_version_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_version_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_ring_args extends TBase { + static $_TSPEC; + + public $keyspace = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'keyspace', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_ring_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_ring_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_ring_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_ring_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_TokenRange', + ), + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_ring_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_ring_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_ring_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_partitioner_args extends TBase { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'Cassandra_describe_partitioner_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_partitioner_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_partitioner_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_partitioner_result extends TBase { + static $_TSPEC; + + public $success = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_partitioner_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_partitioner_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_partitioner_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_snitch_args extends TBase { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'Cassandra_describe_snitch_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_snitch_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_snitch_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_snitch_result extends TBase { + static $_TSPEC; + + public $success = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_snitch_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_snitch_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_snitch_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_keyspace_args extends TBase { + static $_TSPEC; + + public $keyspace = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'keyspace', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_keyspace_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_keyspace_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_keyspace_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_keyspace_result extends TBase { + static $_TSPEC; + + public $success = null; + public $nfe = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRUCT, + 'class' => 'cassandra_KsDef', + ), + 1 => array( + 'var' => 'nfe', + 'type' => TType::STRUCT, + 'class' => 'cassandra_NotFoundException', + ), + 2 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_keyspace_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_keyspace_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_keyspace_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_splits_args extends TBase { + static $_TSPEC; + + public $cfName = null; + public $start_token = null; + public $end_token = null; + public $keys_per_split = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'cfName', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'start_token', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'end_token', + 'type' => TType::STRING, + ), + 4 => array( + 'var' => 'keys_per_split', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_splits_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_splits_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_splits_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_describe_splits_result extends TBase { + static $_TSPEC; + + public $success = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::LST, + 'etype' => TType::STRING, + 'elem' => array( + 'type' => TType::STRING, + ), + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_describe_splits_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_describe_splits_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_describe_splits_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_add_column_family_args extends TBase { + static $_TSPEC; + + public $cf_def = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'cf_def', + 'type' => TType::STRUCT, + 'class' => 'cassandra_CfDef', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_add_column_family_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_add_column_family_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_add_column_family_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_add_column_family_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_add_column_family_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_add_column_family_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_add_column_family_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_drop_column_family_args extends TBase { + static $_TSPEC; + + public $column_family = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'column_family', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_drop_column_family_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_drop_column_family_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_drop_column_family_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_drop_column_family_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_drop_column_family_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_drop_column_family_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_drop_column_family_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_add_keyspace_args extends TBase { + static $_TSPEC; + + public $ks_def = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'ks_def', + 'type' => TType::STRUCT, + 'class' => 'cassandra_KsDef', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_add_keyspace_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_add_keyspace_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_add_keyspace_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_add_keyspace_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_add_keyspace_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_add_keyspace_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_add_keyspace_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_drop_keyspace_args extends TBase { + static $_TSPEC; + + public $keyspace = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'keyspace', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_drop_keyspace_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_drop_keyspace_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_drop_keyspace_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_drop_keyspace_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_drop_keyspace_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_drop_keyspace_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_drop_keyspace_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_update_keyspace_args extends TBase { + static $_TSPEC; + + public $ks_def = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'ks_def', + 'type' => TType::STRUCT, + 'class' => 'cassandra_KsDef', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_update_keyspace_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_update_keyspace_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_update_keyspace_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_update_keyspace_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_update_keyspace_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_update_keyspace_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_update_keyspace_result', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_update_column_family_args extends TBase { + static $_TSPEC; + + public $cf_def = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'cf_def', + 'type' => TType::STRUCT, + 'class' => 'cassandra_CfDef', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_update_column_family_args'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_update_column_family_args', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_update_column_family_args', self::$_TSPEC, $output); + } +} + +class cassandra_Cassandra_system_update_column_family_result extends TBase { + static $_TSPEC; + + public $success = null; + public $ire = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 0 => array( + 'var' => 'success', + 'type' => TType::STRING, + ), + 1 => array( + 'var' => 'ire', + 'type' => TType::STRUCT, + 'class' => 'cassandra_InvalidRequestException', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Cassandra_system_update_column_family_result'; + } + + public function read($input) + { + return $this->_read('Cassandra_system_update_column_family_result', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Cassandra_system_update_column_family_result', self::$_TSPEC, $output); + } +} + +?> diff --git a/library/phpcassa/thrift/packages/cassandra/cassandra_constants.php b/library/phpcassa/thrift/packages/cassandra/cassandra_constants.php new file mode 100644 index 000000000..d6594b647 --- /dev/null +++ b/library/phpcassa/thrift/packages/cassandra/cassandra_constants.php @@ -0,0 +1,13 @@ + diff --git a/library/phpcassa/thrift/packages/cassandra/cassandra_types.php b/library/phpcassa/thrift/packages/cassandra/cassandra_types.php new file mode 100644 index 000000000..2c1c7fd0f --- /dev/null +++ b/library/phpcassa/thrift/packages/cassandra/cassandra_types.php @@ -0,0 +1,1172 @@ + 1, + 'QUORUM' => 2, + 'LOCAL_QUORUM' => 3, + 'EACH_QUORUM' => 4, + 'ALL' => 5, + 'ANY' => 6, + 'TWO' => 7, + 'THREE' => 8, +); + +final class cassandra_ConsistencyLevel { + const ONE = 1; + const QUORUM = 2; + const LOCAL_QUORUM = 3; + const EACH_QUORUM = 4; + const ALL = 5; + const ANY = 6; + const TWO = 7; + const THREE = 8; + static public $__names = array( + 1 => 'ONE', + 2 => 'QUORUM', + 3 => 'LOCAL_QUORUM', + 4 => 'EACH_QUORUM', + 5 => 'ALL', + 6 => 'ANY', + 7 => 'TWO', + 8 => 'THREE', + ); +} + +$GLOBALS['cassandra_E_IndexOperator'] = array( + 'EQ' => 0, + 'GTE' => 1, + 'GT' => 2, + 'LTE' => 3, + 'LT' => 4, +); + +final class cassandra_IndexOperator { + const EQ = 0; + const GTE = 1; + const GT = 2; + const LTE = 3; + const LT = 4; + static public $__names = array( + 0 => 'EQ', + 1 => 'GTE', + 2 => 'GT', + 3 => 'LTE', + 4 => 'LT', + ); +} + +$GLOBALS['cassandra_E_IndexType'] = array( + 'KEYS' => 0, +); + +final class cassandra_IndexType { + const KEYS = 0; + static public $__names = array( + 0 => 'KEYS', + ); +} + +class cassandra_Column extends TBase { + static $_TSPEC; + + public $name = null; + public $value = null; + public $timestamp = null; + public $ttl = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'name', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'value', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'timestamp', + 'type' => TType::I64, + ), + 4 => array( + 'var' => 'ttl', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Column'; + } + + public function read($input) + { + return $this->_read('Column', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Column', self::$_TSPEC, $output); + } +} + +class cassandra_SuperColumn extends TBase { + static $_TSPEC; + + public $name = null; + public $columns = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'name', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'columns', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_Column', + ), + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'SuperColumn'; + } + + public function read($input) + { + return $this->_read('SuperColumn', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('SuperColumn', self::$_TSPEC, $output); + } +} + +class cassandra_ColumnOrSuperColumn extends TBase { + static $_TSPEC; + + public $column = null; + public $super_column = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'column', + 'type' => TType::STRUCT, + 'class' => 'cassandra_Column', + ), + 2 => array( + 'var' => 'super_column', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SuperColumn', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'ColumnOrSuperColumn'; + } + + public function read($input) + { + return $this->_read('ColumnOrSuperColumn', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('ColumnOrSuperColumn', self::$_TSPEC, $output); + } +} + +class cassandra_NotFoundException extends TException { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'NotFoundException'; + } + + public function read($input) + { + return $this->_read('NotFoundException', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('NotFoundException', self::$_TSPEC, $output); + } +} + +class cassandra_InvalidRequestException extends TException { + static $_TSPEC; + + public $message = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'message', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'InvalidRequestException'; + } + + public function read($input) + { + return $this->_read('InvalidRequestException', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('InvalidRequestException', self::$_TSPEC, $output); + } +} + +class cassandra_UnavailableException extends TException { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'UnavailableException'; + } + + public function read($input) + { + return $this->_read('UnavailableException', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('UnavailableException', self::$_TSPEC, $output); + } +} + +class cassandra_TimedOutException extends TException { + static $_TSPEC; + + + public function __construct() { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + ); + } + } + + public function getName() { + return 'TimedOutException'; + } + + public function read($input) + { + return $this->_read('TimedOutException', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('TimedOutException', self::$_TSPEC, $output); + } +} + +class cassandra_AuthenticationException extends TException { + static $_TSPEC; + + public $message = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'message', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'AuthenticationException'; + } + + public function read($input) + { + return $this->_read('AuthenticationException', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('AuthenticationException', self::$_TSPEC, $output); + } +} + +class cassandra_AuthorizationException extends TException { + static $_TSPEC; + + public $message = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'message', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'AuthorizationException'; + } + + public function read($input) + { + return $this->_read('AuthorizationException', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('AuthorizationException', self::$_TSPEC, $output); + } +} + +class cassandra_ColumnParent extends TBase { + static $_TSPEC; + + public $column_family = null; + public $super_column = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 3 => array( + 'var' => 'column_family', + 'type' => TType::STRING, + ), + 4 => array( + 'var' => 'super_column', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'ColumnParent'; + } + + public function read($input) + { + return $this->_read('ColumnParent', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('ColumnParent', self::$_TSPEC, $output); + } +} + +class cassandra_ColumnPath extends TBase { + static $_TSPEC; + + public $column_family = null; + public $super_column = null; + public $column = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 3 => array( + 'var' => 'column_family', + 'type' => TType::STRING, + ), + 4 => array( + 'var' => 'super_column', + 'type' => TType::STRING, + ), + 5 => array( + 'var' => 'column', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'ColumnPath'; + } + + public function read($input) + { + return $this->_read('ColumnPath', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('ColumnPath', self::$_TSPEC, $output); + } +} + +class cassandra_SliceRange extends TBase { + static $_TSPEC; + + public $start = null; + public $finish = null; + public $reversed = false; + public $count = 100; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'start', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'finish', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'reversed', + 'type' => TType::BOOL, + ), + 4 => array( + 'var' => 'count', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'SliceRange'; + } + + public function read($input) + { + return $this->_read('SliceRange', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('SliceRange', self::$_TSPEC, $output); + } +} + +class cassandra_SlicePredicate extends TBase { + static $_TSPEC; + + public $column_names = null; + public $slice_range = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'column_names', + 'type' => TType::LST, + 'etype' => TType::STRING, + 'elem' => array( + 'type' => TType::STRING, + ), + ), + 2 => array( + 'var' => 'slice_range', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SliceRange', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'SlicePredicate'; + } + + public function read($input) + { + return $this->_read('SlicePredicate', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('SlicePredicate', self::$_TSPEC, $output); + } +} + +class cassandra_IndexExpression extends TBase { + static $_TSPEC; + + public $column_name = null; + public $op = null; + public $value = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'column_name', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'op', + 'type' => TType::I32, + ), + 3 => array( + 'var' => 'value', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'IndexExpression'; + } + + public function read($input) + { + return $this->_read('IndexExpression', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('IndexExpression', self::$_TSPEC, $output); + } +} + +class cassandra_IndexClause extends TBase { + static $_TSPEC; + + public $expressions = null; + public $start_key = null; + public $count = 100; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'expressions', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_IndexExpression', + ), + ), + 2 => array( + 'var' => 'start_key', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'count', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'IndexClause'; + } + + public function read($input) + { + return $this->_read('IndexClause', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('IndexClause', self::$_TSPEC, $output); + } +} + +class cassandra_KeyRange extends TBase { + static $_TSPEC; + + public $start_key = null; + public $end_key = null; + public $start_token = null; + public $end_token = null; + public $count = 100; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'start_key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'end_key', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'start_token', + 'type' => TType::STRING, + ), + 4 => array( + 'var' => 'end_token', + 'type' => TType::STRING, + ), + 5 => array( + 'var' => 'count', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'KeyRange'; + } + + public function read($input) + { + return $this->_read('KeyRange', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('KeyRange', self::$_TSPEC, $output); + } +} + +class cassandra_KeySlice extends TBase { + static $_TSPEC; + + public $key = null; + public $columns = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'columns', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnOrSuperColumn', + ), + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'KeySlice'; + } + + public function read($input) + { + return $this->_read('KeySlice', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('KeySlice', self::$_TSPEC, $output); + } +} + +class cassandra_KeyCount extends TBase { + static $_TSPEC; + + public $key = null; + public $count = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'key', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'count', + 'type' => TType::I32, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'KeyCount'; + } + + public function read($input) + { + return $this->_read('KeyCount', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('KeyCount', self::$_TSPEC, $output); + } +} + +class cassandra_Deletion extends TBase { + static $_TSPEC; + + public $timestamp = null; + public $super_column = null; + public $predicate = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'timestamp', + 'type' => TType::I64, + ), + 2 => array( + 'var' => 'super_column', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'predicate', + 'type' => TType::STRUCT, + 'class' => 'cassandra_SlicePredicate', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Deletion'; + } + + public function read($input) + { + return $this->_read('Deletion', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Deletion', self::$_TSPEC, $output); + } +} + +class cassandra_Mutation extends TBase { + static $_TSPEC; + + public $column_or_supercolumn = null; + public $deletion = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'column_or_supercolumn', + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnOrSuperColumn', + ), + 2 => array( + 'var' => 'deletion', + 'type' => TType::STRUCT, + 'class' => 'cassandra_Deletion', + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'Mutation'; + } + + public function read($input) + { + return $this->_read('Mutation', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('Mutation', self::$_TSPEC, $output); + } +} + +class cassandra_TokenRange extends TBase { + static $_TSPEC; + + public $start_token = null; + public $end_token = null; + public $endpoints = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'start_token', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'end_token', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'endpoints', + 'type' => TType::LST, + 'etype' => TType::STRING, + 'elem' => array( + 'type' => TType::STRING, + ), + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'TokenRange'; + } + + public function read($input) + { + return $this->_read('TokenRange', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('TokenRange', self::$_TSPEC, $output); + } +} + +class cassandra_AuthenticationRequest extends TBase { + static $_TSPEC; + + public $credentials = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'credentials', + 'type' => TType::MAP, + 'ktype' => TType::STRING, + 'vtype' => TType::STRING, + 'key' => array( + 'type' => TType::STRING, + ), + 'val' => array( + 'type' => TType::STRING, + ), + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'AuthenticationRequest'; + } + + public function read($input) + { + return $this->_read('AuthenticationRequest', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('AuthenticationRequest', self::$_TSPEC, $output); + } +} + +class cassandra_ColumnDef extends TBase { + static $_TSPEC; + + public $name = null; + public $validation_class = null; + public $index_type = null; + public $index_name = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'name', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'validation_class', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'index_type', + 'type' => TType::I32, + ), + 4 => array( + 'var' => 'index_name', + 'type' => TType::STRING, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'ColumnDef'; + } + + public function read($input) + { + return $this->_read('ColumnDef', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('ColumnDef', self::$_TSPEC, $output); + } +} + +class cassandra_CfDef extends TBase { + static $_TSPEC; + + public $keyspace = null; + public $name = null; + public $column_type = "Standard"; + public $comparator_type = "BytesType"; + public $subcomparator_type = null; + public $comment = null; + public $row_cache_size = 0; + public $key_cache_size = 200000; + public $read_repair_chance = 1; + public $column_metadata = null; + public $gc_grace_seconds = null; + public $default_validation_class = null; + public $id = null; + public $min_compaction_threshold = null; + public $max_compaction_threshold = null; + public $row_cache_save_period_in_seconds = null; + public $key_cache_save_period_in_seconds = null; + public $memtable_flush_after_mins = null; + public $memtable_throughput_in_mb = null; + public $memtable_operations_in_millions = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'keyspace', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'name', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'column_type', + 'type' => TType::STRING, + ), + 5 => array( + 'var' => 'comparator_type', + 'type' => TType::STRING, + ), + 6 => array( + 'var' => 'subcomparator_type', + 'type' => TType::STRING, + ), + 8 => array( + 'var' => 'comment', + 'type' => TType::STRING, + ), + 9 => array( + 'var' => 'row_cache_size', + 'type' => TType::DOUBLE, + ), + 11 => array( + 'var' => 'key_cache_size', + 'type' => TType::DOUBLE, + ), + 12 => array( + 'var' => 'read_repair_chance', + 'type' => TType::DOUBLE, + ), + 13 => array( + 'var' => 'column_metadata', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_ColumnDef', + ), + ), + 14 => array( + 'var' => 'gc_grace_seconds', + 'type' => TType::I32, + ), + 15 => array( + 'var' => 'default_validation_class', + 'type' => TType::STRING, + ), + 16 => array( + 'var' => 'id', + 'type' => TType::I32, + ), + 17 => array( + 'var' => 'min_compaction_threshold', + 'type' => TType::I32, + ), + 18 => array( + 'var' => 'max_compaction_threshold', + 'type' => TType::I32, + ), + 19 => array( + 'var' => 'row_cache_save_period_in_seconds', + 'type' => TType::I32, + ), + 20 => array( + 'var' => 'key_cache_save_period_in_seconds', + 'type' => TType::I32, + ), + 21 => array( + 'var' => 'memtable_flush_after_mins', + 'type' => TType::I32, + ), + 22 => array( + 'var' => 'memtable_throughput_in_mb', + 'type' => TType::I32, + ), + 23 => array( + 'var' => 'memtable_operations_in_millions', + 'type' => TType::DOUBLE, + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'CfDef'; + } + + public function read($input) + { + return $this->_read('CfDef', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('CfDef', self::$_TSPEC, $output); + } +} + +class cassandra_KsDef extends TBase { + static $_TSPEC; + + public $name = null; + public $strategy_class = null; + public $strategy_options = null; + public $replication_factor = null; + public $cf_defs = null; + + public function __construct($vals=null) { + if (!isset(self::$_TSPEC)) { + self::$_TSPEC = array( + 1 => array( + 'var' => 'name', + 'type' => TType::STRING, + ), + 2 => array( + 'var' => 'strategy_class', + 'type' => TType::STRING, + ), + 3 => array( + 'var' => 'strategy_options', + 'type' => TType::MAP, + 'ktype' => TType::STRING, + 'vtype' => TType::STRING, + 'key' => array( + 'type' => TType::STRING, + ), + 'val' => array( + 'type' => TType::STRING, + ), + ), + 4 => array( + 'var' => 'replication_factor', + 'type' => TType::I32, + ), + 5 => array( + 'var' => 'cf_defs', + 'type' => TType::LST, + 'etype' => TType::STRUCT, + 'elem' => array( + 'type' => TType::STRUCT, + 'class' => 'cassandra_CfDef', + ), + ), + ); + } + if (is_array($vals)) { + parent::__construct(self::$_TSPEC, $vals); + } + } + + public function getName() { + return 'KsDef'; + } + + public function read($input) + { + return $this->_read('KsDef', self::$_TSPEC, $input); + } + public function write($output) { + return $this->_write('KsDef', self::$_TSPEC, $output); + } +} + +?> diff --git a/library/phpcassa/thrift/protocol/TBinaryProtocol.php b/library/phpcassa/thrift/protocol/TBinaryProtocol.php new file mode 100644 index 000000000..f4579e142 --- /dev/null +++ b/library/phpcassa/thrift/protocol/TBinaryProtocol.php @@ -0,0 +1,429 @@ +strictRead_ = $strictRead; + $this->strictWrite_ = $strictWrite; + } + + public function writeMessageBegin($name, $type, $seqid) { + if ($this->strictWrite_) { + $version = self::VERSION_1 | $type; + return + $this->writeI32($version) + + $this->writeString($name) + + $this->writeI32($seqid); + } else { + return + $this->writeString($name) + + $this->writeByte($type) + + $this->writeI32($seqid); + } + } + + public function writeMessageEnd() { + return 0; + } + + public function writeStructBegin($name) { + return 0; + } + + public function writeStructEnd() { + return 0; + } + + public function writeFieldBegin($fieldName, $fieldType, $fieldId) { + return + $this->writeByte($fieldType) + + $this->writeI16($fieldId); + } + + public function writeFieldEnd() { + return 0; + } + + public function writeFieldStop() { + return + $this->writeByte(TType::STOP); + } + + public function writeMapBegin($keyType, $valType, $size) { + return + $this->writeByte($keyType) + + $this->writeByte($valType) + + $this->writeI32($size); + } + + public function writeMapEnd() { + return 0; + } + + public function writeListBegin($elemType, $size) { + return + $this->writeByte($elemType) + + $this->writeI32($size); + } + + public function writeListEnd() { + return 0; + } + + public function writeSetBegin($elemType, $size) { + return + $this->writeByte($elemType) + + $this->writeI32($size); + } + + public function writeSetEnd() { + return 0; + } + + public function writeBool($value) { + $data = pack('c', $value ? 1 : 0); + $this->trans_->write($data, 1); + return 1; + } + + public function writeByte($value) { + $data = pack('c', $value); + $this->trans_->write($data, 1); + return 1; + } + + public function writeI16($value) { + $data = pack('n', $value); + $this->trans_->write($data, 2); + return 2; + } + + public function writeI32($value) { + $data = pack('N', $value); + $this->trans_->write($data, 4); + return 4; + } + + public function writeI64($value) { + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + $neg = $value < 0; + + if ($neg) { + $value *= -1; + } + + $hi = (int)($value / 4294967296); + $lo = (int)$value; + + if ($neg) { + $hi = ~$hi; + $lo = ~$lo; + if (($lo & (int)0xffffffff) == (int)0xffffffff) { + $lo = 0; + $hi++; + } else { + $lo++; + } + } + $data = pack('N2', $hi, $lo); + + } else { + $hi = $value >> 32; + $lo = $value & 0xFFFFFFFF; + $data = pack('N2', $hi, $lo); + } + + $this->trans_->write($data, 8); + return 8; + } + + public function writeDouble($value) { + $data = pack('d', $value); + $this->trans_->write(strrev($data), 8); + return 8; + } + + public function writeString($value) { + $len = strlen($value); + $result = $this->writeI32($len); + if ($len) { + $this->trans_->write($value, $len); + } + return $result + $len; + } + + public function readMessageBegin(&$name, &$type, &$seqid) { + $result = $this->readI32($sz); + if ($sz < 0) { + $version = (int) ($sz & self::VERSION_MASK); + if ($version != (int) self::VERSION_1) { + throw new TProtocolException('Bad version identifier: '.$sz, TProtocolException::BAD_VERSION); + } + $type = $sz & 0x000000ff; + $result += + $this->readString($name) + + $this->readI32($seqid); + } else { + if ($this->strictRead_) { + throw new TProtocolException('No version identifier, old protocol client?', TProtocolException::BAD_VERSION); + } else { + // Handle pre-versioned input + $name = $this->trans_->readAll($sz); + $result += + $sz + + $this->readByte($type) + + $this->readI32($seqid); + } + } + return $result; + } + + public function readMessageEnd() { + return 0; + } + + public function readStructBegin(&$name) { + $name = ''; + return 0; + } + + public function readStructEnd() { + return 0; + } + + public function readFieldBegin(&$name, &$fieldType, &$fieldId) { + $result = $this->readByte($fieldType); + if ($fieldType == TType::STOP) { + $fieldId = 0; + return $result; + } + $result += $this->readI16($fieldId); + return $result; + } + + public function readFieldEnd() { + return 0; + } + + public function readMapBegin(&$keyType, &$valType, &$size) { + return + $this->readByte($keyType) + + $this->readByte($valType) + + $this->readI32($size); + } + + public function readMapEnd() { + return 0; + } + + public function readListBegin(&$elemType, &$size) { + return + $this->readByte($elemType) + + $this->readI32($size); + } + + public function readListEnd() { + return 0; + } + + public function readSetBegin(&$elemType, &$size) { + return + $this->readByte($elemType) + + $this->readI32($size); + } + + public function readSetEnd() { + return 0; + } + + public function readBool(&$value) { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1] == 1; + return 1; + } + + public function readByte(&$value) { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1]; + return 1; + } + + public function readI16(&$value) { + $data = $this->trans_->readAll(2); + $arr = unpack('n', $data); + $value = $arr[1]; + if ($value > 0x7fff) { + $value = 0 - (($value - 1) ^ 0xffff); + } + return 2; + } + + public function readI32(&$value) { + $data = $this->trans_->readAll(4); + $arr = unpack('N', $data); + $value = $arr[1]; + if ($value > 0x7fffffff) { + $value = 0 - (($value - 1) ^ 0xffffffff); + } + return 4; + } + + public function readI64(&$value) { + $data = $this->trans_->readAll(8); + + $arr = unpack('N2', $data); + + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + + $hi = $arr[1]; + $lo = $arr[2]; + $isNeg = $hi < 0; + + // Check for a negative + if ($isNeg) { + $hi = ~$hi & (int)0xffffffff; + $lo = ~$lo & (int)0xffffffff; + + if ($lo == (int)0xffffffff) { + $hi++; + $lo = 0; + } else { + $lo++; + } + } + + // Force 32bit words in excess of 2G to pe positive - we deal wigh sign + // explicitly below + + if ($hi & (int)0x80000000) { + $hi &= (int)0x7fffffff; + $hi += 0x80000000; + } + + if ($lo & (int)0x80000000) { + $lo &= (int)0x7fffffff; + $lo += 0x80000000; + } + + $value = $hi * 4294967296 + $lo; + + if ($isNeg) { + $value = 0 - $value; + } + } else { + + // Upcast negatives in LSB bit + if ($arr[2] & 0x80000000) { + $arr[2] = $arr[2] & 0xffffffff; + } + + // Check for a negative + if ($arr[1] & 0x80000000) { + $arr[1] = $arr[1] & 0xffffffff; + $arr[1] = $arr[1] ^ 0xffffffff; + $arr[2] = $arr[2] ^ 0xffffffff; + $value = 0 - $arr[1]*4294967296 - $arr[2] - 1; + } else { + $value = $arr[1]*4294967296 + $arr[2]; + } + } + + return 8; + } + + public function readDouble(&$value) { + $data = strrev($this->trans_->readAll(8)); + $arr = unpack('d', $data); + $value = $arr[1]; + return 8; + } + + public function readString(&$value) { + $result = $this->readI32($len); + if ($len) { + $value = $this->trans_->readAll($len); + } else { + $value = ''; + } + return $result + $len; + } +} + +/** + * Binary Protocol Factory + */ +class TBinaryProtocolFactory implements TProtocolFactory { + private $strictRead_ = false; + private $strictWrite_ = false; + + public function __construct($strictRead=false, $strictWrite=false) { + $this->strictRead_ = $strictRead; + $this->strictWrite_ = $strictWrite; + } + + public function getProtocol($trans) { + return new TBinaryProtocol($trans, $this->strictRead, $this->strictWrite); + } +} + +/** + * Accelerated binary protocol: used in conjunction with the thrift_protocol + * extension for faster deserialization + */ +class TBinaryProtocolAccelerated extends TBinaryProtocol { + public function __construct($trans, $strictRead=false, $strictWrite=true) { + // If the transport doesn't implement putBack, wrap it in a + // TBufferedTransport (which does) + if (!method_exists($trans, 'putBack')) { + $trans = new TBufferedTransport($trans); + } + parent::__construct($trans, $strictRead, $strictWrite); + } + public function isStrictRead() { + return $this->strictRead_; + } + public function isStrictWrite() { + return $this->strictWrite_; + } +} diff --git a/library/phpcassa/thrift/protocol/TBinarySerializer.php b/library/phpcassa/thrift/protocol/TBinarySerializer.php new file mode 100644 index 000000000..2913d32aa --- /dev/null +++ b/library/phpcassa/thrift/protocol/TBinarySerializer.php @@ -0,0 +1,69 @@ +getName(), + TMessageType::REPLY, $object, + 0, $protocol->isStrictWrite()); + + $protocol->readMessageBegin($unused_name, $unused_type, + $unused_seqid); + } else { + $object->write($protocol); + } + return $transport->getBuffer(); + } + + public static function deserialize($string_object, $class_name) { + $transport = new TMemoryBuffer(); + $protocol = new TBinaryProtocolAccelerated($transport); + if (function_exists('thrift_protocol_read_binary')) { + $protocol->writeMessageBegin('', TMessageType::REPLY, 0); + $transport->write($string_object); + return thrift_protocol_read_binary($protocol, $class_name, + $protocol->isStrictRead()); + } else { + $transport->write($string_object); + $object = new $class_name(); + $object->read($protocol); + return $object; + } + } +} diff --git a/library/phpcassa/thrift/protocol/TProtocol.php b/library/phpcassa/thrift/protocol/TProtocol.php new file mode 100644 index 000000000..ac1facde4 --- /dev/null +++ b/library/phpcassa/thrift/protocol/TProtocol.php @@ -0,0 +1,374 @@ +trans_ = $trans; + } + + /** + * Accessor for transport + * + * @return TTransport + */ + public function getTransport() { + return $this->trans_; + } + + /** + * Writes the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + public abstract function writeMessageBegin($name, $type, $seqid); + + /** + * Close the message + */ + public abstract function writeMessageEnd(); + + /** + * Writes a struct header. + * + * @param string $name Struct name + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeStructBegin($name); + + /** + * Close a struct. + * + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeStructEnd(); + + /* + * Starts a field. + * + * @param string $name Field name + * @param int $type Field type + * @param int $fid Field id + * @throws TException on write error + * @return int How many bytes written + */ + public abstract function writeFieldBegin($fieldName, $fieldType, $fieldId); + + public abstract function writeFieldEnd(); + + public abstract function writeFieldStop(); + + public abstract function writeMapBegin($keyType, $valType, $size); + + public abstract function writeMapEnd(); + + public abstract function writeListBegin($elemType, $size); + + public abstract function writeListEnd(); + + public abstract function writeSetBegin($elemType, $size); + + public abstract function writeSetEnd(); + + public abstract function writeBool($bool); + + public abstract function writeByte($byte); + + public abstract function writeI16($i16); + + public abstract function writeI32($i32); + + public abstract function writeI64($i64); + + public abstract function writeDouble($dub); + + public abstract function writeString($str); + + /** + * Reads the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @parem int $seqid The sequence id of this message + */ + public abstract function readMessageBegin(&$name, &$type, &$seqid); + + /** + * Read the close of message + */ + public abstract function readMessageEnd(); + + public abstract function readStructBegin(&$name); + + public abstract function readStructEnd(); + + public abstract function readFieldBegin(&$name, &$fieldType, &$fieldId); + + public abstract function readFieldEnd(); + + public abstract function readMapBegin(&$keyType, &$valType, &$size); + + public abstract function readMapEnd(); + + public abstract function readListBegin(&$elemType, &$size); + + public abstract function readListEnd(); + + public abstract function readSetBegin(&$elemType, &$size); + + public abstract function readSetEnd(); + + public abstract function readBool(&$bool); + + public abstract function readByte(&$byte); + + public abstract function readI16(&$i16); + + public abstract function readI32(&$i32); + + public abstract function readI64(&$i64); + + public abstract function readDouble(&$dub); + + public abstract function readString(&$str); + + /** + * The skip function is a utility to parse over unrecognized date without + * causing corruption. + * + * @param TType $type What type is it + */ + public function skip($type) { + switch ($type) { + case TType::BOOL: + return $this->readBool($bool); + case TType::BYTE: + return $this->readByte($byte); + case TType::I16: + return $this->readI16($i16); + case TType::I32: + return $this->readI32($i32); + case TType::I64: + return $this->readI64($i64); + case TType::DOUBLE: + return $this->readDouble($dub); + case TType::STRING: + return $this->readString($str); + case TType::STRUCT: + { + $result = $this->readStructBegin($name); + while (true) { + $result += $this->readFieldBegin($name, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + $result += $this->skip($ftype); + $result += $this->readFieldEnd(); + } + $result += $this->readStructEnd(); + return $result; + } + case TType::MAP: + { + $result = $this->readMapBegin($keyType, $valType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($keyType); + $result += $this->skip($valType); + } + $result += $this->readMapEnd(); + return $result; + } + case TType::SET: + { + $result = $this->readSetBegin($elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($elemType); + } + $result += $this->readSetEnd(); + return $result; + } + case TType::LST: + { + $result = $this->readListBegin($elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($elemType); + } + $result += $this->readListEnd(); + return $result; + } + default: + return 0; + } + } + + /** + * Utility for skipping binary data + * + * @param TTransport $itrans TTransport object + * @param int $type Field type + */ + public static function skipBinary($itrans, $type) { + switch ($type) { + case TType::BOOL: + return $itrans->readAll(1); + case TType::BYTE: + return $itrans->readAll(1); + case TType::I16: + return $itrans->readAll(2); + case TType::I32: + return $itrans->readAll(4); + case TType::I64: + return $itrans->readAll(8); + case TType::DOUBLE: + return $itrans->readAll(8); + case TType::STRING: + $len = unpack('N', $itrans->readAll(4)); + $len = $len[1]; + if ($len > 0x7fffffff) { + $len = 0 - (($len - 1) ^ 0xffffffff); + } + return 4 + $itrans->readAll($len); + case TType::STRUCT: + { + $result = 0; + while (true) { + $ftype = 0; + $fid = 0; + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $ftype = $arr[1]; + if ($ftype == TType::STOP) { + break; + } + // I16 field id + $result += $itrans->readAll(2); + $result += self::skipBinary($itrans, $ftype); + } + return $result; + } + case TType::MAP: + { + // Ktype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $ktype = $arr[1]; + // Vtype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $vtype = $arr[1]; + // Size + $data = $itrans->readAll(4); + $arr = unpack('N', $data); + $size = $arr[1]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + $result = 6; + for ($i = 0; $i < $size; $i++) { + $result += self::skipBinary($itrans, $ktype); + $result += self::skipBinary($itrans, $vtype); + } + return $result; + } + case TType::SET: + case TType::LST: + { + // Vtype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $vtype = $arr[1]; + // Size + $data = $itrans->readAll(4); + $arr = unpack('N', $data); + $size = $arr[1]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + $result = 5; + for ($i = 0; $i < $size; $i++) { + $result += self::skipBinary($itrans, $vtype); + } + return $result; + } + default: + return 0; + } + } +} + +/** + * Protocol factory creates protocol objects from transports + */ +interface TProtocolFactory { + /** + * Build a protocol from the base transport + * + * @return TProtocol protocol + */ + public function getProtocol($trans); +} diff --git a/library/phpcassa/thrift/transport/TBufferedTransport.php b/library/phpcassa/thrift/transport/TBufferedTransport.php new file mode 100644 index 000000000..e841564d5 --- /dev/null +++ b/library/phpcassa/thrift/transport/TBufferedTransport.php @@ -0,0 +1,161 @@ +transport_ = $transport; + $this->rBufSize_ = $rBufSize; + $this->wBufSize_ = $wBufSize; + } + + /** + * The underlying transport + * + * @var TTransport + */ + protected $transport_ = null; + + /** + * The receive buffer size + * + * @var int + */ + protected $rBufSize_ = 512; + + /** + * The write buffer size + * + * @var int + */ + protected $wBufSize_ = 512; + + /** + * The write buffer. + * + * @var string + */ + protected $wBuf_ = ''; + + /** + * The read buffer. + * + * @var string + */ + protected $rBuf_ = ''; + + public function isOpen() { + return $this->transport_->isOpen(); + } + + public function open() { + $this->transport_->open(); + } + + public function close() { + $this->transport_->close(); + } + + public function putBack($data) { + if (strlen($this->rBuf_) === 0) { + $this->rBuf_ = $data; + } else { + $this->rBuf_ = ($data . $this->rBuf_); + } + } + + /** + * The reason that we customize readAll here is that the majority of PHP + * streams are already internally buffered by PHP. The socket stream, for + * example, buffers internally and blocks if you call read with $len greater + * than the amount of data available, unlike recv() in C. + * + * Therefore, use the readAll method of the wrapped transport inside + * the buffered readAll. + */ + public function readAll($len) { + $have = strlen($this->rBuf_); + if ($have == 0) { + $data = $this->transport_->readAll($len); + } else if ($have < $len) { + $data = $this->rBuf_; + $this->rBuf_ = ''; + $data .= $this->transport_->readAll($len - $have); + } else if ($have == $len) { + $data = $this->rBuf_; + $this->rBuf_ = ''; + } else if ($have > $len) { + $data = substr($this->rBuf_, 0, $len); + $this->rBuf_ = substr($this->rBuf_, $len); + } + return $data; + } + + public function read($len) { + if (strlen($this->rBuf_) === 0) { + $this->rBuf_ = $this->transport_->read($this->rBufSize_); + } + + if (strlen($this->rBuf_) <= $len) { + $ret = $this->rBuf_; + $this->rBuf_ = ''; + return $ret; + } + + $ret = substr($this->rBuf_, 0, $len); + $this->rBuf_ = substr($this->rBuf_, $len); + return $ret; + } + + public function write($buf) { + $this->wBuf_ .= $buf; + if (strlen($this->wBuf_) >= $this->wBufSize_) { + $out = $this->wBuf_; + + // Note that we clear the internal wBuf_ prior to the underlying write + // to ensure we're in a sane state (i.e. internal buffer cleaned) + // if the underlying write throws up an exception + $this->wBuf_ = ''; + $this->transport_->write($out); + } + } + + public function flush() { + if (strlen($this->wBuf_) > 0) { + $this->transport_->write($this->wBuf_); + $this->wBuf_ = ''; + } + $this->transport_->flush(); + } + +} diff --git a/library/phpcassa/thrift/transport/TFramedTransport.php b/library/phpcassa/thrift/transport/TFramedTransport.php new file mode 100644 index 000000000..b1c0bb5c6 --- /dev/null +++ b/library/phpcassa/thrift/transport/TFramedTransport.php @@ -0,0 +1,179 @@ +transport_ = $transport; + $this->read_ = $read; + $this->write_ = $write; + } + + public function isOpen() { + return $this->transport_->isOpen(); + } + + public function open() { + $this->transport_->open(); + } + + public function close() { + $this->transport_->close(); + } + + /** + * Reads from the buffer. When more data is required reads another entire + * chunk and serves future reads out of that. + * + * @param int $len How much data + */ + public function read($len) { + if (!$this->read_) { + return $this->transport_->read($len); + } + + if (strlen($this->rBuf_) === 0) { + $this->readFrame(); + } + + // Just return full buff + if ($len >= strlen($this->rBuf_)) { + $out = $this->rBuf_; + $this->rBuf_ = null; + return $out; + } + + // Return substr + $out = substr($this->rBuf_, 0, $len); + $this->rBuf_ = substr($this->rBuf_, $len); + return $out; + } + + /** + * Put previously read data back into the buffer + * + * @param string $data data to return + */ + public function putBack($data) { + if (strlen($this->rBuf_) === 0) { + $this->rBuf_ = $data; + } else { + $this->rBuf_ = ($data . $this->rBuf_); + } + } + + /** + * Reads a chunk of data into the internal read buffer. + */ + private function readFrame() { + $buf = $this->transport_->readAll(4); + $val = unpack('N', $buf); + $sz = $val[1]; + + $this->rBuf_ = $this->transport_->readAll($sz); + } + + /** + * Writes some data to the pending output buffer. + * + * @param string $buf The data + * @param int $len Limit of bytes to write + */ + public function write($buf, $len=null) { + if (!$this->write_) { + return $this->transport_->write($buf, $len); + } + + if ($len !== null && $len < strlen($buf)) { + $buf = substr($buf, 0, $len); + } + $this->wBuf_ .= $buf; + } + + /** + * Writes the output buffer to the stream in the format of a 4-byte length + * followed by the actual data. + */ + public function flush() { + if (!$this->write_ || strlen($this->wBuf_) == 0) { + return $this->transport_->flush(); + } + + $out = pack('N', strlen($this->wBuf_)); + $out .= $this->wBuf_; + + // Note that we clear the internal wBuf_ prior to the underlying write + // to ensure we're in a sane state (i.e. internal buffer cleaned) + // if the underlying write throws up an exception + $this->wBuf_ = ''; + $this->transport_->write($out); + $this->transport_->flush(); + } + +} diff --git a/library/phpcassa/thrift/transport/THttpClient.php b/library/phpcassa/thrift/transport/THttpClient.php new file mode 100644 index 000000000..102dbbb76 --- /dev/null +++ b/library/phpcassa/thrift/transport/THttpClient.php @@ -0,0 +1,200 @@ + 0) && ($uri{0} != '/')) { + $uri = '/'.$uri; + } + $this->scheme_ = $scheme; + $this->host_ = $host; + $this->port_ = $port; + $this->uri_ = $uri; + $this->buf_ = ''; + $this->handle_ = null; + $this->timeout_ = null; + } + + /** + * Set read timeout + * + * @param float $timeout + */ + public function setTimeoutSecs($timeout) { + $this->timeout_ = $timeout; + } + + /** + * Whether this transport is open. + * + * @return boolean true if open + */ + public function isOpen() { + return true; + } + + /** + * Open the transport for reading/writing + * + * @throws TTransportException if cannot open + */ + public function open() {} + + /** + * Close the transport. + */ + public function close() { + if ($this->handle_) { + @fclose($this->handle_); + $this->handle_ = null; + } + } + + /** + * Read some data into the array. + * + * @param int $len How much to read + * @return string The data that has been read + * @throws TTransportException if cannot read any more data + */ + public function read($len) { + $data = @fread($this->handle_, $len); + if ($data === FALSE || $data === '') { + $md = stream_get_meta_data($this->handle_); + if ($md['timed_out']) { + throw new TTransportException('THttpClient: timed out reading '.$len.' bytes from '.$this->host_.':'.$this->port_.'/'.$this->uri_, TTransportException::TIMED_OUT); + } else { + throw new TTransportException('THttpClient: Could not read '.$len.' bytes from '.$this->host_.':'.$this->port_.'/'.$this->uri_, TTransportException::UNKNOWN); + } + } + return $data; + } + + /** + * Writes some data into the pending buffer + * + * @param string $buf The data to write + * @throws TTransportException if writing fails + */ + public function write($buf) { + $this->buf_ .= $buf; + } + + /** + * Opens and sends the actual request over the HTTP connection + * + * @throws TTransportException if a writing error occurs + */ + public function flush() { + // God, PHP really has some esoteric ways of doing simple things. + $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : ''); + + $headers = array('Host: '.$host, + 'Accept: application/x-thrift', + 'User-Agent: PHP/THttpClient', + 'Content-Type: application/x-thrift', + 'Content-Length: '.strlen($this->buf_)); + + $options = array('method' => 'POST', + 'header' => implode("\r\n", $headers), + 'max_redirects' => 1, + 'content' => $this->buf_); + if ($this->timeout_ > 0) { + $options['timeout'] = $this->timeout_; + } + $this->buf_ = ''; + + $contextid = stream_context_create(array('http' => $options)); + $this->handle_ = @fopen($this->scheme_.'://'.$host.$this->uri_, 'r', false, $contextid); + + // Connect failed? + if ($this->handle_ === FALSE) { + $this->handle_ = null; + $error = 'THttpClient: Could not connect to '.$host.$this->uri_; + throw new TTransportException($error, TTransportException::NOT_OPEN); + } + } + +} diff --git a/library/phpcassa/thrift/transport/TMemoryBuffer.php b/library/phpcassa/thrift/transport/TMemoryBuffer.php new file mode 100644 index 000000000..a0b1a546b --- /dev/null +++ b/library/phpcassa/thrift/transport/TMemoryBuffer.php @@ -0,0 +1,82 @@ +buf_ = $buf; + } + + protected $buf_ = ''; + + public function isOpen() { + return true; + } + + public function open() {} + + public function close() {} + + public function write($buf) { + $this->buf_ .= $buf; + } + + public function read($len) { + if (strlen($this->buf_) === 0) { + throw new TTransportException('TMemoryBuffer: Could not read ' . + $len . ' bytes from buffer.', + TTransportException::UNKNOWN); + } + + if (strlen($this->buf_) <= $len) { + $ret = $this->buf_; + $this->buf_ = ''; + return $ret; + } + + $ret = substr($this->buf_, 0, $len); + $this->buf_ = substr($this->buf_, $len); + + return $ret; + } + + function getBuffer() { + return $this->buf_; + } + + public function available() { + return strlen($this->buf_); + } +} diff --git a/library/phpcassa/thrift/transport/TNullTransport.php b/library/phpcassa/thrift/transport/TNullTransport.php new file mode 100644 index 000000000..6e5340d30 --- /dev/null +++ b/library/phpcassa/thrift/transport/TNullTransport.php @@ -0,0 +1,46 @@ +read_ = $mode & self::MODE_R; + $this->write_ = $mode & self::MODE_W; + } + + public function open() { + if ($this->read_) { + $this->inStream_ = @fopen(self::inStreamName(), 'r'); + if (!is_resource($this->inStream_)) { + throw new TException('TPhpStream: Could not open php://input'); + } + } + if ($this->write_) { + $this->outStream_ = @fopen('php://output', 'w'); + if (!is_resource($this->outStream_)) { + throw new TException('TPhpStream: Could not open php://output'); + } + } + } + + public function close() { + if ($this->read_) { + @fclose($this->inStream_); + $this->inStream_ = null; + } + if ($this->write_) { + @fclose($this->outStream_); + $this->outStream_ = null; + } + } + + public function isOpen() { + return + (!$this->read_ || is_resource($this->inStream_)) && + (!$this->write_ || is_resource($this->outStream_)); + } + + public function read($len) { + $data = @fread($this->inStream_, $len); + if ($data === FALSE || $data === '') { + throw new TException('TPhpStream: Could not read '.$len.' bytes'); + } + return $data; + } + + public function write($buf) { + while (strlen($buf) > 0) { + $got = @fwrite($this->outStream_, $buf); + if ($got === 0 || $got === FALSE) { + throw new TException('TPhpStream: Could not write '.strlen($buf).' bytes'); + } + $buf = substr($buf, $got); + } + } + + public function flush() { + @fflush($this->outStream_); + } + + private static function inStreamName() { + if (php_sapi_name() == 'cli') { + return 'php://stdin'; + } + return 'php://input'; + } + +} diff --git a/library/phpcassa/thrift/transport/TServerSocket.php b/library/phpcassa/thrift/transport/TServerSocket.php new file mode 100644 index 000000000..cac12ab71 --- /dev/null +++ b/library/phpcassa/thrift/transport/TServerSocket.php @@ -0,0 +1,96 @@ +host_ = $host; + $this->port_ = $port; + } + + /** + * Sets the accept timeout + * + * @param int $acceptTimeout + * @return void + */ + public function setAcceptTimeout($acceptTimeout) { + $this->acceptTimeout_ = $acceptTimeout; + } + + /** + * Opens a new socket server handle + * + * @return void + */ + public function listen() { + $this->listener_ = stream_socket_server('tcp://' . $this->host_ . ':' . $this->port_); + } + + /** + * Closes the socket server handle + * + * @return void + */ + public function close() { + @fclose($this->listener_); + $this->listener_ = null; + } + + /** + * Implementation of accept. If not client is accepted in the given time + * + * @return TSocket + */ + protected function acceptImpl() { + $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0); + if(!$handle) return null; + + $socket = new TSocket(); + $socket->setHandle($handle); + + return $socket; + } +} diff --git a/library/phpcassa/thrift/transport/TServerTransport.php b/library/phpcassa/thrift/transport/TServerTransport.php new file mode 100644 index 000000000..e92ca77c7 --- /dev/null +++ b/library/phpcassa/thrift/transport/TServerTransport.php @@ -0,0 +1,50 @@ +acceptImpl(); + + if ($transport == null) { + throw new TTransportException("accept() may not return NULL"); + } + + return $transport; + } +} diff --git a/library/phpcassa/thrift/transport/TSocket.php b/library/phpcassa/thrift/transport/TSocket.php new file mode 100644 index 000000000..f7130161e --- /dev/null +++ b/library/phpcassa/thrift/transport/TSocket.php @@ -0,0 +1,320 @@ +host_ = $host; + $this->port_ = $port; + $this->persist_ = $persist; + $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log'; + } + + /** + * @param resource $handle + * @return void + */ + public function setHandle($handle) { + $this->handle_ = $handle; + } + + /** + * Sets the send timeout. + * + * @param int $timeout Timeout in milliseconds. + */ + public function setSendTimeout($timeout) { + $this->sendTimeoutSec_ = floor($timeout / 1000); + $this->sendTimeoutUsec_ = + ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000; + } + + /** + * Sets the receive timeout. + * + * @param int $timeout Timeout in milliseconds. + */ + public function setRecvTimeout($timeout) { + $this->recvTimeoutSec_ = floor($timeout / 1000); + $this->recvTimeoutUsec_ = + ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000; + } + + /** + * Sets debugging output on or off + * + * @param bool $debug + */ + public function setDebug($debug) { + $this->debug_ = $debug; + } + + /** + * Get the host that this socket is connected to + * + * @return string host + */ + public function getHost() { + return $this->host_; + } + + /** + * Get the remote port that this socket is connected to + * + * @return int port + */ + public function getPort() { + return $this->port_; + } + + /** + * Tests whether this is open + * + * @return bool true if the socket is open + */ + public function isOpen() { + return is_resource($this->handle_); + } + + /** + * Connects the socket. + */ + public function open() { + if ($this->isOpen()) { + throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN); + } + + if (empty($this->host_)) { + throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN); + } + + if ($this->port_ <= 0) { + throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN); + } + + if ($this->persist_) { + $this->handle_ = @pfsockopen($this->host_, + $this->port_, + $errno, + $errstr, + $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); + } else { + $this->handle_ = @fsockopen($this->host_, + $this->port_, + $errno, + $errstr, + $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); + } + + // Connect failed? + if ($this->handle_ === FALSE) { + $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])'; + if ($this->debug_) { + call_user_func($this->debugHandler_, $error); + } + throw new TException($error); + } + } + + /** + * Closes the socket. + */ + public function close() { + if (!$this->persist_) { + @fclose($this->handle_); + $this->handle_ = null; + } + } + + /** + * Read from the socket at most $len bytes. + * + * This method will not wait for all the requested data, it will return as + * soon as any data is received. + * + * @param int $len Maximum number of bytes to read. + * @return string Binary data + */ + public function read($len) { + $null = null; + $read = array($this->handle_); + $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec_, $this->recvTimeoutUsec_); + + if ($readable > 0) { + $data = @stream_socket_recvfrom($this->handle_, $len); + if ($data === false) { + throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } elseif($data == '' && feof($this->handle_)) { + throw new TTransportException('TSocket read 0 bytes'); + } + + return $data; + } else if ($readable === 0) { + throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } else { + throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } + } + + /** + * Write to the socket. + * + * @param string $buf The data to write + */ + public function write($buf) { + $null = null; + $write = array($this->handle_); + + // keep writing until all the data has been written + while (strlen($buf) > 0) { + // wait for stream to become available for writing + $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_); + if ($writable > 0) { + // write buffer to stream + $written = @stream_socket_sendto($this->handle_, $buf); + if ($written === -1 || $written === false) { + throw new TTransportException('TSocket: Could not write '.strlen($buf).' bytes '. + $this->host_.':'.$this->port_); + } + // determine how much of the buffer is left to write + $buf = substr($buf, $written); + } else if ($writable === 0) { + throw new TTransportException('TSocket: timed out writing '.strlen($buf).' bytes from '. + $this->host_.':'.$this->port_); + } else { + throw new TTransportException('TSocket: Could not write '.strlen($buf).' bytes '. + $this->host_.':'.$this->port_); + } + } + } + + /** + * Flush output to the socket. + * + * Since read(), readAll() and write() operate on the sockets directly, + * this is a no-op + * + * If you wish to have flushable buffering behaviour, wrap this TSocket + * in a TBufferedTransport. + */ + public function flush() { + // no-op + } + } diff --git a/library/phpcassa/thrift/transport/TSocketPool.php b/library/phpcassa/thrift/transport/TSocketPool.php new file mode 100644 index 000000000..ac46c3605 --- /dev/null +++ b/library/phpcassa/thrift/transport/TSocketPool.php @@ -0,0 +1,294 @@ + $val) { + $ports[$key] = $port; + } + } + + foreach ($hosts as $key => $host) { + $this->servers_ []= array('host' => $host, + 'port' => $ports[$key]); + } + } + + /** + * Add a server to the pool + * + * This function does not prevent you from adding a duplicate server entry. + * + * @param string $host hostname or IP + * @param int $port port + */ + public function addServer($host, $port) { + $this->servers_[] = array('host' => $host, 'port' => $port); + } + + /** + * Sets how many time to keep retrying a host in the connect function. + * + * @param int $numRetries + */ + public function setNumRetries($numRetries) { + $this->numRetries_ = $numRetries; + } + + /** + * Sets how long to wait until retrying a host if it was marked down + * + * @param int $numRetries + */ + public function setRetryInterval($retryInterval) { + $this->retryInterval_ = $retryInterval; + } + + /** + * Sets how many time to keep retrying a host before marking it as down. + * + * @param int $numRetries + */ + public function setMaxConsecutiveFailures($maxConsecutiveFailures) { + $this->maxConsecutiveFailures_ = $maxConsecutiveFailures; + } + + /** + * Turns randomization in connect order on or off. + * + * @param bool $randomize + */ + public function setRandomize($randomize) { + $this->randomize_ = $randomize; + } + + /** + * Whether to always try the last server. + * + * @param bool $alwaysTryLast + */ + public function setAlwaysTryLast($alwaysTryLast) { + $this->alwaysTryLast_ = $alwaysTryLast; + } + + + /** + * Connects the socket by iterating through all the servers in the pool + * and trying to find one that works. + */ + public function open() { + // Check if we want order randomization + if ($this->randomize_) { + shuffle($this->servers_); + } + + // Count servers to identify the "last" one + $numServers = count($this->servers_); + + for ($i = 0; $i < $numServers; ++$i) { + + // This extracts the $host and $port variables + extract($this->servers_[$i]); + + // Check APC cache for a record of this server being down + $failtimeKey = 'thrift_failtime:'.$host.':'.$port.'~'; + + // Cache miss? Assume it's OK + $lastFailtime = apc_fetch($failtimeKey); + if ($lastFailtime === FALSE) { + $lastFailtime = 0; + } + + $retryIntervalPassed = FALSE; + + // Cache hit...make sure enough the retry interval has elapsed + if ($lastFailtime > 0) { + $elapsed = time() - $lastFailtime; + if ($elapsed > $this->retryInterval_) { + $retryIntervalPassed = TRUE; + if ($this->debug_) { + call_user_func($this->debugHandler_, + 'TSocketPool: retryInterval '. + '('.$this->retryInterval_.') '. + 'has passed for host '.$host.':'.$port); + } + } + } + + // Only connect if not in the middle of a fail interval, OR if this + // is the LAST server we are trying, just hammer away on it + $isLastServer = FALSE; + if ($this->alwaysTryLast_) { + $isLastServer = ($i == ($numServers - 1)); + } + + if (($lastFailtime === 0) || + ($isLastServer) || + ($lastFailtime > 0 && $retryIntervalPassed)) { + + // Set underlying TSocket params to this one + $this->host_ = $host; + $this->port_ = $port; + + // Try up to numRetries_ connections per server + for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) { + try { + // Use the underlying TSocket open function + parent::open(); + + // Only clear the failure counts if required to do so + if ($lastFailtime > 0) { + apc_store($failtimeKey, 0); + } + + // Successful connection, return now + return; + + } catch (TException $tx) { + // Connection failed + } + } + + // Mark failure of this host in the cache + $consecfailsKey = 'thrift_consecfails:'.$host.':'.$port.'~'; + + // Ignore cache misses + $consecfails = apc_fetch($consecfailsKey); + if ($consecfails === FALSE) { + $consecfails = 0; + } + + // Increment by one + $consecfails++; + + // Log and cache this failure + if ($consecfails >= $this->maxConsecutiveFailures_) { + if ($this->debug_) { + call_user_func($this->debugHandler_, + 'TSocketPool: marking '.$host.':'.$port. + ' as down for '.$this->retryInterval_.' secs '. + 'after '.$consecfails.' failed attempts.'); + } + // Store the failure time + apc_store($failtimeKey, time()); + + // Clear the count of consecutive failures + apc_store($consecfailsKey, 0); + } else { + apc_store($consecfailsKey, $consecfails); + } + } + } + + // Oh no; we failed them all. The system is totally ill! + $error = 'TSocketPool: All hosts in pool are down. '; + $hosts = array(); + foreach ($this->servers_ as $server) { + $hosts []= $server['host'].':'.$server['port']; + } + $hostlist = implode(',', $hosts); + $error .= '('.$hostlist.')'; + if ($this->debug_) { + call_user_func($this->debugHandler_, $error); + } + throw new TException($error); + } +} diff --git a/library/phpcassa/thrift/transport/TTransport.php b/library/phpcassa/thrift/transport/TTransport.php new file mode 100644 index 000000000..e0e336d28 --- /dev/null +++ b/library/phpcassa/thrift/transport/TTransport.php @@ -0,0 +1,106 @@ +read($len); + + $data = ''; + $got = 0; + while (($got = strlen($data)) < $len) { + $data .= $this->read($len - $got); + } + return $data; + } + + /** + * Writes the given data out. + * + * @param string $buf The data to write + * @throws TTransportException if writing fails + */ + public abstract function write($buf); + + /** + * Flushes any pending data out of a buffer + * + * @throws TTransportException if a writing error occurs + */ + public function flush() {} +} diff --git a/library/phpcassa/thrift/transport/TTransportFactory.php b/library/phpcassa/thrift/transport/TTransportFactory.php new file mode 100644 index 000000000..ac0a65ac1 --- /dev/null +++ b/library/phpcassa/thrift/transport/TTransportFactory.php @@ -0,0 +1,12 @@ +string; + } + + public function __get($var) { + switch($var) { + case "bytes": + return $this->bytes; + case "hex": + return bin2hex($this->bytes); + case "string": + return $this->__toString(); + case "urn": + return "urn:uuid:".$this->__toString(); + case "version": + return ord($this->bytes[6]) >> 4; + case "variant": + $byte = ord($this->bytes[8]); + if ($byte >= self::varRes) + return 3; + if ($byte >= self::varMS) + return 2; + if ($byte >= self::varRFC) + return 1; + else + return 0; + case "node": + if (ord($this->bytes[6])>>4==1) + return bin2hex(substr($this->bytes,10)); + else + return NULL; + case "time": + if (ord($this->bytes[6])>>4==1) { + // Restore contiguous big-endian byte order + $time = bin2hex($this->bytes[6].$this->bytes[7].$this->bytes[4].$this->bytes[5].$this->bytes[0].$this->bytes[1].$this->bytes[2].$this->bytes[3]); + // Clear version flag + $time[0] = "0"; + // Do some reverse arithmetic to get a Unix timestamp + $time = (hexdec($time) - self::interval) / 10000000; + return $time; + } + else + return NULL; + default: + return NULL; + } + } + + protected function __construct($uuid) { + if (strlen($uuid) != 16) + throw new UUIDException("Input must be a 128-bit integer."); + $this->bytes = $uuid; + // Optimize the most common use + $this->string = + bin2hex(substr($uuid,0,4))."-". + bin2hex(substr($uuid,4,2))."-". + bin2hex(substr($uuid,6,2))."-". + bin2hex(substr($uuid,8,2))."-". + bin2hex(substr($uuid,10,6)); + } + + protected static function get_time() { + $time1 = microtime(); + settype($time1, 'string'); //convert to string to keep trailing zeroes + $time2 = explode(" ", $time1); + $sub_secs = preg_replace('/0./', '', $time2[0], 1); + $time3 = ($time2[1].$sub_secs)/100; + return $time3; + } + + + protected static function mintTime($node = NULL, $time_arg = NULL) { + /* Generates a Version 1 UUID. + These are derived from the time at which they were generated. */ + // Get time since Gregorian calendar reform in 100ns intervals + // This is exceedingly difficult because of PHP's (and pack()'s) + // integer size limits. + // Note that this will never be more accurate than to the microsecond. + if ($time_arg == NULL) { + $time = CassandraUtil::get_time() * 10 + self::interval; + } else { + $time = $time_arg * 10 + self::interval; + } + // Convert to a string representation + $time = sprintf("%F", $time); + preg_match("/^\d+/", $time, $time); //strip decimal point + // And now to a 64-bit binary representation + $time = base_convert($time[0], 10, 16); + $time = pack("H*", str_pad($time, 16, "0", STR_PAD_LEFT)); + // Reorder bytes to their proper locations in the UUID + $uuid = $time[4].$time[5].$time[6].$time[7].$time[2].$time[3].$time[0].$time[1]; + // Generate a random clock sequence + $uuid .= self::randomBytes(2); + // set variant + $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); + // set version + $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version1); + // Set the final 'node' parameter, a MAC address + if ($node) + $node = self::makeBin($node, 6); + if (!$node) { + // If no node was provided or if the node was invalid, + // generate a random MAC address and set the multicast bit + $node = self::randomBytes(6); + $node[0] = pack("C", ord($node[0]) | 1); + } + $uuid .= $node; + return $uuid; + } + + protected static function mintRand() { + /* Generate a Version 4 UUID. + These are derived soly from random numbers. */ + // generate random fields + $uuid = self::randomBytes(16); + // set variant + $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); + // set version + $uuid[6] = chr(ord($uuid[6]) & self::clearVer | self::version4); + return $uuid; + } + + protected static function mintName($ver, $node, $ns) { + /* Generates a Version 3 or Version 5 UUID. + These are derived from a hash of a name and its namespace, in binary form. */ + if (!$node) + throw new UUIDException("A name-string is required for Version 3 or 5 UUIDs."); + // if the namespace UUID isn't binary, make it so + $ns = self::makeBin($ns, 16); + if (!$ns) + throw new UUIDException("A binary namespace is required for Version 3 or 5 UUIDs."); + switch($ver) { + case self::MD5: + $version = self::version3; + $uuid = md5($ns.$node,1); + break; + case self::SHA1: + $version = self::version5; + $uuid = substr(sha1($ns.$node,1),0, 16); + break; + } + // set variant + $uuid[8] = chr(ord($uuid[8]) & self::clearVar | self::varRFC); + // set version + $uuid[6] = chr(ord($uuid[6]) & self::clearVer | $version); + return ($uuid); + } + + protected static function makeBin($str, $len) { + /* Insure that an input string is either binary or hexadecimal. + Returns binary representation, or false on failure. */ + if ($str instanceof self) + return $str->bytes; + if (strlen($str)==$len) + return $str; + else + $str = preg_replace("/^urn:uuid:/is", "", $str); // strip URN scheme and namespace + $str = preg_replace("/[^a-f0-9]/is", "", $str); // strip non-hex characters + if (strlen($str) != ($len * 2)) + return FALSE; + else + return pack("H*", $str); + } + + public static function initRandom() { + /* Look for a system-provided source of randomness, which is usually crytographically secure. + /dev/urandom is tried first simply out of bias for Linux systems. */ + if (is_readable('/dev/urandom')) { + self::$randomSource = fopen('/dev/urandom', 'rb'); + self::$randomFunc = 'randomFRead'; + } + else if (class_exists('COM', 0)) { + try { + self::$randomSource = new COM('CAPICOM.Utilities.1'); // See http://msdn.microsoft.com/en-us/library/aa388182(VS.85).aspx + self::$randomFunc = 'randomCOM'; + } + catch(Exception $e) {} + } + return self::$randomFunc; + } + + public static function randomBytes($bytes) { + return call_user_func(array('self', self::$randomFunc), $bytes); + } + + protected static function randomTwister($bytes) { + /* Get the specified number of random bytes, using mt_rand(). + Randomness is returned as a string of bytes. */ + $rand = ""; + for ($a = 0; $a < $bytes; $a++) { + $rand .= chr(mt_rand(0, 255)); + } + return $rand; + } + + protected static function randomFRead($bytes) { + /* Get the specified number of random bytes using a file handle + previously opened with UUID::initRandom(). + Randomness is returned as a string of bytes. */ + return fread(self::$randomSource, $bytes); + } + + protected static function randomCOM($bytes) { + /* Get the specified number of random bytes using Windows' + randomness source via a COM object previously created by UUID::initRandom(). + Randomness is returned as a string of bytes. */ + return base64_decode(self::$randomSource->GetRandom($bytes,0)); // straight binary mysteriously doesn't work, hence the base64 + } +} + +/** + * @package phpcassa + * @subpackage uuid + */ +class UUIDException extends Exception { +}