tarantool / tarantool/tarantool

SQL: prepared join fails to execute

Open
#7,283 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Lua
Stars
3.7k
Forks
419
Avg merge
1d 23h
Merged PRs (30d)
88

Description

Bug description
Intro. When we prepare a statement with parameters in the result columns (for example box.prepare('select ?')) Tarantool has no information about the type of the output column and set it to default boolean. Then, on the execution phase, the type would be recalculated during the parameter binding.

Tarantool expects that there is no way for parameter to appear in the result tuple other than exactly be mentioned in the final projection. But it is incorrect - we can easily propagate parameter from the inner part of the join (for example, box.prepare([[select COLUMN_1 from t1 join (values (?)) as t2 on true]])). In this case column COLUMN_1 in the final projection is not a parameter, but a "reference" to it and its type depends on the parameter from the inner part of the join. But as Tarantool recalculates only binded parameters in the result projection, it doesn't change the default boolean metadata type of the COLUMN_1 and the query fails on comparison with the actual type of the tuple.

Steps to reproduce

box.execute([[create table t1(a int primary key)]], {})
q = box.prepare([[select COLUMN_1 from t1 join (values (?)) as t2 on true]])
q:execute({42})
---
- null
- 'Tuple field 1 (_COLUMN_0) type does not match one required by operation: expected
  boolean, got unsigned'
...

Solution
It seems that Vdbe structure doesn't keep any information about COLUMN_1 referencing the parameter from the inner part of the join. Considering this fact I can suggest two solutions:

  1. Simply change the default type from boolean to any for parameters (something like this)
     diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
     index 67c4cdd85..3fde451d3 100644
     --- a/src/box/sql/expr.c
     +++ b/src/box/sql/expr.c
     @@ -1355,7 +1355,11 @@ expr_new_variable(struct Parse *parse, const struct Token *spec,
             struct Expr *expr = sql_expr_new_empty(parse->db, TK_VARIABLE, len + 1);
             if (expr == NULL)
                     return NULL;
     -       expr->type = FIELD_TYPE_BOOLEAN;
     +       if (spec->n == 1 && spec->z[0] == '?') {
     +               expr->type = FIELD_TYPE_ANY;
     +       } else {
     +               expr->type = FIELD_TYPE_BOOLEAN;
     +       }
             expr->flags = EP_Leaf;
             expr->u.zToken = (char *)(expr + 1);
             expr->u.zToken[0] = spec->z[0];
    
    As a result we'll stop failing on the check of the metadata with actual type. But the result metadata for COLUMN_1 would be any that is not very nice.
  2. We can redesign the Vdbe somehow to keep information that COLUMN_1 refers parameter from the inner join part. If current information exists in some other place (that I haven't found) - please tell me about it. Otherwise, I need your suggestions (@ImeevMA) how to implement this approach in the best way (or any other ideas).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the prepared join in the issue, then trace parameter type handling through src/box/sql/expr.c, src/box/sql/vdbeapi.c, and src/box/execute.c. Compare the result metadata check in src/box/tuple_format.c and determine how the referenced parameter type should be preserved; done means the query executes with 42 without a tuple type mismatch.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, lua, sql
Domain
databases
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.