File Coverage

blib/lib/DR/Tarantool.pm
Criterion Covered Total %
statement 27 43 62.7
branch n/a
condition n/a
subroutine 9 17 52.9
pod 3 4 75.0
total 39 64 60.9


line stmt bran cond sub pod time code
1             package DR::Tarantool;
2              
3             =head1 NAME
4              
5             DR::Tarantool - a Perl driver for L<Tarantool|http://tarantool.org>
6              
7              
8             =head1 SYNOPSIS
9              
10             use DR::Tarantool ':constant', 'tarantool';
11             use DR::Tarantool ':all';
12              
13             my $tnt = tarantool
14             host => '127.0.0.1',
15             port => 123,
16             spaces => {
17             ...
18             }
19             ;
20              
21             $tnt->update( ... );
22              
23             my $tnt = coro_tarantool
24             host => '127.0.0.1',
25             port => 123,
26             spaces => {
27             ...
28             }
29             ;
30              
31             use DR::Tarantool ':constant', 'async_tarantool';
32              
33             async_tarantool
34             host => '127.0.0.1',
35             port => 123,
36             spaces => {
37             ...
38             },
39             sub {
40             ...
41             }
42             ;
43              
44             $tnt->update(...);
45              
46             =head1 DESCRIPTION
47              
48             This module provides a synchronous and asynchronous driver for
49             L<Tarantool|http://tarantool.org>.
50              
51             The driver does not have external dependencies, but includes the
52             official light-weight Tarantool C client (a single C header which
53             implements all protocol formatting) for packing requests and unpacking
54             server responses.
55              
56             This driver implements "iproto" protocol described in
57             https://github.com/mailru/tarantool/blob/master/doc/box-protocol.txt
58              
59             It is built on top of L<AnyEvent> - an asynchronous event
60             framework, and is therefore easiest to integrate into a program
61             which is already based on L<AnyEvent>. A synchronous version of
62             the driver exists as well, it starts L<AnyEvent> event machine for
63             every request.
64              
65             The driver supports three work flow types:
66              
67             =over
68              
69             =item L<DR::Tarantool::AsyncClient>
70              
71             The primary type, provides an asynchronous, callback-based
72             API. Requires a running L<AnyEvent> machine.
73              
74             =item L<DR::Tarantool::SyncClient>
75              
76             Is built on top of L<DR::Tarantool::AsyncClient>. Starts
77             L<AnyEvent> machine for every request. After a request is
78             served, the event loop is stopped, and the results
79             are returned to the caller, or, in case of an error, an
80             exception is thrown.
81              
82             =item L<DR::Tarantool::CoroClient>
83              
84             Is also built on top of L<DR::Tarantool::AsyncClient>, but is
85             designed to work in cooperative multitasking environment provided
86             by L<Coro>. Is fully syntax-compatible with
87             L<DR::Tarantool::SyncClient>, but requires a running event loop to
88             operate, like L<DR::Tarantool::AsyncClient>. Requests from
89             different coroutines are served concurrently.
90              
91             =back
92              
93             L<Tarantool|http://tarantool.org> binary protocol
94             contains no representation of database schema or tuple field types.
95             Due to this deficiency, to easily integrate with Perl and automatically
96             convert tuple fields to Perl values, the driver needs to know field names
97             and types. To tell the driver about them, an instance of a dedicated class
98             must be used.
99             L<DR::Tarantool::Spaces> is essentially a Perl hash which
100             describes field types and names for each space used in the program.
101             It can hardly be useful on its own, but once a connection is
102             "enlightened" with an instance of this class, access to all tuple
103             fields by a field name becomes possible. Type conversion, as
104             well as packing/unpacking from Tarantool binary format is
105             performed automatically.
106              
107             Please follow the docs for L<DR::Tarantool::Spaces> to learn
108             how to describe a schema.
109              
110             =head2 Establishing a connection
111              
112             =head3 L<DR::Tarantool::AsyncClient>
113              
114             DR::Tarantool::AsyncClient->connect(
115             host => $host,
116             port => $port,
117             spaces => { ... },
118             sub {
119             my ($tnt) = @_;
120             ...
121              
122             }
123             );
124              
125             The callback passed to connect() gets invoked after a connection
126             is established. The only argument of the callback is the newly
127             established connection handle. The handle's type is
128             L<DR::Tarantool::AsyncClient>.
129              
130             =head3 L<DR::Tarantool::CoroClient> and L<DR::Tarantool::SyncClient>
131              
132             my $tnt = DR::Tarantool::SyncClient->connect(
133             host => $host,
134             port => $port,
135             spaces => { ... }
136             );
137              
138             my $tnt = DR::Tarantool::CoroClient->connect(
139             host => $host,
140             port => $port,
141             spaces => { ... }
142             );
143              
144             The only difference of synchronous versions from the asynchronous
145             one is absence of a callback. The created connection handle
146             is returned directly from connect().
147             In this spirit, the only difference of any synchronous API all
148             from the asynchronous counterpart is also in absence of the callback.
149              
150             =head2 Working with tuples
151              
152             =head3 Querying
153              
154             my $user123 = $tnt->select('users' => 123);
155              
156             my $users_by_roles = $tnt->select('users' => 'admins' => 'role_index');
157            
158              
159             It is possible to select data by a primary key (expects a Perl scalar),
160             secondary, multi-part key (expects an array).
161              
162             The default index used for selection is the primary one, a non-default index
163             can be set by providing index name.
164              
165             The contents of the result set is interpreted in accordance with
166             schema description provided in L<DR::Tarantool::Spaces>.
167             Supported data types are numbers, Unicode strings, JSON,
168             fixed-point decimals.
169              
170             =head3 Insertion
171              
172             $tnt->insert('users' => [ 123, 'vasya', 'admin' ]);
173              
174             Insert a tuple into space 'users', defined in B<spaces> hash on
175             connect.
176              
177             =head3 Deletion
178              
179             $tnt->delete(users => 123);
180              
181             Delete a tuple from space 'users'. The deletion is always
182             performed by the primary key.
183              
184              
185             =head3 Update
186              
187             $tnt->update(users => 123 => [[ role => set => 'not_admin' ]]);
188              
189             It is possible to modify any field in a tuple. A field can be
190             accessed by field name or number. A set of modifications can be
191             provided in a Perl array.
192              
193             The following update operations are supported:
194              
195             =over
196              
197             =item set
198              
199             Assign a field
200              
201             =item add, and, or, xor
202              
203             Arithmetic and bitwise operations for integers.
204              
205             =item substr
206              
207             Replace a substring with a paste (similar to Perl splice).
208              
209             =item insert
210              
211             Insert a field before the given field.
212              
213             =item delete
214              
215             Delete a field.
216              
217             =item push
218              
219             Append a field at the tail of the tuple.
220              
221             =item pop
222              
223             Pop a field from the tail of the tuple.
224              
225             =back
226              
227             =head3 Lua
228              
229             $tnt->call_lua(my_proc_name => [ arguments, ...]);
230              
231             Invoke a Lua stored procedure by name.
232              
233             =head2 Supported data types
234              
235             The driver supports all Tarantool types (B<NUM>, B<NUM64>, B<STR>),
236             as well as some client-only types, which are converted to the
237             above server types automatically on the client:
238              
239             =over
240              
241             =item UTF8STR
242              
243             A unicode string.
244              
245             =item MONEY
246              
247             Fixed decimal currency. Stores the value on the server in B<NUM> type,
248             by multiplying the given amount by 100. The largest amount
249             that can be stored in this type is, therefore, around 20 000 000.
250             Can store negative values.
251              
252             =item BIGMONEY
253              
254             The same as above, but uses B<NUM64> as the underlying storage.
255              
256             =item JSON
257              
258             An arbitrary Perl object is automatically serialized to JSON with
259             L<JSON::XS> on insertion, and deserialized on selection.
260              
261             =back
262              
263             The basic data transfer unit in Tarantool protocol is a single
264             tuple. A selected tuple is automatically wrapped into an instance
265             of class L<DR::Tarantool::Tuple>. An object of this class can be
266             used as an associative container, in which any field can be
267             accessed by field name:
268              
269             my $user = $tnt->select(users => 123);
270              
271             printf("user: %s, role: %s\n", $user->name, $user->role);
272              
273              
274              
275             To run driver tests, the following Perl modules are also necessary:
276             L<AnyEvent>, L<Coro>, L<Test::Pod>, L<Test::Spelling>,
277             L<Devel::GlobalDestruction>, L<JSON::XS>.
278              
279             To run tests, do:
280             perl Makefile.PL
281             make
282             make test
283              
284             The test suite attempts to find the server and start it, thus
285             make sure L<tarantool_box> is available in the path, or export
286             TARANTOOL_BOX=/path/to/tarantool_box.
287              
288             =cut
289              
290 11     11   202818 use 5.008008;
  11         51  
  11         579  
291 11     11   63 use strict;
  11         22  
  11         460  
292 11     11   76 use warnings;
  11         38  
  11         378  
293 11     11   73 use Carp;
  11         19  
  11         2319  
294             $Carp::Internal{ (__PACKAGE__) }++;
295              
296 11     11   65 use base qw(Exporter);
  11         23  
  11         2981  
297              
298              
299             our %EXPORT_TAGS = (
300             client => [ qw( tarantool async_tarantool coro_tarantool) ],
301             constant => [
302             qw(
303             TNT_INSERT TNT_SELECT TNT_UPDATE TNT_DELETE TNT_CALL TNT_PING
304             TNT_FLAG_RETURN TNT_FLAG_ADD TNT_FLAG_REPLACE
305             )
306             ],
307             );
308              
309             our @EXPORT_OK = ( map { @$_ } values %EXPORT_TAGS );
310             $EXPORT_TAGS{all} = \@EXPORT_OK;
311             our @EXPORT = @{ $EXPORT_TAGS{client} };
312             our $VERSION = '0.42';
313              
314             =head1 EXPORT
315              
316             =head2 tarantool
317              
318             connects to L<Tarantool|http://tarantool.org> in synchronous mode
319             using L<DR::Tarantool::SyncClient>.
320              
321             =cut
322              
323             sub tarantool {
324 0     0 1   require DR::Tarantool::SyncClient;
325 11     11   61 no warnings 'redefine';
  11         23  
  11         1679  
326             *tarantool = sub {
327 0     0     DR::Tarantool::SyncClient->connect(@_);
328 0           };
329 0           goto \&tarantool;
330             }
331              
332              
333             =head2 rsync_tarantool
334              
335             connects to L<Tarantool|http://tarantool.org> in synchronous mode
336             using L<DR::Tarantool::RealSyncClient>.
337              
338             =cut
339              
340             sub rsync_tarantool {
341 0     0 1   require DR::Tarantool::RealSyncClient;
342 11     11   59 no warnings 'redefine';
  11         30  
  11         1312  
343             *rsync_tarantool = sub {
344 0     0     DR::Tarantool::RealSyncClient->connect(@_);
345 0           };
346 0           goto \&rsync_tarantool;
347             }
348              
349              
350             =head2 async_tarantool
351              
352             connects to L<tarantool|http://tarantool.org> in async mode using
353             L<DR::Tarantool::AsyncClient>.
354              
355             =cut
356              
357             sub async_tarantool {
358 0     0 1   require DR::Tarantool::AsyncClient;
359 11     11   59 no warnings 'redefine';
  11         26  
  11         1491  
360             *async_tarantool = sub {
361 0     0     DR::Tarantool::AsyncClient->connect(@_);
362 0           };
363 0           goto \&async_tarantool;
364             }
365              
366              
367             =head2 coro_tarantol
368              
369             connects to L<tarantool|http://tarantool.org> in async mode using
370             L<DR::Tarantool::CoroClient>.
371              
372              
373             =cut
374              
375             sub coro_tarantool {
376 0     0 0   require DR::Tarantool::CoroClient;
377 11     11   61 no warnings 'redefine';
  11         20  
  11         1892  
378             *coro_tarantool = sub {
379 0     0     DR::Tarantool::CoroClient->connect(@_);
380 0           };
381 0           goto \&coro_tarantool;
382             }
383              
384              
385             =head2 :constant
386              
387             Exports constants to use in a client request as flags:
388              
389             =over
390              
391             =item TNT_FLAG_RETURN
392              
393             With this flag on, each INSERT/UPDATE request
394             returns the new value of the tuple. DELETE returns the deleted
395             tuple, if it is found.
396              
397             =item TNT_FLAG_ADD
398              
399             With this flag on, INSERT returns an error if an old tuple
400             with the same primary key already exists. No tuple is inserted
401             in this case.
402              
403             =item TNT_FLAG_REPLACE
404              
405             With this flag on, INSERT returns an error if an old
406             tuple for the primary key does not exist.
407             Without either of the flags, INSERT replaces the old
408             tuple if it doesn't exist.
409              
410             =back
411              
412             =cut
413              
414             require XSLoader;
415             XSLoader::load('DR::Tarantool', $VERSION);
416              
417              
418              
419             =head2 :all
420              
421             Exports all functions and constants.
422              
423             =head1 TODO
424              
425             =over
426              
427             =item *
428              
429             Support push, pop in UPDATE.
430              
431             =item *
432              
433             Make it possible to construct B<select>, B<delete> keys from Perl
434             hashes, not just Perl arrays.
435              
436             =item *
437              
438             Support L<DR::Tarantool::Tuple> as an argument to B<insert>.
439              
440             =back
441              
442             =head1 COPYRIGHT AND LICENSE
443              
444             Copyright (C) 2011 Dmitry E. Oboukhov <unera@debian.org>
445             Copyright (C) 2011 Roman V. Nikolaev <rshadow@rambler.ru>
446              
447             This program is free software, you can redistribute it and/or
448             modify it under the terms of the Artistic License.
449              
450             =head1 VCS
451              
452             The project is hosted on github in the following git repository:
453             L<https://github.com/dr-co/dr-tarantool/>.
454              
455             =cut
456              
457             1;