From 15f563f7105b47f16925cc3644067bc2f1e91334 Mon Sep 17 00:00:00 2001 From: Leonid Vasiliev Date: Tue, 14 Jul 2020 10:42:35 +0300 Subject: [PATCH] add support of a new binary protocol command for "call" * Added support for a new binary protocol command for "call" (used since tarantool 1.7.2). Unlike the old version, returned data is not converted to tuples (like in case of using "eval"). Default - call_16 mode. * Added options to "call" method. It can be used to force call_16 / call_17 mode. Example: ``` php $c = new Tarantool('localhost', 3301); $c->connect(); $c->call("test_call", array(), array('call_17' => true)); $c->close(); ``` Closes #101 --- README.md | 30 +++++++++++++++---- src/tarantool.c | 70 ++++++++++++++++++++++++++++++++++++++++--- src/tarantool_proto.c | 26 +++++++++++----- src/tarantool_proto.h | 5 +++- test/DMLTest.php | 25 ++++++++++++++-- test/MsgPackTest.php | 23 ++++++++++++-- test/RandomTest.php | 13 +++++++- test/shared/box.lua | 3 ++ 8 files changed, 170 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8053f5c..bb4cc9f 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ Tarantool { public array Tarantool::select (mixed $space [, mixed $key = array() [, mixed $index = 0 [, int $limit = PHP_INT_MAX [, int $offset = 0 [, $iterator = Tarantool::ITERATOR_EQ ] ] ] ] ] ) public array Tarantool::insert (mixed $space, array $tuple) public array Tarantool::replace (mixed $space, array $tuple) - public array Tarantool::call (string $procedure [, mixed args] ) + public array Tarantool::call (string $procedure [, mixed args [, array $opts ] ] ) public array Tarantool::evaluate (string $expression [, mixed args] ) public array Tarantool::delete (mixed $space, mixed $key [, mixed $index] ) public array Tarantool::update (mixed $space, mixed $key, array $ops [, number $index] ) @@ -299,27 +299,47 @@ $tnt->replace("test", array(1, 3, "smth completely different")); ### Tarantool::call ``` php -public array Tarantool::call(string $procedure [, mixed args]) +public array Tarantool::call(string $procedure [, mixed args [, array $opts]]) ``` _**Description**_: Call stored procedure _**Parameters**_ * `procedure`: String, procedure to call (mandatory) -* `args`: Any value to pass to procdure as arguments (empty by default) +* `args`: Any value to pass to procedure as arguments (empty by default) +* `opts`: Array, options + +_**Options**_ +* call_17
+ If true - call_17 mode of "call" will be used + (returned data has an arbitrary structure). Since tarantool 1.7.2.
+ If false - call_16 mode of "call" will be used + (returned data converted to tuples).
+ Default - call_16 mode. + ``` + array( + "call_17" => + ), + ``` _**Return Value**_ +**BOOL**: False and raises `Exception` in case of error. + +call_16 mode (default): + **Array of arrays** in case of success - tuples that were returned by stored procedure. -**BOOL**: False and raises `Exception` in case of error. +call_17 mode: + +**Any value**, that was returned by stored procedure. #### Example ``` php $tnt->call("test_2"); -$tnt->call("test_3", array(3, 4)); +$tnt->call("test_3", array(3, 4), array('call_17' => true)); ``` ### Tarantool::evaluate diff --git a/src/tarantool.c b/src/tarantool.c index 2e03852..e529a28 100644 --- a/src/tarantool.c +++ b/src/tarantool.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "php_tarantool.h" #include "tarantool_internal.h" @@ -14,6 +15,7 @@ #include "utils.h" + static int __tarantool_authenticate(tarantool_connection *obj); static void tarantool_stream_close(tarantool_connection *obj); @@ -548,12 +550,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_tarantool_delete, 0, 0, 2) ZEND_ARG_INFO(0, index) ZEND_END_ARG_INFO() -/* call, eval */ +/* eval */ ZEND_BEGIN_ARG_INFO_EX(arginfo_tarantool_proc_tuple, 0, 0, 1) ZEND_ARG_INFO(0, proc) ZEND_ARG_INFO(0, tuple) ZEND_END_ARG_INFO() +/* call */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_tarantool_proc_call, 0, 0, 1) + ZEND_ARG_INFO(0, proc) + ZEND_ARG_INFO(0, tuple) + ZEND_ARG_INFO(0, opts) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_tarantool_update, 0, 0, 3) ZEND_ARG_INFO(0, space) ZEND_ARG_INFO(0, key) @@ -580,7 +589,7 @@ const zend_function_entry Tarantool_methods[] = { TNT_MEP(select, arginfo_tarantool_select ) TNT_MEP(insert, arginfo_tarantool_space_tuple ) TNT_MEP(replace, arginfo_tarantool_space_tuple ) - TNT_MEP(call, arginfo_tarantool_proc_tuple ) + TNT_MEP(call, arginfo_tarantool_proc_call ) TNT_MEP(eval, arginfo_tarantool_proc_tuple ) TNT_MEP(delete, arginfo_tarantool_delete ) TNT_MEP(update, arginfo_tarantool_update ) @@ -1194,6 +1203,7 @@ PHP_METHOD(Tarantool, __construct) { } zend_string_release(plist_id); } + t_obj->obj = obj; t_obj->is_persistent = is_persistent; return; @@ -1485,17 +1495,69 @@ PHP_METHOD(Tarantool, delete) { TARANTOOL_RETURN_DATA(&body, &header, &body); } +/** + * "call" options. + */ +struct call_opts { + bool call_17; +}; + +/** + * Initialization value for "call" options. + */ +static const struct call_opts call_opts_def = { + .call_17 = false +}; + +static int parse_call_opts(zval *opts, struct call_opts *res) { + HashTable *ht = HASH_OF(opts); + if (ht == NULL) { + THROW_EXC("call(): bad type of \"options\"." + " Should be an array."); + return FAILURE; + } + + zval *call_17_zval = zend_hash_str_find(ht, "call_17", + strlen("call_17")); + if (call_17_zval != NULL) { + if (Z_TYPE_P(call_17_zval) == IS_TRUE) { + res->call_17 = true; + } else if (Z_TYPE_P(call_17_zval) == IS_FALSE) { + res->call_17 = false; + } else { + THROW_EXC("call(): bad type of call_17 option." + " Should be a boolean."); + return FAILURE; + } + } + + return SUCCESS; +} + PHP_METHOD(Tarantool, call) { char *proc; size_t proc_len; zval *tuple = NULL, tuple_new; + zval *opts = NULL; - TARANTOOL_FUNCTION_BEGIN(obj, id, "s|z", &proc, &proc_len, &tuple); + TARANTOOL_FUNCTION_BEGIN(obj, id, "s|zz", &proc, &proc_len, + &tuple, &opts); TARANTOOL_CONNECT_ON_DEMAND(obj); + struct call_opts call_opts = call_opts_def; + if (opts != NULL && parse_call_opts(opts, &call_opts) == FAILURE) { + RETURN_FALSE; + } + pack_key(tuple, 1, &tuple_new); long sync = TARANTOOL_G(sync_counter)++; - php_tp_encode_call(obj->value, sync, proc, proc_len, &tuple_new); + if (call_opts.call_17) { + php_tp_encode_call(obj->value, sync, proc, proc_len, + &tuple_new); + } else { + php_tp_encode_call_16(obj->value, sync, proc, proc_len, + &tuple_new); + } zval_ptr_dtor(&tuple_new); if (tarantool_stream_send(obj) == FAILURE) RETURN_FALSE; diff --git a/src/tarantool_proto.c b/src/tarantool_proto.c index f8c69e5..8501a67 100644 --- a/src/tarantool_proto.c +++ b/src/tarantool_proto.c @@ -172,9 +172,9 @@ void php_tp_encode_delete(smart_string *str, uint32_t sync, php_mp_pack(str, tuple); } -size_t php_tp_sizeof_call(uint32_t sync, - uint32_t proc_len, zval *tuple) { - return php_tp_sizeof_header(TNT_CALL, sync) + +static size_t php_tp_sizeof_call(uint32_t sync, uint32_t proc_len, + enum tnt_request_type type, zval *tuple) { + return php_tp_sizeof_header(type, sync) + php_mp_sizeof_hash(2) + php_mp_sizeof_long(TNT_FUNCTION) + php_mp_sizeof_string(proc_len) + @@ -182,12 +182,12 @@ size_t php_tp_sizeof_call(uint32_t sync, php_mp_sizeof(tuple) ; } -void php_tp_encode_call(smart_string *str, uint32_t sync, - char *proc, uint32_t proc_len, zval *tuple) { - size_t packet_size = php_tp_sizeof_call(sync, - proc_len, tuple); +static void php_tp_encode_call_impl(smart_string *str, uint32_t sync, + char *proc, uint32_t proc_len, + enum tnt_request_type type, zval *tuple) { + size_t packet_size = php_tp_sizeof_call(sync, proc_len, type, tuple); smart_string_ensure(str, packet_size + 5); - php_tp_pack_header(str, packet_size, TNT_CALL, sync); + php_tp_pack_header(str, packet_size, type, sync); php_mp_pack_hash(str, 2); php_mp_pack_long(str, TNT_FUNCTION); php_mp_pack_string(str, proc, proc_len); @@ -195,6 +195,16 @@ void php_tp_encode_call(smart_string *str, uint32_t sync, php_mp_pack(str, tuple); } +void php_tp_encode_call(smart_string *str, uint32_t sync, char *proc, + uint32_t proc_len, zval *tuple) { + php_tp_encode_call_impl(str,sync, proc, proc_len, TNT_CALL, tuple); +} + +void php_tp_encode_call_16(smart_string *str, uint32_t sync, char *proc, + uint32_t proc_len, zval *tuple) { + php_tp_encode_call_impl(str,sync, proc, proc_len, TNT_CALL_16, tuple); +} + size_t php_tp_sizeof_eval(uint32_t sync, uint32_t proc_len, zval *tuple) { return php_tp_sizeof_header(TNT_EVAL, sync) + diff --git a/src/tarantool_proto.h b/src/tarantool_proto.h index 49f0432..5454bda 100644 --- a/src/tarantool_proto.h +++ b/src/tarantool_proto.h @@ -87,10 +87,11 @@ enum tnt_request_type { TNT_REPLACE = 0x03, TNT_UPDATE = 0x04, TNT_DELETE = 0x05, - TNT_CALL = 0x06, + TNT_CALL_16 = 0x06, TNT_AUTH = 0x07, TNT_EVAL = 0x08, TNT_UPSERT = 0x09, + TNT_CALL = 0x0a, TNT_PING = 0x40 }; @@ -138,6 +139,8 @@ void php_tp_encode_delete(smart_string *str, uint32_t sync, uint32_t space_no, uint32_t index_no, zval *tuple); void php_tp_encode_call(smart_string *str, uint32_t sync, char *proc, uint32_t proc_len, zval *tuple); +void php_tp_encode_call_16(smart_string *str, uint32_t sync, char *proc, + uint32_t proc_len, zval *tuple); void php_tp_encode_eval(smart_string *str, uint32_t sync, char *proc, uint32_t proc_len, zval *tuple); diff --git a/test/DMLTest.php b/test/DMLTest.php index 7b3bdcf..84caf3c 100644 --- a/test/DMLTest.php +++ b/test/DMLTest.php @@ -235,7 +235,8 @@ public function test_11_update_error() { } public function test_12_call() { - $result = self::$tarantool->call("test_6", array(true, false, false)); + $result = self::$tarantool->call("test_6", array(true, false, false), + array('call_17' => false)); $this->assertEquals(array(array(true), array(false), array(false)), $result); $this->assertEquals( array( @@ -243,10 +244,28 @@ public function test_12_call() { '0' => array('k1' => 'v2', 'k2' => 'v') ) ), - self::$tarantool->call("test_2") + self::$tarantool->call("test_2", array(), array('call_17' => false)) ); $this->assertEquals( - self::$tarantool->call("test_3", array(3, 4)), array('0' => array('0' => 7))); + self::$tarantool->call("test_3", array(3, 4), array('call_17' => false)), + array('0' => array('0' => 7)) + ); + + $check_call_17 = self::$tarantool->call('tarantool_version_at_least', + array(1, 7, 2, 0)); + if ($check_call_17[0][0]) { + $result = self::$tarantool->call("test_6", array(true, false, false), + array('call_17' => true)); + $this->assertEquals(array(true, false, false), $result); + $this->assertEquals( + array('0' => array('k1' => 'v2', 'k2' => 'v')), + self::$tarantool->call("test_2", array(), array('call_17' => true)) + ); + $this->assertEquals( + self::$tarantool->call("test_3", array(3, 4), array('call_17' => true)), + array('0' => 7) + ); + } } public function test_13_eval() { diff --git a/test/MsgPackTest.php b/test/MsgPackTest.php index ddc2c08..b593809 100644 --- a/test/MsgPackTest.php +++ b/test/MsgPackTest.php @@ -22,14 +22,31 @@ public function test_00_msgpack_call() { '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ 'browser_stats_first_session_hits' => 1 ] - ]); + ], ['call_17' => false]); $this->assertEquals($resp[0][0], 2); $resp = self::$tarantool->call('test_4', [ '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ 'browser_stats_first_session_hit' => 1 ] - ]); + ], ['call_17' => false]); $this->assertEquals($resp[0][0], 2); + + $check_call_17 = self::$tarantool->call('tarantool_version_at_least', + array(1,7,2,0)); + if ($check_call_17[0][0]) { + $resp = self::$tarantool->call('test_4', [ + '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ + 'browser_stats_first_session_hits' => 1 + ] + ], ['call_17' => true]); + $this->assertEquals($resp[0], 2); + $resp = self::$tarantool->call('test_4', [ + '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ + 'browser_stats_first_session_hit' => 1 + ] + ], ['call_17' => true]); + $this->assertEquals($resp[0], 2); + } } public function test_01_msgpack_array_key() { @@ -81,6 +98,6 @@ public function test_05_msgpack_string_keys() { public function test_06_msgpack_array_reference() { $data = array('key1' => 'value1'); $link = &$data['key1']; - self::$tarantool->call('test_4', [$data]); + self::$tarantool->call('test_4', [$data], ['call_17' => false]); } } diff --git a/test/RandomTest.php b/test/RandomTest.php index 9eb60d1..a2ffb2a 100644 --- a/test/RandomTest.php +++ b/test/RandomTest.php @@ -42,9 +42,20 @@ public function test_02_very_big_response() { public function test_03_another_big_response() { for ($i = 100; $i <= 5200; $i += 100) { - $result = self::$tarantool->call('test_5', array($i)); + $result = self::$tarantool->call('test_5', array($i), + array('call_17' => false)); $this->assertEquals($i, count($result)); } + + $check_call_17 = self::$tarantool->call('tarantool_version_at_least', + array(1,7,2,0)); + if ($check_call_17[0][0]) { + for ($i = 100; $i <= 5200; $i += 100) { + $result = self::$tarantool->call('test_5', array($i), + array('call_17' => true)); + $this->assertEquals($i, count($result[0])); + } + } } /** diff --git a/test/shared/box.lua b/test/shared/box.lua index 33838c8..c90315a 100755 --- a/test/shared/box.lua +++ b/test/shared/box.lua @@ -197,5 +197,8 @@ box.session.on_connect(function() end end) +-- export tarantool_version_at_least function +_G.tarantool_version_at_least = tarantool_version_at_least + require('console').listen(os.getenv('ADMIN_PORT'))