Skip to content

Commit

Permalink
add support of a new binary protocol command for "call"
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
LeonidVas committed Jul 21, 2020
1 parent 59d0739 commit 7114c29
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 25 deletions.
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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] )
Expand Down Expand Up @@ -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" => <bool>
),
```

_**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
Expand Down
70 changes: 66 additions & 4 deletions src/tarantool.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <time.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>

#include "php_tarantool.h"
#include "tarantool_internal.h"
Expand All @@ -14,6 +15,7 @@

#include "utils.h"


static int __tarantool_authenticate(tarantool_connection *obj);
static void tarantool_stream_close(tarantool_connection *obj);

Expand Down Expand Up @@ -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)
Expand All @@ -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 )
Expand Down Expand Up @@ -1194,6 +1203,7 @@ PHP_METHOD(Tarantool, __construct) {
}
zend_string_release(plist_id);
}

t_obj->obj = obj;
t_obj->is_persistent = is_persistent;
return;
Expand Down Expand Up @@ -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;
Expand Down
26 changes: 18 additions & 8 deletions src/tarantool_proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,29 +172,39 @@ 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) +
php_mp_sizeof_long(TNT_TUPLE) +
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);
php_mp_pack_long(str, TNT_TUPLE);
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) +
Expand Down
5 changes: 4 additions & 1 deletion src/tarantool_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down Expand Up @@ -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);

Expand Down
25 changes: 22 additions & 3 deletions test/DMLTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,18 +235,37 @@ 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(
'0' => array(
'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() {
Expand Down
23 changes: 20 additions & 3 deletions test/MsgPackTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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]);
}
}
13 changes: 12 additions & 1 deletion test/RandomTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]));
}
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions test/shared/box.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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'))

0 comments on commit 7114c29

Please sign in to comment.