Skip to content

Executing stored procedures with List type input #162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
enescaglar opened this issue Aug 3, 2020 · 5 comments
Closed

Executing stored procedures with List type input #162

enescaglar opened this issue Aug 3, 2020 · 5 comments
Labels
question Further information is requested

Comments

@enescaglar
Copy link

Hi,

I am trying to call a function on tarantool which inserts given list input into a space. Function looks like below, it gets list, loop over records and inserts into a space named tester.
When I call the function within tarantool like the following, it works as expected and inserts records into the space.

tarantool> lst = {{1,'Test',1}, {2, 'Test2', 2}}
tarantool> batch_insert_tester(lst)

When I try to call the function through the library, it fails with the following error.

raise DatabaseError(self._return_code, self._return_message)
tarantool.error.DatabaseError: (32, '[string "function batch_insert_tester(list)..."]:4: attempt to index local 'record' (a number value)')

This is the code I have been using, as an input I am sending list of value tuples (tester space has 3 fields). How can I call a function with a list input like this?

connection = tarantool.connect(...)
tester_data = []
tester_data.append((3, 'ABBA', 3))
tester_data.append((4, 'ABBA', 4))
connection.call('batch_insert_tester', tester_data)

Here is the function defined on tarantool:

function batch_insert_tester(list)
box.begin()
for _, record in ipairs(list) do
box.space.tester:replace{record[1], record[2], record[3]}
end
box.commit()
end

@Totktonada Totktonada added the question Further information is requested label Aug 3, 2020
@Totktonada
Copy link
Member

In short: connection.call('batch_insert_tester', [tester_data]) (the second argument is a list of arguments to pass into the Lua function).

I'll give a bit background soon.

@enescaglar
Copy link
Author

enescaglar commented Aug 3, 2020

Ok, thank you this worked. I passed the arguments as tuple, when given as a list input it worked like you said.
If the documentation can be improved by adding a sample call with a list input (if there is already I might have missed it) would be great. If there is a way that I can contribute to the documentation, I would be happy to help as well.

Appreciate the answer.

@Totktonada
Copy link
Member

Sorry for the long read. It is for ones who will confused by the same API details and will want to understand what is going on.

Let's define a function to use in the example below (very similar to your one):

function log_tuples(tuples)
    for _, t in ipairs(tuples) do
        require('log').info('tuple: ' .. require('json').encode(t))
    end
end

(Note: It is written for ease experimenting: it is not good style for an application or a library code. Please, avoid global functions and requires inside a function where possible.)

First, let's look as the net.box — tarantool built-in connector — as the example.

tuples = {}
table.insert(tuples, {1, 2, 3})
table.insert(tuples, {4, 5, 6})
table.insert(tuples, {7, 8, 9})
connection:call('log_tuples', {tuples})

Result:

2020-08-03 23:02:54.340 [24265] main/119/main I> tuple: [1,2,3]
2020-08-03 23:02:54.340 [24265] main/119/main I> tuple: [4,5,6]
2020-08-03 23:02:54.340 [24265] main/119/main I> tuple: [7,8,9]

Why we need to wrap the argument into the list? Because one may want to pass several arguments to a function, say:

function log_tuples(tuples, prefix)
    local prefix = prefix or ''
    for _, t in ipairs(tuples) do
        require('log').info(prefix .. 'tuple: ' .. require('json').encode(t))
    end
end

Check:

tuples = {}
table.insert(tuples, {1, 2, 3})
table.insert(tuples, {4, 5, 6})
table.insert(tuples, {7, 8, 9})
connection:call('log_tuples', {tuples, 'my_app | '})

Result:

2020-08-03 23:07:56.260 [24265] main/121/main I> my_app | tuple: [1,2,3]
2020-08-03 23:07:56.260 [24265] main/121/main I> my_app | tuple: [4,5,6]
2020-08-03 23:07:56.260 [24265] main/121/main I> my_app | tuple: [7,8,9]

Back to Python. Everything is very same as for net.box:

import tarantool
t = tarantool.connect('localhost', 3301)
tuples = []
tuples.append((1, 2, 3))
tuples.append((4, 5, 6))
tuples.append((7, 8, 9))
t.call('log_tuples', [tuples])

The result is the same as for the first net.box call.

However there is strange peculiar in the tarantool-python API (the code below is part of the call method):

# This allows to use a tuple or list as an argument
if len(args) == 1 and isinstance(args[0], (list, tuple)):
args = args[0]

So if several arguments are passed to the call method and the first one is a list or a tuple, then a caller don't need to wrap arguments into the list. The following two calls are equivalent (when tuples is a list or a tuple, of course):

t.call('log_tuples', [tuples, 'my_app | '])
t.call('log_tuples', tuples, 'my_app | ')

Why? Who knows... I guess somebody in the past had the thought that it is convenient to have such autoguessing.

@Totktonada
Copy link
Member

Totktonada commented Aug 3, 2020

BTW, there is the Telegram channel, where tarantool developers and power users answer questions interactively. Questions are usually answered faster in the chat.

If the documentation can be improved by adding a sample call with a list input (if there is already I might have missed it) would be great. If there is a way that I can contribute to the documentation, I would be happy to help as well.

The is the documentation (the link is in the header of README) with the following relevant pages:

I would not say that the documentation is satisfying. The documentation is generated from the doc/ directory in the source code (when a new version of the connector is released). Feel free to open a pull request against this repository.

We're not always very responsible (shame on me), so expect a delay between sending PR and review / merge.

I think this issue is resolved and should be closed, but if you have a clear plan around the documentation, let's file another one about it.

@enescaglar
Copy link
Author

Thank you for all the detailed explanation and the code samples and for sharing the telegram channel info, I will look into it for further questions/discussions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants