<!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>.
</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 ['A','BB'] into space[0] via the high-level Python API.
    To prepare, paste the code into a file named example.py and say:<code>
    export PYTHONPATH=<replaceable>tarantool-directory</replaceable>/test/lib</code>
    where <replaceable>tarantool-directory</replaceable>/test/lib = the directory that contains
    the necessary file <filename>box_connection.py</filename>. This will be
    the directory where Tarantool Python library files were placed at installation time
    for a source download.

    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, send the request, and display <quote>Insert OK, 1 row affected</quote> if all went well.
    If the row already exists, the program will print <quote>Duplicate key exists in unique index 0</quote>.
    </para>
<programlisting language="python">
#!/usr/bin/python
from box_connection import BoxConnection

c = BoxConnection("127.0.0.1", 33013)
result = c.execute("INSERT INTO t0 VALUES ('A','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="https://github.com/mailru/tarantool-python"><filename>http://github.com/mailru/tarantool-python</filename></link> and
       <link xlink:href="https://github.com/zlobspb/txtarantool"><filename>https://github.com/zlobspb/txtarantool</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
-->
