[Tarantool-patches] [PATCH v1 2/2] sql: introduce syntax for MAP values
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Sat Nov 20 03:46:57 MSK 2021
Thanks for the patch!
See 7 comments below.
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 74a98c550..789d8906c 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -3432,6 +3432,35 @@ expr_code_array(struct Parse *parser, struct Expr *expr, int reg)
> sqlVdbeAddOp3(vdbe, OP_Array, count, reg, values_reg);
> }
>
> +static void
> +expr_code_map(struct Parse *parser, struct Expr *expr, int reg)
1. I thought the policy was that we name functions, generating VDBE code,
using 'emit' suffix. For instance, `vdbe_emit_map()` or `sql_emit_map()`.
Don't know about prefix though. I see both vdbe_ and sql_ are used.
> diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
> index b598fe5c2..fe7029341 100644
> --- a/src/box/sql/mem.c
> +++ b/src/box/sql/mem.c
> @@ -3043,6 +3043,45 @@ mem_encode_array(const struct Mem *mems, uint32_t count, uint32_t *size,
> return array;
> }
>
> +char *
> +mem_encode_map(const struct Mem *mems, uint32_t count, uint32_t *size,
> + struct region *region)
> +{
> + assert(count % 2 == 0);
> + size_t used = region_used(region);
> + bool is_error = false;
> + struct mpstream stream;
> + mpstream_init(&stream, region, region_reserve_cb, region_alloc_cb,
> + set_encode_error, &is_error);
> + mpstream_encode_map(&stream, (count + 1) / 2);
> + for (uint32_t i = 0; i < count / 2; ++i) {
> + const struct Mem *key = &mems[2 * i];
> + const struct Mem *value = &mems[2 * i + 1];
> + if (mem_is_metatype(key) ||
> + (key->type & (MEM_TYPE_UINT | MEM_TYPE_INT | MEM_TYPE_UUID |
> + MEM_TYPE_STR)) == 0) {
> + diag_set(ClientError, ER_SQL_TYPE_MISMATCH,
> + mem_str(key), "integer, string or uuid");
> + return NULL;
> + }
> + mem_to_mpstream(key, &stream);
> + mem_to_mpstream(value, &stream);
> + }
> + mpstream_flush(&stream);
> + if (is_error) {
2. The error could happen in the last moment after some allocations
were made. Better add a truncate here.
> + diag_set(OutOfMemory, stream.pos - stream.buf,
> + "mpstream_flush", "stream");
> + return NULL;
> + }
> + *size = region_used(region) - used;
> + char *map = region_join(region, *size);
> + if (map == NULL) {
3. Ditto. And the same in mem_encode_array().
> + diag_set(OutOfMemory, *size, "region_join", "map");
> + return NULL;
> + }
> + return map;
> +}
> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index 06e6244e3..db7fef71a 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -1140,6 +1140,37 @@ expr(A) ::= LB(X) exprlist(Y) RB(E). {
> spanSet(&A, &X, &E);
> }
>
> +expr(A) ::= LCB(X) maplist(Y) RCB(E). {
> + struct sql *db = pParse->db;
> + struct Expr *expr = sql_expr_new_dequoted(db, TK_MAP, NULL);
4. sql_expr_new_anon().
> + if (expr == NULL) {
> + sql_expr_list_delete(db, Y);
> + pParse->is_aborted = true;
> + return;
> + }
> + expr->x.pList = Y;
> + expr->type = FIELD_TYPE_MAP;
> + sqlExprSetHeightAndFlags(pParse, expr);
> + A.pExpr = expr;
> + spanSet(&A, &X, &E);
> +}
> +
> +maplist(A) ::= nmaplist(A).
> +maplist(A) ::= . {A = 0;}
5. Lets remove these extra spaces between . and the code block.
> +nmaplist(A) ::= nmaplist(A) COMMA expr(X) COLON expr(Y). {
> + A = sql_expr_list_append(pParse->db, A, X.pExpr);
> + A = sql_expr_list_append(pParse->db, A, Y.pExpr);
> +}
> +nmaplist(A) ::= expr(X) COLON expr(Y). {
> + A = sql_expr_list_append(pParse->db, NULL, X.pExpr);
> + A = sql_expr_list_append(pParse->db, A, Y.pExpr);
> +}
> +
> +%type maplist {ExprList*}
> +%destructor maplist {sql_expr_list_delete(pParse->db, $$);}
> +%type nmaplist {ExprList*}
> +%destructor nmaplist {sql_expr_list_delete(pParse->db, $$);}
6. Could you please add a whitespace between type and '*'?
7. Lets add some more complicated syntax tests:
tarantool> box.execute('SELECT {:name}', {{[':name'] = 1}})
---
- null
- Syntax error at line 1 near '}'
...
tarantool> box.execute('SELECT {:name: 5}', {{[':name'] = 1}})
---
- metadata:
- name: COLUMN_1
type: map
rows:
- [{1: 5}]
...
tarantool> box.execute('SELECT {5::name}', {{[':name'] = 1}})
---
- metadata:
- name: COLUMN_1
type: map
rows:
- [{5: 1}]
...
To see if the parse is able to handle both map ':' and variable ':'.
More information about the Tarantool-patches
mailing list