1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/sogou-workflow

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
tutorial-12-mysql_cli.md 15 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 01.03.2025 10:20 552780a

Асинхронный клиент MySQL: mysql_cli

Пример кода

tutorial-12-mysql_cli.cc

Описание mysql_cli

Пример использования mysql_cli в учебниках аналогичен официальному клиенту. Это асинхронный командно-строчный клиент MySQL.

Способ запуска: ./mysql_cli <URL>

После запуска можно вводить команды MySQL прямо в терминале для взаимодействия с базой данных. Для выхода используйте quit или Ctrl-C.

Формат URL MySQL

mysql://имя_пользователя:пароль@хост:порт/имя_базы?character_set=charset&character_set_results=charset

  • Если требуется SSL-соединение, схема должна быть установлена как mysqls://. Поддерживается MySQL серверами версии OnClickListener onClick="window.location.href='/docs/about-upstream.md'" version="5.7">5.7 и выше;

  • имя пользователя и пароль должны быть указаны при необходимости. Если пароль содержит специальные символы, они должны быть экранированы перед добавлением в URL;

// Пароль: @@@@####
std::string url = "mysql://root:" + StringUtil::url_encode_component("@@@@####") + "@127.0.0.1";
  • порт по умолчанию равен 3306;

  • имя_базы — это имя базы данных, которую вы хотите использовать. Обычно рекомендуется указывать его, если SQL-запросы оперируют одной базой данных;

  • если вам необходим выбор upstream, обратитесь к документации по upstream;

  • character_set представляет собой набор символов клиента, эквивалентный параметру --default-character-set при запуске официального клиента. По умолчанию utf8, подробнее см. MySQL официальную документацию character-set.html.

  • character_set_results представляет собой набор символов для клиента, соединения и результатов. Если вы хотите использовать SET NAME для указания этих наборов символов внутри SQL-запроса, то следует указать их здесь в URL.

Пример URL MySQL:

mysql://root:пароль@127.0.0.1

mysql://@test.mysql.com:3306/db1?character_set=utf8&character_set_results=utf8

mysqls://localhost/db1?character_set=big5

Создание и запуск задач MySQL

Пользователи могут создавать задачи MySQL с помощью WFTaskFactory. Интерфейсы создания и вызова обратных функций аналогичны другим задачам workflow:

using mysql_callback_t = std::function<void (WFMySQLTask *)>;

WFMySQLTask *create_mysql_task(const std::string& url, int retry_max, mysql_callback_t callback);

void set_query(const std::string& query);

После создания объекта WFMySQLTask пользователи могут использовать метод set_query() для записи SQL-запросов.

Если метод set_query() не был вызван, но задача была запущена, пользователи получат ошибку WFT_ERR_MYSQL_QUERY_NOT_SET в обратной функции.

Другие элементы, такие как обратные функции, последовательность, данные пользователя и прочее, используются аналогично другим задачам workflow.

Пример использования:

int main(int argc, char *argv[])
{
    ...
    WFMySQLTask *task = WFTaskFactory::create_mysql_task(url, RETRY_MAX, mysql_callback);
    task->get_req()->set_query("SHOW TABLES;");
    ...
    task->start();
    ...
}

Поддерживаемые команды

На данный момент поддерживаются команды COM_QUERY, что позволяет выполнять основные операции CRUD, создавать и удалять базы данных, таблицы, подготовленные запросы, процедуры и транзакции.

Интерактивные команды не поддерживают выбор базы данных (команда USE), поэтому для выполнения операций через несколько баз данных используется способ указания конкретной базы данных и таблицы в виде db_name.table_name.

Все остальные команды могут быть объединены вместе с использованием set_query() и переданы WFMySQLTask (например, INSERT/UPDATE/SELECT/DELETE/PREPARE/CALL).

Объединённые команды будут выполняться последовательно до первой ошибки, все предыдущие команды будут успешно выполнены.

Пример:

req->set_query("SELECT * FROM table1; CALL procedure1(); INSERT INTO table3 (id) VALUES (1);");

Анализ результатов

Аналогично другим задачам workflow, результаты можно получить с помощью task->get_resp(), который возвращает объект MySQLResponse. Мы можем использовать MySQLResultCursor для прохождения по результатам. Дополнительные интерфейсы доступны в MySQLResult.h.

Ответ на один запрос состоит из трёхмерной структуры данных:

  • В ответе может содержаться одна или более результатных множеств (result sets);
  • Каждое результатное множество может иметь тип MYSQL_STATUS_GET_RESULT или MYSQL_STATUS_OK;
  • Тип MYSQL_STATUS_GET_RESULT представляет собой чтение, где одно множество представляет собой двумерную таблицу, полученную от чтения;
  • Тип MYSQL_STATUS_OK представляет собой запись, где одно множество показывает успешность операции записи.

Два типа результатных множеств можно проверять с помощью cursor->get_cursor_status().

MYSQL_STATUS_GET_RESULT MYSQL_STATUS_OK
SQL-запрос SELECT (включая каждый SELECT в хранимых процедурах) INSERT / UPDATE / DELETE / ...
Значение Чтение, одно множество представляет собой двумерную таблицу Запись, одно множество показывает успешность операции записи
Основные интерфейсы fetch_fields();fetch_row(&row_arr);... get_insert_id();get_affected_rows();...

При наличии ошибок в объединённых запросах, можно использовать MySQLResultCursor для получения нескольких результатных множеств, правильно выполненных ранее, а также использовать resp->get_error_code() и resp->get_error_msg() для получения информации об ошибке.

Хранимая процедура, содержащая n команд SELECT, будет возвращать n результатных множеств типа MYSQL_STATUS_GET_RESULT и одно множество типа MYSQL_STATUS_OK, которое можно игнорировать.

Основные шаги анализа результатов следуют в порядке:

  1. Проверьте состояние задачи (состояние уровня связи): пользователи могут проверить успех выполнения задачи, сравнивая task->get_state() с WFT_STATE_SUCCESS;

  2. Проверьте тип пакета ответа (состояние парсинга ответа): используйте resp->get_packet_type() для проверки типа последнего MySQL-запроса. Некоторые распространённые типы:

    • MYSQL_PACKET_OK: Успешно, можно использовать курсор для прохождения по результатам;
    • MYSQL_PACKET_EOF: Успешно, можно использовать курсор для прохождения по результатам;
    • MYSQL_PACKET_ERROR: Ошибка или частичная ошибка, успешные части можно использовать для прохождения по результатам;## WFMySQLConnection

Поскольку мы работаем с высокопроизводительной асинхронной клиентской частью, это означает, что количество соединений с одним сервером может превышать одно. В то же время операции транзакций и подготовленных запросов в MySQL имеют состояние, поэтому для обеспечения того, чтобы каждая транзакция или подготовленный запрос использовала уникальное соединение, пользователи могут использовать нашу двойную завернутую реализацию WFMySQLConnection. Каждое соединение WFMySQLConnection гарантирует использование одного уникального соединения. Подробнее см. WFMySQLConnection.h.

1. Создание и инициализация WFMySQLConnection

При создании объекта WFMySQLConnection требуется передать идентификатор, который будет использоваться внутри при вызовах.

Инициализация требует передачи URL, после чего все задачи, созданные на этом соединении, больше не будут нуждаться в указании URL.

class WFMySQLConnection
{
public:
    WFMySQLConnection(int id);
    int init(const std::string& url);
    ...
};

2. Создание задач и закрытие соединения

Чтобы создать задачу, используйте метод create_query_task() и передайте SQL-запрос и обратный вызов. Эта задача гарантировано будет отправлена через это конкретное соединение.

Когда соединение больше не используется, его можно закрыть, используя метод create_disconnect_task(). Это позволит освободить ресурсы.

class WFMySQLConnection
{
public:
    ...
    WFMySQLTask *create_query_task(const std::string& query,
                                   mysql_callback_t callback);
    WFMySQLTask *create_disconnect_task(mysql_callback_t callback);
}

Объект WFMySQLConnection работает как двойная завернутая реализация. Мы рекомендуем не хранить объекты завершенных задач, следующий код полностью легален:

    WFMySQLConnection *conn = new WFMySQLConnection(1234);
    conn->init(url);
    auto *task = conn->create_query_task("SELECT * FROM table", my_callback);
    conn->deinit();
    delete conn;
    task->start();

3. Учтите следующее

Не следует бесконечно генерировать новые идентификаторы для создания новых соединений, так как каждый идентификатор занимает небольшую область памяти. Когда соединение больше не используется, его можно вернуть внутреннему пулу соединений, вместо того чтобы создавать и запускать задачу отключения.Если несколько задач начинаются параллельно на одном соединении, они могут получить ошибку EAGAIN.

Если во время выполнения транзакции произойдет разрыв соединения до COMMIT или ROLLBACK, соединение будет автоматически восстановлено фреймворком. При следующей попытке запроса пользователю будет выдана ошибка ECONNRESET. Незавершенная транзакция станет недействительной и потребует повторного выполнения.

4. Предварительная подготовка

Пользователи также могут использовать WFMySQLConnection для выполнения предварительной подготовки PREPARE, что позволяет защититься от SQL-инъекций. Если соединение было восстановлено, будет получена ошибка ECONNRESET.

5. Полный пример

WFMySQLConnection conn(1);
conn.init("mysql://root@127.0.0.1/test");

// тест транзакции
const char *query = "BEGIN;";
WFMySQLTask *t1 = conn.create_query_task(query, task_callback);
query = "SELECT * FROM check_tiny FOR UPDATE;";
WFMySQLTask *t2 = conn.create_query_task(query, task_callback);
query = "INSERT INTO check_tiny VALUES (8);";
WFMySQLTask *t3 = conn.create_query_task(query, task_callback);
query = "COMMIT;";
WFMySQLTask *t4 = conn.create_query_task(query, task_callback);
WFMySQLTask *t5 = conn.create_disconnect_task(task_callback);
((*t1) > t2 > t3 > t4 > t5).start();

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/sogou-workflow.git
git@api.gitlife.ru:oschina-mirror/sogou-workflow.git
oschina-mirror
sogou-workflow
sogou-workflow
master