Top.Mail.Ru
Коннекторы | Tarantool
Tarantool
Узнайте содержание релиза 2.8
Коннекторы

Коннекторы

В этой главе описаны API для различных языков программирования:

Бинарный протокол передачи данных в Tarantool был разработан с учетом потребностей асинхронного ввода-вывода для облегчения интеграции с прокси-серверами. Каждый клиентский запрос начинается с бинарного заголовка переменной длины. В заголовке указывается идентификатор и тип запроса, идентификатор экземпляра, номер записи в журнале и т.д.

Также в заголовке обязательно указывается длина запроса, что облегчает обмен данными с клиентом или прокси. Ответ на запрос посылается по мере готовности. В заголовке ответа указывается тот же идентификатор и тип запроса, что и в изначальном запросе. По идентификатору легко соотнести запрос с ответом, даже если ответы приходят в произвольном порядке.

Вдаваться в тонкости реализации бинарного протокола нужно, только если вы разрабатываете новый коннектор для Tarantool. В остальных случаях достаточно взять уже существующий коннектор для нужного вам языка программирования. Такие коннекторы позволяют без труда хранить в формате Tarantool структуры данных из разных языков. Полное описание бинарного протокола в Tarantool хранится в дереве исходного кода в виде аннотированных форм Бэкуса — Наура (BNF-диаграмм). Подробные примеры и диаграммы всех запросов и ответов можно найти в описании бинарного протокола в Tarantool.

С помощью API Tarantool клиентские программы могут отправлять на адрес экземпляра пакеты с запросами и получать на них ответы. Пример ниже относится к клиентскому запросу box.space[513]:insert{'A', 'BB'}. Описания компонентов запроса, представленные в форме Бэкуса — Наура, вы найдете на странице о бинарном протоколе Tarantool.

Компонент Байт 0 Байт 1 Байт 2 Байт 3
код операции insert 02      
остальная часть заголовка
двузначное число: ID спейса cd 02 01  
код кортежа 21      
однозначное число: количество полей = 2 92      
односимвольная строка: поле[1] a1 41    
двухсимвольная строка: поле[2] a2 42 42  

Этот пакет можно отправить экземпляру Tarantool, а затем расшифровать ответ. Формат пакетов запроса и ответа также описан на странице о бинарном протоколе Tarantool. Однако проще и надежнее вызвать процедуру, которая сформирует готовый пакет с заданными параметрами. Например, она могла бы выглядеть так: response = tarantool_routine("insert", 513, "A", "B");. Для этого и существуют API для драйверов Perl, Python, PHP и т. д.

В этой главе приводятся примеры того, как можно установить соединение с сервером Tarantool с помощью коннекторов для языков Perl, PHP, Python, Node.js и C. Примеры содержат фиксированные значения и будут корректно работать только при следующих условиях:

  • Экземпляр (tarantool) запущен на локальной машине (localhost = 127.0.0.1), а прослушивание для него настроено на порту 3301 (box.cfg.listen = '3301').
  • В базе есть спейс examples с идентификатором 999 (box.space.examples.id = 999). Первичный индекс этого спейса построен по ключу числового типа (box.space[999].index[0].parts[1].type = "unsigned").
  • Пользователь „guest“ имеет разрешение на чтение и запись.

Можно легко соблюсти все условия, запустив экземпляр и выполнив следующий скрипт:

box.cfg{listen=3301}
box.schema.space.create('examples',{id=999})
box.space.examples:create_index('primary', {type = 'hash', parts = {1, 'unsigned'}})
box.schema.user.grant('guest','read,write','space','examples')
box.schema.user.grant('guest','read','space','_space')

В Tarantool доступны два Java-коннектора:

  • cartridge-java поддерживает как отдельные узлы и кластеры Tarantool, так и приложения, созданные с использованием фреймворка Cartridge и его модулей. Команда Tarantool активно добавляет в этот модуль новейшие функциональные возможности платформы.
  • tarantool-java предназначен для ранних версий Tarantool (1.6 и выше) и предоставляет поддержку интерфейса JDBC для одиночных узлов Tarantool. Этот модуль в настоящее время не поддерживается и не работает ни с новейшими функциональными возможностями Tarantool 2.x, ни с кластерами Tarantool.

Для работы с библиотеками и фреймворками Java в Tarantool есть следующие модули:

В Tarantool доступны два коннектора для Go:

  • go-tarantool, поддерживаемый командой Tarantool.
  • Клиент на Go, поддерживаемый сообществом разработчиков.

Используйте коннектор tarantoolr.

Используйте драйвер Tarantool на Erlang.

Самый распространенный драйвер для Perl — tarantool-perl. Он не входит в репозиторий Tarantool, его необходимо устанавливать отдельно. Проще всего установить его, склонировав с GitHub.

Чтобы избежать нескольких незначительных предупреждений, которые может выдать система после первой установки tarantool-perl, начните установку с модуля CPAN (Comprehensive Perl Archive Network, Всеобъемлющая сеть архивов Perl). Этот модуль необходим для работы tarantool-perl:

$ sudo cpan install AnyEvent
$ sudo cpan install Devel::GlobalDestruction

Затем установите tarantool-perl:

$ git clone https://github.com/tarantool/tarantool-perl.git tarantool-perl
$ cd tarantool-perl
$ git submodule init
$ git submodule update --recursive
$ perl Makefile.PL
$ make
$ sudo make install

В следующем примере приводится полноценная программа на Perl, которая осуществляет вставку кортежа [99999,'BB'] в спейс space[999] с помощью API для языка Perl. Перед запуском проверьте, что экземпляр прослушивает порт localhost:3301, а в базе создан спейс examples, как предложено выше. Чтобы запустить программу, сохраните код в файл example.pl и выполните команду perl example.pl. Программа установит соединение, используя определение спейса. Затем она откроет сокет для соединения с экземпляром по адресу localhost:3301 и отправит запрос space_object:INSERT. Если запрос будет выполнен успешно, программа после этого закончит работу, не выводя никаких сообщений. Если Tarantool не запущен по адресу localhost и порт 3301 не прослушивается, то программа выведет сообщение об ошибке «Connection refused».

#!/usr/bin/perl
use DR::Tarantool ':constant', 'tarantool';
use DR::Tarantool ':all';
use DR::Tarantool::MsgPack::SyncClient;

my $tnt = DR::Tarantool::MsgPack::SyncClient->connect(
  host    => '127.0.0.1',                      # поиск сервера Tarantool по адресу localhost
  port    => 3301,                             # на порту 3301
  user    => 'guest',                          # имя пользователя; здесь же можно задать пароль через 'password=>...'
                                                        # для пользователя 'guest' пароль не задается
  spaces  => {
    999 => {                                   # определение спейса 'space[999]' ...
      name => 'examples',                      # имя спейса 'space[999]' = 'examples'
      default_type => 'STR',                   # если тип поля в 'space[999]' не задан, то = 'STR'
      fields => [ {                            # определение полей в спейсе 'space[999]' ...
          name => 'field1', type => 'NUM' } ], # имя поля space[999].field[1]='field1', тип ='NUM'
      indexes => {                             # определение индексов спейса 'space[999]' ...
        0 => {
          name => 'primary', fields => [ 'field1' ] } } } } );

$tnt->insert('examples' => [ 99999, 'BB' ]);

Из-за временных ограничений языка Perl вместо полей типа „string“ и „unsigned“ в тестовой программе используются поля типа „STR“ и „NUM“.

Эта программа — пример отправки единственного запроса. Чтобы узнать больше о том, как работать с Tarantool, пользуясь языком Perl, обратитесь к документации в репозитории tarantool-perl.

tarantool-php — официальный PHP-коннектор для Tarantool. Он не входит в репозиторий Tarantool, так что его необходимо устанавливать отдельно. Прочитайте инструкции по установке в файле коннектора README.

В следующем примере приводится полноценная программа на языке PHP, которая осуществляет вставку кортежа [99999,'BB'] в спейс examples с помощью API для языка PHP.

Перед запуском проверьте, что экземпляр прослушивает порт localhost:3301, а в базе создан спейс examples, как предложено выше.

Чтобы запустить программу, сохраните код в файл example.php и выполните следующую команду:

$ php -d extension=~/tarantool-php/modules/tarantool.so example.php

Программа откроет сокет для соединения с экземпляром по адресу localhost:3301 и отправит INSERT-запрос. При успешном выполнении запроса программа выведет сообщение «Insert succeeded».

Если такой кортеж уже существует, то программа выведет сообщение об ошибке: “Duplicate key exists in unique index „primary“ in space „examples“”.

<?php
$tarantool = new Tarantool('localhost', 3301);

try {
    $tarantool->insert('examples', [99999, 'BB']);
    echo "Insert succeeded\n";
} catch (Exception $e) {
    echo $e->getMessage(), "\n";
}

Эта программа — пример отправки единственного запроса. Чтобы узнать больше о том, как работать с Tarantool, пользуясь языком PHP, обратитесь к документации проекта tarantool-php на GitHub.

Кроме того, сообщество разработчиков поддерживает несколько проектов на GitHub. Среди них — проект tarantool-php, который включает в себя вариант коннектора, написанный на чистом PHP, модуль сопоставления объектов, администратор очередей и другие пакеты.

tarantool-python — официальный Python-коннектор для Tarantool. Он не входит в репозиторий Tarantool, так что его необходимо устанавливать отдельно (см. подробную информацию ниже).

В следующем примере приводится полноценная программа на языке Python, которая осуществляет вставку кортежа [99999,'Value','Value'] в спейс examples с помощью высокоуровневого API для языка Python.

#!/usr/bin/python
from tarantool import Connection

c = Connection("127.0.0.1", 3301)
result = c.insert("examples",(99999,'Value', 'Value'))
print result

Перед запуском тестовой программы сохраните ее код в файл example.py и установите коннектор tarantool-python. Чтобы установить его в директорию /usr, используйте команду pip install tarantool>0.4. Для этого требуются права уровня root. Вместо этого вы можете установить коннектор в директорию ~, т.е. в используемую по умолчанию директорию текущего пользователя. Для этого выполните команду pip install tarantool>0.4 --user.

Перед запуском проверьте, что экземпляр прослушивает порт localhost:3301, а в базе создан спейс examples, как предложено выше. Чтобы запустить тестовую программу, выполните команду python example.py. Программа установит соединение с сервером Tarantool и отправит запрос INSERT. Если запрос будет выполнен успешно, программа не сгенерирует исключений. Если окажется, что такой кортеж уже существует, то программа сгенерирует исключение tarantool.error.DatabaseError: (3, «Duplicate key exists in unique index „primary“ in space „examples“»).

Эта программа — пример отправки единственного запроса. Чтобы узнать больше о том, как работать с Tarantool, пользуясь языком Python, обратитесь к документации проекта tarantool-python на GitHub. А на странице проекта queue-python на GitHub вы найдете примеры использования Python API для работы с очередями сообщений в Tarantool.

Кроме того, сообщество разработчиков поддерживает следующие Python-коннекторы:

  • asynctnt с поддержкой asyncio
  • aiotarantool, также с поддержкой asyncio
  • gtarantool с поддержкой gevent, не обновляется

В этой таблице сравниваются функции коннекторов asynctnt, gtarantool и tarantool-python. aiotarantool здесь отсутствует, так как он устарел и больше не поддерживается.

Последнее обновление: февраль 2022

Параметры igorcoding/asynctnt shveenkov/gtarantool tarantool/tarantool-python
Лицензия Apache License 2.0 LGPL BSD-2
Поддерживается Да Нет (последнее обновление в 2018) Да
Известные проблемы тикет #18 (нет событийного цикла) Нет тикет #105 (распаковка бинарных данных)
Документация Есть (github.io) Нет Есть (tarantool.io и readthedocs (неактуальна))
Тестирование / CI / CD GitHub Actions Нет (есть тесты) AppVeyor (только на Windows)
GitHub-звезды 51 17 78
Статический анализ Есть (Flake8) Нет Нет
Способ упаковки pip pip deb, rpm, pip
Покрытие кода Да Нет Да
Поддержка асинхронного режима Есть, asyncio Есть, gevent (пример: test_gevent.py) Нет
Поддержка пакетной обработки Нет Нет Нет (тикет #55)
Обновление схемы Автоматическое (см. auto_refetch_schema) Автоматическое Автоматическое
Имена спейсов/индексов Да Да Да
Доступ к полям кортежей по именам Да Нет Нет
Поддержка SQL Есть (tests/test_op_sql.py) Нет Есть (tarantool/connection.py)
Интерактивные транзакции Нет Нет Нет (тикет #163)
Поддержка varbinary Нет Нет Есть (тикет #105)
Поддержка UUID Нет Нет Нет
Поддержка decimal Частично (в ветке v2) Нет Нет
Поддержка EXT_ERROR Нет Нет Нет
Поддержка datetime Нет Нет Нет
Возвращаемые значения box.session.push() Есть (см. push_subscribe option и docs/pushes.rst) Нет Нет
Настройки сессии Нет Нет Нет
Мягкое завершение Нет Нет Нет
IPROTO_ID (обнаружение поддерживаемых функций) Нет Нет Нет
Поддержка CRUD Нет Нет Нет
Прозрачная переотправка запроса Нет Нет Нет
Прозрачное переподключение Автоматическое переподключение Есть (reconnect_max_attempts, reconnect_delay) Есть (reconnect_max_attempts, reconnect_delay), проверка состояния соединения
Пул соединений Нет Нет Есть (циклическое восстановление после сбоев)
Поддержка PEP 249 — Python Database API Specification v2.0 Нет Нет Есть

Самый распространенный драйвер для Node.js — Node Tarantool driver. Он не входит в репозиторий Tarantool, так что его необходимо устанавливать отдельно. Проще всего установить его с помощью npm. Например, на Ubuntu установка драйвера будет выглядеть следующим образом (в этом примере менеджер пакетов npm уже установлен):

$ npm install tarantool-driver --global

В следующем примере приводится полноценная программа, написанная с использованием Node.js. Эта программа осуществляет вставку кортежа [99999,'BB'] в спейс space[999] с помощью API для Node.js. Перед запуском проверьте, что экземпляр прослушивает порт localhost:3301, а в базе создан спейс examples, как предложено выше. Чтобы запустить программу, сохраните код в файл example.rs и выполните команду node example.rs. Программа установит соединение, используя определение спейса. Затем она откроет сокет для соединения с экземпляром по адресу localhost:3301 и отправит INSERT-запрос. Если запрос будет выполнен успешно, программа выведет сообщение «Insert succeeded». Если Tarantool не запущен по адресу localhost и порт 3301 не прослушивается, то программа выведет сообщение об ошибке “Connect failed”. Если у пользователя „guest“ нет прав на установку соединения, программа выведет сообщение об ошибке «Auth failed». Если запрос на вставку по какой-либо причине не сработает (например, такой кортеж уже существует), то программа выведет сообщение об ошибке «Insert failed».

var TarantoolConnection = require('tarantool-driver');
var conn = new TarantoolConnection({port: 3301});
var insertTuple = [99999, "BB"];
conn.connect().then(function() {
    conn.auth("guest", "").then(function() {
        conn.insert(999, insertTuple).then(function() {
            console.log("Insert succeeded");
            process.exit(0);
    }, function(e) { console.log("Insert failed");  process.exit(1); });
    }, function(e) { console.log("Auth failed");    process.exit(1); });
    }, function(e) { console.log("Connect failed"); process.exit(1); });

Эта программа — пример отправки единственного запроса. Чтобы узнать больше о том, как работать с Tarantool, пользуясь Node.js, обратитесь к документации в репозитории драйвера для Node.js.

Самый распространенный драйвер для C# — progaudi.tarantool, который раньше назывался tarantool-csharp. Он не входит в репозиторий Tarantool, так что его необходимо устанавливать отдельно. Создатели драйвера рекомендуют кроссплатформенную установку с помощью Nuget.

По аналогии с остальными инструкциями в главе приводится способ установки драйвера напрямую на Ubuntu 16.04.

  1. Установите среду .NET Core от Microsoft. Следуйте инструкциям по установке .NET Core.

Примечание

  • Так как на Linux и macOS поддерживается только .NET Core, установить коннектор с помощью Mono или .NET от xbuild невозможно.
  • Прочитайте Условия лицензионного соглашения Microsoft, поскольку оно не похоже на обычные соглашения для ПО с открытым кодом. Во время установки появится сообщение, что ПО может собирать информацию о вас и о том, как вы используете программу: «This software may collect information about you and your use of the software, and send that to Microsoft». Тем не менее вы можете задать переменные окружения, чтобы не участвовать в сборе телеметрических данных.
  1. Создайте новый проект консольного приложения:

    $ cd ~
    $ mkdir progaudi.tarantool.test
    $ cd progaudi.tarantool.test
    $ dotnet new console
    
  2. Добавьте пакет progaudi.tarantool:

    $ dotnet add package progaudi.tarantool
    
  3. Замените код в Program.cs на следующий:

    $ cat <<EOT > Program.cs
    using System;
    using System.Threading.Tasks;
    using ProGaudi.Tarantool.Client;
    
    public class HelloWorld
    {
      static public void Main ()
      {
        Test().GetAwaiter().GetResult();
      }
      static async Task Test()
      {
        var box = await Box.Connect("127.0.0.1:3301");
        var schema = box.GetSchema();
        var space = await schema.GetSpace("examples");
        await space.Insert((99999, "BB"));
      }
    }
    EOT
    
  4. Соберите и запустите приложение.

    Перед запуском проверьте, что экземпляр прослушивает порт localhost:3301, а в базе создан спейс examples, как предложено выше.

    $ dotnet restore
    $ dotnet run
    

    Программа выполнит следующие действия:

    • Установит соединение, используя определение спейса.
    • Откроет сокет для соединения с сервером Tarantool по адресу localhost:3301.
    • Отправит INSERT-запрос. Если он будет выполнен успешно, программа закончит работу, не выводя сообщений.

    Если Tarantool не запущен по адресу localhost, порт 3301 не прослушивается, у пользователя „guest“ нет прав на установку соединения или запрос на вставку по какой-либо причине не сработал, то программа выведет сообщение об ошибке и сопутствующую информацию (в частности, трассировку стека).

Эта программа — пример отправки единственного запроса. Чтобы узнать больше о том, как работать с Tarantool, пользуясь PHP API, обратитесь к документации проекта tarantool-php на GitHub.

В этом разделе приведены два примера использования высокоуровневого API Tarantool для языка C.

В следующем примере приводится полноценная программа на языке C, которая осуществляет вставку кортежа :code:[99999,'B'] в спейс examples с помощью высокоуровневого API для языка C.

#include <stdio.h>
#include <stdlib.h>

#include <tarantool/tarantool.h>
#include <tarantool/tnt_net.h>
#include <tarantool/tnt_opt.h>

void main() {
   struct tnt_stream *tnt = tnt_net(NULL);          /* См. раздел "Настройка" ниже */
   tnt_set(tnt, TNT_OPT_URI, "localhost:3301");
   if (tnt_connect(tnt) < 0) {                      /* См. раздел "Соединение" ниже */
       printf("Connection refused\n");
       exit(-1);
   }
   struct tnt_stream *tuple = tnt_object(NULL);     /* См. раздел "Формирование запроса" ниже */
   tnt_object_format(tuple, "[%d%s]", 99999, "B");
   tnt_insert(tnt, 999, tuple);                     /* См. раздел "Отправка запроса" ниже */
   tnt_flush(tnt);
   struct tnt_reply reply;  tnt_reply_init(&reply); /* См. раздел "Получение ответа" ниже */
   tnt->read_reply(tnt, &reply);
   if (reply.code != 0) {
       printf("Insert failed %lu.\n", reply.code);
   }
   tnt_close(tnt);                                  /* См. раздел "Завершение" ниже */
   tnt_stream_free(tuple);
   tnt_stream_free(tnt);
}

Скопируйте код программы в файл example.c и установите коннектор tarantool-c. Вот один из способов установки tarantool-c (на Ubuntu):

$ git clone git://github.com/tarantool/tarantool-c.git ~/tarantool-c
$ cd ~/tarantool-c
$ git submodule init
$ git submodule update
$ cmake .
$ make
$ make install

Чтобы скомпилировать и слинковать программу, выполните следующую команду:

$ # иногда это необходимо:
$ export LD_LIBRARY_PATH=/usr/local/lib
$ gcc -o example example.c -ltarantool

Перед запуском проверьте, что экземпляр прослушивает порт localhost:3301, а в базе создан спейс examples, как предложено выше. Чтобы запустить программу, выполните команду ./example. Программа установит соединение с экземпляром Tarantool и отправит запрос. Если Tarantool не запущен по адресу localhost и порт 3301 не прослушивается, то программа выведет сообщение об ошибке “Connection refused”. Если запрос на вставку не будет выполнен, программа выведет сообщение «Insert failed» и код ошибки. Все коды ошибок можно найти в исходном файле /src/box/errcode.h.

Ниже вы найдете подробные комментарии к коду тестовой программы.

Настройка

Настройка начинается с создания потока.

struct tnt_stream *tnt = tnt_net(NULL);
tnt_set(tnt, TNT_OPT_URI, "localhost:3301");

В нашей программе поток назван tnt. Перед установкой соединения с потоком tnt нужно задать ряд опций. Самая важная из них — TNT_OPT_URI. Для этой опции указан URI localhost:3301: по этому адресу должно быть настроено прослушивание на стороне экземпляра Tarantool.

Описание функции:

struct tnt_stream *tnt_net(struct tnt_stream *s)
int tnt_set(struct tnt_stream *s, int option, variant option-value)

Соединение

Теперь, когда мы создали поток tnt и связали его с конкретным URI, наша программа может установить соединение с экземпляром.

if (tnt_connect(tnt) < 0)
   { printf("Connection refused\n"); exit(-1); }

Описание функции:

int tnt_connect(struct tnt_stream *s)

Попытка соединения может оказаться неудачной по разным причинам, например если Tarantool-сервер не запущен или в строке URI указан неверный пароль. В случае неудачи функция вернет -1.

Создание запроса

В большинстве запросов требуется передавать структурированные данные, например содержимое кортежа.

struct tnt_stream *tuple = tnt_object(NULL);
tnt_object_format(tuple, "[%d%s]", 99999, "B");

В этом примере программы формируется запрос INSERT, а кортеж содержит целое число и строку. Это простой набор значений без каких-либо вложенных структур или массивов. Так что передаваемые значения можно указать самым простым образом — аналогично тому, как это сделано в стандартной C-функции printf(): целое число %d, строка %s, числовое значение, указатель на строковое значение.

Описание функции:

ssize_t tnt_object_format(struct tnt_stream *s, const char *fmt, ...)

Отправка запроса

Отправка запросов на изменение данных в базе выполняется аналогично тому, как это реализовано в библиотеке Tarantool box.

tnt_insert(tnt, 999, tuple);
tnt_flush(tnt);

В этом примере выполняется INSERT-запрос. Передается поток tnt, который использовался для установки соединения, и поток tuple, ранее настроенный с помощью функции tnt_object_format().

Описание функции:

ssize_t tnt_insert(struct tnt_stream *s, uint32_t space, struct tnt_stream *tuple)
ssize_t tnt_replace(struct tnt_stream *s, uint32_t space, struct tnt_stream *tuple)
ssize_t tnt_select(struct tnt_stream *s, uint32_t space, uint32_t index,
                   uint32_t limit, uint32_t offset, uint8_t iterator,
                   struct tnt_stream *key)
ssize_t tnt_update(struct tnt_stream *s, uint32_t space, uint32_t index,
                   struct tnt_stream *key, struct tnt_stream *ops)

Получение ответа

В большинстве случаев ответ клиенту содержит информацию о том, был ли запрос выполнен успешно, а также набор кортежей.

struct tnt_reply reply;  tnt_reply_init(&reply);
tnt->read_reply(tnt, &reply);
if (reply.code != 0)
   { printf("Insert failed %lu.\n", reply.code); }

Эта программа проверяет, был ли запрос выполнен успешно, но никак не интерпретирует оставшуюся часть ответа.

Описание функции:

struct tnt_reply *tnt_reply_init(struct tnt_reply *r)
tnt->read_reply(struct tnt_stream *s, struct tnt_reply *r)
void tnt_reply_free(struct tnt_reply *r)

Завершение

По окончании сессии необходимо завершить соединение, созданное с помощью функции tnt_connect(), и удалить объекты, созданные на этапе настройки.

tnt_close(tnt);
tnt_stream_free(tuple);
tnt_stream_free(tnt);

Описание функции:

void tnt_close(struct tnt_stream *s)
void tnt_stream_free(struct tnt_stream *s)

В следующем примере приводится полноценная программа на языке C, которая осуществляет выборку по индекс-ключу [99999] из спейса examples с помощью высокоуровневого API для языка C. Результаты выводятся с помощью функций из библиотеки MsgPuck, позволяющих декодировать массивы значений в формате MessagePack.

#include <stdio.h>
#include <stdlib.h>
#include <tarantool/tarantool.h>
#include <tarantool/tnt_net.h>
#include <tarantool/tnt_opt.h>

#define MP_SOURCE 1
#include <msgpuck.h>

void main() {
    struct tnt_stream *tnt = tnt_net(NULL);
    tnt_set(tnt, TNT_OPT_URI, "localhost:3301");
    if (tnt_connect(tnt) < 0) {
        printf("Connection refused\n");
        exit(1);
    }
    struct tnt_stream *tuple = tnt_object(NULL);
    tnt_object_format(tuple, "[%d]", 99999); /* кортеж tuple = ключ для поиска */
    tnt_select(tnt, 999, 0, (2^32) - 1, 0, 0, tuple);
    tnt_flush(tnt);
    struct tnt_reply reply; tnt_reply_init(&reply);
    tnt->read_reply(tnt, &reply);
    if (reply.code != 0) {
        printf("Select failed.\n");
        exit(1);
    }
    char field_type;
    field_type = mp_typeof(*reply.data);
    if (field_type != MP_ARRAY) {
        printf("no tuple array\n");
        exit(1);
    }
    long unsigned int row_count;
    uint32_t tuple_count = mp_decode_array(&reply.data);
    printf("tuple count=%u\n", tuple_count);
    unsigned int i, j;
    for (i = 0; i < tuple_count; ++i) {
        field_type = mp_typeof(*reply.data);
        if (field_type != MP_ARRAY) {
            printf("no field array\n");
            exit(1);
        }
        uint32_t field_count = mp_decode_array(&reply.data);
        printf("  field count=%u\n", field_count);
        for (j = 0; j < field_count; ++j) {
            field_type = mp_typeof(*reply.data);
            if (field_type == MP_UINT) {
                uint64_t num_value = mp_decode_uint(&reply.data);
                printf("    value=%lu.\n", num_value);
            } else if (field_type == MP_STR) {
                const char *str_value;
                uint32_t str_value_length;
                str_value = mp_decode_str(&reply.data, &str_value_length);
                printf("    value=%.*s.\n", str_value_length, str_value);
            } else {
                printf("wrong field type\n");
                exit(1);
            }
        }
    }
    tnt_close(tnt);
    tnt_stream_free(tuple);
    tnt_stream_free(tnt);
}

Как и в первом примере, сохраните исходный код программы в файле example2.c.

Чтобы скомпилировать и слинковать программу, выполните следующую команду:

$ gcc -o example2 example2.c -ltarantool

Чтобы запустить программу, выполните команду ./example2.

Эти две программы представляют собой примеры отправки двух запросов. Чтобы узнать больше о том, как работать с Tarantool, пользуясь API для языка C, обратитесь к документации проекта tarantool-c на GitHub.

При работе с любым Tarantool-коннектором функции, вызванные Tarantool, возвращают значения в формате MsgPack. Для функций, вызываемых через API коннектора, формат возвращаемых значений следующий: скалярные значения возвращаются в виде кортежей (идентификатор типа в формате MsgPack, затем значение); все прочие (не скалярные) значения возвращаются в виде групп кортежей (идентификатор массива в формате MsgPack, затем скалярные значения). Если функция вызывается в рамках бинарного протокола (с помощью команды eval), а не через API коннектора, формат возвращаемых ею значений не меняется.

В примере ниже создается Lua-функция. Поскольку эту функцию будет вызывать внешний пользователь „guest“ user, необходимо с помощью grant настроить права на исполнение. Функция возвращает пустой массив, строку-скаляр, два логических значения и короткое целое число. Значения соответствуют приведенным в таблице стандартных типов в MsgPack-кодировке.

tarantool> box.cfg{listen=3301}
2016-03-03 18:45:52.802 [27381] main/101/interactive I> ready to accept requests
---
...
tarantool> function f() return {},'a',false,true,127; end
---
...
tarantool> box.schema.func.create('f')
---
...
tarantool> box.schema.user.grant('guest','execute','function','f')
---
...

Следующая программа на C вызывает эту функцию. Хотя в примере приводится код на C, результат будет одинаковым, на каком бы языке ни была написана программа: Perl, PHP, Python, Go или Java.

#include <stdio.h>
#include <stdlib.h>
#include <tarantool/tarantool.h>
#include <tarantool/tnt_net.h>
#include <tarantool/tnt_opt.h>
void main() {
  struct tnt_stream *tnt = tnt_net(NULL);              /* Настройка */
  tnt_set(tnt, TNT_OPT_URI, "localhost:3301");
   if (tnt_connect(tnt) < 0) {                         /* Соединение */
       printf("Connection refused\n");
       exit(-1);
   }
   struct tnt_stream *arg; arg = tnt_object(NULL);     /* Формирование запроса */
   tnt_object_add_array(arg, 0);
   struct tnt_request *req1 = tnt_request_call(NULL);  /* Вызов функции f() */
   tnt_request_set_funcz(req1, "f");
   uint64_t sync1 = tnt_request_compile(tnt, req1);
   tnt_flush(tnt);                                     /* Отправка запроса */
   struct tnt_reply reply;  tnt_reply_init(&reply);    /* Получение ответа */
   tnt->read_reply(tnt, &reply);
   if (reply.code != 0) {
     printf("Call failed %lu.\n", reply.code);
     exit(-1);
   }
   const unsigned char *p= (unsigned char*)reply.data; /* Вывод ответа */
   while (p < (unsigned char *) reply.data_end)
   {
     printf("%x ", *p);
     ++p;
   }
   printf("\n");
   tnt_close(tnt);                                     /* Завершение */
   tnt_stream_free(arg);
   tnt_stream_free(tnt);
}

По завершении программа выведет на экран следующие значения:

dd 0 0 0 5 90 91 a1 61 91 c2 91 c3 91 7f

Первые пять байт, dd 0 0 0 5, представляют собой фрагмент данных в формате MsgPack и обозначают 32-битный заголовок массива со значением 5 (см. спецификацию формата MsgPack). Остальные значения описаны в таблице стандартных типов в MsgPack-кодировке.