diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/README.md b/README.md
index e073e3c..0678831 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,10 @@ Cassandra client library for PHP
-Cassandra client library for PHP, which support Protocol v3 (Cassandra 2.1) and asynchronous request
+Cassandra client library for PHP, which support Protocol v4 (Cassandra 4) and asynchronous request
## Features
-* Using Protocol v3 (Cassandra 2.1)
+* Using Protocol v4 (Cassandra 4)
* Support ssl/tls with stream transport layer
* Support asynchronous and synchronous request
* Support for logged, unlogged and counter batches
@@ -233,6 +233,18 @@ All types are supported.
// Inet
new Cassandra\Type\Inet('127.0.0.1');
+// Date
+ new Cassandra\Type\Date(date('Y-m-d', strtotime('now')));
+
+// Time
+ new Cassandra\Type\Time(date('H:i:s', strtotime('now')));
+
+// Smallint
+ new Cassandra\Type\SmallInt(32700);
+
+// Tinyint
+ new Cassandra\Type\TinyInt(255);
+
// Int
new Cassandra\Type\PhpInt(1);
diff --git a/composer.json b/composer.json
index 0c75e64..b675e3b 100755
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "duoshuo/php-cassandra",
- "description": "Cassandra client library for PHP, which support Protocol v3 and asynchronous request",
+ "description": "Cassandra client library for PHP, which support Protocol v4 and asynchronous request",
"minimum-stability": "stable",
"keywords" : [
"cassandra",
@@ -18,6 +18,10 @@
{
"name": "Evseev Nikolay",
"email": "evseevnn@gmail.com"
+ },
+ {
+ "name": "Ciaran Moore",
+ "email": "ciaran.f.moore@gmail.com"
}
],
"require": {
diff --git a/src/Connection.php b/src/Connection.php
index 5b4fc3e..2b2cda1 100644
--- a/src/Connection.php
+++ b/src/Connection.php
@@ -9,7 +9,7 @@ class Connection {
* @var array
*/
protected $_options = [
- 'CQL_VERSION' => '3.0.0'
+ 'CQL_VERSION' => '4.0.0'
];
/**
@@ -179,7 +179,7 @@ protected function _getResponse() {
return $response;
default:
- throw new Exception('php-cassandra supports CQL binary protocol v3 only, please upgrade your Cassandra to 2.1 or later.');
+ throw new Exception('php-cassandra supports CQL binary protocol v3+ only, please upgrade your Cassandra to 2.1 or later.');
}
}
diff --git a/src/Request/Startup.php b/src/Request/Startup.php
index 01915b1..037a3c3 100644
--- a/src/Request/Startup.php
+++ b/src/Request/Startup.php
@@ -30,6 +30,14 @@ class Startup extends Request{
* different from the protocol version.
* - "COMPRESSION": the compression algorithm to use for frames (See section 5).
* This is optional, if not specified no compression will be used.
+ * - "NO_COMPACT": whether or not connection has to be established in compatibility
+ * mode. This mode will make all Thrift and Compact Tables to be exposed as if
+ * they were CQL Tables. This is optional; if not specified, the option will
+ * not be used.
+ * - "THROW_ON_OVERLOAD": In case of server overloaded with too many requests, by
+ * default the server puts back pressure on the client connection. Instead, the server
+ * can send an OverloadedException error message back to the client if this option is
+ * set to true.
*
* @param array $options
*/
diff --git a/src/Response/Error.php b/src/Response/Error.php
index 175d661..c3fa8dc 100644
--- a/src/Response/Error.php
+++ b/src/Response/Error.php
@@ -12,6 +12,9 @@ class Error extends Response {
const TRUNCATE_ERROR = 0x1003;
const WRITE_TIMEOUT = 0x1100;
const READ_TIMEOUT = 0x1200;
+ const READ_FAILURE = 0x1300;
+ const FUNCTION_FAILURE = 0x1400;
+ const WRITE_FAILURE = 0x1500;
const SYNTAX_ERROR = 0x2000;
const UNAUTHORIZED = 0x2100;
const INVALID = 0x2200;
@@ -27,95 +30,129 @@ class Error extends Response {
*
* @return array
*/
- public function getData() {
- $this->_stream->offset(0);
- $data = [];
- $data['code'] = $this->_stream->readInt();
-
- switch($data['code']){
- case self::SERVER_ERROR:
- $data['message'] = "Server error: " . $this->_stream->readString();
- break;
-
- case self::PROTOCOL_ERROR:
- $data['message'] = "Protocol error: " . $this->_stream->readString();
- break;
-
- case self::BAD_CREDENTIALS:
- $data['message'] = "Bad credentials: " . $this->_stream->readString();
- break;
-
- case self::UNAVAILABLE_EXCEPTION:
- $data['message'] = "Unavailable exception. Error data: " . var_export([
+ public function getData() {
+ $this->_stream->offset(0);
+ $data = [];
+ $data['code'] = $this->_stream->readInt();
+
+ switch($data['code']){
+ case self::SERVER_ERROR:
+ $data['message'] = "Server error - Something unexpected happened. This indicates a server-side bug: " . $this->_stream->readString();
+ break;
+
+ case self::PROTOCOL_ERROR:
+ $data['message'] = "Protocol error - Some client message triggered a protocol violation (for instance a QUERY message is sent before a STARTUP one has been sent): " . $this->_stream->readString();
+ break;
+
+ case self::BAD_CREDENTIALS:
+ $data['message'] = "Authentication error - Authentication was required and failed. The possible reason for failing depends on the authenticator in use, which may or may not include more detail in the accompanying error message: " . $this->_stream->readString();
+ break;
+ // ERROR message body:
+ case self::UNAVAILABLE_EXCEPTION:
+ $data['message'] = "Unavailable exception. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
'node' => $this->_stream->readInt(),
- 'replica' => $this->_stream->readInt()
- ], true);
- break;
-
- case self::OVERLOADED:
- $data['message'] = "Overloaded: " . $this->_stream->readString();
- break;
-
- case self::IS_BOOTSTRAPPING:
- $data['message'] = "Is_bootstrapping: " . $this->_stream->readString();
- break;
-
- case self::TRUNCATE_ERROR:
- $data['message'] = "Truncate_error: " . $this->_stream->readString();
- break;
-
- case self::WRITE_TIMEOUT:
- $data['message'] = "Write_timeout. Error data: " . var_export([
- 'error'=>$this->_stream->readString(),
- 'consistency' => $this->_stream->readShort(),
- 'node' => $this->_stream->readInt(),
- 'replica' => $this->_stream->readInt(),
- 'writeType' => $this->_stream->readString()
- ], true);
- break;
-
- case self::READ_TIMEOUT:
- $data['message'] = "Read_timeout. Error data: " . var_export([
+ 'replica' => $this->_stream->readInt()
+ ], true);
+ break;
+
+ case self::OVERLOADED:
+ $data['message'] = "Overloaded - the request cannot be processed because the coordinator node is overloaded: " . $this->_stream->readString();
+ break;
+
+ case self::IS_BOOTSTRAPPING:
+ $data['message'] = "Is_bootstrapping - the request was a read request but the coordinator node is bootstrapping: " . $this->_stream->readString();
+ break;
+
+ case self::TRUNCATE_ERROR:
+ $data['message'] = "Truncate_error - Error during a truncation error: " . $this->_stream->readString();
+ break;
+ // ERROR message body:
+ case self::WRITE_TIMEOUT:
+ $data['message'] = "Write_timeout - Timeout exception during a write request. Error data: " . var_export([
'error'=>$this->_stream->readString(),
'consistency' => $this->_stream->readShort(),
- 'node' => $this->_stream->readInt(),
- 'replica' => $this->_stream->readInt(),
- 'dataPresent' => $this->_stream->readChar()
- ], true);
- break;
-
- case self::SYNTAX_ERROR:
- $data['message'] = "Syntax_error: " . $this->_stream->readString();
- break;
-
- case self::UNAUTHORIZED:
- $data['message'] = "Unauthorized: " . $this->_stream->readString();
- break;
-
- case self::INVALID:
- $data['message'] = "Invalid: " . $this->_stream->readString();
- break;
-
- case self::CONFIG_ERROR:
- $data['message'] = "Config_error: " . $this->_stream->readString();
- break;
-
- case self::ALREADY_EXIST:
- $data['message'] = "Already_exists: " . $this->_stream->readString();
- break;
-
- case self::UNPREPARED:
- $data['message'] = "Unprepared: " . $this->_stream->readShort();
- break;
-
- default:
- $data['message'] = 'Unknown error';
- }
-
- return $data;
- }
+ 'node' => $this->_stream->readInt(),
+ 'replica' => $this->_stream->readInt(),
+ 'writeType' => $this->_stream->readString()
+ ], true);
+ break;
+ // ERROR message body:
+ case self::READ_TIMEOUT:
+ $data['message'] = "Read_timeout - Timeout exception during a read request. Error data: " . var_export([
+ 'error'=>$this->_stream->readString(),
+ 'consistency' => $this->_stream->readShort(),
+ 'node' => $this->_stream->readInt(),
+ 'replica' => $this->_stream->readInt(),
+ 'dataPresent' => $this->_stream->readChar()
+ ], true);
+ break;
+ // ERROR message body:
+ case self::READ_FAILURE:
+ $data['message'] = "Read_failure - A non-timeout exception during a read request. Error data: " . var_export([
+ 'error'=>$this->_stream->readString(),
+ 'consistency' => $this->_stream->readShort(),
+ 'node' => $this->_stream->readInt(),
+ 'replica' => $this->_stream->readInt(),
+ 'numfailures' => $this->_stream->readInt(),
+ 'dataPresent' => $this->_stream->readChar()
+ ], true);
+ break;
+ // ERROR message body:
+ case self::FUNCTION_FAILURE:
+ $data['message'] = "Function_failure - A (user defined) function failed during execution. Error data: " . var_export([
+ 'error'=>$this->_stream->readString(),
+ 'keyspace' => $this->_stream->readString(),
+ 'function' => $this->_stream->readString(),
+ 'arg_types' => $this->_stream->readString(),
+ ], true);
+ break;
+ // ERROR message body:
+ case self::WRITE_FAILURE:
+ $data['message'] = "Write_failure - A non-timeout exception during a write request. Error data: " . var_export([
+ 'error'=>$this->_stream->readString(),
+ 'consistency' => $this->_stream->readShort(),
+ 'node' => $this->_stream->readInt(),
+ 'replica' => $this->_stream->readInt(),
+ 'writeType' => $this->_stream->readString()
+ ], true);
+ break;
+
+ case self::SYNTAX_ERROR:
+ $data['message'] = "Syntax_error - The submitted query has a syntax error: " . $this->_stream->readString();
+ break;
+
+ case self::UNAUTHORIZED:
+ $data['message'] = "Unauthorized - The logged user doesn't have the right to perform the query: " . $this->_stream->readString();
+ break;
+
+ case self::INVALID:
+ $data['message'] = "Invalid - The query is syntactically correct but invalid: " . $this->_stream->readString();
+ break;
+
+ case self::CONFIG_ERROR:
+ $data['message'] = "Config_error - The query is invalid because of some configuration issue: " . $this->_stream->readString();
+ break;
+ // ERROR message body:
+ case self::ALREADY_EXIST:
+ $data['message'] = "Already_exists - The query attempted to create a keyspace or a table that was already existing: " . var_export([
+ 'error'=>$this->_stream->readString(),
+ 'keyspace' => $this->_stream->readString(),
+ 'table' => $this->_stream->readString()
+ ], true);
+ break;
+
+ case self::UNPREPARED:
+ $data['message'] = "Unprepared - Can be thrown while a prepared statement tries to be executed if the provided prepared statement ID is not known by this host: " . $this->_stream->readShort();
+ break;
+
+ default:
+ $data['message'] = 'Unknown error';
+ }
+
+ return $data;
+ }
/**
*
diff --git a/src/Type/Base.php b/src/Type/Base.php
index 8949c13..8e37e50 100644
--- a/src/Type/Base.php
+++ b/src/Type/Base.php
@@ -13,13 +13,16 @@ abstract class Base{
const DOUBLE = 0x0007;
const FLOAT = 0x0008;
const INT = 0x0009;
- const TEXT = 0x000A; // deprecated in Protocol v3
const TIMESTAMP = 0x000B;
const UUID = 0x000C;
const VARCHAR = 0x000D;
const VARINT = 0x000E;
const TIMEUUID = 0x000F;
const INET = 0x0010;
+ const DATE = 0x0011;
+ const TIME = 0x0012;
+ const SMALLINT = 0x0013;
+ const TINYINT = 0x0014;
const COLLECTION_LIST = 0x0020;
const COLLECTION_MAP = 0x0021;
const COLLECTION_SET = 0x0022;
@@ -29,7 +32,6 @@ abstract class Base{
public static $typeClassMap = [
self::ASCII => 'Cassandra\Type\Ascii',
self::VARCHAR => 'Cassandra\Type\Varchar',
- self::TEXT => 'Cassandra\Type\Varchar', // deprecated in Protocol v3
self::VARINT => 'Cassandra\Type\Varint',
self::BIGINT => 'Cassandra\Type\Bigint',
self::COUNTER => 'Cassandra\Type\Counter',
@@ -43,6 +45,10 @@ abstract class Base{
self::UUID => 'Cassandra\Type\Uuid',
self::TIMEUUID => 'Cassandra\Type\Timeuuid',
self::INET => 'Cassandra\Type\Inet',
+ self::DATE => 'Cassandra\Type\Date',
+ self::TIME => 'Cassandra\Type\Time',
+ self::SMALLINT => 'Cassandra\Type\SmallInt',
+ self::TINYINT => 'Cassandra\Type\TinyInt',
self::COLLECTION_LIST => 'Cassandra\Type\CollectionList',
self::COLLECTION_SET => 'Cassandra\Type\CollectionSet',
self::COLLECTION_MAP => 'Cassandra\Type\CollectionMap',
diff --git a/src/Type/Date.php b/src/Type/Date.php
new file mode 100644
index 0000000..4d2d238
--- /dev/null
+++ b/src/Type/Date.php
@@ -0,0 +1,36 @@
+_value = $parsedDate;
+ }
+
+ public static function binary($dateString){
+ $parsedDate = strtotime($dateString);
+ return pack('N', $parsedDate);
+ }
+
+ /**
+ * @return string
+ */
+ public static function parse($binary){
+ $unpacked = unpack('N', $binary);
+ return date('Y-m-d', $unpacked[1]);
+ }
+}
+Explanation:
+
diff --git a/src/Type/SmallInt.php b/src/Type/SmallInt.php
new file mode 100644
index 0000000..f68a0d6
--- /dev/null
+++ b/src/Type/SmallInt.php
@@ -0,0 +1,34 @@
+ 32767)
+ throw new Exception('Smallint value out of range.');
+
+ $this->_value = (int) $value;
+ }
+
+ public static function binary($value){
+ return pack('n', $value);
+ }
+
+ /**
+ * @return int
+ */
+ public static function parse($binary){
+ $unpacked = unpack('n', $binary);
+ return $unpacked[1];
+ }
+}
\ No newline at end of file
diff --git a/src/Type/Time.php b/src/Type/Time.php
new file mode 100644
index 0000000..05d746c
--- /dev/null
+++ b/src/Type/Time.php
@@ -0,0 +1,34 @@
+_value = $parsedTime;
+ }
+
+ public static function binary($timeString){
+ $parsedTime = strtotime($timeString);
+ return pack('N', $parsedTime);
+ }
+
+ /**
+ * @return string
+ */
+ public static function parse($binary){
+ $unpacked = unpack('N', $binary);
+ return date('H:i:s', $unpacked[1]);
+ }
+}
\ No newline at end of file
diff --git a/src/Type/TinyInt.php b/src/Type/TinyInt.php
new file mode 100644
index 0000000..77cd1a3
--- /dev/null
+++ b/src/Type/TinyInt.php
@@ -0,0 +1,34 @@
+ 255)
+ throw new Exception('Tinyint value out of range.');
+
+ $this->_value = (int) $value;
+ }
+
+ public static function binary($value){
+ return pack('C', $value);
+ }
+
+ /**
+ * @return int
+ */
+ public static function parse($binary){
+ $unpacked = unpack('C', $binary);
+ return $unpacked[1];
+ }
+}
\ No newline at end of file