Странное поведение при использовании int64

170 views
Skip to first unread message

maxim

unread,
Feb 10, 2016, 2:59:42 AM2/10/16
to tarantool-ru
Здравствуйте, столкнулся с такой проблемой... прошу помощи

использую в tarantool драйвер PostgreSQL, получаю оттуда значения bigint, вставляю в спейс тарантула (но есть подозрение что проблема не в этом)
при селекте по этому спейсу возникает непонятная ситуация, такое ощущение что либо я делаю что-то не так, либо в спейсе хранятся значения не в int64, либо они как-то преобразуются


pg = require('pg')

conn = pg.connect({ ... })

box.once('schema', function()
    items = box.schema.space.create('items')
    items:create_index('primary', {type = 'hash', parts = {1, 'NUM'}})
end)

function import_sql()
    local items = conn:execute('SELECT id, extarticle, name, description FROM items') -- id - в PostgreSQL это bigint
    for i, item in ipairs(items) do
        box.space.items:replace{tonumber64(item["id"]), item["extarticle"], item["name"], item["description"]}
    end
end

-- где-то в другом участке кода

        -- basket_item["Item_id"] -- считано из массива msgpack из tarantool спейса, записывалось uint64 значение 7806703326452360320 из драйвера Golang

        -- на всякий случай посмотрим как выводится basket_item["Item_id"]
        print("type(basket_item[\"Item_id\"])             ", type(basket_item["Item_id"]))
        print("basket_item[\"Item_id\"]                     ", basket_item["Item_id"])
        print("tonumber(basket_item[\"Item_id\"])      ", tonumber(basket_item["Item_id"]))
        print("tonumber64(basket_item[\"Item_id\"])  ", tonumber64(basket_item["Item_id"]))
        print("")
        -- попробуем разные варианты селекта
        print("select{tonumber64(basket_item[\"Item_id\"])}")
        res = box.space.items:select{tonumber64(basket_item["Item_id"])} -- так не выбирает ничего
        if res[1] ~= nil then
            print("    type(res[1][1])                   ", type(res[1][1]))
            print("    res[1][1]                           ", res[1][1])
            print("    tonumber(res[1][1])            ", tonumber(res[1][1]))
            print("    tonumber64(res[1][1])        ", tonumber64(res[1][1]))
        else
            print("    no rows")
        end
        print("select{basket_item[\"Item_id\"]}")
        res = box.space.items:select{basket_item["Item_id"]}            -- так тоже ничего не выбирает
        if res[1] ~= nil then
            print("    type(res[1][1])                   ", type(res[1][1]))
            print("    res[1][1]                           ", res[1][1])
            print("    tonumber(res[1][1])            ", tonumber(res[1][1]))
            print("    tonumber64(res[1][1])        ", tonumber64(res[1][1]))
        else
            print("    no rows")
        end
        print("select{tonumber(basket_item[\"Item_id\"])}")
        res = box.space.items:select{tonumber(basket_item["Item_id"])}  -- а вот так находит правильную строку!
        if res[1] ~= nil then        -- но в первом элементе строки не тот id, который запрашивали, при этоv других строк нет, а содержимое остальной части ответа корректно и соответствует правильному id...
            print("    type(res[1][1])                   ", type(res[1][1]))
            print("    res[1][1]                           ", res[1][1])
            print("    tonumber(res[1][1])            ", tonumber(res[1][1]))
            print("    tonumber64(res[1][1])        ", tonumber64(res[1][1]))
        else
            print("    no rows")
        end

-- =================================================================
-- ======================== вывод в консоль ========================
type(basket_item["Item_id"])                 cdata
basket_item["Item_id"]                         7806703326452360320ULL
tonumber(basket_item["Item_id"])         7.8067033264524e+18
tonumber64(basket_item["Item_id"])      7806703326452360320ULL

select{tonumber64(basket_item["Item_id"])}
    no rows
select{basket_item["Item_id"]}
    no rows
select{tonumber(basket_item["Item_id"])}
    type(res[1][1])                     cdata
    res[1][1]                             7806703326452360192ULL
    tonumber(res[1][1])              7.8067033264524e+18
    tonumber64(res[1][1])          7806703326452360192ULL
-- =================================================================

я не понимаю откуда взялось 7806703326452360192ULL вместо 7806703326452360320ULL
и почему select{} работает только в случае если мы передаем tonumber(basket_item["Item_id"])

Георгий Кириченко

unread,
Feb 11, 2016, 2:05:52 AM2/11/16
to tarantool-ru
Добрый день!

Думаю, это связано с внутренней конвертацией int64 между тарантулом драйверами постгреса и Golang, буду чинить.

среда, 10 февраля 2016 г., 10:59:42 UTC+3 пользователь maxim написал:

Георгий Кириченко

unread,
Feb 11, 2016, 8:22:50 AM2/11/16
to tarantool-ru
По постгрессу таск с фиксом: https://github.com/tarantool/pg/issues/6
Пока можно обойти следующим образом - получать из pg bigint в виде строки (::varchar(25) в sql), а в lua коде конвертировать при помощи tonumber64.

С драйвером golang скорее всего таже проблема - в спейс было записано значение в формате number, в которое не влезли все значащие цифры. Поэтому и select отрабатывает только с tonumber(..) - так как он аналогично теряет точность.

maxim

unread,
Feb 11, 2016, 4:17:53 PM2/11/16
to tarantool-ru

Георгий Кириченко, спасибо! Буду пробовать, тестить...

maxim

unread,
Feb 15, 2016, 12:03:07 AM2/15/16
to tarantool-ru
Еще заметил непонятную проблему... 
В lua из PostgreSQL вставил в спейс тарантула double precision значения... когда достаю из тарантула и получаю в драйвере Golang, то они оказываются строкой...
Если попробовать сделать tonumber() в lua, то приходят в Golang как uint32
Не понятно почему так происходит, как принудительно сконвертить их в double? 

maxim

unread,
Feb 15, 2016, 1:01:01 AM2/15/16
to tarantool-ru
похоже lua выдает то uint32, то int32, то float32 динамически в зависимости от значения...
На стороне Golang выехал пока вот на таком костыле:
func int64FromInterface(int64_interface interface{}) (int64, error) {
switch int64_val := int64_interface.(type) {
case nil:
return -1.0, errors.New("int64FromInterface: int64_interface is nil")
case int64:
return int64(int64_val), nil
case int32:
return int64(int64_val), nil
case uint64:
return int64(int64_val), nil
case uint32:
return int64(int64_val), nil
case string:
val, err := strconv.ParseInt(int64_val, 10, 64)
if err != nil {
return -1.0, errors.New("int64FromInterface: Can't parse int64_val string")
}
return val, nil
}
return -1.0, errors.New("int64FromInterface: Can't cast int64_interface to uint64, uint32, int64, int32 and string")
}

func float64FromInterface(float64_interface interface{}) (float64, error) {
switch float64_val := float64_interface.(type) {
case nil:
return -1.0, errors.New("float64FromInterface: float64_interface is nil")
case float64:
return float64(float64_val), nil
case float32:
return float64(float64_val), nil
case int64:
return float64(float64_val), nil
case int32:
return float64(float64_val), nil
case uint64:
return float64(float64_val), nil
case uint32:
return float64(float64_val), nil
case string:
val, err := strconv.ParseFloat(float64_val, 64)
if err != nil {
return -1.0, errors.New("float64FromInterface: Can't parse float64_val string")
}
return val, nil
}
return -1.0, errors.New("float64FromInterface: Can't cast float64_interface to float64, float32, uint64, uint32, int64, int32 and string")
}

Для моих случаев вроде бы подходит.
Reply all
Reply to author
Forward
0 new messages