tarantool / tarantool/tarantool

Strange behavior of SQL bind variables

Open
#12,733 0 comments 0 reactions 1 assignee View on GitHub

@ImeevMA is already working on this.

Since May 25, 2026.

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

Description

Reproducer

tarantool> box.execute([[select :a, $1;]], {{[':a'] = 123}})
---
- metadata:
  - name: COLUMN_1
    type: integer
  - name: COLUMN_2
    type: boolean
  rows:
  - [123, 123]
...

tarantool> box.execute([[select $1, :a;]], {{[':a'] = 123}})
---
- metadata:
  - name: COLUMN_1
    type: boolean
  - name: COLUMN_2
    type: integer
  rows:
  - [null, 123]
...

tarantool> box.execute([[select $2, :a;]], {321, {[':a'] = 123}})
---
- metadata:
  - name: COLUMN_1
    type: integer
  - name: COLUMN_2
    type: boolean
  rows:
  - [null, 123]
...

tarantool> box.execute([[select :a, $2;]], {321, {[':a'] = 123}})
---
- metadata:
  - name: COLUMN_1
    type: integer
  - name: COLUMN_2
    type: boolean
  rows:
  - [123, null]
...

tarantool> box.execute([[select :a, $1;]], {321, {[':a'] = 123}})
---
- metadata:
  - name: COLUMN_1
    type: integer
  - name: COLUMN_2
    type: boolean
  rows:
  - [123, 123]
...

tarantool> box.execute([[select $2, $1;]], {321, {['a'] = 123}})
---
- null
- Parameter 'a' was not found in the statement
...

tarantool> box.execute([[select #a, ?, :a;]], {111, {['#a'] = 222}, {[':a'] = 333}})
---
- metadata:
  - name: COLUMN_1
    type: integer
  - name: COLUMN_2
    type: boolean
  - name: COLUMN_3
    type: integer
  rows:
  - [222, null, 333]
...

We see that positional bindings behave unexpectedly. It is proposed to fix these issues using the following rules:

  1. Binding variables must be provided as an array, each element of which is a 'positional' variable, and some of them may be 'named' variables.

  2. If a variable is a Lua table with a single field, and the key of this field is a string consisting of '#' or ':' concatenated with the corresponding ID, then this key is the name of the binding variable, and the corresponding value is the value of the binding variable in the case of named and positional variables.

  3. Any variable that does not check condition 2 is not considered a 'named' variable.

  4. If '?' is used to get the value of the next positional variable, then the position of the last variable should be used to calculate the position of the next positional variable with 0 being used if there were no last variable in the statement.

For example, this

vars = {
    {[':asd' = 11},                 -- integer, position 1, name ':asd'
    {asd = 22},                     -- map, position 2, not named
    33,                             -- integer, position 3, not named
    {['#qwe'] = '44'},              -- string, position 4, name '#qwe'
    {[':asd'] = 5, ['#qwe'] = 5}    -- map, position 5, not named
    66,                             -- integer, position 6, not named
}
box.execute([[SELECT :asd, $1, #qwe, ?, ?, $2, ?;]], vars)

should return

{11, 11, '44', {[':asd'] = 5, ['#qwe'] = 5}, 66, {asd = 22}, 33}

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.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.