<!DOCTYPE book [
<!ENTITY % tnt SYSTEM "../tnt.ent">
%tnt;
]>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xml:id="connectors">

<title>Connectors</title>
<blockquote><para>
  This chapter documents APIs for various programming languages.
</para></blockquote>

<para>Apart from the native Tarantool client driver, you can always use a <emphasis role="strong">Memcached</emphasis> driver of your choice, after enabling Memcached protocol in the configuration file.</para>

<section xml:id="connector-packet-example">
<title>Packet example</title>
<para>
The Tarantool API exists so that a client program can send a request packet
to the server, and receive a response. Here is an example of a what the client
 would send for <command>INSERT INTO t0 VALUES ('A','BB')</command>. The BNF description of the components
is in file <link xlink:href="https://github.com/tarantool/tarantool/blob/master/doc/box-protocol.txt" xlink:title="A complete BNF of Tarantool client/server protocol">doc/box-protocol.txt</link>.
 A third-party contribution written in Lua for unpacking Tarantool messages is in file 
 <link xlink:href="https://github.com/negram/Tnt-dissector/blob/master/tarantool.dis.lua" xlink:title="tarantool.dis.lua">Tnt-dissector</link>.

</para>
                <informaltable frame='topbot'>
                <tgroup cols='5' align='left' colsep='1' rowsep='1'>
                <colspec colname='c1'/>
                <colspec colname='c2'/>
                <colspec colname='c3'/>
                <colspec colname='c4'/>
                <colspec colname='c5'/>
                <thead>
                <row>
                <entry>Component</entry>
                <entry>Byte#0</entry>
                <entry>Byte#1</entry>
                <entry>Byte#2</entry>
                <entry>Byte#3</entry>
                </row>
                </thead>
                      <tbody>
                        <row>
                          <entry>type</entry>
                          <entry>13</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                        </row>
                        <row>
                          <entry>body_length</entry>
                          <entry>17</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                        </row>
                        <row>
                          <entry>request_id</entry>
                          <entry>1</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                        </row>
                        <row>
                          <entry>space_no</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                        </row>
                        <row>
                          <entry>flags</entry>
                          <entry>2</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                        </row>
                        <row>
                          <entry>cardinality</entry>
                          <entry>2</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                          <entry>0</entry>
                        </row>
                        <row>
                          <entry>field[0] size</entry>
                          <entry>1</entry>
                          <entry namest="c1" nameend="c4"></entry>
                        </row>
                        <row>
                          <entry>field[0] data</entry>
                          <entry>65</entry>
                          <entry namest="c2" nameend="c4"></entry>
                        </row>
                        <row>
                          <entry>field[1] size</entry>
                          <entry>2</entry>
                          <entry namest="c2" nameend="c5"></entry>
                        </row>
                        <row>
                          <entry>field[1] data</entry>
                          <entry>66</entry>
                          <entry>66</entry>
                          <entry namest="c3" nameend="c4"></entry>
                        </row>
                       </tbody>
                    </tgroup>
                </informaltable>
<para>
Now, one could send that packet to the tarantool_box server,
and interpret the response (box-protocol.txt has a description
of the packet format for responses as well as requests).
But it would be easier, and less error-prone, if one could
invoke a routine that formats the packet according to typed
parameters. Something like <code>response=tarantool_routine("insert",0,"A","B");</code>.
And that is why APIs exist for drivers for C, Perl, Python, PHP, Ruby, and so on.
</para>
  </section>

  <section xml:id="connector-c">
    <title>C</title>
    <para>
    Here is a complete C program that inserts [99999,'BB'] into space[0] via the C API for the
    binary protocol. To compile, paste the code into a file named example.c and say <code>
    gcc -o example example.c -I/<replaceable>tarantool-directory</replaceable>/connector/c/include</code>
    where tarantool-directory = the directory that contains
    the necessary file <filename>tp.h</filename>, and the default library path contains
    the directory where Tarantool library files were placed at installation time.
    Before trying to run, check that the server
    (tarantool_box) is running on localhost (127.0.0.1) and its primary port is the default (33013) and
    space[0]'s primary key type is numeric (space[0].index[0].key_field[0].type = "NUM" in configuration file).
    To run, say <code>./example</code>.
    The program will format a buffer for sending an INSERT request, then open a socket connection
    with the tarantool_box server at localhost:33013, then send the request, then check if the
    server returned an error, then &mdash; if all is well &mdash; print "Insert succeeded". If the
    row already exists, the program will print <quote>Duplicate key exists in unique index 0</quote>.
    </para>
<programlisting language="cpp">
#include &lt;arpa/inet.h&gt;
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;tp.h&gt;                                                /* the usual Tarantool include */

int main()
{
  struct tp request;                                           /* area for sending to server */
  struct tp reply;                                             /* area for getting server reply */
  int fd;                                                      /* file descriptor for socket */
  int pk_field_value = 99999;
  struct sockaddr_in tt;                                       /* the usual socket address info */
  tp_init(&amp;request, NULL, 0, tp_realloc, NULL);                /* initialize request buffer */
  tp_insert(&amp;request, 0, 2);                                   /* append INSERT header */
  tp_tuple(&amp;request);                                          /* begin appending body */
  tp_field(&amp;request, (char*) &amp;pk_field_value, 4);              /* append field[0] */
  tp_sz(&amp;request, "BB");                                       /* append field[1] */
  if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) &lt;= 0)   /* open the socket, abort if failure */
    exit(1);
  memset(&amp;tt, 0, sizeof(tt));                                  /* connect to localhost:33013 */
  tt.sin_family = AF_INET;
  tt.sin_addr.s_addr = inet_addr("127.0.0.1");
  tt.sin_port = htons(33013);
  if (connect(fd, (struct sockaddr *) &amp;tt, sizeof(tt)) &lt; 0)    /* connect, abort if failure */
    exit(1);
  int rc = write(fd, tp_buf(&amp;request), tp_used(&amp;request));     /* send the INSERT request */
  if (rc != tp_used(&amp;request))                                 /* abort if send failed */
    exit(1);
  tp_init(&amp;reply, NULL, 0, tp_realloc, NULL);                  /* initialize reply buffer */
  while (1) {
    ssize_t to_read = tp_req(&amp;reply);
    if (to_read &lt;= 0)
      break;
    ssize_t new_size = tp_ensure(&amp;reply, to_read);
    if (new_size == -1)                                        /* abort if error e.g. no memory */
      exit(1);
    ssize_t res = read(fd, reply.p, to_read);                  /* get reply */
    if (res &lt;= 0)                                              /* abort if error e.g. no reply */
      exit(1);
    tp_use(&amp;reply, res);
  }
  ssize_t server_code = tp_reply(&amp;reply);                      /* display+abort if error e.g. duplicate key */
  if (server_code != 0) {
    printf("error: %-.*s\n", tp_replyerrorlen(&amp;reply),
           tp_replyerror(&amp;reply));
    tp_free(&amp;reply);
    exit(1);
  }
  tp_free(&amp;request);                                           /* clean up */
  tp_free(&amp;reply);
  close(fd);
  printf("Insert succeeded\n");                                /* congratulate self */
  exit(0);
}
</programlisting>
    <para>
       The example program only shows one command and does not show all that's necessary for
       good practice. For that, please see <link
       xlink:href="https://github.com/tarantool/tarantool/blob/master/connector/c/include/tp.h"><filename>connector/c</filename></link> in the source tree.
    </para>
  </section>

  <section xml:id="connector-erlang">
    <title>Erlang</title>
    <para>
       Please see <link xlink:href="https://github.com/tarantool/tarantool-erlang"><filename>https://github.com/tarantool/tarantool-erlang</filename></link>.
    </para>
  </section>

  <section xml:id="connector-java">
    <title>Java</title>
    <para>
       Please see <link xlink:href="http://dgreenru.github.io/tarantool-java/"><filename>http://dgreenru.github.io/tarantool-java/</filename></link>.
    </para>
  </section>

  <section xml:id="connector-node.js">
    <title>node.js</title>
    <para>
       Please see <link xlink:href="https://github.com/devgru/node-tarantool"><filename>http://github.com/devgru/node-tarantool</filename></link>.
    </para>
  </section>

  <section xml:id="connector-perl">
    <title>Perl</title>
    <para>
     The most commonly used Perl driver is <link xlink:href='http://search.cpan.org/~unera/DR-Tarantool/'>DR::Tarantool</link>.
     It is not supplied as part of the Tarantool repository; it must be installed separately.
     The most common way to install it is with <link xlink:href='https://en.wikipedia.org/wiki/Cpan'>CPAN, the Comprehensive Perl Archive Network</link>.
     DR::Tarantool requires other modules which should be installed first.
     For example, on Ubuntu, the installation could look like this:
      <programlisting>
sudo cpan install AnyEvent
sudo cpan install Devel::GlobalDestruction
sudo cpan install Coro
sudo cpan install Test::Pod
sudo cpan install Test::Spelling
sudo cpan install PAR::Dist
sudo cpan install DR::Tarantool
      </programlisting>
     </para>
    <para>
    Here is a complete Perl program that inserts [99999,'BB'] into space[0] via the Perl API.
    Before trying to run, check that the server
    (tarantool_box) is running on localhost (127.0.0.1) and its primary port is the default (33013) and
    space[0]'s primary key type is numeric (space[0].index[0].key_field[0].type = "NUM" in configuration file).
    To run, paste the code into a file named example.pl and say <code>perl example.pl</code>.
    The program will connect using an application-specific definition of the space.
    The program will open a socket connection
    with the tarantool_box server at localhost:33013, then send an INSERT request,
    then &mdash; if all is well &mdash; end without displaying any messages.
    If tarantool_box is not running on localhost with primary port = 33013, the program will print
    <quote>Connection refused</quote>.
    </para>
    <para>
     <programlisting language="perl">
#!/usr/bin/perl
use DR::Tarantool ':constant', 'tarantool';
use DR::Tarantool ':all';

my $tnt = tarantool
  host    =&gt; '127.0.0.1',                      # look for tarantool_box on localhost
  port    =&gt; 33013,                            # assume tarantool_box primary port = default
  spaces  =&gt; {
    0 =&gt; {                                     # definition of space[0] ...
      name =&gt; 't0',                            #   space[0] name = 't0'
      default_type =&gt; 'STR',                   #   space[0] field type is 'STR' if undefined
      fields =&gt; [ {                            #   definition of space[0].fields ...
          name =&gt; 'k0', type =&gt; 'NUM' } ],     #     space[0].field[0] name='k0',type='NUM'
      indexes =&gt; {                             #   definition of space[0] indexes ...
        0 =&gt; {
          name =&gt; 'k0', fields =&gt; 'k0' } } } };

$tnt-&gt;insert('t0' => [ 99999, 'BB' ]);         # INSERT INTO t0 VALUES (99999,'BB')
     </programlisting>
    </para>
    <para>
       The example program only shows one command and does not show all that's necessary for
       good practice. For that, please see
       <link xlink:href='http://search.cpan.org/~unera/DR-Tarantool/'>DR::Tarantool CPAN repository</link>.
    </para>
  </section>

  <section xml:id="connector-php">
    <title>PHP</title>
    
    <para>
    The PHP driver is <link xlink:href='https://github.com/tarantool/tarantool-php'>tarantool-php</link>.
    It is not supplied as part of the Tarantool repository; it must be installed separately.
    It can be installed with git.
    It requires other modules which should be installed first.
    For example, on Ubuntu, the installation could look like this:
    <programlisting>
sudo apt-get install php5-cli
sudo apt-get install php5-dev
sudo apt-get install php-pear
cd ~
git clone https://github.com/tarantool/tarantool-php.git
cd tarantool-php
phpize
./configure
make
make install
    </programlisting>
    </para>    
    <para>
    At this point there is a file named <filename>~/tarantool-php/modules/tarantool.so</filename>.
    PHP will only find it if the PHP initialization file <filename>php.ini</filename> contains a line like
    <code>extension=./tarantool.so</code>.
    So copy <filename>tarantool.so</filename> to the working directory and tell PHP where
    to find the <filename>php.ini</filename> file that contains that line ...
    <programlisting>
cd ~
cp ./tarantool-php/modules/tarantool.so .
export PHP_INI_SCAN_DIR=~/tarantool-php/test/share
   </programlisting>
    </para>
    <para>
    Here is a complete PHP program that inserts [99999,'BB'] into space[0] via the PHP API.
    Before trying to run, check that the server
    (tarantool_box) is running on localhost (127.0.0.1) and its primary port is the default (33013) and
    space[0]'s primary key type is numeric (space[0].index[0].key_field[0].type = "NUM" in configuration file).
    To run, paste the code into a file named example.php and say <code>php example.php</code>.
    The program will open a socket connection
    with the tarantool_box server at localhost:33013, then send an INSERT request,
    then &mdash; if all is well &mdash; print "Insert succeeded".
    If the tuple already exists, the program will print <quote>Duplicate key exists in unique index 0</quote>.
    </para>
    <para>
    <programlisting>
&lt;?php
$tarantool = new Tarantool("localhost", 33013, 33015);
try {
  $tarantool-&gt;insert(0, array(99999, "BB"), TARANTOOL_FLAGS_ADD);
  print "Insert succeeded\n";
  }
catch (Exception $e) {
  echo "Exception: ", $e-&gt;getMessage(), "\n";
  }
?&gt;
    </programlisting>
    </para>
    <para>
    The example program only shows one command and does not show all that's necessary for
    good practice. For that, please see
    <link
    xlink:href="https://github.com/tarantool/tarantool-php/"><filename>tarantool-php</filename></link> project at GitHub.
    </para>
  </section>

  <section xml:id="connector-python">
    <title>Python</title>
    <para>
    Here is a complete Python program that inserts [99999,'BB'] into space[0] via the high-level Python API.
    To prepare, paste the code into a file named example.py and install tarantool-python with either
    <userinput><code>pip install tarantool</code></userinput> to install in <filename>/usr</filename> (requires root privilege)
    or
    <userinput><code>pip install tarantool --user</code></userinput> to install in <filename>~</filename> i.e. user's default directory.
    Before trying to run, check that the server (tarantool_box) is running on localhost (127.0.0.1) and its primary port
    is the default (33013) and space[0]'s primary key type is string (space[0].index[0].key_field[0].type = "STR" in configuration file).
    To run, say <code>python example.py</code>.
    The program will connect to the server, will send the request, and will not throw an exception if all went well.
    If the row already exists, the program will throw DatabaseException(“Duplicate key exists in unique index 0”).
    </para>
<programlisting language="python">
#!/usr/bin/python
from tarantool import Connection

c = Connection("127.0.0.1", 33013)
result = c.insert(0,(99999,'BB'))
print result
</programlisting>
    <para>
       The example program only shows one command and does not show all that's necessary for
       good practice. For that, please see
       <link xlink:href="http://tarantool-python.readthedocs.org/en/latest/"><filename>http://tarantool-python.readthedocs.org/en/latest/</filename></link>.
       For an example of a Python API for <link xlink:href="https://github.com/tarantool/queue">Queue managers on Tarantool</link>, see 
       <link xlink:href="https://github.com/tarantool/tarantool-queue-python"><filename>https://github.com/tarantool/tarantool-queue-python</filename></link>.
    </para>
  </section>

  <section xml:id="connector-ruby">
    <title>Ruby</title>
    <para>
       You need <emphasis role="strong">Ruby 1.9</emphasis> or later
       to use this connector. Connector sources are located in <link
       xlink:href="https://github.com/mailru/tarantool-ruby"><filename>http://github.com/mailru/tarantool-ruby</filename></link>.
    </para>
  </section>

</chapter>

<!--
vim: tw=66 syntax=docbk
vim: spell spelllang=en_us
-->
