tarantool / tarantool/tarantool
SQL: prepared join fails to execute
Nobody has claimed this yet.
- 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:
- Simply change the default type from
booleantoanyfor parameters (something like this)
As a result we'll stop failing on the check of the metadata with actual type. But the result metadata fordiff --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];COLUMN_1would beanythat is not very nice. - We can redesign the
Vdbesomehow to keep information thatCOLUMN_1refers 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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