tarantool / go-tarantool

Tarantool 1.10+ client for Go language
https://pkg.go.dev/github.com/tarantool/go-tarantool/v2
BSD 2-Clause "Simplified" License
180 stars 57 forks source link

Integer/Unsigned number no longer valid since 3.x+v2? #398

Closed kokizzu closed 3 months ago

kokizzu commented 3 months ago

Previously this code inserting properly on tarantool 2.x + go-tarantool (v1)

row, err := conn.Insert("plans", arr)

but no longer work properly for tarantool 3.x + go-tarantool/v2

row, err := conn.Do(tarantool.NewInsertRequest(`plans`).
            Tuple(arr)).Get()

where input arr is: image

and output row is: image

but from terminal it's stored not as is:

SELECT * FROM plans WHERE id = 6;
---
- metadata:
  - name: id
    type: unsigned
  - name: tenantCode
    type: string
  - name: planType
    type: string
  - name: parentId
    type: unsigned
  - name: createdAt
    type: integer
  - name: createdBy
    type: unsigned
  - name: updatedAt
    type: integer
  - name: updatedBy
    type: unsigned
  - name: deletedAt
    type: integer
  - name: deletedBy
    type: unsigned
  - name: restoredBy
    type: unsigned
  - name: title
    type: string
  - name: description
    type: string
  - name: orgId
    type: unsigned
  - name: yearOf
    type: integer
  - name: budgetIDR
    type: unsigned
  - name: budgetUSD
    type: unsigned
  - name: quantity
    type: unsigned
  - name: unit
    type: string
  rows:
  - [6, '', 'program', 0, -20, 1, -20, 1, -32, 0, 0, 'ayaya', '', 0, -5, 1, 0, 4,
    'test']
...

for example that createdAt should be unix timestamp integer value but it's now stored as -20?

oleg-jukovec commented 3 months ago

I don't remember any related changes in the go-tarantool (but of course we could have a regression) and Tarantool 3.

At first glance it looks like the int64 values ​​were passed successfully to Tarantool 3, but it decoded/applied them incorrectly (or not as we expect). However, we may have problems with encoding/decoding on the connector side.

It works fine for me with go-tarantool v2.1.0 and Tarantool 3.1, so we need a little more information about the issue:

func TestInsertInt64(t *testing.T) {
    conn := test_helpers.ConnectWithValidation(t, dialer, opts)
    defer conn.Close()

    _, err := conn.Do(NewExecuteRequest("CREATE TABLE \"q\" (\"q\" INTEGER PRIMARY KEY);")).Get()
    require.NoError(t, err)

    row, err := conn.Do(NewInsertRequest("q").Tuple([]interface{}{int64(1717345988)})).Get()
    require.NoError(t, err)
    assert.Equal(t, []interface{}{[]interface{}{int64(1717345988)}}, row)

    row, err = conn.Do(NewSelectRequest("q")).Get()
    require.NoError(t, err)
    assert.Equal(t, []interface{}{[]interface{}{int64(1717345988)}}, row)
}
The patch for the `tarantool_test.go` ```patch diff --git a/tarantool_test.go b/tarantool_test.go index 03b786f..466d3bf 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -848,6 +848,22 @@ func TestFutureMultipleGetGetTyped(t *testing.T) { } } +func TestInsertInt64(t *testing.T) { + conn := test_helpers.ConnectWithValidation(t, dialer, opts) + defer conn.Close() + + _, err := conn.Do(NewExecuteRequest("CREATE TABLE \"q\" (\"q\" INTEGER PRIMARY KEY);")).Get() + require.NoError(t, err) + + row, err := conn.Do(NewInsertRequest("q").Tuple([]interface{}{int64(1717345988)})).Get() + require.NoError(t, err) + assert.Equal(t, []interface{}{[]interface{}{int64(1717345988)}}, row) + + row, err = conn.Do(NewSelectRequest("q")).Get() + require.NoError(t, err) + assert.Equal(t, []interface{}{[]interface{}{int64(1717345988)}}, row) +} + func TestFutureMultipleGetWithError(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() ```
The patch for the `tarantool_test.go` with another attempt ```patch diff --git a/config.lua b/config.lua index 2553b94..ba6e749 100644 --- a/config.lua +++ b/config.lua @@ -62,6 +62,16 @@ box.once("init", function() if_not_exists = true }) + local s = box.schema.space.create('q', { + id = 1010, + if_not_exists = true, + }) + s:create_index('primary', { + type = 'tree', + parts = {1, 'string'}, + if_not_exists = true + }) + local s = box.schema.space.create('testintint', { id = 619, if_not_exists = true, diff --git a/tarantool_test.go b/tarantool_test.go index 03b786f..a77f787 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -848,6 +848,19 @@ func TestFutureMultipleGetGetTyped(t *testing.T) { } } +func TestInsertInt64(t *testing.T) { + conn := test_helpers.ConnectWithValidation(t, dialer, opts) + defer conn.Close() + + row, err := conn.Do(NewInsertRequest("q").Tuple([]interface{}{"foo", int64(1717345988)})).Get() + require.NoError(t, err) + assert.Equal(t, []interface{}{[]interface{}{"foo", int64(1717345988)}}, row) + + row, err = conn.Do(NewSelectRequest("q")).Get() + require.NoError(t, err) + assert.Equal(t, []interface{}{[]interface{}{"foo", int64(1717345988)}}, row) +} + func TestFutureMultipleGetWithError(t *testing.T) { conn := test_helpers.ConnectWithValidation(t, dialer, opts) defer conn.Close() ```

What do you get in a response if you select the tuple with the go-tarantool? What version of the go-tarantool do you use? What version of the Tarantool 3? It would be great if you could to analyze the network data between go-tarantool -> Tarantool 3 and check the transferred value by IPROTO.

Please, provide the space format/the space creation code and the insert request code (or a reproducer) to make it easier for us to check the problem.

Please, try to use go-tarantool v2.1.0 with Tarantool 2.x and post the result.

oleg-jukovec commented 3 months ago

Oh, I have the reproducer:

diff --git a/config.lua b/config.lua
index 2553b94..4039119 100644
--- a/config.lua
+++ b/config.lua
@@ -62,6 +62,20 @@ box.once("init", function()
         if_not_exists = true
     })

+    local s = box.schema.space.create('q', {
+        id = 1010,
+        if_not_exists = true,
+        format = {
+            {name = "id", type = "string"},
+            {name = "age", type = "integer"},
+        },
+    })
+    s:create_index('primary', {
+        type = 'tree',
+        parts = {1, 'string'},
+        if_not_exists = true
+    })
+
     local s = box.schema.space.create('testintint', {
         id = 619,
         if_not_exists = true,
diff --git a/tarantool_test.go b/tarantool_test.go
index 03b786f..c1b46c5 100644
--- a/tarantool_test.go
+++ b/tarantool_test.go
@@ -848,6 +848,25 @@ func TestFutureMultipleGetGetTyped(t *testing.T) {
     }
 }

+func TestInsertInt64(t *testing.T) {
+    conn := test_helpers.ConnectWithValidation(t, dialer, opts)
+    defer conn.Close()
+
+    row, err := conn.Do(NewInsertRequest("q").Tuple([]interface{}{"foo", int64(1717345988)})).Get()
+    require.NoError(t, err)
+    assert.Equal(t, []interface{}{[]interface{}{"foo", int64(1717345988)}}, row)
+
+    row, err = conn.Do(NewSelectRequest("q")).Get()
+    require.NoError(t, err)
+    assert.Equal(t, []interface{}{[]interface{}{"foo", int64(1717345988)}}, row)
+
+    row, err = conn.Do(NewExecuteRequest("SELECT * FROM seqscan q;")).Get()
+    require.NoError(t, err)
+    assert.Equal(t, []interface{}{[]interface{}{"foo", int8(-28)}}, row)
+}
+
 func TestFutureMultipleGetWithError(t *testing.T) {
     conn := test_helpers.ConnectWithValidation(t, dialer, opts)
     defer conn.Close()

It's funny.

With the net.box connector:

tarantool> conn = require('net.box'):connect("test:test@localhost:3013")
---
...

tarantool> conn.space.q:select()
---
- - ['foo', 1717345988]
...

tarantool> conn:execute("SELECT * FROM seqscan q")
---
- metadata:
  - name: id
    type: string
  - name: age
    type: integer
  rows:
  - ['foo', -28]
...

With the tarantool console directly:

2024-06-02 23:27:22.077 [12900] main/104/config.lua/box.load_cfg I> set 'listen' configuration option to "3013"
tarantool> box.space.q:select()
2024-06-02 23:27:37.405 [12900] main/104/config.lua C> Potentially long select from space 'q' (1010)
 stack traceback:
    builtin/box/schema.lua:2505: in function 'log_long_select'
    builtin/box/schema.lua:2522: in function <builtin/box/schema.lua:2508>
    [C]: in function 'pcall'
    builtin/box/console.lua:427: in function 'eval'
    builtin/box/console.lua:819: in function 'repl'
    builtin/box/console.lua:870: in function 'start'
    config.lua:312: in main chunk
---
- - ['foo', 1717345988]
...

tarantool> box.execute("SELECT * FROM seqscan q;")
---
- metadata:
  - name: id
    type: string
  - name: age
    type: integer
  rows:
  - ['foo', -28]
...
oleg-jukovec commented 3 months ago

@kokizzu , could you confirm that values that you get with box.space.q.select() or with NewSelectRequest the ones you expect?

If so, the problem on the Tarantool 3 side somewhere in the SQL code or we pass an unexpected type (for the SQL code) by the connector.

kokizzu commented 3 months ago

3.x

 box.space.plans:select()
---
  - [6, '', 'program', 0, 1717346220, 1, 1717346220, 1, 0, 0, 0, 'ayaya', '', 0, 0,
    0, 0, 0, 'test']

works fine

oleg-jukovec commented 3 months ago

It seems like a Tarantool's issue: https://github.com/tarantool/tarantool/issues/10084

I see two ways to make a workaround:

  1. Use uint64/uint instead of int64 for values >= 0 with the InsertRequest.
  2. Use SelectRequest to get valid values instead of the ExecuteRequest.

I'll re-open the issue again if we can get something done here.