Browse Source

#108 - fixed. #66 - done.

tags/v2.6-rc3
V. Soshnikov 1 year ago
parent
commit
db9356ef8b

+ 57
- 0
README.md View File

@@ -75,6 +75,8 @@ https://hub.docker.com/r/tarantool/tarantool
* [tnt_select_limit_max](#tnt_select_limit_max)
* [tnt_allowed_spaces](#tnt_allowed_spaces)
* [tnt_allowed_indexes](#tnt_allowed_indexes)
* [tnt_update](#tnt_update)
* [tnt_upsert](#tnt_upsert)
* [Performance tuning](#performance-tuning)
* [Examples](#examples)
* [Copyright & license](#copyright--license)
@@ -904,15 +906,21 @@ A good example is:
HTTP GET ... /url?space_id=512&value=some+string
it could be matched by using the following format 'space_id=%%space_id,value=%s'
```
Also this works with HTTP forms, i.e. HTTP POST, HTTP PUT and so on.
Here is a full list of {FMT_TYPE} types:
```
TUPLES
%n - int64
%f - float
%d - double
%s - string
%b - boolean
Special types
%%space_id - space_id
%%idx_id - index_id
%%off - [select](#tnt_select) offset
@@ -920,6 +928,23 @@ Here is a full list of {FMT_TYPE} types:
%%it - [select](#tnt_select) iterator type, allowed values are:
eq,req,all,lt,le,ge,gt,all_set,any_set,
all_non_set,overlaps,neighbor
KEYS (for UPDATE)
%kn - int64
%kf - float
%kd - double
%ks - string
%kb - boolean
Operations (for UPSERT)
%on - int64
%of - float
%od - double
%os - string
%ob - boolean
```
Examples can be found at:
@@ -1059,6 +1084,38 @@ This directive works like [tnt_allowed_spaces], but for indexes.
[Back to contents](#contents)
tnt_update
----------
**syntax:** *tnt_update [SIZE or off] [KEYS] [FMT]*
**default:** *None*
**context:** *location, location if*
This directive allows executing an update query with Tarantool.
* The first argument is a space id.
* The second argument is a [KEYS (for UPDATE)](#format) string.
* The third argument is a [format](#format) string.
[Back to contents](#contents)
tnt_upsert
----------
**syntax:** *tnt_upsert [SIZE or off] [FMT] [OPERATIONS]*
**default:** *None*
**context:** *location, location if*
This directive allows executing an upsert query with Tarantool.
* The first argument is a space id.
* The second argument is a [format](#format) string.
* The third argument is a [OPERATIONS (for UPSERT)](#format) string.
[Back to contents](#contents)
## Examples
-----------

+ 1
- 1
src/debug.h View File

@@ -27,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2015-2017 Tarantool AUTHORS:
* Copyright (C) 2015-2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/


+ 2
- 1
src/json_encoders.c View File

@@ -1,3 +1,4 @@

/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
@@ -26,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2016 Tarantool AUTHORS:
* Copyright (C) 2016 - 2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/


+ 2
- 1
src/json_encoders.h View File

@@ -1,3 +1,4 @@

/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
@@ -26,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2016 Tarantool AUTHORS:
* Copyright (C) 2016 - 2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/


+ 544
- 74
src/ngx_http_tnt_module.c View File

@@ -27,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2015-2017 Tarantool AUTHORS:
* Copyright (C) 2015-2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/

@@ -68,11 +68,16 @@ struct ngx_http_tnt_header_val_s {


typedef struct ngx_http_tnt_prepare_result {
ngx_str_t in;
ngx_uint_t limit;
ngx_uint_t offset;
ngx_uint_t index_id;
ngx_uint_t space_id;
ngx_uint_t iter_type;
ngx_uint_t update_keys_count;
ngx_uint_t update_operation_count;
ngx_uint_t upsert_tuple_count;
ngx_uint_t upsert_operations_count;
} ngx_http_tnt_prepare_result_t;


@@ -80,6 +85,8 @@ typedef struct ngx_http_tnt_format_value {
ngx_str_t name;
ngx_str_t value;
enum tp_type type;
ngx_int_t update_key:1;
ngx_int_t upsert_op:1;
} ngx_http_tnt_format_value_t;


@@ -306,6 +313,10 @@ static char *ngx_http_tnt_replace_add(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_tnt_delete_add(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_tnt_update_add(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_tnt_upsert_add(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_tnt_read_array_of_uint(ngx_pool_t *pool,
ngx_array_t *arr, ngx_str_t *str, u_char sep);
static ngx_int_t ngx_http_tnt_test_allowed(ngx_array_t *arr, ngx_uint_t with);
@@ -370,6 +381,8 @@ static ngx_int_t ngx_http_tnt_output(ngx_http_request_t *r,
* Functions are existing for helping to conversation between HTTP
* and MsgPack.
*/
static ngx_int_t ngx_http_tnt_format_read_input(ngx_http_request_t *r,
ngx_str_t *dst);
static char *ngx_http_tnt_format_compile(ngx_conf_t *cf,
ngx_http_tnt_loc_conf_t *conf, ngx_str_t *format);
static ngx_int_t ngx_http_tnt_format_init(ngx_http_tnt_loc_conf_t *conf,
@@ -378,8 +391,14 @@ static ngx_int_t ngx_http_tnt_format_prepare(ngx_http_tnt_loc_conf_t *conf,
ngx_http_request_t *r, ngx_http_tnt_prepare_result_t *prepare_result);
static ngx_int_t ngx_http_tnt_format_prepare_values(ngx_http_request_t *r,
ngx_str_t *key, ngx_str_t *value);
static u_char * ngx_http_tnt_read_next(ngx_str_t *str, u_char sep);
static ngx_int_t ngx_http_tnt_format_bind_bad_request(ngx_http_request_t *r,
ngx_str_t *name, const char *msg);
static ngx_int_t ngx_http_tnt_format_bind_operation(ngx_http_request_t *r,
struct tp *tp, ngx_str_t *name, ngx_str_t *val);

static ngx_int_t ngx_http_tnt_format_bind(ngx_http_request_t *r,
struct tp *tp);
ngx_http_tnt_prepare_result_t *prepare_result, struct tp *tp);

/** Module's objects {{{
*/
@@ -584,6 +603,20 @@ static ngx_command_t ngx_http_tnt_commands[] = {
0,
NULL },

{ ngx_string("tnt_update"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE3,
ngx_http_tnt_update_add,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },

{ ngx_string("tnt_upsert"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE3,
ngx_http_tnt_upsert_add,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },

{ ngx_string("tnt_select_limit_max"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
@@ -982,6 +1015,7 @@ ngx_http_tnt_insert_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_http_tnt_loc_conf_t *tlcf = conf;

ngx_str_t *value;
ngx_int_t tmp;

if (tlcf->req_type != 0 && tlcf->req_type != NGX_CONF_UNSET_SIZE) {
return "is duplicate";
@@ -995,7 +1029,12 @@ ngx_http_tnt_insert_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
tlcf->space_id = 0;
}
else {
tlcf->space_id = (ngx_uint_t) atoi((const char *) value[1].data);

tmp = ngx_atoi(value[1].data, value[1].len);
if (tmp < 0) {
return "space id should be non negative number";
}
tlcf->space_id = (ngx_uint_t) tmp;
}
return ngx_http_tnt_format_compile(cf, tlcf, &value[2]);
}
@@ -1041,6 +1080,7 @@ ngx_http_tnt_select_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_tnt_loc_conf_t *tlcf = conf;

ngx_int_t tmp;
ngx_str_t *value;

if (tlcf->req_type != 0 && tlcf->req_type != NGX_CONF_UNSET_SIZE) {
@@ -1054,18 +1094,37 @@ ngx_http_tnt_select_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strcmp(value[1].data, "off") == 0) {
tlcf->space_id = 0;
} else {
tlcf->space_id = (ngx_uint_t) atoi((const char *) value[1].data);

tmp = ngx_atoi(value[1].data, value[1].len);
if (tmp < 0) {
return "space id should be non negative number";
}
tlcf->space_id = (ngx_uint_t) tmp;
}

if (ngx_strcmp(value[2].data, "off") == 0) {
tlcf->index_id = 0;
} else {
tlcf->index_id = (ngx_uint_t) atoi((const char *) value[2].data);

tmp = ngx_atoi(value[2].data, value[2].len);
if (tmp < 0) {
return "index id should be non negative number";
}
tlcf->index_id = (ngx_uint_t) tmp;
}

tmp = ngx_atoi(value[3].data, value[3].len);
if (tmp < 0) {
return "select offset should be non negative number";
}
tlcf->select_offset = (ngx_uint_t) tmp;

tlcf->select_offset = (ngx_uint_t) atoi((const char *) value[3].data);

tlcf->select_limit = (ngx_uint_t) atoi((const char *) value[4].data);
tmp = ngx_atoi(value[4].data, value[4].len);
if (tmp < 0) {
return "select limit should be non negative number";
}
tlcf->select_limit = (ngx_uint_t) tmp;

if (ngx_http_tnt_set_iter_type(&value[5], &tlcf->iter_type) != NGX_OK) {
return "unknown iterator type, allowed "
@@ -1074,6 +1133,7 @@ ngx_http_tnt_select_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}

return ngx_http_tnt_format_compile(cf, tlcf, &value[6]);

}


@@ -1082,6 +1142,7 @@ ngx_http_tnt_replace_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_tnt_loc_conf_t *tlcf = conf;

ngx_int_t tmp;
ngx_str_t *value;

if (tlcf->req_type != 0 && tlcf->req_type != NGX_CONF_UNSET_SIZE) {
@@ -1094,7 +1155,12 @@ ngx_http_tnt_replace_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strcmp(value[1].data, "off") == 0) {
tlcf->space_id = 0;
} else {
tlcf->space_id = (ngx_uint_t) atoi((const char *) value[1].data);

tmp = ngx_atoi(value[1].data, value[1].len);
if (tmp < 0) {
return "space id should be non negative number";
}
tlcf->space_id = (ngx_uint_t) tmp;
}

return ngx_http_tnt_format_compile(cf, tlcf, &value[2]);
@@ -1106,6 +1172,7 @@ ngx_http_tnt_delete_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_tnt_loc_conf_t *tlcf = conf;

ngx_int_t tmp;
ngx_str_t *value;

if (tlcf->req_type != 0 && tlcf->req_type != NGX_CONF_UNSET_SIZE) {
@@ -1119,15 +1186,103 @@ ngx_http_tnt_delete_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strcmp(value[1].data, "off") == 0) {
tlcf->space_id = 0;
} else {
tlcf->space_id = (ngx_uint_t) atoi((const char *) value[1].data);

tmp = ngx_atoi(value[1].data, value[1].len);
if (tmp < 0) {
return "space id should be non negative number";
}
tlcf->space_id = (ngx_uint_t) tmp;
}

if (ngx_strcmp(value[2].data, "off") == 0) {
tlcf->index_id = 0;
} else {
tlcf->index_id = (ngx_uint_t) atoi((const char *) value[2].data);

tmp = ngx_atoi(value[2].data, value[2].len);
if (tmp < 0) {
return "index id should be non negative number";
}
tlcf->index_id = (ngx_uint_t) tmp;
}

return ngx_http_tnt_format_compile(cf, tlcf, &value[3]);
}


static char *
ngx_http_tnt_update_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_tnt_loc_conf_t *tlcf = conf;

ngx_int_t tmp;
ngx_str_t *value;
char *ok;

if (tlcf->req_type != 0 && tlcf->req_type != NGX_CONF_UNSET_SIZE) {
return "is duplicate";
}

tlcf->req_type = (ngx_uint_t) TP_UPDATE;

value = cf->args->elts;

if (ngx_strcmp(value[1].data, "off") == 0) {
tlcf->space_id = 0;
} else {

tmp = ngx_atoi(value[1].data, value[1].len);
if (tmp < 0) {
return "space id should be non negative number";
}
tlcf->space_id = (ngx_uint_t) tmp;
}

/** Keys */
ok = ngx_http_tnt_format_compile(cf, tlcf, &value[2]);
if (ok != NGX_CONF_OK) {
return ok;
}

/** Tuples */
return ngx_http_tnt_format_compile(cf, tlcf, &value[3]);
}


static char *
ngx_http_tnt_upsert_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_tnt_loc_conf_t *tlcf = conf;

ngx_int_t tmp;
ngx_str_t *value;
char *ok;

if (tlcf->req_type != 0 && tlcf->req_type != NGX_CONF_UNSET_SIZE) {
return "is duplicate";
}

tlcf->req_type = (ngx_uint_t) TP_UPSERT;

value = cf->args->elts;

if (ngx_strcmp(value[1].data, "off") == 0) {
tlcf->space_id = 0;
} else {

tmp = ngx_atoi(value[1].data, value[1].len);
if (tmp < 0) {
return "space id should be non negative number";
}
tlcf->space_id = (ngx_uint_t) tmp;
}

/** Tuples */
ok = ngx_http_tnt_format_compile(cf, tlcf, &value[2]);
if (ok != NGX_CONF_OK) {
return ok;
}

/** Operations */
return ngx_http_tnt_format_compile(cf, tlcf, &value[3]);
}

@@ -1233,6 +1388,59 @@ ngx_http_tnt_allowed_indexes_add(ngx_conf_t *cf, ngx_command_t *cmd,
}


static ngx_int_t
ngx_http_tnt_format_read_input(ngx_http_request_t *r, ngx_str_t *dst)
{
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t *body;
ngx_buf_t unparsed_body;
ngx_str_t tmp;

tmp = r->args;

if (r->headers_in.content_length_n > 0) {

unparsed_body.pos = ngx_pnalloc(r->pool,
sizeof(u_char) * r->headers_in.content_length_n + 1);

if (unparsed_body.pos == NULL) {
return NGX_ERROR;
}

unparsed_body.last = unparsed_body.pos;
unparsed_body.start = unparsed_body.pos;
unparsed_body.end = unparsed_body.pos + r->headers_in.content_length_n + 1;

for (body = r->upstream->request_bufs; body; body = body->next) {

b = body->buf;

if (b->in_file) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"in-file buffer found. aborted. "
"consider increasing your 'client_body_buffer_size' "
"setting");
return NGX_ERROR;
}

unparsed_body.last = ngx_copy(unparsed_body.last,
b->pos, b->last - b->pos);
}

tmp.data = unparsed_body.start;
tmp.len = unparsed_body.last - unparsed_body.start;
}

rc = ngx_http_tnt_unescape_uri(r, dst, &tmp);
if (rc != NGX_OK) {
return rc;
}

return NGX_OK;
}


static char *
ngx_http_tnt_format_compile(ngx_conf_t *cf, ngx_http_tnt_loc_conf_t *conf,
ngx_str_t *format)
@@ -1269,6 +1477,7 @@ ngx_http_tnt_format_compile(ngx_conf_t *cf, ngx_http_tnt_loc_conf_t *conf,
{
fmt_val.name.data = name;
fmt_val.name.len = (ngx_uint_t) (type - name);

name = NULL;

/** Spec. values */
@@ -1288,14 +1497,14 @@ ngx_http_tnt_format_compile(ngx_conf_t *cf, ngx_http_tnt_loc_conf_t *conf,
ngx_strncmp(type, "%%idx_id", sizeof("%%idx_id") - 1) == 0) {

conf->index_id_name = fmt_val.name;

/** Scalar values */
/** Tuple */
} else {

val = ngx_array_push(conf->format_values);
if (val == NULL) {
return NGX_CONF_ERROR;
}

val->name = fmt_val.name;
val->value.len = 0;

@@ -1309,10 +1518,56 @@ ngx_http_tnt_format_compile(ngx_conf_t *cf, ngx_http_tnt_loc_conf_t *conf,
val->type = TP_STR;
} else if (ngx_strncmp(type, "%b", sizeof("%b") - 1) == 0) {
val->type = TP_BOOL;

/* If a type has a prefix '%k' then it is a key for TP_UPDATE
*/
} else if (conf->req_type == TP_UPDATE) {

if (ngx_strncmp(type, "%kn", sizeof("%kn") - 1) == 0) {
val->type = TP_INT;
val->update_key = 1;
} else if (ngx_strncmp(type, "%kd", sizeof("%kd") - 1) == 0) {
val->type = TP_DOUBLE;
val->update_key = 1;
} else if (ngx_strncmp(type, "%kf", sizeof("%kf") - 1) == 0) {
val->type = TP_FLOAT;
val->update_key = 1;
} else if (ngx_strncmp(type, "%ks", sizeof("%ks") - 1) == 0) {
val->type = TP_STR;
val->update_key = 1;
} else if (ngx_strncmp(type, "%kb", sizeof("%kb") - 1) == 0) {
val->type = TP_BOOL;
val->update_key = 1;
} else {
goto unknown_format_error;
}
}
/* If a type has a prefix '%o' then it is an operation for
* TP_UPDATE
*/
else if (conf->req_type == TP_UPSERT) {

if (ngx_strncmp(type, "%on", sizeof("%on") - 1) == 0) {
val->type = TP_INT;
val->upsert_op = 1;
} else if (ngx_strncmp(type, "%od", sizeof("%od") - 1) == 0) {
val->type = TP_DOUBLE;
val->upsert_op = 1;
} else if (ngx_strncmp(type, "%of", sizeof("%of") - 1) == 0) {
val->type = TP_FLOAT;
val->upsert_op = 1;
} else if (ngx_strncmp(type, "%os", sizeof("%os") - 1) == 0) {
val->type = TP_STR;
val->upsert_op = 1;
} else if (ngx_strncmp(type, "%ob", sizeof("%ob") - 1) == 0) {
val->type = TP_BOOL;
val->upsert_op = 1;
} else {
goto unknown_format_error;
}

} else {
return "unknown format has been found, "
"allowed %n,%d,%f,%s,%b,%lim,%off,%it,"
"%space_id,%idx_id";
goto unknown_format_error;
}
}

@@ -1321,6 +1576,11 @@ ngx_http_tnt_format_compile(ngx_conf_t *cf, ngx_http_tnt_loc_conf_t *conf,
}

return NGX_CONF_OK;

unknown_format_error:
return "unknown format has been found, "
"allowed %n,%d,%f,%s,%b,%lim,%off,%it,"
"%space_id,%idx_id,%kn,%kd,%kf,%ks";
}


@@ -1414,12 +1674,15 @@ static ngx_int_t
ngx_http_tnt_format_init(ngx_http_tnt_loc_conf_t *conf,
ngx_http_request_t *r, ngx_http_tnt_prepare_result_t *prepare_result)
{
ngx_int_t rc;
ngx_uint_t i;
ngx_http_tnt_ctx_t *ctx;
ngx_http_tnt_format_value_t *fmt_val, *conf_fmt_val;

ctx = ngx_http_get_module_ctx(r, ngx_http_tnt_module);

memset(prepare_result, 0, sizeof(ngx_http_tnt_prepare_result_t));

prepare_result->space_id = conf->space_id;
prepare_result->index_id = conf->index_id;
prepare_result->limit = conf->select_limit;
@@ -1437,6 +1700,11 @@ ngx_http_tnt_format_init(ngx_http_tnt_loc_conf_t *conf,
return NGX_ERROR;
}

rc = ngx_http_tnt_format_read_input(r, &prepare_result->in);
if (rc != NGX_OK) {
return rc;
}

conf_fmt_val = conf->format_values->elts;

for (i = 0; i < conf->format_values->nelts; ++i) {
@@ -1450,6 +1718,29 @@ ngx_http_tnt_format_init(ngx_http_tnt_loc_conf_t *conf,
fmt_val->type = conf_fmt_val[i].type;
fmt_val->value.data = NULL;
fmt_val->value.len = 0;
fmt_val->update_key = conf_fmt_val[i].update_key;
fmt_val->upsert_op = conf_fmt_val[i].upsert_op;

switch (conf->req_type) {
case TP_UPDATE:

if (fmt_val->update_key) {
++prepare_result->update_keys_count;
} else {
++prepare_result->update_operation_count;
}
break;
case TP_UPSERT:

if (fmt_val->upsert_op) {
++prepare_result->upsert_operations_count;
} else {
++prepare_result->upsert_tuple_count;
}
break;
default:
break;
}
}

return NGX_OK;
@@ -1478,7 +1769,9 @@ ngx_http_tnt_format_prepare(ngx_http_tnt_loc_conf_t *conf,
ngx_http_tnt_loc_conf_t *tlcf;

expects = NOTHING;
arg.it = r->args.data;
arg.it = prepare_result->in.data;
arg_begin = arg.it;
end = arg.it + prepare_result->in.len;
arg.value = NULL;

if (conf->limit_name.len != 0) {
@@ -1501,7 +1794,7 @@ ngx_http_tnt_format_prepare(ngx_http_tnt_loc_conf_t *conf,
expects |= EXPECTS_INDEX_ID;
}

for (arg_begin = arg.it, end = arg.it + r->args.len; arg.it < end; ) {
for (; arg.it < end; ) {

arg = ngx_http_tnt_get_next_arg(arg.it, end);

@@ -1660,16 +1953,116 @@ ngx_http_tnt_format_prepare_values(ngx_http_request_t *r, ngx_str_t *key,
}


static u_char *
ngx_http_tnt_read_next(ngx_str_t *str, u_char sep)
{
u_char *beg = str->data;
u_char *end = str->data + str->len;

for (; str->data < end; ++str->data, --str->len) {

if (*str->data == sep) {
++str->data;
--str->len;
return beg;
}
}

return NULL;
}

static ngx_int_t
ngx_http_tnt_format_bind_bad_request(ngx_http_request_t *r, ngx_str_t *name,
const char *msg)
{
ngx_int_t rc;
u_char err_msg[64];

if (msg == NULL) {
snprintf((char *) err_msg, sizeof(err_msg), "'%.*s' is invalid",
(int) name->len, (char *) name->data);
} else {
snprintf((char *) err_msg, sizeof(err_msg), "'%.*s' is invalid. %s",
(int) name->len, (char *) name->data, msg);
}

rc = ngx_http_tnt_set_err(r, NGX_HTTP_BAD_REQUEST, err_msg,
ngx_strlen(err_msg));
if (rc != NGX_OK) {
return rc;
}

return NGX_HTTP_BAD_REQUEST;
}


static ngx_int_t
ngx_http_tnt_format_bind_operation(ngx_http_request_t *r, struct tp *tp,
ngx_str_t *name, ngx_str_t *val)
{
u_char *update_operation, *filedno_pt, *filedno_pt_end;
char filedno_s[sizeof("1867996680")];

update_operation = ngx_http_tnt_read_next(val, ',');

if (update_operation == NULL) {
goto no_operation_type;
}

switch (*update_operation) {
case '=':
case '!':
case '+':
case '-':
case '&':
case '^':
case '|':
break;
default:
goto no_operation_type;
}

filedno_pt = ngx_http_tnt_read_next(val, ',');
filedno_pt_end = (val->data - 1);

if (filedno_pt == NULL || (size_t) (filedno_pt_end - filedno_pt) == 0) {
goto no_fieldno;
}

snprintf(filedno_s, sizeof(filedno_s), "%.*s",
(int) (filedno_pt_end - filedno_pt), (char *) filedno_pt);

if (tp_op(tp, *update_operation, (uint32_t) atoi(filedno_s)) == NULL) {
return NGX_ERROR;
}

return NGX_OK;

no_operation_type:
return ngx_http_tnt_format_bind_bad_request( r, name,
"Operation type is expecting");

no_fieldno:
return ngx_http_tnt_format_bind_bad_request( r, name,
"Fieldno is expecting");
}


static ngx_int_t
ngx_http_tnt_format_bind(ngx_http_request_t *r, struct tp *tp)
ngx_http_tnt_format_bind(ngx_http_request_t *r,
ngx_http_tnt_prepare_result_t *prepare_result, struct tp *tp)
{
ngx_int_t rc;
ngx_str_t unescaped_value;
ngx_str_t *value;
ngx_uint_t i;
ngx_http_tnt_format_value_t *fmt_val;
ngx_http_tnt_ctx_t *ctx;
u_char err_msg[32];
ngx_http_tnt_loc_conf_t *tlcf;
ngx_int_t update_started;
ngx_int_t upsert_add_ops_started;
char num_buf[16];

tlcf = ngx_http_get_module_loc_conf(r, ngx_http_tnt_module);

ctx = ngx_http_get_module_ctx(r, ngx_http_tnt_module);

@@ -1677,6 +2070,9 @@ ngx_http_tnt_format_bind(ngx_http_request_t *r, struct tp *tp)
return NGX_OK;
}

update_started = 0;
upsert_add_ops_started = 0;

fmt_val = ctx->format_values->elts;

for (i = 0; i < ctx->format_values->nelts; i++) {
@@ -1684,18 +2080,62 @@ ngx_http_tnt_format_bind(ngx_http_request_t *r, struct tp *tp)
value = &fmt_val[i].value;

if (value->len == 0 && value->data == NULL) {
return ngx_http_tnt_format_bind_bad_request(r,
&fmt_val[i].name, "Value is null.");
}

snprintf((char *) err_msg, sizeof(err_msg), "invalid '%.*s'",
(int) fmt_val[i].name.len, (char *) fmt_val[i].name.data);
/** Update {{{ */
if (tlcf->req_type == TP_UPDATE) {

rc = ngx_http_tnt_set_err(r, NGX_HTTP_BAD_REQUEST, err_msg,
ngx_strlen(err_msg));
if (rc != NGX_OK) {
return rc;
if (!update_started && !fmt_val[i].update_key) {

if (tp_updatebegin(tp,
(uint32_t) prepare_result->update_operation_count)
== NULL)
{
goto oom;
}

update_started = 1;
}

return NGX_HTTP_BAD_REQUEST;
if (update_started) {

rc = ngx_http_tnt_format_bind_operation(r, tp,
&fmt_val[i].name, value);
if (rc == NGX_ERROR) {
goto oom;
} else if (rc != NGX_OK) {
return rc;
}
}
}
/* }}} */

/** Upsert {{{ */
if (tlcf->req_type == TP_UPSERT && fmt_val[i].upsert_op) {

if (!upsert_add_ops_started) {

if (tp_upsertbegin_add_ops(tp,
(uint32_t) prepare_result->upsert_operations_count)
== NULL)
{
goto oom;
}

upsert_add_ops_started = 1;
}

rc = ngx_http_tnt_format_bind_operation(r, tp,
&fmt_val[i].name, value);
if (rc == NGX_ERROR) {
goto oom;
} else if (rc != NGX_OK) {
return rc;
}
}
/* }}} */

switch (fmt_val[i].type) {
case TP_BOOL:
@@ -1715,58 +2155,50 @@ ngx_http_tnt_format_bind(ngx_http_request_t *r, struct tp *tp)
}
}
else {

snprintf((char *)err_msg, sizeof(err_msg), "invalid '%.*s'",
(int) fmt_val[i].name.len, (char *) fmt_val[i].name.data);

rc = ngx_http_tnt_set_err(r, NGX_HTTP_BAD_REQUEST, err_msg,
ngx_strlen(err_msg));
if (rc != NGX_OK) {
return rc;
}

return NGX_HTTP_BAD_REQUEST;
return ngx_http_tnt_format_bind_bad_request(
r, &fmt_val[i].name, "True/False is expecting.");
}

break;
case TP_INT:

snprintf(num_buf, sizeof(num_buf), "%.*s",
(int) value->len, (char *) value->data);

if (*value->data == '-') {

if (tp_encode_int(tp,
(int64_t) atoll((const char *) value->data)) == NULL)
{
if (tp_encode_int(tp, (int64_t) atoll(num_buf)) == NULL) {
goto oom;
}

} else if (tp_encode_uint(tp,
(int64_t) atoll((const char *) value->data)) ==
NULL)
{
} else if (tp_encode_uint(tp, (int64_t) atoll(num_buf)) == NULL) {
goto oom;
}

break;
case TP_DOUBLE:
if (tp_encode_double(tp, atof((const char *) value->data)) ==
NULL)
{

snprintf((char *) num_buf, sizeof(num_buf), "%.*s",
(int) value->len, (char *) value->data);

if (tp_encode_double(tp, atof(num_buf)) == NULL) {
goto oom;
}

break;
case TP_FLOAT:
if (tp_encode_double(tp,
(float) atof((const char *) value->data)) ==
NULL)
{

snprintf((char *) num_buf, sizeof(num_buf), "%.*s",
(int) value->len, (char *) value->data);

if (tp_encode_double(tp, (float) atof(num_buf)) == NULL) {
goto oom;
}

break;
case TP_STR:
rc = ngx_http_tnt_unescape_uri(r, &unescaped_value, value);
if (rc != NGX_OK) {
goto oom;
}
if (tp_encode_str(tp, (const char *) unescaped_value.data,
unescaped_value.len) == NULL)
if (tp_encode_str(tp, (const char *) value->data,
value->len) == NULL)
{
goto oom;
}
@@ -2026,26 +2458,32 @@ static ngx_int_t
ngx_http_tnt_filter_reply(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_buf_t *b)
{
ngx_int_t rc;

ngx_http_tnt_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_tnt_module);
ssize_t bytes = b->last - b->pos;

dd("filter_reply -> recv bytes: %i, rest: %i", (int)bytes, (int)ctx->rest);
dd("filter_reply -> recv bytes: %i, rest: %i",
(int) bytes, (int) ctx->rest);

if (ctx->state == READ_PAYLOAD) {

ssize_t payload_rest = ngx_min(ctx->payload.e - ctx->payload.p, bytes);

if (payload_rest > 0) {

ctx->payload.p = ngx_copy(ctx->payload.p, b->pos, payload_rest);
bytes -= payload_rest;
b->pos += payload_rest;
payload_rest = ctx->payload.e - ctx->payload.p;

dd("filter_reply -> payload rest:%i", (int)payload_rest);
dd("filter_reply -> payload rest:%i", (int) payload_rest);
}

if (payload_rest == 0) {
ctx->payload_size = tp_read_payload((char *)&ctx->payload.mem[0],
(char *)ctx->payload.e);

ctx->payload_size = tp_read_payload((char *) &ctx->payload.mem[0],
(char *) ctx->payload.e);
if (ctx->payload_size <= 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"[BUG] tp_read_payload failed, ret:%i",
@@ -2056,8 +2494,8 @@ ngx_http_tnt_filter_reply(ngx_http_request_t *r, ngx_http_upstream_t *u,
ctx->rest = ctx->payload_size - 5 /* - header size */;

dd("filter_reply -> got header payload:%i, rest:%i",
(int)ctx->payload_size,
(int)ctx->rest);
(int) ctx->payload_size,
(int) ctx->rest);

ctx->tp_cache = ngx_create_temp_buf(r->pool, ctx->payload_size);
if (ctx->tp_cache == NULL) {
@@ -2075,12 +2513,14 @@ ngx_http_tnt_filter_reply(ngx_http_request_t *r, ngx_http_upstream_t *u,
ctx->payload.p = &ctx->payload.mem[0];

ctx->state = READ_BODY;

} else {
return NGX_OK;
}
}

ngx_int_t rc = NGX_OK;
rc = NGX_OK;

if (ctx->state == READ_BODY) {

ssize_t rest = ctx->rest - bytes, read_on = bytes;
@@ -2260,7 +2700,8 @@ static size_t
ngx_http_tnt_get_output_size(ngx_http_request_t *r, ngx_http_tnt_ctx_t *ctx,
ngx_http_tnt_loc_conf_t *tlcf, ngx_buf_t *request_b)
{
(void)ctx;
(void) ctx;

size_t output_size = ngx_http_tnt_overhead();

if (r->headers_in.content_length_n > 0) {
@@ -2520,7 +2961,9 @@ ngx_http_tnt_get_next_arg(u_char *it, u_char *end)

for ( ; it != end; ++it) {

if (*it == '=') {
if (next_arg.value == NULL /* CASE: ARG==.. */ &&
*it == '=')
{
next_arg.value = it + 1;
continue;
} else if (*it == '&') {
@@ -2834,8 +3277,8 @@ ngx_http_tnt_get_request_data(ngx_http_request_t *r,
b->pos, b->last - b->pos);
}

/** Actually this is array not map, I used this variable since it's
* avaliable
/** Actually this is an array not a map, I used this variable
* since it's avaliable in this scope
*/
map_items = 0;

@@ -3364,7 +3807,6 @@ ngx_http_tnt_dml_handler(ngx_http_request_t *r)
}
break;
case TP_SELECT:

if (tp_select(&tp, (uint32_t) prepare_result.space_id,
(uint32_t) prepare_result.index_id,
prepare_result.offset, prepare_result.iter_type,
@@ -3374,12 +3816,28 @@ ngx_http_tnt_dml_handler(ngx_http_request_t *r)
goto cant_issue_request;
}
break;
case TP_UPDATE:
if (tp_update(&tp, (uint32_t) prepare_result.space_id,
(uint32_t) prepare_result.index_id) == NULL ||
tp_key(&tp, (uint32_t) prepare_result.update_keys_count) == NULL)
{
goto cant_issue_request;
}
break;
case TP_UPSERT:
if (tp_upsert(&tp, (uint32_t) prepare_result.space_id) == NULL ||
tp_tuple(&tp, (uint32_t) prepare_result.upsert_tuple_count)
== NULL)
{
goto cant_issue_request;
}
break;
default:
goto cant_issue_request;
}

/** Bind values to the request */
rc = ngx_http_tnt_format_bind(r, &tp);
/** Bind values */
rc = ngx_http_tnt_format_bind(r, &prepare_result, &tp);

if (rc != NGX_OK) {

@@ -3521,6 +3979,8 @@ ngx_http_tnt_set_err(ngx_http_request_t *r, int errcode, const u_char *msg,
"}"
"}");

u_char escaped_msg[len * 2];
u_char *p;
ngx_http_tnt_ctx_t *ctx;
ngx_buf_t *b;

@@ -3535,14 +3995,24 @@ ngx_http_tnt_set_err(ngx_http_request_t *r, int errcode, const u_char *msg,
b->memory = 1;
b->pos = b->start;

p = &escaped_msg[0];
memset(escaped_msg, 0, sizeof(escaped_msg));
const char *x;
if ((x = json_encode_string((char **) &p, sizeof(escaped_msg) - 1,
(const char *) msg, (size_t) len, true))
!= NULL)
{
escaped_msg[0] = 0;
}

b->last = ngx_snprintf(b->start, msglen, "{"
"\"error\":{"
"\"code\":%d,"
"\"message\":\"%s\""
"\"message\":%s"
"}"
"}",
errcode,
msg);
(escaped_msg[0] == 0 ? (u_char *) "\"\"" : escaped_msg));

ctx->in_err = b;


+ 3
- 2
src/ngx_http_tnt_version.h View File

@@ -1,3 +1,4 @@

/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
@@ -26,13 +27,13 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2015-2016 Tarantool AUTHORS:
* Copyright (C) 2015-2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/

#ifndef NGX_HTTP_TNT_VERSION_H
#define NGX_HTTP_TNT_VERSION_H 1

#define NGX_HTTP_TNT_MODULE_VERSION_STRING "v2.6-rc1"
#define NGX_HTTP_TNT_MODULE_VERSION_STRING "v2.6-rc2"

#endif

+ 2
- 1
src/ngx_http_tnt_version.h.in View File

@@ -1,3 +1,4 @@

/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
@@ -26,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2015-2016 Tarantool AUTHORS:
* Copyright (C) 2015-2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/


+ 1
- 1
src/tp_ext.h View File

@@ -27,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2015-2016 Tarantool AUTHORS:
* Copyright (C) 2015-2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/


+ 8
- 7
src/tp_transcode.c View File

@@ -27,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2015-2016 Tarantool AUTHORS:
* Copyright (C) 2015-2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/

@@ -734,12 +734,13 @@ yajl_json2tp_transcode(void *ctx, const char *input, size_t input_size)

yajl_ctx_t *s_ctx = (yajl_ctx_t *)ctx;

/* Some yajl versions does not properly handle(SAX) [] and {}
* So! PWN this via bool flag and some checks
/* Some versions of YAJL does not handle properly [] and {}.
* So, for fixing this I use a bool flag and some extra checks.
*
* NOTE
* Check len(buffer) is wrong.
* We may have a very large buffer which passed to this function by 1-byte.
* Check the 'len(buffer)' is wrong.
* We may have a very large buffer which passed to this function by
* 1-byte.
*/
if (unlikely(!s_ctx->transcode_first_enter)) {
s_ctx->transcode_first_enter = true;
@@ -761,11 +762,11 @@ yajl_json2tp_transcode(void *ctx, const char *input, size_t input_size)
stat = yajl_complete_parse(s_ctx->hand);
unsigned char *err = yajl_get_error(s_ctx->hand, 0,
input_, input_size);
const int l = strlen((char *)err) - 1 /* skip \n */;
const int l = strlen((char *) err) - 1 /* skip \n */;
if (l > 0) {
s_ctx->tc->errmsg = ALLOC(s_ctx, l);
if (likely(s_ctx->tc->errmsg != NULL))
say_error_(s_ctx->tc, 0, (char *)err, l);
say_error_(s_ctx->tc, 0, (char *) err, l);
}
yajl_free_error(s_ctx->hand, err);
s_ctx->tc->errcode = -32700;

+ 4
- 1
src/tp_transcode.h View File

@@ -1,3 +1,4 @@

/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
@@ -26,7 +27,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Copyright (C) 2015-2016 Tarantool AUTHORS:
* Copyright (C) 2015-2018 Tarantool AUTHORS:
* please see AUTHORS file.
*/

@@ -37,6 +38,8 @@
#include <stdbool.h>
#include <string.h>

#include <json_encoders.h>

#ifdef __cplusplus
extern "C" {
#endif

+ 1
- 0
t/http_utils.py View File

@@ -174,6 +174,7 @@ def get(url, data, headers):
res = urllib2.urlopen(req)
out = res.read()
out = out + res.read()

rc = res.getcode()
if VERBOSE:
print("code: ", rc, " recv: '", out, "'")

+ 69
- 3
t/ngx_confs/tnt_server_test.conf View File

@@ -7,9 +7,10 @@ http {
default_type application/octet-stream;

client_body_buffer_size 1m;
client_header_buffer_size 1k;
client_header_buffer_size 256k;
large_client_header_buffers 8 1024k;

client_max_body_size 64m;
large_client_header_buffers 2 1k;

client_body_timeout 12;
client_header_timeout 12;
@@ -18,7 +19,7 @@ http {

upstream tnt {
server 127.0.0.1:9999 max_fails=1 fail_timeout=1s;
keepalive 10;
keepalive 20000;
}

server {
@@ -363,7 +364,72 @@ http {
tnt_insert off "s=%%space_id,i=%%idx_id,v=%n";
tnt_pass tnt;
}
location /update_fmt {
tnt_update off "value=%kn" "space_id=%%space_id&string=%s&float=%f&double=%d&bool=%b&int=%n";
tnt_pass tnt;
}
location /delete_mt_fmt {
tnt_delete off off "space_id=%%space_id,index_id=%%idx_id,key=%n,key1=%s";
tnt_pass tnt;
}
location /insert_mt_fmt {
tnt_insert off
"space_id=%%space_id,index_id=%%idx_id&key=%n&key1=%s&string=%s";
tnt_pass tnt;
}
location /update_mt_fmt {
tnt_update off "key=%kn,key1=%ks"
"space_id=%%space_id,index_id=%%idx_id,string=%s";
tnt_pass tnt;
}
location /upsert_fmt {
tnt_upsert off "space_id=%%space_id,key=%n,key1=%s" "string=%os";
tnt_pass tnt;
}
location /select_514 {
tnt_select 514 1 0 100 all "key=%n,key1=%s";
tnt_pass tnt;
}
## ]]]


# Issue - https://github.com/tarantool/nginx_upstream_module/issues/108 [[[
location = /issue_108/ {
add_header Cache-Control "private, no-cache, no-store";
add_header Expires "Thu, 01 Jan 1970 00:00:01 GMT";

tnt_http_rest_methods get;
tnt_pure_result on;
tnt_multireturn_skip_count 2;
tnt_pass_http_request parse_args;
tnt_method "issue_108";
tnt_pass tnt;
tnt_pass_http_request_buffer_size 32k;

access_log off;
}
# ]]]

location /delete_post {
tnt_delete 515 0 "id=%n";
tnt_pass tnt;
}
location /select_post {
tnt_select 515 0 0 100 ge "id=%n";
tnt_pass tnt;
}
location /insert_post {
tnt_replace 515 "id=%n";
tnt_pass tnt;
}
location /replace_post {
tnt_replace 515 "id=%n";
tnt_pass tnt;
}
location /update_post {
tnt_update 515 "id=%kn" "value=%s,value1=%f";
tnt_pass tnt;
}

}
}

+ 8
- 5
t/run_all.sh View File

@@ -5,9 +5,8 @@ set -e #-x
WORK_DIR=$PWD/t
NGINX_PREFIX=$PWD/test-root

## Tests
echo "[+] Testing ..."

echo "[+] Basic test"
for i in {1..10}; do
echo "[+] try: $i"
$WORK_DIR/basic_features.py 1> /dev/null || (
@@ -32,7 +31,10 @@ for i in {1..10}; do
echo "[-] $WORK_DIR/v26_features.py failed" && exit 1
)
done
echo '[+] OK'


echo '[+] Parallel clients'
clients_pids=
for i in {1..3}; do
`$WORK_DIR/basic_features.py 1> /dev/null || (
@@ -59,13 +61,14 @@ for i in {1..3}; do
) &

# XXX It couldn't be work in parallel
$WORK_DIR/v26_features.py 1> /dev/null || (
echo "[-] $WORK_DIR/v26_features.py failed" && exit 1
)
#$WORK_DIR/v26_features.py 1> /dev/null || (
# echo "[-] $WORK_DIR/v26_features.py failed" && exit 1
# )

clients_pids="$clients_pids $!"
done
for job in $clients_pids; do
wait $job
done
echo '[+] OK'


+ 11
- 0
t/test.lua View File

@@ -187,6 +187,11 @@ function error_if_escaped(req)
return true
end

-- Issue - https://github.com/tarantool/nginx_upstream_module/issues/108
function issue_108(req)
return req
end

-- CFG
box.cfg {
log_level = 5,
@@ -206,3 +211,9 @@ t:create_index('pk', {if_not_exists=true})
t = box.schema.space.create('t2', {if_not_exists=true})
t:create_index('pk', {if_not_exists=true})

t = box.schema.space.create('t3', {if_not_exists=true})
t:create_index('pk', {if_not_exists=true})
i = t:create_index('sk', {parts={1,'unsigned', 2, 'str'}, if_not_exists=true})

t = box.schema.space.create('t4', {if_not_exists=true})
t:create_index('pk', {if_not_exists=true})

+ 165
- 1
t/v26_features.py View File

@@ -13,8 +13,8 @@ def arr_of_dicts_to_arr(arr_of_dicts, start_from = 0):
res.append(k.values()[0])
return res[start_from:]

print ('[+] basic insert')

print ('[+] basic insert')
result = get_success(BASE_URL + '/delete', {'index': 1}, None, False)
assert 'result' in result and 'id' in result, 'expected: result and id'
result = get_success(BASE_URL + '/delete', {'index': 2}, None, False)
@@ -289,3 +289,167 @@ rc, result = get(BASE_URL + '/insert_ext_fmt', data, None)
assert rc == 400, 'Expected 400'
print ('[+] OK')

print ('[+] basic update')

result = get_success(BASE_URL + '/delete', {'index': 1}, None, False)
assert 'result' in result and 'id' in result, 'expected: result and id'

insert_1 = [
{'index': 1},
{'string': 'some big string'},
{'float': 2.1},
{'double': 3.1},
{'bool': True},
{'int': -1000}
]
result = get_success(BASE_URL + '/insert', insert_1, None)
assert result == arr_of_dicts_to_arr(insert_1), "Expected != result"

update = [
{'space_id': 512},
{'value': 1},
{'string': '=,1,some XXXX big string'},
{'float': '-,2,2.1'},
{'double': '=,3,4.1'},
{'bool': '=,4,false'},
{'int': '%2B,5,1001'}
]

expected = [
{'value': 1},
{'string': 'some XXXX big string'},
{'float': 0},
{'double': 4.1},
{'bool': False},
{'int': 1}
]

result = get_success(BASE_URL + '/update_fmt', update, None)
assert result == arr_of_dicts_to_arr(expected), "Expected != result"

print ('[+] OK')


print ('[+] Update multipart index')

result = get_success(BASE_URL + '/delete_mt_fmt', {
'space_id': 514,
'index_id': 1,
'key': 1,
'key1': 'str',
}, None, False)
assert 'result' in result and 'id' in result, 'expected: result and id'

insert_1 = [
{'space_id': 514},
{'index_id': 1},
{'key': 1},
{'key1': 'str'},
{'string': 'some big string'}
]
result = get_success(BASE_URL + '/insert_mt_fmt', insert_1, None)
assert result == arr_of_dicts_to_arr([{'key': 1}, {'key1': 'str'}, \
{'string': 'some big string'} ]),\
"Expected != result"

update = [
{'space_id': 514},
{'index_id': 1},
{'key': 1},
{'key1': 'str'},
{'string': '=,2,updated string'}
]

expected = [
{'key': 1},
{'key1': 'str'},
{'string': 'updated string'}
]

result = get_success(BASE_URL + '/update_mt_fmt', update, None)
assert result == arr_of_dicts_to_arr(expected), "Expected != result"

print ('[+] OK')


print ('[+] Upsert')

upsert = [
{'space_id': 514},
{'key': 2},
{'key1': 'str2'},
{'string': '=,2,Text'}
]

result = get_success(BASE_URL + '/upsert_fmt', upsert, None, False)
assert 'result' in result and 'id' in result, 'expected: result and id'

upsert = [
{'space_id': 514},
{'key': 2},
{'key1': 'str2'},
{'string': '=,2,'}
]

result = get_success(BASE_URL + '/upsert_fmt', upsert, None, False)
assert 'result' in result and 'id' in result, 'expected: result and id'

expected = [
{'key': 2},
{'key1': 'str2'},
{'string': ''}
]

result = get_success(BASE_URL + '/select_514', {'key':2, 'key1': 'str2'}, None)
assert result == arr_of_dicts_to_arr(expected), "Expected != result"

print ('[+] OK')


print ('[+] Issue - https://github.com/tarantool/nginx_upstream_module/issues/108')

data = []
for i in range(1, 300):
e = {}
e['key' + str(i)] = i
data.append(e)
result = get_success(BASE_URL + '/issue_108', data, None, False)
assert 'headers' in result, "Expected != result"

print ('[+] OK')


print ('[+] Post form')

post_form_success(BASE_URL + '/delete_post', {'id': 11}, None)
post_form_success(BASE_URL + '/delete_post', {'id': 12}, None)
post_form_success(BASE_URL + '/insert_post', {'id': 11}, None)
post_form_success(BASE_URL + '/insert_post', {'id': 12}, None)
result = post_form_success(BASE_URL + '/select_post', {'id': 11}, None)
assert result['result'][0][0] == 11 and result['result'][1][0] == 12, \
'Expected != result'
result = post_form_success(BASE_URL + '/update_post', {'id': 12, 'value': '=,1,TEXT',
'value1': '=,2,3.14'}, None)
assert result['result'][0][0] == 12 and result['result'][0][1] == 'TEXT' \
and result['result'][0][2] == 3.14, 'Expected != result'

print ('[+] OK')


print ('[+] Update format validation')

_, result = post_form(BASE_URL + '/update_post', {'id': 12, 'value': '=,TEXT',
'value1': '=,2,3.14'}, None)
assert _ == 400 and 'error' in result, 'Expected != result'

_, result = post_form(BASE_URL + '/update_post', {'id': 12, 'value': 'TEXT',
'value1': '=,2,3.14'}, None)
assert _ == 400 and 'error' in result, 'Expected != result'
_, result = post_form(BASE_URL + '/update_post', {'id': 12, 'value': '=,,TEXT',
'value1': '=,2,3.14'}, None)
assert _ == 400 and 'error' in result, 'Expected != result'
_, result = post_form(BASE_URL + '/update_post', {'id': 12, 'value': '=,TEXT',
'value1': '=,2,3.14'}, None)
assert _ == 400 and 'error' in result, 'Expected != result'

print ('[+] OK')

+ 58
- 4
third_party/tp.h View File

@@ -100,6 +100,7 @@ enum tp_request_type {
TP_REPLACE = 3,
TP_UPDATE = 4,
TP_DELETE = 5,
TP_UPSERT = 9,
TP_CALL = 10,
TP_AUTH = 7,
TP_EVAL = 8,
@@ -383,7 +384,7 @@ tp_delete(struct tp *p, uint32_t space, uint32_t index);
* tp_sz(&req, "value"); // .. a value "value"
*/
static inline char *
tp_update(struct tp *p, uint32_t space);
tp_update(struct tp *p, uint32_t space, uint32_t index);

/**
* Begin update operations.
@@ -392,6 +393,18 @@ tp_update(struct tp *p, uint32_t space);
static inline char *
tp_updatebegin(struct tp *p, uint32_t op_count);

/**
* Create an upsert request.
*/
static inline char *
tp_upsert(struct tp *p, uint32_t space);

/**
* Upsert. Begin add operations.
*/
static inline char *
tp_upsertbegin_add_ops(struct tp *p, uint32_t op_count);

/**
* Add an update operation.
* See tp_update description.
@@ -1172,23 +1185,64 @@ tp_eval(struct tp *p, const char *expr, int len)
* tp_sz(&req, "value"); // .. a value "value"
*/
static inline char *
tp_update(struct tp *p, uint32_t space)
tp_update(struct tp *p, uint32_t space, uint32_t index)
{
int hsz = tpi_sizeof_header(TP_UPDATE);
int sz = mp_sizeof_map(3) +
int sz = mp_sizeof_map(4) +
mp_sizeof_uint(TP_SPACE) +
mp_sizeof_uint(space) +
mp_sizeof_uint(TP_INDEX) +
mp_sizeof_uint(index) +
mp_sizeof_uint(TP_KEY);
if (tpunlikely(tp_ensure(p, sz) == -1))
return NULL;
char *h = tpi_encode_header(p, TP_UPDATE);
h = mp_encode_map(h, 3);
h = mp_encode_map(h, 4);
h = mp_encode_uint(h, TP_SPACE);
h = mp_encode_uint(h, space);
h = mp_encode_uint(h, TP_INDEX);
h = mp_encode_uint(h, index);
h = mp_encode_uint(h, TP_KEY);
return tp_add(p, sz + hsz);
}


/**
* Create an upsert request.
*/
static inline char *
tp_upsert(struct tp *p, uint32_t space)
{
int hsz = tpi_sizeof_header(TP_UPSERT);
int sz = mp_sizeof_map(3) +
mp_sizeof_uint(TP_SPACE) +
mp_sizeof_uint(space) +
mp_sizeof_uint(TP_TUPLE);
if (tpunlikely(tp_ensure(p, sz) == -1))
return NULL;
char *h = tpi_encode_header(p, TP_UPSERT);
h = mp_encode_map(h, 3);
h = mp_encode_uint(h, TP_SPACE);
h = mp_encode_uint(h, space);
h = mp_encode_uint(h, TP_TUPLE);
return tp_add(p, sz + hsz);

}

/**
* Upsert. Begin add operations.
*/
static inline char *
tp_upsertbegin_add_ops(struct tp *p, uint32_t op_count)
{
int sz = mp_sizeof_uint(TP_OPS) + mp_sizeof_array(op_count);
if (tpunlikely(tp_ensure(p, sz) == -1))
return NULL;
char *h = mp_encode_uint(p->p, TP_OPS);
mp_encode_array(h, op_count);
return tp_add(p, sz);
}

/**
* Begin update operations.
* See tp_update description for details.

Loading…
Cancel
Save