Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
bb455cd
feat(mysql): clean-room MySQL authentication codec
rajvarun77 Jun 4, 2026
91c9df8
feat(mysql): full MySQL text protocol with transactions and prepared …
rajvarun77 Jun 4, 2026
a292dbc
test(mysql): clean-room integration tests + prepared-stmt error fix +…
rajvarun77 Jun 4, 2026
9c4c1f2
refactor(mysql): consolidate sources under policy/mysql/; drop inheri…
rajvarun77 Jun 4, 2026
0bb55ee
fix(mysql): address code-review findings (binary protocol, auth, edge…
rajvarun77 Jun 4, 2026
f152fcb
fix(mysql): address review findings — license header, flag typo, head…
rajvarun77 Jun 6, 2026
679e73e
fix(mysql): use the standard ASF license header on new example/test f…
rajvarun77 Jun 6, 2026
0fac6c5
chore(mysql): add diagnostic error logs on silent failure paths
rajvarun77 Jun 9, 2026
a4aef4c
chore(mysql): demote non-fatal diagnostic logs to LOG(WARNING)
rajvarun77 Jun 9, 2026
7e79601
fix(test): make AuthCase a C++11 aggregate so push_back{...} compiles
rajvarun77 Jun 9, 2026
34f6807
Merge remote-tracking branch 'origin/master' into mysql-text-protocol…
rajvarun77 Jun 9, 2026
bf59627
fix(mysql,ci): run integration tests against a real CI-provisioned my…
rajvarun77 Jun 9, 2026
d1e259f
address review comments: bind-sock flag, non-copyable response, naming
rajvarun77 Jun 9, 2026
b6aedc9
test(mysql): move unittests from mysql/ subdir into test/
rajvarun77 Jun 10, 2026
3386fff
test(mysql): drop run_tests.sh mysql/ subdir entry after move
rajvarun77 Jun 10, 2026
523b9de
Merge remote-tracking branch 'origin/master' into mysql-text-protocol…
rajvarun77 Jun 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
556 changes: 556 additions & 0 deletions docs/cn/mysql_client.md

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions example/mysql_c++/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
cmake_minimum_required(VERSION 2.8.10)
project(mysql_c++ C CXX)

# Install dependencies:
# With apt:
# sudo apt-get install libreadline-dev
# sudo apt-get install ncurses-dev
# With yum:
# sudo yum install readline-devel
# sudo yum install ncurses-devel

option(EXAMPLE_LINK_SO "Whether examples are linked dynamically" OFF)

execute_process(
COMMAND bash -c "find ${PROJECT_SOURCE_DIR}/../.. -type d -regex \".*output/include$\" | head -n1 | xargs dirname | tr -d '\n'"
OUTPUT_VARIABLE OUTPUT_PATH
)

set(CMAKE_PREFIX_PATH ${OUTPUT_PATH})

include(FindThreads)
include(FindProtobuf)

# Search for libthrift* by best effort. If it is not found and brpc is
# compiled with thrift protocol enabled, a link error would be reported.
find_library(THRIFT_LIB NAMES thrift)
if (NOT THRIFT_LIB)
set(THRIFT_LIB "")
endif()
find_library(THRIFTNB_LIB NAMES thriftnb)
if (NOT THRIFTNB_LIB)
set(THRIFTNB_LIB "")
endif()

find_path(BRPC_INCLUDE_PATH NAMES brpc/server.h)
if(EXAMPLE_LINK_SO)
find_library(BRPC_LIB NAMES brpc)
else()
find_library(BRPC_LIB NAMES libbrpc.a brpc)
endif()
if((NOT BRPC_INCLUDE_PATH) OR (NOT BRPC_LIB))
message(FATAL_ERROR "Fail to find brpc")
endif()
include_directories(${BRPC_INCLUDE_PATH})

find_path(GFLAGS_INCLUDE_PATH gflags/gflags.h)
find_library(GFLAGS_LIBRARY NAMES gflags libgflags)
if((NOT GFLAGS_INCLUDE_PATH) OR (NOT GFLAGS_LIBRARY))
message(FATAL_ERROR "Fail to find gflags")
endif()
include_directories(${GFLAGS_INCLUDE_PATH})

execute_process(
COMMAND bash -c "grep \"namespace [_A-Za-z0-9]\\+ {\" ${GFLAGS_INCLUDE_PATH}/gflags/gflags_declare.h | head -1 | awk '{print $2}' | tr -d '\n'"
OUTPUT_VARIABLE GFLAGS_NS
)
if(${GFLAGS_NS} STREQUAL "GFLAGS_NAMESPACE")
execute_process(
COMMAND bash -c "grep \"#define GFLAGS_NAMESPACE [_A-Za-z0-9]\\+\" ${GFLAGS_INCLUDE_PATH}/gflags/gflags_declare.h | head -1 | awk '{print $3}' | tr -d '\n'"
OUTPUT_VARIABLE GFLAGS_NS
)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
include(CheckFunctionExists)
CHECK_FUNCTION_EXISTS(clock_gettime HAVE_CLOCK_GETTIME)
if(NOT HAVE_CLOCK_GETTIME)
set(DEFINE_CLOCK_GETTIME "-DNO_CLOCK_GETTIME_IN_MAC")
endif()
endif()

set(CMAKE_CPP_FLAGS "${DEFINE_CLOCK_GETTIME} -DGFLAGS_NS=${GFLAGS_NS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CPP_FLAGS} -DNDEBUG -O2 -D__const__= -pipe -W -Wall -Wno-unused-parameter -fPIC -fno-omit-frame-pointer")

if(CMAKE_VERSION VERSION_LESS "3.1.3")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
else()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()

find_path(LEVELDB_INCLUDE_PATH NAMES leveldb/db.h)
find_library(LEVELDB_LIB NAMES leveldb)
if ((NOT LEVELDB_INCLUDE_PATH) OR (NOT LEVELDB_LIB))
message(FATAL_ERROR "Fail to find leveldb")
endif()
include_directories(${LEVELDB_INCLUDE_PATH})

find_library(SSL_LIB NAMES ssl)
if (NOT SSL_LIB)
message(FATAL_ERROR "Fail to find ssl")
endif()

find_library(CRYPTO_LIB NAMES crypto)
if (NOT CRYPTO_LIB)
message(FATAL_ERROR "Fail to find crypto")
endif()

# find_path(MYSQL_INCLUDE_PATH NAMES mysql/mysql.h)
# find_library(MYSQL_LIB NAMES mysqlclient)
# if (NOT MYSQL_LIB)
# message(FATAL_ERROR "Fail to find mysqlclient")
# endif()
# include_directories(${MYSQL_INCLUDE_PATH})

set(DYNAMIC_LIB
${CMAKE_THREAD_LIBS_INIT}
${GFLAGS_LIBRARY}
${PROTOBUF_LIBRARIES}
${LEVELDB_LIB}
${SSL_LIB}
${CRYPTO_LIB}
${THRIFT_LIB}
${THRIFTNB_LIB}
# ${MYSQL_LIB}
dl
)

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(DYNAMIC_LIB ${DYNAMIC_LIB}
pthread
"-framework CoreFoundation"
"-framework CoreGraphics"
"-framework CoreData"
"-framework CoreText"
"-framework Security"
"-framework Foundation"
"-Wl,-U,_MallocExtension_ReleaseFreeMemory"
"-Wl,-U,_ProfilerStart"
"-Wl,-U,_ProfilerStop")
endif()

add_executable(mysql_cli mysql_cli.cpp)
add_executable(mysql_tx mysql_tx.cpp)
add_executable(mysql_stmt mysql_stmt.cpp)
add_executable(mysql_press mysql_press.cpp)
# add_executable(mysqlclient_press mysqlclient_press.cpp)

set(AUX_LIB readline ncurses)
target_link_libraries(mysql_cli ${BRPC_LIB} ${DYNAMIC_LIB} ${AUX_LIB})
target_link_libraries(mysql_tx ${BRPC_LIB} ${DYNAMIC_LIB})
target_link_libraries(mysql_stmt ${BRPC_LIB} ${DYNAMIC_LIB})
target_link_libraries(mysql_press ${BRPC_LIB} ${DYNAMIC_LIB})
# target_link_libraries(mysqlclient_press ${BRPC_LIB} ${DYNAMIC_LIB})
173 changes: 173 additions & 0 deletions example/mysql_c++/mysql_cli.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// A brpc based command-line interface to talk with mysql-server

#include <signal.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <gflags/gflags.h>
#include <butil/logging.h>
#include <brpc/channel.h>
#include "brpc/policy/mysql/mysql.h"
#include "brpc/policy/mysql/mysql_authenticator.h"

DEFINE_string(connection_type, "pooled", "Connection type. Available values: pooled, short");
DEFINE_string(server, "127.0.0.1", "IP Address of server");
DEFINE_int32(port, 3306, "Port of server");
DEFINE_string(user, "brpcuser", "user name");
DEFINE_string(password, "12345678", "password");
DEFINE_string(schema, "brpc_test", "schema");
DEFINE_string(params, "", "params");
DEFINE_string(collation, "utf8mb4_general_ci", "collation");
DEFINE_int32(timeout_ms, 5000, "RPC timeout in milliseconds");
DEFINE_int32(connect_timeout_ms, 5000, "RPC timeout in milliseconds");
DEFINE_int32(max_retry, 0, "Max retries(not including the first RPC)");

namespace brpc {
const char* logo();
}

// Send `command' to mysql-server via `channel'
static bool access_mysql(brpc::Channel& channel, const char* command) {
brpc::MysqlRequest request;
if (!request.Query(command)) {
LOG(ERROR) << "Fail to add command";
return false;
}
brpc::MysqlResponse response;
brpc::Controller cntl;
channel.CallMethod(NULL, &cntl, &request, &response, NULL);
if (!cntl.Failed()) {
std::cout << response << std::endl;
} else {
LOG(ERROR) << "Fail to access mysql, " << cntl.ErrorText();
return false;
}
return true;
}

// For freeing the memory returned by readline().
struct Freer {
void operator()(char* mem) {
free(mem);
}
};

static void dummy_handler(int) {}

// The getc for readline. The default getc retries reading when meeting
// EINTR, which is not what we want.
static bool g_canceled = false;
static int cli_getc(FILE* stream) {
int c = getc(stream);
if (c == EOF && errno == EINTR) {
g_canceled = true;
return '\n';
}
return c;
}

int main(int argc, char* argv[]) {
// Parse gflags. We recommend you to use gflags as well.
GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true);

// A Channel represents a communication line to a Server. Notice that
// Channel is thread-safe and can be shared by all threads in your program.
brpc::Channel channel;

// Initialize the channel, NULL means using default options.
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_MYSQL;
options.connection_type = FLAGS_connection_type;
options.timeout_ms = FLAGS_timeout_ms /*milliseconds*/;
options.connect_timeout_ms = FLAGS_connect_timeout_ms;
options.max_retry = FLAGS_max_retry;
options.auth = new brpc::policy::MysqlAuthenticator(
FLAGS_user, FLAGS_password, FLAGS_schema, FLAGS_params, FLAGS_collation);
if (channel.Init(FLAGS_server.c_str(), FLAGS_port, &options) != 0) {
LOG(ERROR) << "Fail to initialize channel";
return -1;
}

if (argc <= 1) { // interactive mode
// We need this dummy signal hander to interrupt getc (and returning
// EINTR), SIG_IGN did not work.
signal(SIGINT, dummy_handler);

// Hook getc of readline.
rl_getc_function = cli_getc;

// Print welcome information.
printf("%s\n", brpc::logo());
printf(
"This command-line tool mimics the look-n-feel of official "
"mysql-cli, as a demostration of brpc's capability of"
" talking to mysql-server. The output and behavior is "
"not exactly same with the official one.\n\n");

for (;;) {
char prompt[128];
snprintf(prompt, sizeof(prompt), "mysql %s> ", FLAGS_server.c_str());
std::unique_ptr<char, Freer> command(readline(prompt));
if (command == NULL || *command == '\0') {
if (g_canceled) {
// No input after the prompt and user pressed Ctrl-C,
// quit the CLI.
return 0;
}
// User entered an empty command by just pressing Enter.
continue;
}
if (g_canceled) {
// User entered sth. and pressed Ctrl-C, start a new prompt.
g_canceled = false;
continue;
}
// Add user's command to history so that it's browse-able by
// UP-key and search-able by Ctrl-R.
add_history(command.get());

if (!strcmp(command.get(), "help")) {
printf("This is a mysql CLI written in brpc.\n");
continue;
}
if (!strcmp(command.get(), "quit")) {
// Although quit is a valid mysql command, it does not make
// too much sense to run it in this CLI, just quit.
return 0;
}
access_mysql(channel, command.get());
}
} else {
std::string command;
command.reserve(argc * 16);
for (int i = 1; i < argc; ++i) {
if (i != 1) {
command.push_back(';');
}
command.append(argv[i]);
}
if (!access_mysql(channel, command.c_str())) {
return -1;
}
}
return 0;
}
Loading
Loading